Commit 42e8e6d9 authored by David S. Miller's avatar David S. Miller

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:

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

from the net-next tree and commit:

5182a5d4 ("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: default avatarDavid S. Miller <davem@davemloft.net>
parents 9d435073 c39596f6
...@@ -11,6 +11,7 @@ enum metadata_type { ...@@ -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 { ...@@ -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 { ...@@ -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) ...@@ -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, ...@@ -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;
} }
......
...@@ -22,7 +22,7 @@ struct xfrm_state; ...@@ -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)
{ {
......
...@@ -312,9 +312,15 @@ struct km_event { ...@@ -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 { ...@@ -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); ...@@ -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 ...@@ -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); ...@@ -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 ...@@ -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;
} }
......
...@@ -695,6 +695,7 @@ enum { ...@@ -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
}; };
......
...@@ -15,6 +15,7 @@ enum lwtunnel_encap_types { ...@@ -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 { ...@@ -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_ */
...@@ -50,6 +50,7 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type) ...@@ -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 */
......
...@@ -471,30 +471,38 @@ static int ah4_err(struct sk_buff *skb, u32 info) ...@@ -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) ...@@ -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;
} }
......
...@@ -1008,16 +1008,17 @@ static void esp_destroy(struct xfrm_state *x) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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,18 +1114,17 @@ static int esp_init_authenc(struct xfrm_state *x) ...@@ -1107,18 +1114,17 @@ 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);
memcpy(p, x->ealg->alg_key, (x->ealg->alg_key_len + 7) / 8); memcpy(p, x->ealg->alg_key, (x->ealg->alg_key_len + 7) / 8);
...@@ -1132,7 +1138,7 @@ static int esp_init_authenc(struct xfrm_state *x) ...@@ -1132,7 +1138,7 @@ static int esp_init_authenc(struct xfrm_state *x)
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) ...@@ -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) ...@@ -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:
......
...@@ -117,7 +117,8 @@ static int ipcomp_tunnel_attach(struct xfrm_state *x) ...@@ -117,7 +117,8 @@ static int ipcomp_tunnel_attach(struct xfrm_state *x)
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,18 +130,21 @@ static int ipcomp4_init_state(struct xfrm_state *x) ...@@ -129,18 +130,21 @@ 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;
out: out:
......
...@@ -22,13 +22,17 @@ static int ipip_xfrm_rcv(struct xfrm_state *x, struct sk_buff *skb) ...@@ -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);
......
...@@ -666,30 +666,38 @@ static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -1051,16 +1051,17 @@ static void esp6_destroy(struct xfrm_state *x) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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,18 +1157,17 @@ static int esp_init_authenc(struct xfrm_state *x) ...@@ -1150,18 +1157,17 @@ 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);
memcpy(p, x->ealg->alg_key, (x->ealg->alg_key_len + 7) / 8); memcpy(p, x->ealg->alg_key, (x->ealg->alg_key_len + 7) / 8);
...@@ -1175,7 +1181,7 @@ static int esp_init_authenc(struct xfrm_state *x) ...@@ -1175,7 +1181,7 @@ static int esp_init_authenc(struct xfrm_state *x)
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) ...@@ -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) ...@@ -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:
......
...@@ -136,7 +136,8 @@ static int ipcomp6_tunnel_attach(struct xfrm_state *x) ...@@ -136,7 +136,8 @@ static int ipcomp6_tunnel_attach(struct xfrm_state *x)
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,18 +149,21 @@ static int ipcomp6_init_state(struct xfrm_state *x) ...@@ -148,18 +149,21 @@ 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;
out: out:
......
...@@ -247,15 +247,14 @@ static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb, ...@@ -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) ...@@ -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;
} }
......
...@@ -270,13 +270,17 @@ static int xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -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);
......
...@@ -207,7 +207,8 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur ...@@ -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, ...@@ -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, ...@@ -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,9 +285,11 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, ...@@ -277,9 +285,11 @@ 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;
} }
......
...@@ -20,6 +20,7 @@ ...@@ -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,6 +720,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) ...@@ -719,6 +720,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
sp = skb_sec_path(skb); sp = skb_sec_path(skb);
if (sp) if (sp)
sp->olen = 0; sp->olen = 0;
if (skb_valid_dst(skb))
skb_dst_drop(skb); skb_dst_drop(skb);
gro_cells_receive(&gro_cells, skb); gro_cells_receive(&gro_cells, skb);
return 0; return 0;
...@@ -737,6 +739,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) ...@@ -737,6 +739,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
sp = skb_sec_path(skb); sp = skb_sec_path(skb);
if (sp) if (sp)
sp->olen = 0; sp->olen = 0;
if (skb_valid_dst(skb))
skb_dst_drop(skb); skb_dst_drop(skb);
gro_cells_receive(&gro_cells, skb); gro_cells_receive(&gro_cells, skb);
return err; return err;
......
...@@ -41,6 +41,7 @@ ...@@ -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; ...@@ -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) ...@@ -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, ...@@ -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;
xi = netdev_priv(dev);
res->net = xi->net;
return netdev_priv(dev); 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,6 +254,9 @@ static int xfrmi_create(struct net_device *dev) ...@@ -157,6 +254,9 @@ static int xfrmi_create(struct net_device *dev)
if (err < 0) if (err < 0)
goto out; goto out;
if (xi->p.collect_md)
rcu_assign_pointer(xfrmn->collect_md_xfrmi, xi);
else
xfrmi_link(xfrmn, xi); xfrmi_link(xfrmn, xi);
return 0; return 0;
...@@ -185,6 +285,9 @@ static void xfrmi_dev_uninit(struct net_device *dev) ...@@ -185,6 +285,9 @@ 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);
if (xi->p.collect_md)
RCU_INIT_POINTER(xfrmn->collect_md_xfrmi, NULL);
else
xfrmi_unlink(xfrmn, xi); xfrmi_unlink(xfrmn, xi);
} }
...@@ -214,6 +317,7 @@ static int xfrmi_rcv_cb(struct sk_buff *skb, int err) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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[], ...@@ -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,6 +777,18 @@ static int xfrmi_newlink(struct net *src_net, struct net_device *dev, ...@@ -645,6 +777,18 @@ 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.collect_md) {
struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
if (p.link || p.if_id) {
NL_SET_ERR_MSG(extack, "link and if_id must be zero");
return -EINVAL;
}
if (rtnl_dereference(xfrmn->collect_md_xfrmi))
return -EEXIST;
} else {
if (!p.if_id) { if (!p.if_id) {
NL_SET_ERR_MSG(extack, "if_id must be non zero"); NL_SET_ERR_MSG(extack, "if_id must be non zero");
return -EINVAL; return -EINVAL;
...@@ -653,6 +797,7 @@ static int xfrmi_newlink(struct net *src_net, struct net_device *dev, ...@@ -653,6 +797,7 @@ static int xfrmi_newlink(struct net *src_net, struct net_device *dev,
xi = xfrmi_locate(net, &p); xi = xfrmi_locate(net, &p);
if (xi) if (xi)
return -EEXIST; 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[], ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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_UNSPEC] = { .strict_start_type = IFLA_XFRM_COLLECT_METADATA },
[IFLA_XFRM_LINK] = { .type = NLA_U32 }, [IFLA_XFRM_LINK] = { .type = NLA_U32 },
[IFLA_XFRM_IF_ID] = { .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) ...@@ -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) ...@@ -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 @@ static int __init xfrmi_init(void) ...@@ -1017,6 +1182,7 @@ static int __init xfrmi_init(void)
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();
......
...@@ -325,18 +325,22 @@ void ipcomp_destroy(struct xfrm_state *x) ...@@ -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);
......
...@@ -1889,7 +1889,7 @@ EXPORT_SYMBOL(xfrm_policy_walk_done); ...@@ -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 * ...@@ -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, ...@@ -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 * ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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();
......
...@@ -766,18 +766,22 @@ int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb) ...@@ -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;
......
...@@ -2611,7 +2611,8 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -35,7 +35,8 @@ ...@@ -35,7 +35,8 @@
#endif #endif
#include <asm/unaligned.h> #include <asm/unaligned.h>
static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type,
struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[type]; struct nlattr *rt = attrs[type];
struct xfrm_algo *algp; struct xfrm_algo *algp;
...@@ -44,8 +45,10 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) ...@@ -44,8 +45,10 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
return 0; return 0;
algp = nla_data(rt); algp = nla_data(rt);
if (nla_len(rt) < (int)xfrm_alg_len(algp)) if (nla_len(rt) < (int)xfrm_alg_len(algp)) {
NL_SET_ERR_MSG(extack, "Invalid AUTH/CRYPT/COMP attribute length");
return -EINVAL; return -EINVAL;
}
switch (type) { switch (type) {
case XFRMA_ALG_AUTH: case XFRMA_ALG_AUTH:
...@@ -54,6 +57,7 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) ...@@ -54,6 +57,7 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Invalid algorithm attribute type");
return -EINVAL; return -EINVAL;
} }
...@@ -61,7 +65,8 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) ...@@ -61,7 +65,8 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
return 0; return 0;
} }
static int verify_auth_trunc(struct nlattr **attrs) static int verify_auth_trunc(struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC]; struct nlattr *rt = attrs[XFRMA_ALG_AUTH_TRUNC];
struct xfrm_algo_auth *algp; struct xfrm_algo_auth *algp;
...@@ -70,14 +75,16 @@ static int verify_auth_trunc(struct nlattr **attrs) ...@@ -70,14 +75,16 @@ static int verify_auth_trunc(struct nlattr **attrs)
return 0; return 0;
algp = nla_data(rt); algp = nla_data(rt);
if (nla_len(rt) < (int)xfrm_alg_auth_len(algp)) if (nla_len(rt) < (int)xfrm_alg_auth_len(algp)) {
NL_SET_ERR_MSG(extack, "Invalid AUTH_TRUNC attribute length");
return -EINVAL; return -EINVAL;
}
algp->alg_name[sizeof(algp->alg_name) - 1] = '\0'; algp->alg_name[sizeof(algp->alg_name) - 1] = '\0';
return 0; return 0;
} }
static int verify_aead(struct nlattr **attrs) static int verify_aead(struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[XFRMA_ALG_AEAD]; struct nlattr *rt = attrs[XFRMA_ALG_AEAD];
struct xfrm_algo_aead *algp; struct xfrm_algo_aead *algp;
...@@ -86,8 +93,10 @@ static int verify_aead(struct nlattr **attrs) ...@@ -86,8 +93,10 @@ static int verify_aead(struct nlattr **attrs)
return 0; return 0;
algp = nla_data(rt); algp = nla_data(rt);
if (nla_len(rt) < (int)aead_len(algp)) if (nla_len(rt) < (int)aead_len(algp)) {
NL_SET_ERR_MSG(extack, "Invalid AEAD attribute length");
return -EINVAL; return -EINVAL;
}
algp->alg_name[sizeof(algp->alg_name) - 1] = '\0'; algp->alg_name[sizeof(algp->alg_name) - 1] = '\0';
return 0; return 0;
...@@ -102,7 +111,7 @@ static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type, ...@@ -102,7 +111,7 @@ static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type,
*addrp = nla_data(rt); *addrp = nla_data(rt);
} }
static inline int verify_sec_ctx_len(struct nlattr **attrs) static inline int verify_sec_ctx_len(struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[XFRMA_SEC_CTX]; struct nlattr *rt = attrs[XFRMA_SEC_CTX];
struct xfrm_user_sec_ctx *uctx; struct xfrm_user_sec_ctx *uctx;
...@@ -112,42 +121,59 @@ static inline int verify_sec_ctx_len(struct nlattr **attrs) ...@@ -112,42 +121,59 @@ static inline int verify_sec_ctx_len(struct nlattr **attrs)
uctx = nla_data(rt); uctx = nla_data(rt);
if (uctx->len > nla_len(rt) || if (uctx->len > nla_len(rt) ||
uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len)) uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len)) {
NL_SET_ERR_MSG(extack, "Invalid security context length");
return -EINVAL; return -EINVAL;
}
return 0; return 0;
} }
static inline int verify_replay(struct xfrm_usersa_info *p, static inline int verify_replay(struct xfrm_usersa_info *p,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL]; struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
struct xfrm_replay_state_esn *rs; struct xfrm_replay_state_esn *rs;
if (!rt) if (!rt) {
return (p->flags & XFRM_STATE_ESN) ? -EINVAL : 0; if (p->flags & XFRM_STATE_ESN) {
NL_SET_ERR_MSG(extack, "Missing required attribute for ESN");
return -EINVAL;
}
return 0;
}
rs = nla_data(rt); rs = nla_data(rt);
if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8) if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8) {
NL_SET_ERR_MSG(extack, "ESN bitmap length must be <= 128");
return -EINVAL; return -EINVAL;
}
if (nla_len(rt) < (int)xfrm_replay_state_esn_len(rs) && if (nla_len(rt) < (int)xfrm_replay_state_esn_len(rs) &&
nla_len(rt) != sizeof(*rs)) nla_len(rt) != sizeof(*rs)) {
NL_SET_ERR_MSG(extack, "ESN attribute is too short to fit the full bitmap length");
return -EINVAL; return -EINVAL;
}
/* As only ESP and AH support ESN feature. */ /* As only ESP and AH support ESN feature. */
if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH)) if ((p->id.proto != IPPROTO_ESP) && (p->id.proto != IPPROTO_AH)) {
NL_SET_ERR_MSG(extack, "ESN only supported for ESP and AH");
return -EINVAL; return -EINVAL;
}
if (p->replay_window != 0) if (p->replay_window != 0) {
NL_SET_ERR_MSG(extack, "ESN not compatible with legacy replay_window");
return -EINVAL; return -EINVAL;
}
return 0; return 0;
} }
static int verify_newsa_info(struct xfrm_usersa_info *p, static int verify_newsa_info(struct xfrm_usersa_info *p,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
int err; int err;
...@@ -161,10 +187,12 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, ...@@ -161,10 +187,12 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
break; break;
#else #else
err = -EAFNOSUPPORT; err = -EAFNOSUPPORT;
NL_SET_ERR_MSG(extack, "IPv6 support disabled");
goto out; goto out;
#endif #endif
default: default:
NL_SET_ERR_MSG(extack, "Invalid address family");
goto out; goto out;
} }
...@@ -173,65 +201,98 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, ...@@ -173,65 +201,98 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
break; break;
case AF_INET: case AF_INET:
if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) {
NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 32 for IPv4)");
goto out; goto out;
}
break; break;
case AF_INET6: case AF_INET6:
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) {
NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 128 for IPv6)");
goto out; goto out;
}
break; break;
#else #else
NL_SET_ERR_MSG(extack, "IPv6 support disabled");
err = -EAFNOSUPPORT; err = -EAFNOSUPPORT;
goto out; goto out;
#endif #endif
default: default:
NL_SET_ERR_MSG(extack, "Invalid address family in selector");
goto out; goto out;
} }
err = -EINVAL; err = -EINVAL;
switch (p->id.proto) { switch (p->id.proto) {
case IPPROTO_AH: case IPPROTO_AH:
if ((!attrs[XFRMA_ALG_AUTH] && if (!attrs[XFRMA_ALG_AUTH] &&
!attrs[XFRMA_ALG_AUTH_TRUNC]) || !attrs[XFRMA_ALG_AUTH_TRUNC]) {
attrs[XFRMA_ALG_AEAD] || NL_SET_ERR_MSG(extack, "Missing required attribute for AH: AUTH_TRUNC or AUTH");
goto out;
}
if (attrs[XFRMA_ALG_AEAD] ||
attrs[XFRMA_ALG_CRYPT] || attrs[XFRMA_ALG_CRYPT] ||
attrs[XFRMA_ALG_COMP] || attrs[XFRMA_ALG_COMP] ||
attrs[XFRMA_TFCPAD]) attrs[XFRMA_TFCPAD]) {
NL_SET_ERR_MSG(extack, "Invalid attributes for AH: AEAD, CRYPT, COMP, TFCPAD");
goto out; goto out;
}
break; break;
case IPPROTO_ESP: case IPPROTO_ESP:
if (attrs[XFRMA_ALG_COMP]) if (attrs[XFRMA_ALG_COMP]) {
NL_SET_ERR_MSG(extack, "Invalid attribute for ESP: COMP");
goto out; goto out;
}
if (!attrs[XFRMA_ALG_AUTH] && if (!attrs[XFRMA_ALG_AUTH] &&
!attrs[XFRMA_ALG_AUTH_TRUNC] && !attrs[XFRMA_ALG_AUTH_TRUNC] &&
!attrs[XFRMA_ALG_CRYPT] && !attrs[XFRMA_ALG_CRYPT] &&
!attrs[XFRMA_ALG_AEAD]) !attrs[XFRMA_ALG_AEAD]) {
NL_SET_ERR_MSG(extack, "Missing required attribute for ESP: at least one of AUTH, AUTH_TRUNC, CRYPT, AEAD");
goto out; goto out;
}
if ((attrs[XFRMA_ALG_AUTH] || if ((attrs[XFRMA_ALG_AUTH] ||
attrs[XFRMA_ALG_AUTH_TRUNC] || attrs[XFRMA_ALG_AUTH_TRUNC] ||
attrs[XFRMA_ALG_CRYPT]) && attrs[XFRMA_ALG_CRYPT]) &&
attrs[XFRMA_ALG_AEAD]) attrs[XFRMA_ALG_AEAD]) {
NL_SET_ERR_MSG(extack, "Invalid attribute combination for ESP: AEAD can't be used with AUTH, AUTH_TRUNC, CRYPT");
goto out; goto out;
}
if (attrs[XFRMA_TFCPAD] && if (attrs[XFRMA_TFCPAD] &&
p->mode != XFRM_MODE_TUNNEL) p->mode != XFRM_MODE_TUNNEL) {
NL_SET_ERR_MSG(extack, "TFC padding can only be used in tunnel mode");
goto out; goto out;
}
break; break;
case IPPROTO_COMP: case IPPROTO_COMP:
if (!attrs[XFRMA_ALG_COMP] || if (!attrs[XFRMA_ALG_COMP]) {
attrs[XFRMA_ALG_AEAD] || NL_SET_ERR_MSG(extack, "Missing required attribute for COMP: COMP");
goto out;
}
if (attrs[XFRMA_ALG_AEAD] ||
attrs[XFRMA_ALG_AUTH] || attrs[XFRMA_ALG_AUTH] ||
attrs[XFRMA_ALG_AUTH_TRUNC] || attrs[XFRMA_ALG_AUTH_TRUNC] ||
attrs[XFRMA_ALG_CRYPT] || attrs[XFRMA_ALG_CRYPT] ||
attrs[XFRMA_TFCPAD] || attrs[XFRMA_TFCPAD]) {
(ntohl(p->id.spi) >= 0x10000)) NL_SET_ERR_MSG(extack, "Invalid attributes for COMP: AEAD, AUTH, AUTH_TRUNC, CRYPT, TFCPAD");
goto out;
}
if (ntohl(p->id.spi) >= 0x10000) {
NL_SET_ERR_MSG(extack, "SPI is too large for COMP (must be < 0x10000)");
goto out; goto out;
}
break; break;
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
...@@ -244,29 +305,36 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, ...@@ -244,29 +305,36 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
attrs[XFRMA_ALG_CRYPT] || attrs[XFRMA_ALG_CRYPT] ||
attrs[XFRMA_ENCAP] || attrs[XFRMA_ENCAP] ||
attrs[XFRMA_SEC_CTX] || attrs[XFRMA_SEC_CTX] ||
attrs[XFRMA_TFCPAD] || attrs[XFRMA_TFCPAD]) {
!attrs[XFRMA_COADDR]) NL_SET_ERR_MSG(extack, "Invalid attributes for DSTOPTS/ROUTING");
goto out; goto out;
}
if (!attrs[XFRMA_COADDR]) {
NL_SET_ERR_MSG(extack, "Missing required COADDR attribute for DSTOPTS/ROUTING");
goto out;
}
break; break;
#endif #endif
default: default:
NL_SET_ERR_MSG(extack, "Unsupported protocol");
goto out; goto out;
} }
if ((err = verify_aead(attrs))) if ((err = verify_aead(attrs, extack)))
goto out; goto out;
if ((err = verify_auth_trunc(attrs))) if ((err = verify_auth_trunc(attrs, extack)))
goto out; goto out;
if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH))) if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH, extack)))
goto out; goto out;
if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT))) if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT, extack)))
goto out; goto out;
if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP))) if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP, extack)))
goto out; goto out;
if ((err = verify_sec_ctx_len(attrs))) if ((err = verify_sec_ctx_len(attrs, extack)))
goto out; goto out;
if ((err = verify_replay(p, attrs))) if ((err = verify_replay(p, attrs, extack)))
goto out; goto out;
err = -EINVAL; err = -EINVAL;
...@@ -278,14 +346,19 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, ...@@ -278,14 +346,19 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Unsupported mode");
goto out; goto out;
} }
err = 0; err = 0;
if (attrs[XFRMA_MTIMER_THRESH]) if (attrs[XFRMA_MTIMER_THRESH]) {
if (!attrs[XFRMA_ENCAP]) if (!attrs[XFRMA_ENCAP]) {
NL_SET_ERR_MSG(extack, "MTIMER_THRESH attribute can only be set on ENCAP states");
err = -EINVAL; err = -EINVAL;
goto out;
}
}
out: out:
return err; return err;
...@@ -293,7 +366,7 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, ...@@ -293,7 +366,7 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
struct xfrm_algo_desc *(*get_byname)(const char *, int), struct xfrm_algo_desc *(*get_byname)(const char *, int),
struct nlattr *rta) struct nlattr *rta, struct netlink_ext_ack *extack)
{ {
struct xfrm_algo *p, *ualg; struct xfrm_algo *p, *ualg;
struct xfrm_algo_desc *algo; struct xfrm_algo_desc *algo;
...@@ -304,8 +377,10 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, ...@@ -304,8 +377,10 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
ualg = nla_data(rta); ualg = nla_data(rta);
algo = get_byname(ualg->alg_name, 1); algo = get_byname(ualg->alg_name, 1);
if (!algo) if (!algo) {
NL_SET_ERR_MSG(extack, "Requested COMP algorithm not found");
return -ENOSYS; return -ENOSYS;
}
*props = algo->desc.sadb_alg_id; *props = algo->desc.sadb_alg_id;
p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);
...@@ -317,7 +392,8 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, ...@@ -317,7 +392,8 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
return 0; return 0;
} }
static int attach_crypt(struct xfrm_state *x, struct nlattr *rta) static int attach_crypt(struct xfrm_state *x, struct nlattr *rta,
struct netlink_ext_ack *extack)
{ {
struct xfrm_algo *p, *ualg; struct xfrm_algo *p, *ualg;
struct xfrm_algo_desc *algo; struct xfrm_algo_desc *algo;
...@@ -328,8 +404,10 @@ static int attach_crypt(struct xfrm_state *x, struct nlattr *rta) ...@@ -328,8 +404,10 @@ static int attach_crypt(struct xfrm_state *x, struct nlattr *rta)
ualg = nla_data(rta); ualg = nla_data(rta);
algo = xfrm_ealg_get_byname(ualg->alg_name, 1); algo = xfrm_ealg_get_byname(ualg->alg_name, 1);
if (!algo) if (!algo) {
NL_SET_ERR_MSG(extack, "Requested CRYPT algorithm not found");
return -ENOSYS; return -ENOSYS;
}
x->props.ealgo = algo->desc.sadb_alg_id; x->props.ealgo = algo->desc.sadb_alg_id;
p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL); p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);
...@@ -343,7 +421,7 @@ static int attach_crypt(struct xfrm_state *x, struct nlattr *rta) ...@@ -343,7 +421,7 @@ static int attach_crypt(struct xfrm_state *x, struct nlattr *rta)
} }
static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
struct nlattr *rta) struct nlattr *rta, struct netlink_ext_ack *extack)
{ {
struct xfrm_algo *ualg; struct xfrm_algo *ualg;
struct xfrm_algo_auth *p; struct xfrm_algo_auth *p;
...@@ -355,8 +433,10 @@ static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, ...@@ -355,8 +433,10 @@ static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
ualg = nla_data(rta); ualg = nla_data(rta);
algo = xfrm_aalg_get_byname(ualg->alg_name, 1); algo = xfrm_aalg_get_byname(ualg->alg_name, 1);
if (!algo) if (!algo) {
NL_SET_ERR_MSG(extack, "Requested AUTH algorithm not found");
return -ENOSYS; return -ENOSYS;
}
*props = algo->desc.sadb_alg_id; *props = algo->desc.sadb_alg_id;
p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL); p = kmalloc(sizeof(*p) + (ualg->alg_key_len + 7) / 8, GFP_KERNEL);
...@@ -373,7 +453,7 @@ static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props, ...@@ -373,7 +453,7 @@ static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
} }
static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
struct nlattr *rta) struct nlattr *rta, struct netlink_ext_ack *extack)
{ {
struct xfrm_algo_auth *p, *ualg; struct xfrm_algo_auth *p, *ualg;
struct xfrm_algo_desc *algo; struct xfrm_algo_desc *algo;
...@@ -384,10 +464,14 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, ...@@ -384,10 +464,14 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
ualg = nla_data(rta); ualg = nla_data(rta);
algo = xfrm_aalg_get_byname(ualg->alg_name, 1); algo = xfrm_aalg_get_byname(ualg->alg_name, 1);
if (!algo) if (!algo) {
NL_SET_ERR_MSG(extack, "Requested AUTH_TRUNC algorithm not found");
return -ENOSYS; return -ENOSYS;
if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) }
if (ualg->alg_trunc_len > algo->uinfo.auth.icv_fullbits) {
NL_SET_ERR_MSG(extack, "Invalid length requested for truncated ICV");
return -EINVAL; return -EINVAL;
}
*props = algo->desc.sadb_alg_id; *props = algo->desc.sadb_alg_id;
p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL); p = kmemdup(ualg, xfrm_alg_auth_len(ualg), GFP_KERNEL);
...@@ -402,7 +486,8 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props, ...@@ -402,7 +486,8 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
return 0; return 0;
} }
static int attach_aead(struct xfrm_state *x, struct nlattr *rta) static int attach_aead(struct xfrm_state *x, struct nlattr *rta,
struct netlink_ext_ack *extack)
{ {
struct xfrm_algo_aead *p, *ualg; struct xfrm_algo_aead *p, *ualg;
struct xfrm_algo_desc *algo; struct xfrm_algo_desc *algo;
...@@ -413,8 +498,10 @@ static int attach_aead(struct xfrm_state *x, struct nlattr *rta) ...@@ -413,8 +498,10 @@ static int attach_aead(struct xfrm_state *x, struct nlattr *rta)
ualg = nla_data(rta); ualg = nla_data(rta);
algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1); algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1);
if (!algo) if (!algo) {
NL_SET_ERR_MSG(extack, "Requested AEAD algorithm not found");
return -ENOSYS; return -ENOSYS;
}
x->props.ealgo = algo->desc.sadb_alg_id; x->props.ealgo = algo->desc.sadb_alg_id;
p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL); p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL);
...@@ -579,7 +666,8 @@ static void xfrm_smark_init(struct nlattr **attrs, struct xfrm_mark *m) ...@@ -579,7 +666,8 @@ static void xfrm_smark_init(struct nlattr **attrs, struct xfrm_mark *m)
static struct xfrm_state *xfrm_state_construct(struct net *net, static struct xfrm_state *xfrm_state_construct(struct net *net,
struct xfrm_usersa_info *p, struct xfrm_usersa_info *p,
struct nlattr **attrs, struct nlattr **attrs,
int *errp) int *errp,
struct netlink_ext_ack *extack)
{ {
struct xfrm_state *x = xfrm_state_alloc(net); struct xfrm_state *x = xfrm_state_alloc(net);
int err = -ENOMEM; int err = -ENOMEM;
...@@ -606,21 +694,21 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -606,21 +694,21 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
if (attrs[XFRMA_SA_EXTRA_FLAGS]) if (attrs[XFRMA_SA_EXTRA_FLAGS])
x->props.extra_flags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]); x->props.extra_flags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]);
if ((err = attach_aead(x, attrs[XFRMA_ALG_AEAD]))) if ((err = attach_aead(x, attrs[XFRMA_ALG_AEAD], extack)))
goto error; goto error;
if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo,
attrs[XFRMA_ALG_AUTH_TRUNC]))) attrs[XFRMA_ALG_AUTH_TRUNC], extack)))
goto error; goto error;
if (!x->props.aalgo) { if (!x->props.aalgo) {
if ((err = attach_auth(&x->aalg, &x->props.aalgo, if ((err = attach_auth(&x->aalg, &x->props.aalgo,
attrs[XFRMA_ALG_AUTH]))) attrs[XFRMA_ALG_AUTH], extack)))
goto error; goto error;
} }
if ((err = attach_crypt(x, attrs[XFRMA_ALG_CRYPT]))) if ((err = attach_crypt(x, attrs[XFRMA_ALG_CRYPT], extack)))
goto error; goto error;
if ((err = attach_one_algo(&x->calg, &x->props.calgo, if ((err = attach_one_algo(&x->calg, &x->props.calgo,
xfrm_calg_get_byname, xfrm_calg_get_byname,
attrs[XFRMA_ALG_COMP]))) attrs[XFRMA_ALG_COMP], extack)))
goto error; goto error;
if (attrs[XFRMA_TFCPAD]) if (attrs[XFRMA_TFCPAD])
...@@ -633,7 +721,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -633,7 +721,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
if (attrs[XFRMA_IF_ID]) if (attrs[XFRMA_IF_ID])
x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]); x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]); err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV], extack);
if (err) if (err)
goto error; goto error;
...@@ -653,7 +741,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -653,7 +741,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
/* sysctl_xfrm_aevent_etime is in 100ms units */ /* sysctl_xfrm_aevent_etime is in 100ms units */
x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M; x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M;
if ((err = xfrm_init_replay(x))) if ((err = xfrm_init_replay(x, extack)))
goto error; goto error;
/* override default values from above */ /* override default values from above */
...@@ -662,7 +750,8 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -662,7 +750,8 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
/* configure the hardware if offload is requested */ /* configure the hardware if offload is requested */
if (attrs[XFRMA_OFFLOAD_DEV]) { if (attrs[XFRMA_OFFLOAD_DEV]) {
err = xfrm_dev_state_add(net, x, err = xfrm_dev_state_add(net, x,
nla_data(attrs[XFRMA_OFFLOAD_DEV])); nla_data(attrs[XFRMA_OFFLOAD_DEV]),
extack);
if (err) if (err)
goto error; goto error;
} }
...@@ -678,7 +767,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, ...@@ -678,7 +767,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
} }
static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_usersa_info *p = nlmsg_data(nlh); struct xfrm_usersa_info *p = nlmsg_data(nlh);
...@@ -686,11 +775,11 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -686,11 +775,11 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
int err; int err;
struct km_event c; struct km_event c;
err = verify_newsa_info(p, attrs); err = verify_newsa_info(p, attrs, extack);
if (err) if (err)
return err; return err;
x = xfrm_state_construct(net, p, attrs, &err); x = xfrm_state_construct(net, p, attrs, &err, extack);
if (!x) if (!x)
return err; return err;
...@@ -757,7 +846,7 @@ static struct xfrm_state *xfrm_user_state_lookup(struct net *net, ...@@ -757,7 +846,7 @@ static struct xfrm_state *xfrm_user_state_lookup(struct net *net,
} }
static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_state *x; struct xfrm_state *x;
...@@ -1254,7 +1343,8 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net, ...@@ -1254,7 +1343,8 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net,
} }
static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrmu_spdhthresh *thresh4 = NULL; struct xfrmu_spdhthresh *thresh4 = NULL;
...@@ -1299,7 +1389,8 @@ static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1299,7 +1389,8 @@ static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct sk_buff *r_skb; struct sk_buff *r_skb;
...@@ -1358,7 +1449,8 @@ static int build_sadinfo(struct sk_buff *skb, struct net *net, ...@@ -1358,7 +1449,8 @@ static int build_sadinfo(struct sk_buff *skb, struct net *net,
} }
static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct sk_buff *r_skb; struct sk_buff *r_skb;
...@@ -1378,7 +1470,7 @@ static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1378,7 +1470,7 @@ static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_usersa_id *p = nlmsg_data(nlh); struct xfrm_usersa_id *p = nlmsg_data(nlh);
...@@ -1402,7 +1494,8 @@ static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1402,7 +1494,8 @@ static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_state *x; struct xfrm_state *x;
...@@ -1477,7 +1570,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1477,7 +1570,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
return err; return err;
} }
static int verify_policy_dir(u8 dir) static int verify_policy_dir(u8 dir, struct netlink_ext_ack *extack)
{ {
switch (dir) { switch (dir) {
case XFRM_POLICY_IN: case XFRM_POLICY_IN:
...@@ -1486,13 +1579,14 @@ static int verify_policy_dir(u8 dir) ...@@ -1486,13 +1579,14 @@ static int verify_policy_dir(u8 dir)
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Invalid policy direction");
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
} }
static int verify_policy_type(u8 type) static int verify_policy_type(u8 type, struct netlink_ext_ack *extack)
{ {
switch (type) { switch (type) {
case XFRM_POLICY_TYPE_MAIN: case XFRM_POLICY_TYPE_MAIN:
...@@ -1502,13 +1596,15 @@ static int verify_policy_type(u8 type) ...@@ -1502,13 +1596,15 @@ static int verify_policy_type(u8 type)
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Invalid policy type");
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
} }
static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) static int verify_newpolicy_info(struct xfrm_userpolicy_info *p,
struct netlink_ext_ack *extack)
{ {
int ret; int ret;
...@@ -1520,6 +1616,7 @@ static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) ...@@ -1520,6 +1616,7 @@ static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Invalid policy share");
return -EINVAL; return -EINVAL;
} }
...@@ -1529,35 +1626,44 @@ static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) ...@@ -1529,35 +1626,44 @@ static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)
break; break;
default: default:
NL_SET_ERR_MSG(extack, "Invalid policy action");
return -EINVAL; return -EINVAL;
} }
switch (p->sel.family) { switch (p->sel.family) {
case AF_INET: case AF_INET:
if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) {
NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 32 for IPv4)");
return -EINVAL; return -EINVAL;
}
break; break;
case AF_INET6: case AF_INET6:
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) {
NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 128 for IPv6)");
return -EINVAL; return -EINVAL;
}
break; break;
#else #else
NL_SET_ERR_MSG(extack, "IPv6 support disabled");
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
#endif #endif
default: default:
NL_SET_ERR_MSG(extack, "Invalid selector family");
return -EINVAL; return -EINVAL;
} }
ret = verify_policy_dir(p->dir); ret = verify_policy_dir(p->dir, extack);
if (ret) if (ret)
return ret; return ret;
if (p->index && (xfrm_policy_id2dir(p->index) != p->dir)) if (p->index && (xfrm_policy_id2dir(p->index) != p->dir)) {
NL_SET_ERR_MSG(extack, "Policy index doesn't match direction");
return -EINVAL; return -EINVAL;
}
return 0; return 0;
} }
...@@ -1599,13 +1705,16 @@ static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, ...@@ -1599,13 +1705,16 @@ static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut,
} }
} }
static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family,
struct netlink_ext_ack *extack)
{ {
u16 prev_family; u16 prev_family;
int i; int i;
if (nr > XFRM_MAX_DEPTH) if (nr > XFRM_MAX_DEPTH) {
NL_SET_ERR_MSG(extack, "Template count must be <= XFRM_MAX_DEPTH (" __stringify(XFRM_MAX_DEPTH) ")");
return -EINVAL; return -EINVAL;
}
prev_family = family; prev_family = family;
...@@ -1625,12 +1734,16 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) ...@@ -1625,12 +1734,16 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
case XFRM_MODE_BEET: case XFRM_MODE_BEET:
break; break;
default: default:
if (ut[i].family != prev_family) if (ut[i].family != prev_family) {
NL_SET_ERR_MSG(extack, "Mode in template doesn't support a family change");
return -EINVAL; return -EINVAL;
}
break; break;
} }
if (ut[i].mode >= XFRM_MODE_MAX) if (ut[i].mode >= XFRM_MODE_MAX) {
NL_SET_ERR_MSG(extack, "Mode in template must be < XFRM_MODE_MAX (" __stringify(XFRM_MODE_MAX) ")");
return -EINVAL; return -EINVAL;
}
prev_family = ut[i].family; prev_family = ut[i].family;
...@@ -1642,17 +1755,21 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) ...@@ -1642,17 +1755,21 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
break; break;
#endif #endif
default: default:
NL_SET_ERR_MSG(extack, "Invalid family in template");
return -EINVAL; return -EINVAL;
} }
if (!xfrm_id_proto_valid(ut[i].id.proto)) if (!xfrm_id_proto_valid(ut[i].id.proto)) {
NL_SET_ERR_MSG(extack, "Invalid XFRM protocol in template");
return -EINVAL; return -EINVAL;
} }
}
return 0; return 0;
} }
static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[XFRMA_TMPL]; struct nlattr *rt = attrs[XFRMA_TMPL];
...@@ -1663,7 +1780,7 @@ static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) ...@@ -1663,7 +1780,7 @@ static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs)
int nr = nla_len(rt) / sizeof(*utmpl); int nr = nla_len(rt) / sizeof(*utmpl);
int err; int err;
err = validate_tmpl(nr, utmpl, pol->family); err = validate_tmpl(nr, utmpl, pol->family, extack);
if (err) if (err)
return err; return err;
...@@ -1672,7 +1789,8 @@ static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs) ...@@ -1672,7 +1789,8 @@ static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs)
return 0; return 0;
} }
static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct nlattr *rt = attrs[XFRMA_POLICY_TYPE]; struct nlattr *rt = attrs[XFRMA_POLICY_TYPE];
struct xfrm_userpolicy_type *upt; struct xfrm_userpolicy_type *upt;
...@@ -1684,7 +1802,7 @@ static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs) ...@@ -1684,7 +1802,7 @@ static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs)
type = upt->type; type = upt->type;
} }
err = verify_policy_type(type); err = verify_policy_type(type, extack);
if (err) if (err)
return err; return err;
...@@ -1719,7 +1837,11 @@ static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_i ...@@ -1719,7 +1837,11 @@ static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_i
p->share = XFRM_SHARE_ANY; /* XXX xp->share */ p->share = XFRM_SHARE_ANY; /* XXX xp->share */
} }
static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp) static struct xfrm_policy *xfrm_policy_construct(struct net *net,
struct xfrm_userpolicy_info *p,
struct nlattr **attrs,
int *errp,
struct netlink_ext_ack *extack)
{ {
struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL); struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL);
int err; int err;
...@@ -1731,11 +1853,11 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_us ...@@ -1731,11 +1853,11 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_us
copy_from_user_policy(xp, p); copy_from_user_policy(xp, p);
err = copy_from_user_policy_type(&xp->type, attrs); err = copy_from_user_policy_type(&xp->type, attrs, extack);
if (err) if (err)
goto error; goto error;
if (!(err = copy_from_user_tmpl(xp, attrs))) if (!(err = copy_from_user_tmpl(xp, attrs, extack)))
err = copy_from_user_sec_ctx(xp, attrs); err = copy_from_user_sec_ctx(xp, attrs);
if (err) if (err)
goto error; goto error;
...@@ -1754,7 +1876,8 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_us ...@@ -1754,7 +1876,8 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_us
} }
static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_userpolicy_info *p = nlmsg_data(nlh); struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
...@@ -1763,14 +1886,14 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1763,14 +1886,14 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
int err; int err;
int excl; int excl;
err = verify_newpolicy_info(p); err = verify_newpolicy_info(p, extack);
if (err) if (err)
return err; return err;
err = verify_sec_ctx_len(attrs); err = verify_sec_ctx_len(attrs, extack);
if (err) if (err)
return err; return err;
xp = xfrm_policy_construct(net, p, attrs, &err); xp = xfrm_policy_construct(net, p, attrs, &err, extack);
if (!xp) if (!xp)
return err; return err;
...@@ -2015,7 +2138,7 @@ static bool xfrm_userpolicy_is_valid(__u8 policy) ...@@ -2015,7 +2138,7 @@ static bool xfrm_userpolicy_is_valid(__u8 policy)
} }
static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_userpolicy_default *up = nlmsg_data(nlh); struct xfrm_userpolicy_default *up = nlmsg_data(nlh);
...@@ -2036,7 +2159,7 @@ static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2036,7 +2159,7 @@ static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct sk_buff *r_skb; struct sk_buff *r_skb;
struct nlmsghdr *r_nlh; struct nlmsghdr *r_nlh;
...@@ -2066,7 +2189,8 @@ static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2066,7 +2189,8 @@ static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_policy *xp; struct xfrm_policy *xp;
...@@ -2081,11 +2205,11 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2081,11 +2205,11 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
p = nlmsg_data(nlh); p = nlmsg_data(nlh);
delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY; delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
err = copy_from_user_policy_type(&type, attrs); err = copy_from_user_policy_type(&type, attrs, extack);
if (err) if (err)
return err; return err;
err = verify_policy_dir(p->dir); err = verify_policy_dir(p->dir, extack);
if (err) if (err)
return err; return err;
...@@ -2101,7 +2225,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2101,7 +2225,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr *rt = attrs[XFRMA_SEC_CTX]; struct nlattr *rt = attrs[XFRMA_SEC_CTX];
struct xfrm_sec_ctx *ctx; struct xfrm_sec_ctx *ctx;
err = verify_sec_ctx_len(attrs); err = verify_sec_ctx_len(attrs, extack);
if (err) if (err)
return err; return err;
...@@ -2149,7 +2273,8 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2149,7 +2273,8 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct km_event c; struct km_event c;
...@@ -2249,7 +2374,7 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct ...@@ -2249,7 +2374,7 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct
} }
static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_state *x; struct xfrm_state *x;
...@@ -2293,7 +2418,7 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2293,7 +2418,7 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_state *x; struct xfrm_state *x;
...@@ -2344,14 +2469,15 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2344,14 +2469,15 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct km_event c; struct km_event c;
u8 type = XFRM_POLICY_TYPE_MAIN; u8 type = XFRM_POLICY_TYPE_MAIN;
int err; int err;
err = copy_from_user_policy_type(&type, attrs); err = copy_from_user_policy_type(&type, attrs, extack);
if (err) if (err)
return err; return err;
...@@ -2372,7 +2498,8 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2372,7 +2498,8 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_policy *xp; struct xfrm_policy *xp;
...@@ -2383,11 +2510,11 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2383,11 +2510,11 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct xfrm_mark m; struct xfrm_mark m;
u32 if_id = 0; u32 if_id = 0;
err = copy_from_user_policy_type(&type, attrs); err = copy_from_user_policy_type(&type, attrs, extack);
if (err) if (err)
return err; return err;
err = verify_policy_dir(p->dir); err = verify_policy_dir(p->dir, extack);
if (err) if (err)
return err; return err;
...@@ -2403,7 +2530,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2403,7 +2530,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr *rt = attrs[XFRMA_SEC_CTX]; struct nlattr *rt = attrs[XFRMA_SEC_CTX];
struct xfrm_sec_ctx *ctx; struct xfrm_sec_ctx *ctx;
err = verify_sec_ctx_len(attrs); err = verify_sec_ctx_len(attrs, extack);
if (err) if (err)
return err; return err;
...@@ -2438,7 +2565,8 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2438,7 +2565,8 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_state *x; struct xfrm_state *x;
...@@ -2472,7 +2600,8 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2472,7 +2600,8 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs,
struct netlink_ext_ack *extack)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct xfrm_policy *xp; struct xfrm_policy *xp;
...@@ -2490,15 +2619,15 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2490,15 +2619,15 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
xfrm_mark_get(attrs, &mark); xfrm_mark_get(attrs, &mark);
err = verify_newpolicy_info(&ua->policy); err = verify_newpolicy_info(&ua->policy, extack);
if (err) if (err)
goto free_state; goto free_state;
err = verify_sec_ctx_len(attrs); err = verify_sec_ctx_len(attrs, extack);
if (err) if (err)
goto free_state; goto free_state;
/* build an XP */ /* build an XP */
xp = xfrm_policy_construct(net, &ua->policy, attrs, &err); xp = xfrm_policy_construct(net, &ua->policy, attrs, &err, extack);
if (!xp) if (!xp)
goto free_state; goto free_state;
...@@ -2577,7 +2706,7 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma, ...@@ -2577,7 +2706,7 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma,
} }
static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); struct xfrm_userpolicy_id *pi = nlmsg_data(nlh);
struct xfrm_migrate m[XFRM_MAX_DEPTH]; struct xfrm_migrate m[XFRM_MAX_DEPTH];
...@@ -2594,7 +2723,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2594,7 +2723,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL; kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL;
err = copy_from_user_policy_type(&type, attrs); err = copy_from_user_policy_type(&type, attrs, extack);
if (err) if (err)
return err; return err;
...@@ -2623,7 +2752,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2623,7 +2752,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
} }
#else #else
static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs) struct nlattr **attrs, struct netlink_ext_ack *extack)
{ {
return -ENOPROTOOPT; return -ENOPROTOOPT;
} }
...@@ -2819,7 +2948,8 @@ static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = { ...@@ -2819,7 +2948,8 @@ static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
}; };
static const struct xfrm_link { static const struct xfrm_link {
int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **); int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **,
struct netlink_ext_ack *);
int (*start)(struct netlink_callback *); int (*start)(struct netlink_callback *);
int (*dump)(struct sk_buff *, struct netlink_callback *); int (*dump)(struct sk_buff *, struct netlink_callback *);
int (*done)(struct netlink_callback *); int (*done)(struct netlink_callback *);
...@@ -2921,7 +3051,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2921,7 +3051,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
goto err; goto err;
} }
err = link->doit(skb, nlh, attrs); err = link->doit(skb, nlh, attrs, extack);
/* We need to free skb allocated in xfrm_alloc_compat() before /* We need to free skb allocated in xfrm_alloc_compat() before
* returning from this function, because consume_skb() won't take * returning from this function, because consume_skb() won't take
...@@ -3272,11 +3402,11 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt, ...@@ -3272,11 +3402,11 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
*dir = -EINVAL; *dir = -EINVAL;
if (len < sizeof(*p) || if (len < sizeof(*p) ||
verify_newpolicy_info(p)) verify_newpolicy_info(p, NULL))
return NULL; return NULL;
nr = ((len - sizeof(*p)) / sizeof(*ut)); nr = ((len - sizeof(*p)) / sizeof(*ut));
if (validate_tmpl(nr, ut, p->sel.family)) if (validate_tmpl(nr, ut, p->sel.family, NULL))
return NULL; return NULL;
if (p->dir > XFRM_POLICY_OUT) if (p->dir > XFRM_POLICY_OUT)
......
...@@ -58,6 +58,8 @@ ...@@ -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; ...@@ -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, ...@@ -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");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment