Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next

Steffen Klassert says:

====================
1) Refactor selftests to use an array of structs in xfrm_fill_key().
   From Gautam Menghani.

2) Drop an unused argument from xfrm_policy_match.
   From Hongbin Wang.

3) Support collect metadata mode for xfrm interfaces.
   From Eyal Birger.

4) Add netlink extack support to xfrm.
   From Sabrina Dubroca.

Please note, there is a merge conflict in:

include/net/dst_metadata.h

between commit:

0a28bfd497 ("net/macsec: Add MACsec skb_metadata_dst Tx Data path support")

from the net-next tree and commit:

5182a5d48c ("net: allow storing xfrm interface metadata in metadata_dst")

from the ipsec-next tree.

Can be solved as done in linux-next.

Please pull or let me know if there are problems.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2022-10-03 07:46:08 +01:00
commit 42e8e6d906
24 changed files with 738 additions and 323 deletions

View File

@ -11,6 +11,7 @@ enum metadata_type {
METADATA_IP_TUNNEL, METADATA_IP_TUNNEL,
METADATA_HW_PORT_MUX, METADATA_HW_PORT_MUX,
METADATA_MACSEC, METADATA_MACSEC,
METADATA_XFRM,
}; };
struct hw_port_info { struct hw_port_info {
@ -22,6 +23,11 @@ struct macsec_info {
sci_t sci; sci_t sci;
}; };
struct xfrm_md_info {
u32 if_id;
int link;
};
struct metadata_dst { struct metadata_dst {
struct dst_entry dst; struct dst_entry dst;
enum metadata_type type; enum metadata_type type;
@ -29,6 +35,7 @@ struct metadata_dst {
struct ip_tunnel_info tun_info; struct ip_tunnel_info tun_info;
struct hw_port_info port_info; struct hw_port_info port_info;
struct macsec_info macsec_info; struct macsec_info macsec_info;
struct xfrm_md_info xfrm_info;
} u; } u;
}; };
@ -60,6 +67,27 @@ skb_tunnel_info(const struct sk_buff *skb)
return NULL; return NULL;
} }
static inline struct xfrm_md_info *lwt_xfrm_info(struct lwtunnel_state *lwt)
{
return (struct xfrm_md_info *)lwt->data;
}
static inline struct xfrm_md_info *skb_xfrm_md_info(const struct sk_buff *skb)
{
struct metadata_dst *md_dst = skb_metadata_dst(skb);
struct dst_entry *dst;
if (md_dst && md_dst->type == METADATA_XFRM)
return &md_dst->u.xfrm_info;
dst = skb_dst(skb);
if (dst && dst->lwtstate &&
dst->lwtstate->type == LWTUNNEL_ENCAP_XFRM)
return lwt_xfrm_info(dst->lwtstate);
return NULL;
}
static inline bool skb_valid_dst(const struct sk_buff *skb) static inline bool skb_valid_dst(const struct sk_buff *skb)
{ {
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb);
@ -92,6 +120,9 @@ static inline int skb_metadata_dst_cmp(const struct sk_buff *skb_a,
case METADATA_MACSEC: case METADATA_MACSEC:
return memcmp(&a->u.macsec_info, &b->u.macsec_info, return memcmp(&a->u.macsec_info, &b->u.macsec_info,
sizeof(a->u.macsec_info)); sizeof(a->u.macsec_info));
case METADATA_XFRM:
return memcmp(&a->u.xfrm_info, &b->u.xfrm_info,
sizeof(a->u.xfrm_info));
default: default:
return 1; return 1;
} }

View File

@ -22,7 +22,7 @@ struct xfrm_state;
int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb); int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb);
int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb); int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb);
void ipcomp_destroy(struct xfrm_state *x); void ipcomp_destroy(struct xfrm_state *x);
int ipcomp_init_state(struct xfrm_state *x); int ipcomp_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack);
static inline struct ip_comp_hdr *ip_comp_hdr(const struct sk_buff *skb) static inline struct ip_comp_hdr *ip_comp_hdr(const struct sk_buff *skb)
{ {

View File

@ -312,9 +312,15 @@ struct km_event {
struct net *net; struct net *net;
}; };
struct xfrm_if_decode_session_result {
struct net *net;
u32 if_id;
};
struct xfrm_if_cb { struct xfrm_if_cb {
struct xfrm_if *(*decode_session)(struct sk_buff *skb, bool (*decode_session)(struct sk_buff *skb,
unsigned short family); unsigned short family,
struct xfrm_if_decode_session_result *res);
}; };
void xfrm_if_register_cb(const struct xfrm_if_cb *ifcb); void xfrm_if_register_cb(const struct xfrm_if_cb *ifcb);
@ -399,7 +405,8 @@ struct xfrm_type {
#define XFRM_TYPE_LOCAL_COADDR 4 #define XFRM_TYPE_LOCAL_COADDR 4
#define XFRM_TYPE_REMOTE_COADDR 8 #define XFRM_TYPE_REMOTE_COADDR 8
int (*init_state)(struct xfrm_state *x); int (*init_state)(struct xfrm_state *x,
struct netlink_ext_ack *extack);
void (*destructor)(struct xfrm_state *); void (*destructor)(struct xfrm_state *);
int (*input)(struct xfrm_state *, struct sk_buff *skb); int (*input)(struct xfrm_state *, struct sk_buff *skb);
int (*output)(struct xfrm_state *, struct sk_buff *pskb); int (*output)(struct xfrm_state *, struct sk_buff *pskb);
@ -985,6 +992,7 @@ void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
struct xfrm_if_parms { struct xfrm_if_parms {
int link; /* ifindex of underlying L2 interface */ int link; /* ifindex of underlying L2 interface */
u32 if_id; /* interface identifyer */ u32 if_id; /* interface identifyer */
bool collect_md;
}; };
struct xfrm_if { struct xfrm_if {
@ -1573,9 +1581,10 @@ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_vali
void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
int xfrm_init_replay(struct xfrm_state *x); int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack);
u32 xfrm_state_mtu(struct xfrm_state *x, int mtu); u32 xfrm_state_mtu(struct xfrm_state *x, int mtu);
int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload); int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload,
struct netlink_ext_ack *extack);
int xfrm_init_state(struct xfrm_state *x); int xfrm_init_state(struct xfrm_state *x);
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type); int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
int xfrm_input_resume(struct sk_buff *skb, int nexthdr); int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
@ -1879,7 +1888,8 @@ void xfrm_dev_resume(struct sk_buff *skb);
void xfrm_dev_backlog(struct softnet_data *sd); void xfrm_dev_backlog(struct softnet_data *sd);
struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again); struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again);
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
struct xfrm_user_offload *xuo); struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack);
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x); bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x);
static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x) static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x)
@ -1942,7 +1952,7 @@ static inline struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_fea
return skb; return skb;
} }
static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo) static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo, struct netlink_ext_ack *extack)
{ {
return 0; return 0;
} }

View File

@ -695,6 +695,7 @@ enum {
IFLA_XFRM_UNSPEC, IFLA_XFRM_UNSPEC,
IFLA_XFRM_LINK, IFLA_XFRM_LINK,
IFLA_XFRM_IF_ID, IFLA_XFRM_IF_ID,
IFLA_XFRM_COLLECT_METADATA,
__IFLA_XFRM_MAX __IFLA_XFRM_MAX
}; };

View File

@ -15,6 +15,7 @@ enum lwtunnel_encap_types {
LWTUNNEL_ENCAP_SEG6_LOCAL, LWTUNNEL_ENCAP_SEG6_LOCAL,
LWTUNNEL_ENCAP_RPL, LWTUNNEL_ENCAP_RPL,
LWTUNNEL_ENCAP_IOAM6, LWTUNNEL_ENCAP_IOAM6,
LWTUNNEL_ENCAP_XFRM,
__LWTUNNEL_ENCAP_MAX, __LWTUNNEL_ENCAP_MAX,
}; };
@ -111,4 +112,13 @@ enum {
#define LWT_BPF_MAX_HEADROOM 256 #define LWT_BPF_MAX_HEADROOM 256
enum {
LWT_XFRM_UNSPEC,
LWT_XFRM_IF_ID,
LWT_XFRM_LINK,
__LWT_XFRM_MAX,
};
#define LWT_XFRM_MAX (__LWT_XFRM_MAX - 1)
#endif /* _UAPI_LWTUNNEL_H_ */ #endif /* _UAPI_LWTUNNEL_H_ */

View File

@ -50,6 +50,7 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
return "IOAM6"; return "IOAM6";
case LWTUNNEL_ENCAP_IP6: case LWTUNNEL_ENCAP_IP6:
case LWTUNNEL_ENCAP_IP: case LWTUNNEL_ENCAP_IP:
case LWTUNNEL_ENCAP_XFRM:
case LWTUNNEL_ENCAP_NONE: case LWTUNNEL_ENCAP_NONE:
case __LWTUNNEL_ENCAP_MAX: case __LWTUNNEL_ENCAP_MAX:
/* should not have got here */ /* should not have got here */

View File

@ -471,30 +471,38 @@ static int ah4_err(struct sk_buff *skb, u32 info)
return 0; return 0;
} }
static int ah_init_state(struct xfrm_state *x) static int ah_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
struct ah_data *ahp = NULL; struct ah_data *ahp = NULL;
struct xfrm_algo_desc *aalg_desc; struct xfrm_algo_desc *aalg_desc;
struct crypto_ahash *ahash; struct crypto_ahash *ahash;
if (!x->aalg) if (!x->aalg) {
NL_SET_ERR_MSG(extack, "AH requires a state with an AUTH algorithm");
goto error; goto error;
}
if (x->encap) if (x->encap) {
NL_SET_ERR_MSG(extack, "AH is not compatible with encapsulation");
goto error; goto error;
}
ahp = kzalloc(sizeof(*ahp), GFP_KERNEL); ahp = kzalloc(sizeof(*ahp), GFP_KERNEL);
if (!ahp) if (!ahp)
return -ENOMEM; return -ENOMEM;
ahash = crypto_alloc_ahash(x->aalg->alg_name, 0, 0); ahash = crypto_alloc_ahash(x->aalg->alg_name, 0, 0);
if (IS_ERR(ahash)) if (IS_ERR(ahash)) {
NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
goto error; goto error;
}
ahp->ahash = ahash; ahp->ahash = ahash;
if (crypto_ahash_setkey(ahash, x->aalg->alg_key, if (crypto_ahash_setkey(ahash, x->aalg->alg_key,
(x->aalg->alg_key_len + 7) / 8)) (x->aalg->alg_key_len + 7) / 8)) {
NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
goto error; goto error;
}
/* /*
* Lookup the algorithm description maintained by xfrm_algo, * Lookup the algorithm description maintained by xfrm_algo,
@ -507,10 +515,7 @@ static int ah_init_state(struct xfrm_state *x)
if (aalg_desc->uinfo.auth.icv_fullbits/8 != if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
crypto_ahash_digestsize(ahash)) { crypto_ahash_digestsize(ahash)) {
pr_info("%s: %s digestsize %u != %u\n", NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
__func__, x->aalg->alg_name,
crypto_ahash_digestsize(ahash),
aalg_desc->uinfo.auth.icv_fullbits / 8);
goto error; goto error;
} }

View File

@ -1008,16 +1008,17 @@ static void esp_destroy(struct xfrm_state *x)
crypto_free_aead(aead); crypto_free_aead(aead);
} }
static int esp_init_aead(struct xfrm_state *x) static int esp_init_aead(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
char aead_name[CRYPTO_MAX_ALG_NAME]; char aead_name[CRYPTO_MAX_ALG_NAME];
struct crypto_aead *aead; struct crypto_aead *aead;
int err; int err;
err = -ENAMETOOLONG;
if (snprintf(aead_name, CRYPTO_MAX_ALG_NAME, "%s(%s)", if (snprintf(aead_name, CRYPTO_MAX_ALG_NAME, "%s(%s)",
x->geniv, x->aead->alg_name) >= CRYPTO_MAX_ALG_NAME) x->geniv, x->aead->alg_name) >= CRYPTO_MAX_ALG_NAME) {
goto error; NL_SET_ERR_MSG(extack, "Algorithm name is too long");
return -ENAMETOOLONG;
}
aead = crypto_alloc_aead(aead_name, 0, 0); aead = crypto_alloc_aead(aead_name, 0, 0);
err = PTR_ERR(aead); err = PTR_ERR(aead);
@ -1035,11 +1036,15 @@ static int esp_init_aead(struct xfrm_state *x)
if (err) if (err)
goto error; goto error;
return 0;
error: error:
NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
return err; return err;
} }
static int esp_init_authenc(struct xfrm_state *x) static int esp_init_authenc(struct xfrm_state *x,
struct netlink_ext_ack *extack)
{ {
struct crypto_aead *aead; struct crypto_aead *aead;
struct crypto_authenc_key_param *param; struct crypto_authenc_key_param *param;
@ -1050,10 +1055,6 @@ static int esp_init_authenc(struct xfrm_state *x)
unsigned int keylen; unsigned int keylen;
int err; int err;
err = -EINVAL;
if (!x->ealg)
goto error;
err = -ENAMETOOLONG; err = -ENAMETOOLONG;
if ((x->props.flags & XFRM_STATE_ESN)) { if ((x->props.flags & XFRM_STATE_ESN)) {
@ -1062,22 +1063,28 @@ static int esp_init_authenc(struct xfrm_state *x)
x->geniv ?: "", x->geniv ? "(" : "", x->geniv ?: "", x->geniv ? "(" : "",
x->aalg ? x->aalg->alg_name : "digest_null", x->aalg ? x->aalg->alg_name : "digest_null",
x->ealg->alg_name, x->ealg->alg_name,
x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME) x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME) {
NL_SET_ERR_MSG(extack, "Algorithm name is too long");
goto error; goto error;
}
} else { } else {
if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME, if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME,
"%s%sauthenc(%s,%s)%s", "%s%sauthenc(%s,%s)%s",
x->geniv ?: "", x->geniv ? "(" : "", x->geniv ?: "", x->geniv ? "(" : "",
x->aalg ? x->aalg->alg_name : "digest_null", x->aalg ? x->aalg->alg_name : "digest_null",
x->ealg->alg_name, x->ealg->alg_name,
x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME) x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME) {
NL_SET_ERR_MSG(extack, "Algorithm name is too long");
goto error; goto error;
}
} }
aead = crypto_alloc_aead(authenc_name, 0, 0); aead = crypto_alloc_aead(authenc_name, 0, 0);
err = PTR_ERR(aead); err = PTR_ERR(aead);
if (IS_ERR(aead)) if (IS_ERR(aead)) {
NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
goto error; goto error;
}
x->data = aead; x->data = aead;
@ -1107,17 +1114,16 @@ static int esp_init_authenc(struct xfrm_state *x)
err = -EINVAL; err = -EINVAL;
if (aalg_desc->uinfo.auth.icv_fullbits / 8 != if (aalg_desc->uinfo.auth.icv_fullbits / 8 !=
crypto_aead_authsize(aead)) { crypto_aead_authsize(aead)) {
pr_info("ESP: %s digestsize %u != %u\n", NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
x->aalg->alg_name,
crypto_aead_authsize(aead),
aalg_desc->uinfo.auth.icv_fullbits / 8);
goto free_key; goto free_key;
} }
err = crypto_aead_setauthsize( err = crypto_aead_setauthsize(
aead, x->aalg->alg_trunc_len / 8); aead, x->aalg->alg_trunc_len / 8);
if (err) if (err) {
NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
goto free_key; goto free_key;
}
} }
param->enckeylen = cpu_to_be32((x->ealg->alg_key_len + 7) / 8); param->enckeylen = cpu_to_be32((x->ealg->alg_key_len + 7) / 8);
@ -1132,7 +1138,7 @@ error:
return err; return err;
} }
static int esp_init_state(struct xfrm_state *x) static int esp_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
struct crypto_aead *aead; struct crypto_aead *aead;
u32 align; u32 align;
@ -1140,10 +1146,14 @@ static int esp_init_state(struct xfrm_state *x)
x->data = NULL; x->data = NULL;
if (x->aead) if (x->aead) {
err = esp_init_aead(x); err = esp_init_aead(x, extack);
else } else if (x->ealg) {
err = esp_init_authenc(x); err = esp_init_authenc(x, extack);
} else {
NL_SET_ERR_MSG(extack, "ESP: AEAD or CRYPT must be provided");
err = -EINVAL;
}
if (err) if (err)
goto error; goto error;
@ -1161,6 +1171,7 @@ static int esp_init_state(struct xfrm_state *x)
switch (encap->encap_type) { switch (encap->encap_type) {
default: default:
NL_SET_ERR_MSG(extack, "Unsupported encapsulation type for ESP");
err = -EINVAL; err = -EINVAL;
goto error; goto error;
case UDP_ENCAP_ESPINUDP: case UDP_ENCAP_ESPINUDP:

View File

@ -117,7 +117,8 @@ out:
return err; return err;
} }
static int ipcomp4_init_state(struct xfrm_state *x) static int ipcomp4_init_state(struct xfrm_state *x,
struct netlink_ext_ack *extack)
{ {
int err = -EINVAL; int err = -EINVAL;
@ -129,17 +130,20 @@ static int ipcomp4_init_state(struct xfrm_state *x)
x->props.header_len += sizeof(struct iphdr); x->props.header_len += sizeof(struct iphdr);
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Unsupported XFRM mode for IPcomp");
goto out; goto out;
} }
err = ipcomp_init_state(x); err = ipcomp_init_state(x, extack);
if (err) if (err)
goto out; goto out;
if (x->props.mode == XFRM_MODE_TUNNEL) { if (x->props.mode == XFRM_MODE_TUNNEL) {
err = ipcomp_tunnel_attach(x); err = ipcomp_tunnel_attach(x);
if (err) if (err) {
NL_SET_ERR_MSG(extack, "Kernel error: failed to initialize the associated state");
goto out; goto out;
}
} }
err = 0; err = 0;

View File

@ -22,13 +22,17 @@ static int ipip_xfrm_rcv(struct xfrm_state *x, struct sk_buff *skb)
return ip_hdr(skb)->protocol; return ip_hdr(skb)->protocol;
} }
static int ipip_init_state(struct xfrm_state *x) static int ipip_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
if (x->props.mode != XFRM_MODE_TUNNEL) if (x->props.mode != XFRM_MODE_TUNNEL) {
NL_SET_ERR_MSG(extack, "IPv4 tunnel can only be used with tunnel mode");
return -EINVAL; return -EINVAL;
}
if (x->encap) if (x->encap) {
NL_SET_ERR_MSG(extack, "IPv4 tunnel is not compatible with encapsulation");
return -EINVAL; return -EINVAL;
}
x->props.header_len = sizeof(struct iphdr); x->props.header_len = sizeof(struct iphdr);

View File

@ -666,30 +666,38 @@ static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return 0; return 0;
} }
static int ah6_init_state(struct xfrm_state *x) static int ah6_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
struct ah_data *ahp = NULL; struct ah_data *ahp = NULL;
struct xfrm_algo_desc *aalg_desc; struct xfrm_algo_desc *aalg_desc;
struct crypto_ahash *ahash; struct crypto_ahash *ahash;
if (!x->aalg) if (!x->aalg) {
NL_SET_ERR_MSG(extack, "AH requires a state with an AUTH algorithm");
goto error; goto error;
}
if (x->encap) if (x->encap) {
NL_SET_ERR_MSG(extack, "AH is not compatible with encapsulation");
goto error; goto error;
}
ahp = kzalloc(sizeof(*ahp), GFP_KERNEL); ahp = kzalloc(sizeof(*ahp), GFP_KERNEL);
if (!ahp) if (!ahp)
return -ENOMEM; return -ENOMEM;
ahash = crypto_alloc_ahash(x->aalg->alg_name, 0, 0); ahash = crypto_alloc_ahash(x->aalg->alg_name, 0, 0);
if (IS_ERR(ahash)) if (IS_ERR(ahash)) {
NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
goto error; goto error;
}
ahp->ahash = ahash; ahp->ahash = ahash;
if (crypto_ahash_setkey(ahash, x->aalg->alg_key, if (crypto_ahash_setkey(ahash, x->aalg->alg_key,
(x->aalg->alg_key_len + 7) / 8)) (x->aalg->alg_key_len + 7) / 8)) {
NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
goto error; goto error;
}
/* /*
* Lookup the algorithm description maintained by xfrm_algo, * Lookup the algorithm description maintained by xfrm_algo,
@ -702,9 +710,7 @@ static int ah6_init_state(struct xfrm_state *x)
if (aalg_desc->uinfo.auth.icv_fullbits/8 != if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
crypto_ahash_digestsize(ahash)) { crypto_ahash_digestsize(ahash)) {
pr_info("AH: %s digestsize %u != %u\n", NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
x->aalg->alg_name, crypto_ahash_digestsize(ahash),
aalg_desc->uinfo.auth.icv_fullbits/8);
goto error; goto error;
} }
@ -721,6 +727,7 @@ static int ah6_init_state(struct xfrm_state *x)
x->props.header_len += sizeof(struct ipv6hdr); x->props.header_len += sizeof(struct ipv6hdr);
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Invalid mode requested for AH, must be one of TRANSPORT, TUNNEL, BEET");
goto error; goto error;
} }
x->data = ahp; x->data = ahp;

View File

@ -1051,16 +1051,17 @@ static void esp6_destroy(struct xfrm_state *x)
crypto_free_aead(aead); crypto_free_aead(aead);
} }
static int esp_init_aead(struct xfrm_state *x) static int esp_init_aead(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
char aead_name[CRYPTO_MAX_ALG_NAME]; char aead_name[CRYPTO_MAX_ALG_NAME];
struct crypto_aead *aead; struct crypto_aead *aead;
int err; int err;
err = -ENAMETOOLONG;
if (snprintf(aead_name, CRYPTO_MAX_ALG_NAME, "%s(%s)", if (snprintf(aead_name, CRYPTO_MAX_ALG_NAME, "%s(%s)",
x->geniv, x->aead->alg_name) >= CRYPTO_MAX_ALG_NAME) x->geniv, x->aead->alg_name) >= CRYPTO_MAX_ALG_NAME) {
goto error; NL_SET_ERR_MSG(extack, "Algorithm name is too long");
return -ENAMETOOLONG;
}
aead = crypto_alloc_aead(aead_name, 0, 0); aead = crypto_alloc_aead(aead_name, 0, 0);
err = PTR_ERR(aead); err = PTR_ERR(aead);
@ -1078,11 +1079,15 @@ static int esp_init_aead(struct xfrm_state *x)
if (err) if (err)
goto error; goto error;
return 0;
error: error:
NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
return err; return err;
} }
static int esp_init_authenc(struct xfrm_state *x) static int esp_init_authenc(struct xfrm_state *x,
struct netlink_ext_ack *extack)
{ {
struct crypto_aead *aead; struct crypto_aead *aead;
struct crypto_authenc_key_param *param; struct crypto_authenc_key_param *param;
@ -1093,10 +1098,6 @@ static int esp_init_authenc(struct xfrm_state *x)
unsigned int keylen; unsigned int keylen;
int err; int err;
err = -EINVAL;
if (!x->ealg)
goto error;
err = -ENAMETOOLONG; err = -ENAMETOOLONG;
if ((x->props.flags & XFRM_STATE_ESN)) { if ((x->props.flags & XFRM_STATE_ESN)) {
@ -1105,22 +1106,28 @@ static int esp_init_authenc(struct xfrm_state *x)
x->geniv ?: "", x->geniv ? "(" : "", x->geniv ?: "", x->geniv ? "(" : "",
x->aalg ? x->aalg->alg_name : "digest_null", x->aalg ? x->aalg->alg_name : "digest_null",
x->ealg->alg_name, x->ealg->alg_name,
x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME) x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME) {
NL_SET_ERR_MSG(extack, "Algorithm name is too long");
goto error; goto error;
}
} else { } else {
if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME, if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME,
"%s%sauthenc(%s,%s)%s", "%s%sauthenc(%s,%s)%s",
x->geniv ?: "", x->geniv ? "(" : "", x->geniv ?: "", x->geniv ? "(" : "",
x->aalg ? x->aalg->alg_name : "digest_null", x->aalg ? x->aalg->alg_name : "digest_null",
x->ealg->alg_name, x->ealg->alg_name,
x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME) x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME) {
NL_SET_ERR_MSG(extack, "Algorithm name is too long");
goto error; goto error;
}
} }
aead = crypto_alloc_aead(authenc_name, 0, 0); aead = crypto_alloc_aead(authenc_name, 0, 0);
err = PTR_ERR(aead); err = PTR_ERR(aead);
if (IS_ERR(aead)) if (IS_ERR(aead)) {
NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
goto error; goto error;
}
x->data = aead; x->data = aead;
@ -1150,17 +1157,16 @@ static int esp_init_authenc(struct xfrm_state *x)
err = -EINVAL; err = -EINVAL;
if (aalg_desc->uinfo.auth.icv_fullbits / 8 != if (aalg_desc->uinfo.auth.icv_fullbits / 8 !=
crypto_aead_authsize(aead)) { crypto_aead_authsize(aead)) {
pr_info("ESP: %s digestsize %u != %u\n", NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
x->aalg->alg_name,
crypto_aead_authsize(aead),
aalg_desc->uinfo.auth.icv_fullbits / 8);
goto free_key; goto free_key;
} }
err = crypto_aead_setauthsize( err = crypto_aead_setauthsize(
aead, x->aalg->alg_trunc_len / 8); aead, x->aalg->alg_trunc_len / 8);
if (err) if (err) {
NL_SET_ERR_MSG(extack, "Kernel was unable to initialize cryptographic operations");
goto free_key; goto free_key;
}
} }
param->enckeylen = cpu_to_be32((x->ealg->alg_key_len + 7) / 8); param->enckeylen = cpu_to_be32((x->ealg->alg_key_len + 7) / 8);
@ -1175,7 +1181,7 @@ error:
return err; return err;
} }
static int esp6_init_state(struct xfrm_state *x) static int esp6_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
struct crypto_aead *aead; struct crypto_aead *aead;
u32 align; u32 align;
@ -1183,10 +1189,14 @@ static int esp6_init_state(struct xfrm_state *x)
x->data = NULL; x->data = NULL;
if (x->aead) if (x->aead) {
err = esp_init_aead(x); err = esp_init_aead(x, extack);
else } else if (x->ealg) {
err = esp_init_authenc(x); err = esp_init_authenc(x, extack);
} else {
NL_SET_ERR_MSG(extack, "ESP: AEAD or CRYPT must be provided");
err = -EINVAL;
}
if (err) if (err)
goto error; goto error;
@ -1214,6 +1224,7 @@ static int esp6_init_state(struct xfrm_state *x)
switch (encap->encap_type) { switch (encap->encap_type) {
default: default:
NL_SET_ERR_MSG(extack, "Unsupported encapsulation type for ESP");
err = -EINVAL; err = -EINVAL;
goto error; goto error;
case UDP_ENCAP_ESPINUDP: case UDP_ENCAP_ESPINUDP:

View File

@ -136,7 +136,8 @@ out:
return err; return err;
} }
static int ipcomp6_init_state(struct xfrm_state *x) static int ipcomp6_init_state(struct xfrm_state *x,
struct netlink_ext_ack *extack)
{ {
int err = -EINVAL; int err = -EINVAL;
@ -148,17 +149,20 @@ static int ipcomp6_init_state(struct xfrm_state *x)
x->props.header_len += sizeof(struct ipv6hdr); x->props.header_len += sizeof(struct ipv6hdr);
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Unsupported XFRM mode for IPcomp");
goto out; goto out;
} }
err = ipcomp_init_state(x); err = ipcomp_init_state(x, extack);
if (err) if (err)
goto out; goto out;
if (x->props.mode == XFRM_MODE_TUNNEL) { if (x->props.mode == XFRM_MODE_TUNNEL) {
err = ipcomp6_tunnel_attach(x); err = ipcomp6_tunnel_attach(x);
if (err) if (err) {
NL_SET_ERR_MSG(extack, "Kernel error: failed to initialize the associated state");
goto out; goto out;
}
} }
err = 0; err = 0;

View File

@ -247,15 +247,14 @@ static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb,
return err; return err;
} }
static int mip6_destopt_init_state(struct xfrm_state *x) static int mip6_destopt_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
if (x->id.spi) { if (x->id.spi) {
pr_info("%s: spi is not 0: %u\n", __func__, x->id.spi); NL_SET_ERR_MSG(extack, "SPI must be 0");
return -EINVAL; return -EINVAL;
} }
if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) { if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) {
pr_info("%s: state's mode is not %u: %u\n", NL_SET_ERR_MSG(extack, "XFRM mode must be XFRM_MODE_ROUTEOPTIMIZATION");
__func__, XFRM_MODE_ROUTEOPTIMIZATION, x->props.mode);
return -EINVAL; return -EINVAL;
} }
@ -333,15 +332,14 @@ static int mip6_rthdr_output(struct xfrm_state *x, struct sk_buff *skb)
return 0; return 0;
} }
static int mip6_rthdr_init_state(struct xfrm_state *x) static int mip6_rthdr_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
if (x->id.spi) { if (x->id.spi) {
pr_info("%s: spi is not 0: %u\n", __func__, x->id.spi); NL_SET_ERR_MSG(extack, "SPI must be 0");
return -EINVAL; return -EINVAL;
} }
if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) { if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) {
pr_info("%s: state's mode is not %u: %u\n", NL_SET_ERR_MSG(extack, "XFRM mode must be XFRM_MODE_ROUTEOPTIMIZATION");
__func__, XFRM_MODE_ROUTEOPTIMIZATION, x->props.mode);
return -EINVAL; return -EINVAL;
} }

View File

@ -270,13 +270,17 @@ static int xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return 0; return 0;
} }
static int xfrm6_tunnel_init_state(struct xfrm_state *x) static int xfrm6_tunnel_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
if (x->props.mode != XFRM_MODE_TUNNEL) if (x->props.mode != XFRM_MODE_TUNNEL) {
NL_SET_ERR_MSG(extack, "IPv6 tunnel can only be used with tunnel mode");
return -EINVAL; return -EINVAL;
}
if (x->encap) if (x->encap) {
NL_SET_ERR_MSG(extack, "IPv6 tunnel is not compatible with encapsulation");
return -EINVAL; return -EINVAL;
}
x->props.header_len = sizeof(struct ipv6hdr); x->props.header_len = sizeof(struct ipv6hdr);

View File

@ -207,7 +207,8 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
EXPORT_SYMBOL_GPL(validate_xmit_xfrm); EXPORT_SYMBOL_GPL(validate_xmit_xfrm);
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
struct xfrm_user_offload *xuo) struct xfrm_user_offload *xuo,
struct netlink_ext_ack *extack)
{ {
int err; int err;
struct dst_entry *dst; struct dst_entry *dst;
@ -216,15 +217,21 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
xfrm_address_t *saddr; xfrm_address_t *saddr;
xfrm_address_t *daddr; xfrm_address_t *daddr;
if (!x->type_offload) if (!x->type_offload) {
NL_SET_ERR_MSG(extack, "Type doesn't support offload");
return -EINVAL; return -EINVAL;
}
/* We don't yet support UDP encapsulation and TFC padding. */ /* We don't yet support UDP encapsulation and TFC padding. */
if (x->encap || x->tfcpad) if (x->encap || x->tfcpad) {
NL_SET_ERR_MSG(extack, "Encapsulation and TFC padding can't be offloaded");
return -EINVAL; return -EINVAL;
}
if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND)) if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND)) {
NL_SET_ERR_MSG(extack, "Unrecognized flags in offload request");
return -EINVAL; return -EINVAL;
}
dev = dev_get_by_index(net, xuo->ifindex); dev = dev_get_by_index(net, xuo->ifindex);
if (!dev) { if (!dev) {
@ -256,6 +263,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
if (x->props.flags & XFRM_STATE_ESN && if (x->props.flags & XFRM_STATE_ESN &&
!dev->xfrmdev_ops->xdo_dev_state_advance_esn) { !dev->xfrmdev_ops->xdo_dev_state_advance_esn) {
NL_SET_ERR_MSG(extack, "Device doesn't support offload with ESN");
xso->dev = NULL; xso->dev = NULL;
dev_put(dev); dev_put(dev);
return -EINVAL; return -EINVAL;
@ -277,8 +285,10 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
xso->real_dev = NULL; xso->real_dev = NULL;
netdev_put(dev, &xso->dev_tracker); netdev_put(dev, &xso->dev_tracker);
if (err != -EOPNOTSUPP) if (err != -EOPNOTSUPP) {
NL_SET_ERR_MSG(extack, "Device failed to offload this state");
return err; return err;
}
} }
return 0; return 0;

View File

@ -20,6 +20,7 @@
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/ip_tunnels.h> #include <net/ip_tunnels.h>
#include <net/ip6_tunnel.h> #include <net/ip6_tunnel.h>
#include <net/dst_metadata.h>
#include "xfrm_inout.h" #include "xfrm_inout.h"
@ -719,7 +720,8 @@ resume:
sp = skb_sec_path(skb); sp = skb_sec_path(skb);
if (sp) if (sp)
sp->olen = 0; sp->olen = 0;
skb_dst_drop(skb); if (skb_valid_dst(skb))
skb_dst_drop(skb);
gro_cells_receive(&gro_cells, skb); gro_cells_receive(&gro_cells, skb);
return 0; return 0;
} else { } else {
@ -737,7 +739,8 @@ resume:
sp = skb_sec_path(skb); sp = skb_sec_path(skb);
if (sp) if (sp)
sp->olen = 0; sp->olen = 0;
skb_dst_drop(skb); if (skb_valid_dst(skb))
skb_dst_drop(skb);
gro_cells_receive(&gro_cells, skb); gro_cells_receive(&gro_cells, skb);
return err; return err;
} }

View File

@ -41,6 +41,7 @@
#include <net/addrconf.h> #include <net/addrconf.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/dst_metadata.h>
#include <net/netns/generic.h> #include <net/netns/generic.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
@ -56,6 +57,89 @@ static const struct net_device_ops xfrmi_netdev_ops;
struct xfrmi_net { struct xfrmi_net {
/* lists for storing interfaces in use */ /* lists for storing interfaces in use */
struct xfrm_if __rcu *xfrmi[XFRMI_HASH_SIZE]; struct xfrm_if __rcu *xfrmi[XFRMI_HASH_SIZE];
struct xfrm_if __rcu *collect_md_xfrmi;
};
static const struct nla_policy xfrm_lwt_policy[LWT_XFRM_MAX + 1] = {
[LWT_XFRM_IF_ID] = NLA_POLICY_MIN(NLA_U32, 1),
[LWT_XFRM_LINK] = NLA_POLICY_MIN(NLA_U32, 1),
};
static void xfrmi_destroy_state(struct lwtunnel_state *lwt)
{
}
static int xfrmi_build_state(struct net *net, struct nlattr *nla,
unsigned int family, const void *cfg,
struct lwtunnel_state **ts,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[LWT_XFRM_MAX + 1];
struct lwtunnel_state *new_state;
struct xfrm_md_info *info;
int ret;
ret = nla_parse_nested(tb, LWT_XFRM_MAX, nla, xfrm_lwt_policy, extack);
if (ret < 0)
return ret;
if (!tb[LWT_XFRM_IF_ID]) {
NL_SET_ERR_MSG(extack, "if_id must be set");
return -EINVAL;
}
new_state = lwtunnel_state_alloc(sizeof(*info));
if (!new_state) {
NL_SET_ERR_MSG(extack, "failed to create encap info");
return -ENOMEM;
}
new_state->type = LWTUNNEL_ENCAP_XFRM;
info = lwt_xfrm_info(new_state);
info->if_id = nla_get_u32(tb[LWT_XFRM_IF_ID]);
if (tb[LWT_XFRM_LINK])
info->link = nla_get_u32(tb[LWT_XFRM_LINK]);
*ts = new_state;
return 0;
}
static int xfrmi_fill_encap_info(struct sk_buff *skb,
struct lwtunnel_state *lwt)
{
struct xfrm_md_info *info = lwt_xfrm_info(lwt);
if (nla_put_u32(skb, LWT_XFRM_IF_ID, info->if_id) ||
(info->link && nla_put_u32(skb, LWT_XFRM_LINK, info->link)))
return -EMSGSIZE;
return 0;
}
static int xfrmi_encap_nlsize(struct lwtunnel_state *lwtstate)
{
return nla_total_size(sizeof(u32)) + /* LWT_XFRM_IF_ID */
nla_total_size(sizeof(u32)); /* LWT_XFRM_LINK */
}
static int xfrmi_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
{
struct xfrm_md_info *a_info = lwt_xfrm_info(a);
struct xfrm_md_info *b_info = lwt_xfrm_info(b);
return memcmp(a_info, b_info, sizeof(*a_info));
}
static const struct lwtunnel_encap_ops xfrmi_encap_ops = {
.build_state = xfrmi_build_state,
.destroy_state = xfrmi_destroy_state,
.fill_encap = xfrmi_fill_encap_info,
.get_encap_size = xfrmi_encap_nlsize,
.cmp_encap = xfrmi_encap_cmp,
.owner = THIS_MODULE,
}; };
#define for_each_xfrmi_rcu(start, xi) \ #define for_each_xfrmi_rcu(start, xi) \
@ -77,17 +161,23 @@ static struct xfrm_if *xfrmi_lookup(struct net *net, struct xfrm_state *x)
return xi; return xi;
} }
xi = rcu_dereference(xfrmn->collect_md_xfrmi);
if (xi && (xi->dev->flags & IFF_UP))
return xi;
return NULL; return NULL;
} }
static struct xfrm_if *xfrmi_decode_session(struct sk_buff *skb, static bool xfrmi_decode_session(struct sk_buff *skb,
unsigned short family) unsigned short family,
struct xfrm_if_decode_session_result *res)
{ {
struct net_device *dev; struct net_device *dev;
struct xfrm_if *xi;
int ifindex = 0; int ifindex = 0;
if (!secpath_exists(skb) || !skb->dev) if (!secpath_exists(skb) || !skb->dev)
return NULL; return false;
switch (family) { switch (family) {
case AF_INET6: case AF_INET6:
@ -107,11 +197,18 @@ static struct xfrm_if *xfrmi_decode_session(struct sk_buff *skb,
} }
if (!dev || !(dev->flags & IFF_UP)) if (!dev || !(dev->flags & IFF_UP))
return NULL; return false;
if (dev->netdev_ops != &xfrmi_netdev_ops) if (dev->netdev_ops != &xfrmi_netdev_ops)
return NULL; return false;
return netdev_priv(dev); xi = netdev_priv(dev);
res->net = xi->net;
if (xi->p.collect_md)
res->if_id = xfrm_input_state(skb)->if_id;
else
res->if_id = xi->p.if_id;
return true;
} }
static void xfrmi_link(struct xfrmi_net *xfrmn, struct xfrm_if *xi) static void xfrmi_link(struct xfrmi_net *xfrmn, struct xfrm_if *xi)
@ -157,7 +254,10 @@ static int xfrmi_create(struct net_device *dev)
if (err < 0) if (err < 0)
goto out; goto out;
xfrmi_link(xfrmn, xi); if (xi->p.collect_md)
rcu_assign_pointer(xfrmn->collect_md_xfrmi, xi);
else
xfrmi_link(xfrmn, xi);
return 0; return 0;
@ -185,7 +285,10 @@ static void xfrmi_dev_uninit(struct net_device *dev)
struct xfrm_if *xi = netdev_priv(dev); struct xfrm_if *xi = netdev_priv(dev);
struct xfrmi_net *xfrmn = net_generic(xi->net, xfrmi_net_id); struct xfrmi_net *xfrmn = net_generic(xi->net, xfrmi_net_id);
xfrmi_unlink(xfrmn, xi); if (xi->p.collect_md)
RCU_INIT_POINTER(xfrmn->collect_md_xfrmi, NULL);
else
xfrmi_unlink(xfrmn, xi);
} }
static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet) static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet)
@ -214,6 +317,7 @@ static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
struct xfrm_state *x; struct xfrm_state *x;
struct xfrm_if *xi; struct xfrm_if *xi;
bool xnet; bool xnet;
int link;
if (err && !secpath_exists(skb)) if (err && !secpath_exists(skb))
return 0; return 0;
@ -224,6 +328,7 @@ static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
if (!xi) if (!xi)
return 1; return 1;
link = skb->dev->ifindex;
dev = xi->dev; dev = xi->dev;
skb->dev = dev; skb->dev = dev;
@ -254,6 +359,17 @@ static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
} }
xfrmi_scrub_packet(skb, xnet); xfrmi_scrub_packet(skb, xnet);
if (xi->p.collect_md) {
struct metadata_dst *md_dst;
md_dst = metadata_dst_alloc(0, METADATA_XFRM, GFP_ATOMIC);
if (!md_dst)
return -ENOMEM;
md_dst->u.xfrm_info.if_id = x->if_id;
md_dst->u.xfrm_info.link = link;
skb_dst_set(skb, (struct dst_entry *)md_dst);
}
dev_sw_netstats_rx_add(dev, skb->len); dev_sw_netstats_rx_add(dev, skb->len);
return 0; return 0;
@ -269,10 +385,23 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
struct net_device *tdev; struct net_device *tdev;
struct xfrm_state *x; struct xfrm_state *x;
int err = -1; int err = -1;
u32 if_id;
int mtu; int mtu;
if (xi->p.collect_md) {
struct xfrm_md_info *md_info = skb_xfrm_md_info(skb);
if (unlikely(!md_info))
return -EINVAL;
if_id = md_info->if_id;
fl->flowi_oif = md_info->link;
} else {
if_id = xi->p.if_id;
}
dst_hold(dst); dst_hold(dst);
dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, xi->p.if_id); dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, if_id);
if (IS_ERR(dst)) { if (IS_ERR(dst)) {
err = PTR_ERR(dst); err = PTR_ERR(dst);
dst = NULL; dst = NULL;
@ -283,7 +412,7 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
if (!x) if (!x)
goto tx_err_link_failure; goto tx_err_link_failure;
if (x->if_id != xi->p.if_id) if (x->if_id != if_id)
goto tx_err_link_failure; goto tx_err_link_failure;
tdev = dst->dev; tdev = dst->dev;
@ -633,6 +762,9 @@ static void xfrmi_netlink_parms(struct nlattr *data[],
if (data[IFLA_XFRM_IF_ID]) if (data[IFLA_XFRM_IF_ID])
parms->if_id = nla_get_u32(data[IFLA_XFRM_IF_ID]); parms->if_id = nla_get_u32(data[IFLA_XFRM_IF_ID]);
if (data[IFLA_XFRM_COLLECT_METADATA])
parms->collect_md = true;
} }
static int xfrmi_newlink(struct net *src_net, struct net_device *dev, static int xfrmi_newlink(struct net *src_net, struct net_device *dev,
@ -645,14 +777,27 @@ static int xfrmi_newlink(struct net *src_net, struct net_device *dev,
int err; int err;
xfrmi_netlink_parms(data, &p); xfrmi_netlink_parms(data, &p);
if (!p.if_id) { if (p.collect_md) {
NL_SET_ERR_MSG(extack, "if_id must be non zero"); struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
return -EINVAL;
}
xi = xfrmi_locate(net, &p); if (p.link || p.if_id) {
if (xi) NL_SET_ERR_MSG(extack, "link and if_id must be zero");
return -EEXIST; return -EINVAL;
}
if (rtnl_dereference(xfrmn->collect_md_xfrmi))
return -EEXIST;
} else {
if (!p.if_id) {
NL_SET_ERR_MSG(extack, "if_id must be non zero");
return -EINVAL;
}
xi = xfrmi_locate(net, &p);
if (xi)
return -EEXIST;
}
xi = netdev_priv(dev); xi = netdev_priv(dev);
xi->p = p; xi->p = p;
@ -682,12 +827,22 @@ static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[],
return -EINVAL; return -EINVAL;
} }
if (p.collect_md) {
NL_SET_ERR_MSG(extack, "collect_md can't be changed");
return -EINVAL;
}
xi = xfrmi_locate(net, &p); xi = xfrmi_locate(net, &p);
if (!xi) { if (!xi) {
xi = netdev_priv(dev); xi = netdev_priv(dev);
} else { } else {
if (xi->dev != dev) if (xi->dev != dev)
return -EEXIST; return -EEXIST;
if (xi->p.collect_md) {
NL_SET_ERR_MSG(extack,
"device can't be changed to collect_md");
return -EINVAL;
}
} }
return xfrmi_update(xi, &p); return xfrmi_update(xi, &p);
@ -700,6 +855,8 @@ static size_t xfrmi_get_size(const struct net_device *dev)
nla_total_size(4) + nla_total_size(4) +
/* IFLA_XFRM_IF_ID */ /* IFLA_XFRM_IF_ID */
nla_total_size(4) + nla_total_size(4) +
/* IFLA_XFRM_COLLECT_METADATA */
nla_total_size(0) +
0; 0;
} }
@ -709,7 +866,8 @@ static int xfrmi_fill_info(struct sk_buff *skb, const struct net_device *dev)
struct xfrm_if_parms *parm = &xi->p; struct xfrm_if_parms *parm = &xi->p;
if (nla_put_u32(skb, IFLA_XFRM_LINK, parm->link) || if (nla_put_u32(skb, IFLA_XFRM_LINK, parm->link) ||
nla_put_u32(skb, IFLA_XFRM_IF_ID, parm->if_id)) nla_put_u32(skb, IFLA_XFRM_IF_ID, parm->if_id) ||
(xi->p.collect_md && nla_put_flag(skb, IFLA_XFRM_COLLECT_METADATA)))
goto nla_put_failure; goto nla_put_failure;
return 0; return 0;
@ -725,8 +883,10 @@ static struct net *xfrmi_get_link_net(const struct net_device *dev)
} }
static const struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = { static const struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = {
[IFLA_XFRM_LINK] = { .type = NLA_U32 }, [IFLA_XFRM_UNSPEC] = { .strict_start_type = IFLA_XFRM_COLLECT_METADATA },
[IFLA_XFRM_IF_ID] = { .type = NLA_U32 }, [IFLA_XFRM_LINK] = { .type = NLA_U32 },
[IFLA_XFRM_IF_ID] = { .type = NLA_U32 },
[IFLA_XFRM_COLLECT_METADATA] = { .type = NLA_FLAG },
}; };
static struct rtnl_link_ops xfrmi_link_ops __read_mostly = { static struct rtnl_link_ops xfrmi_link_ops __read_mostly = {
@ -762,6 +922,9 @@ static void __net_exit xfrmi_exit_batch_net(struct list_head *net_exit_list)
xip = &xi->next) xip = &xi->next)
unregister_netdevice_queue(xi->dev, &list); unregister_netdevice_queue(xi->dev, &list);
} }
xi = rtnl_dereference(xfrmn->collect_md_xfrmi);
if (xi)
unregister_netdevice_queue(xi->dev, &list);
} }
unregister_netdevice_many(&list); unregister_netdevice_many(&list);
rtnl_unlock(); rtnl_unlock();
@ -999,6 +1162,8 @@ static int __init xfrmi_init(void)
if (err < 0) if (err < 0)
goto rtnl_link_failed; goto rtnl_link_failed;
lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM);
xfrm_if_register_cb(&xfrm_if_cb); xfrm_if_register_cb(&xfrm_if_cb);
return err; return err;
@ -1017,6 +1182,7 @@ pernet_dev_failed:
static void __exit xfrmi_fini(void) static void __exit xfrmi_fini(void)
{ {
xfrm_if_unregister_cb(); xfrm_if_unregister_cb();
lwtunnel_encap_del_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM);
rtnl_link_unregister(&xfrmi_link_ops); rtnl_link_unregister(&xfrmi_link_ops);
xfrmi4_fini(); xfrmi4_fini();
xfrmi6_fini(); xfrmi6_fini();

View File

@ -325,18 +325,22 @@ void ipcomp_destroy(struct xfrm_state *x)
} }
EXPORT_SYMBOL_GPL(ipcomp_destroy); EXPORT_SYMBOL_GPL(ipcomp_destroy);
int ipcomp_init_state(struct xfrm_state *x) int ipcomp_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
int err; int err;
struct ipcomp_data *ipcd; struct ipcomp_data *ipcd;
struct xfrm_algo_desc *calg_desc; struct xfrm_algo_desc *calg_desc;
err = -EINVAL; err = -EINVAL;
if (!x->calg) if (!x->calg) {
NL_SET_ERR_MSG(extack, "Missing required compression algorithm");
goto out; goto out;
}
if (x->encap) if (x->encap) {
NL_SET_ERR_MSG(extack, "IPComp is not compatible with encapsulation");
goto out; goto out;
}
err = -ENOMEM; err = -ENOMEM;
ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL); ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL);

View File

@ -1889,7 +1889,7 @@ EXPORT_SYMBOL(xfrm_policy_walk_done);
*/ */
static int xfrm_policy_match(const struct xfrm_policy *pol, static int xfrm_policy_match(const struct xfrm_policy *pol,
const struct flowi *fl, const struct flowi *fl,
u8 type, u16 family, int dir, u32 if_id) u8 type, u16 family, u32 if_id)
{ {
const struct xfrm_selector *sel = &pol->selector; const struct xfrm_selector *sel = &pol->selector;
int ret = -ESRCH; int ret = -ESRCH;
@ -2014,7 +2014,7 @@ static struct xfrm_policy *
__xfrm_policy_eval_candidates(struct hlist_head *chain, __xfrm_policy_eval_candidates(struct hlist_head *chain,
struct xfrm_policy *prefer, struct xfrm_policy *prefer,
const struct flowi *fl, const struct flowi *fl,
u8 type, u16 family, int dir, u32 if_id) u8 type, u16 family, u32 if_id)
{ {
u32 priority = prefer ? prefer->priority : ~0u; u32 priority = prefer ? prefer->priority : ~0u;
struct xfrm_policy *pol; struct xfrm_policy *pol;
@ -2028,7 +2028,7 @@ __xfrm_policy_eval_candidates(struct hlist_head *chain,
if (pol->priority > priority) if (pol->priority > priority)
break; break;
err = xfrm_policy_match(pol, fl, type, family, dir, if_id); err = xfrm_policy_match(pol, fl, type, family, if_id);
if (err) { if (err) {
if (err != -ESRCH) if (err != -ESRCH)
return ERR_PTR(err); return ERR_PTR(err);
@ -2053,7 +2053,7 @@ static struct xfrm_policy *
xfrm_policy_eval_candidates(struct xfrm_pol_inexact_candidates *cand, xfrm_policy_eval_candidates(struct xfrm_pol_inexact_candidates *cand,
struct xfrm_policy *prefer, struct xfrm_policy *prefer,
const struct flowi *fl, const struct flowi *fl,
u8 type, u16 family, int dir, u32 if_id) u8 type, u16 family, u32 if_id)
{ {
struct xfrm_policy *tmp; struct xfrm_policy *tmp;
int i; int i;
@ -2061,8 +2061,7 @@ xfrm_policy_eval_candidates(struct xfrm_pol_inexact_candidates *cand,
for (i = 0; i < ARRAY_SIZE(cand->res); i++) { for (i = 0; i < ARRAY_SIZE(cand->res); i++) {
tmp = __xfrm_policy_eval_candidates(cand->res[i], tmp = __xfrm_policy_eval_candidates(cand->res[i],
prefer, prefer,
fl, type, family, dir, fl, type, family, if_id);
if_id);
if (!tmp) if (!tmp)
continue; continue;
@ -2101,7 +2100,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
ret = NULL; ret = NULL;
hlist_for_each_entry_rcu(pol, chain, bydst) { hlist_for_each_entry_rcu(pol, chain, bydst) {
err = xfrm_policy_match(pol, fl, type, family, dir, if_id); err = xfrm_policy_match(pol, fl, type, family, if_id);
if (err) { if (err) {
if (err == -ESRCH) if (err == -ESRCH)
continue; continue;
@ -2120,7 +2119,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
goto skip_inexact; goto skip_inexact;
pol = xfrm_policy_eval_candidates(&cand, ret, fl, type, pol = xfrm_policy_eval_candidates(&cand, ret, fl, type,
family, dir, if_id); family, if_id);
if (pol) { if (pol) {
ret = pol; ret = pol;
if (IS_ERR(pol)) if (IS_ERR(pol))
@ -3516,17 +3515,17 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
int xerr_idx = -1; int xerr_idx = -1;
const struct xfrm_if_cb *ifcb; const struct xfrm_if_cb *ifcb;
struct sec_path *sp; struct sec_path *sp;
struct xfrm_if *xi;
u32 if_id = 0; u32 if_id = 0;
rcu_read_lock(); rcu_read_lock();
ifcb = xfrm_if_get_cb(); ifcb = xfrm_if_get_cb();
if (ifcb) { if (ifcb) {
xi = ifcb->decode_session(skb, family); struct xfrm_if_decode_session_result r;
if (xi) {
if_id = xi->p.if_id; if (ifcb->decode_session(skb, family, &r)) {
net = xi->net; if_id = r.if_id;
net = r.net;
} }
} }
rcu_read_unlock(); rcu_read_unlock();

View File

@ -766,18 +766,22 @@ int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
} }
#endif #endif
int xfrm_init_replay(struct xfrm_state *x) int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack)
{ {
struct xfrm_replay_state_esn *replay_esn = x->replay_esn; struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
if (replay_esn) { if (replay_esn) {
if (replay_esn->replay_window > if (replay_esn->replay_window >
replay_esn->bmp_len * sizeof(__u32) * 8) replay_esn->bmp_len * sizeof(__u32) * 8) {
NL_SET_ERR_MSG(extack, "ESN replay window is too large for the chosen bitmap size");
return -EINVAL; return -EINVAL;
}
if (x->props.flags & XFRM_STATE_ESN) { if (x->props.flags & XFRM_STATE_ESN) {
if (replay_esn->replay_window == 0) if (replay_esn->replay_window == 0) {
NL_SET_ERR_MSG(extack, "ESN replay window must be > 0");
return -EINVAL; return -EINVAL;
}
x->repl_mode = XFRM_REPLAY_MODE_ESN; x->repl_mode = XFRM_REPLAY_MODE_ESN;
} else { } else {
x->repl_mode = XFRM_REPLAY_MODE_BMP; x->repl_mode = XFRM_REPLAY_MODE_BMP;

View File

@ -2611,7 +2611,8 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
} }
EXPORT_SYMBOL_GPL(xfrm_state_mtu); EXPORT_SYMBOL_GPL(xfrm_state_mtu);
int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload,
struct netlink_ext_ack *extack)
{ {
const struct xfrm_mode *inner_mode; const struct xfrm_mode *inner_mode;
const struct xfrm_mode *outer_mode; const struct xfrm_mode *outer_mode;
@ -2626,12 +2627,16 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload)
if (x->sel.family != AF_UNSPEC) { if (x->sel.family != AF_UNSPEC) {
inner_mode = xfrm_get_mode(x->props.mode, x->sel.family); inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
if (inner_mode == NULL) if (inner_mode == NULL) {
NL_SET_ERR_MSG(extack, "Requested mode not found");
goto error; goto error;
}
if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) && if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
family != x->sel.family) family != x->sel.family) {
NL_SET_ERR_MSG(extack, "Only tunnel modes can accommodate a change of family");
goto error; goto error;
}
x->inner_mode = *inner_mode; x->inner_mode = *inner_mode;
} else { } else {
@ -2639,11 +2644,15 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload)
int iafamily = AF_INET; int iafamily = AF_INET;
inner_mode = xfrm_get_mode(x->props.mode, x->props.family); inner_mode = xfrm_get_mode(x->props.mode, x->props.family);
if (inner_mode == NULL) if (inner_mode == NULL) {
NL_SET_ERR_MSG(extack, "Requested mode not found");
goto error; goto error;
}
if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
NL_SET_ERR_MSG(extack, "Only tunnel modes can accommodate an AF_UNSPEC selector");
goto error; goto error;
}
x->inner_mode = *inner_mode; x->inner_mode = *inner_mode;
@ -2658,24 +2667,27 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload)
} }
x->type = xfrm_get_type(x->id.proto, family); x->type = xfrm_get_type(x->id.proto, family);
if (x->type == NULL) if (x->type == NULL) {
NL_SET_ERR_MSG(extack, "Requested type not found");
goto error; goto error;
}
x->type_offload = xfrm_get_type_offload(x->id.proto, family, offload); x->type_offload = xfrm_get_type_offload(x->id.proto, family, offload);
err = x->type->init_state(x); err = x->type->init_state(x, extack);
if (err) if (err)
goto error; goto error;
outer_mode = xfrm_get_mode(x->props.mode, family); outer_mode = xfrm_get_mode(x->props.mode, family);
if (!outer_mode) { if (!outer_mode) {
NL_SET_ERR_MSG(extack, "Requested mode not found");
err = -EPROTONOSUPPORT; err = -EPROTONOSUPPORT;
goto error; goto error;
} }
x->outer_mode = *outer_mode; x->outer_mode = *outer_mode;
if (init_replay) { if (init_replay) {
err = xfrm_init_replay(x); err = xfrm_init_replay(x, extack);
if (err) if (err)
goto error; goto error;
} }
@ -2690,7 +2702,7 @@ int xfrm_init_state(struct xfrm_state *x)
{ {
int err; int err;
err = __xfrm_init_state(x, true, false); err = __xfrm_init_state(x, true, false, NULL);
if (!err) if (!err)
x->km.state = XFRM_STATE_VALID; x->km.state = XFRM_STATE_VALID;

File diff suppressed because it is too large Load Diff

View File

@ -58,6 +58,8 @@
#define VETH_FMT "ktst-%d" #define VETH_FMT "ktst-%d"
#define VETH_LEN 12 #define VETH_LEN 12
#define XFRM_ALGO_NR_KEYS 29
static int nsfd_parent = -1; static int nsfd_parent = -1;
static int nsfd_childa = -1; static int nsfd_childa = -1;
static int nsfd_childb = -1; static int nsfd_childb = -1;
@ -75,6 +77,43 @@ const unsigned int ping_timeout = 300;
const unsigned int ping_count = 100; const unsigned int ping_count = 100;
const unsigned int ping_success = 80; const unsigned int ping_success = 80;
struct xfrm_key_entry {
char algo_name[35];
int key_len;
};
struct xfrm_key_entry xfrm_key_entries[] = {
{"digest_null", 0},
{"ecb(cipher_null)", 0},
{"cbc(des)", 64},
{"hmac(md5)", 128},
{"cmac(aes)", 128},
{"xcbc(aes)", 128},
{"cbc(cast5)", 128},
{"cbc(serpent)", 128},
{"hmac(sha1)", 160},
{"hmac(rmd160)", 160},
{"cbc(des3_ede)", 192},
{"hmac(sha256)", 256},
{"cbc(aes)", 256},
{"cbc(camellia)", 256},
{"cbc(twofish)", 256},
{"rfc3686(ctr(aes))", 288},
{"hmac(sha384)", 384},
{"cbc(blowfish)", 448},
{"hmac(sha512)", 512},
{"rfc4106(gcm(aes))-128", 160},
{"rfc4543(gcm(aes))-128", 160},
{"rfc4309(ccm(aes))-128", 152},
{"rfc4106(gcm(aes))-192", 224},
{"rfc4543(gcm(aes))-192", 224},
{"rfc4309(ccm(aes))-192", 216},
{"rfc4106(gcm(aes))-256", 288},
{"rfc4543(gcm(aes))-256", 288},
{"rfc4309(ccm(aes))-256", 280},
{"rfc7539(chacha20,poly1305)-128", 0}
};
static void randomize_buffer(void *buf, size_t buflen) static void randomize_buffer(void *buf, size_t buflen)
{ {
int *p = (int *)buf; int *p = (int *)buf;
@ -767,65 +806,12 @@ static int do_ping(int cmd_fd, char *buf, size_t buf_len, struct in_addr from,
static int xfrm_fill_key(char *name, char *buf, static int xfrm_fill_key(char *name, char *buf,
size_t buf_len, unsigned int *key_len) size_t buf_len, unsigned int *key_len)
{ {
/* TODO: use set/map instead */ int i;
if (strncmp(name, "digest_null", ALGO_LEN) == 0)
*key_len = 0; for (i = 0; i < XFRM_ALGO_NR_KEYS; i++) {
else if (strncmp(name, "ecb(cipher_null)", ALGO_LEN) == 0) if (strncmp(name, xfrm_key_entries[i].algo_name, ALGO_LEN) == 0)
*key_len = 0; *key_len = xfrm_key_entries[i].key_len;
else if (strncmp(name, "cbc(des)", ALGO_LEN) == 0) }
*key_len = 64;
else if (strncmp(name, "hmac(md5)", ALGO_LEN) == 0)
*key_len = 128;
else if (strncmp(name, "cmac(aes)", ALGO_LEN) == 0)
*key_len = 128;
else if (strncmp(name, "xcbc(aes)", ALGO_LEN) == 0)
*key_len = 128;
else if (strncmp(name, "cbc(cast5)", ALGO_LEN) == 0)
*key_len = 128;
else if (strncmp(name, "cbc(serpent)", ALGO_LEN) == 0)
*key_len = 128;
else if (strncmp(name, "hmac(sha1)", ALGO_LEN) == 0)
*key_len = 160;
else if (strncmp(name, "hmac(rmd160)", ALGO_LEN) == 0)
*key_len = 160;
else if (strncmp(name, "cbc(des3_ede)", ALGO_LEN) == 0)
*key_len = 192;
else if (strncmp(name, "hmac(sha256)", ALGO_LEN) == 0)
*key_len = 256;
else if (strncmp(name, "cbc(aes)", ALGO_LEN) == 0)
*key_len = 256;
else if (strncmp(name, "cbc(camellia)", ALGO_LEN) == 0)
*key_len = 256;
else if (strncmp(name, "cbc(twofish)", ALGO_LEN) == 0)
*key_len = 256;
else if (strncmp(name, "rfc3686(ctr(aes))", ALGO_LEN) == 0)
*key_len = 288;
else if (strncmp(name, "hmac(sha384)", ALGO_LEN) == 0)
*key_len = 384;
else if (strncmp(name, "cbc(blowfish)", ALGO_LEN) == 0)
*key_len = 448;
else if (strncmp(name, "hmac(sha512)", ALGO_LEN) == 0)
*key_len = 512;
else if (strncmp(name, "rfc4106(gcm(aes))-128", ALGO_LEN) == 0)
*key_len = 160;
else if (strncmp(name, "rfc4543(gcm(aes))-128", ALGO_LEN) == 0)
*key_len = 160;
else if (strncmp(name, "rfc4309(ccm(aes))-128", ALGO_LEN) == 0)
*key_len = 152;
else if (strncmp(name, "rfc4106(gcm(aes))-192", ALGO_LEN) == 0)
*key_len = 224;
else if (strncmp(name, "rfc4543(gcm(aes))-192", ALGO_LEN) == 0)
*key_len = 224;
else if (strncmp(name, "rfc4309(ccm(aes))-192", ALGO_LEN) == 0)
*key_len = 216;
else if (strncmp(name, "rfc4106(gcm(aes))-256", ALGO_LEN) == 0)
*key_len = 288;
else if (strncmp(name, "rfc4543(gcm(aes))-256", ALGO_LEN) == 0)
*key_len = 288;
else if (strncmp(name, "rfc4309(ccm(aes))-256", ALGO_LEN) == 0)
*key_len = 280;
else if (strncmp(name, "rfc7539(chacha20,poly1305)-128", ALGO_LEN) == 0)
*key_len = 0;
if (*key_len > buf_len) { if (*key_len > buf_len) {
printk("Can't pack a key - too big for buffer"); printk("Can't pack a key - too big for buffer");