Commit 179a6f5d authored by Jakub Kicinski's avatar Jakub Kicinski

Merge tag 'ipsec-next-2024-05-03' of...

Merge tag 'ipsec-next-2024-05-03' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next

Steffen Klassert says:

====================
pull request (net-next): ipsec-next 2024-05-03

1) Remove Obsolete UDP_ENCAP_ESPINUDP_NON_IKE Support.
   This was defined by an early version of an IETF draft
   that did not make it to a standard.

2) Introduce direction attribute for xfrm states.
   xfrm states have a direction, a stsate can be used
   either for input or output packet processing.
   Add a direction to xfrm states to make it clear
   for what a xfrm state is used.

* tag 'ipsec-next-2024-05-03' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next:
  xfrm: Restrict SA direction attribute to specific netlink message types
  xfrm: Add dir validation to "in" data path lookup
  xfrm: Add dir validation to "out" data path lookup
  xfrm: Add Direction to the SA in or out
  udpencap: Remove Obsolete UDP_ENCAP_ESPINUDP_NON_IKE Support
====================

Link: https://lore.kernel.org/r/20240503082732.2835810-1-steffen.klassert@secunet.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 46a5d3ab dcf280ea
......@@ -73,6 +73,9 @@ XfrmAcquireError:
XfrmFwdHdrError:
Forward routing of a packet is not allowed
XfrmInStateDirError:
State direction mismatch (lookup found an output state on the input path, expected input or no direction)
Outbound errors
~~~~~~~~~~~~~~~
XfrmOutError:
......@@ -111,3 +114,6 @@ XfrmOutPolError:
XfrmOutStateInvalid:
State is invalid, perhaps expired
XfrmOutStateDirError:
State direction mismatch (lookup found an input state on the output path, expected output or no direction)
......@@ -291,6 +291,7 @@ struct xfrm_state {
/* Private data of this transformer, format is opaque,
* interpreted by xfrm_type methods. */
void *data;
u8 dir;
};
static inline struct net *xs_net(struct xfrm_state *x)
......
......@@ -337,6 +337,8 @@ enum
LINUX_MIB_XFRMFWDHDRERROR, /* XfrmFwdHdrError*/
LINUX_MIB_XFRMOUTSTATEINVALID, /* XfrmOutStateInvalid */
LINUX_MIB_XFRMACQUIREERROR, /* XfrmAcquireError */
LINUX_MIB_XFRMOUTSTATEDIRERROR, /* XfrmOutStateDirError */
LINUX_MIB_XFRMINSTATEDIRERROR, /* XfrmInStateDirError */
__LINUX_MIB_XFRMMAX
};
......
......@@ -36,7 +36,7 @@ struct udphdr {
#define UDP_GRO 104 /* This socket can receive UDP GRO packets */
/* UDP encapsulation types */
#define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */
#define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* unused draft-ietf-ipsec-nat-t-ike-00/01 */
#define UDP_ENCAP_ESPINUDP 2 /* draft-ietf-ipsec-udp-encaps-06 */
#define UDP_ENCAP_L2TPINUDP 3 /* rfc2661 */
#define UDP_ENCAP_GTP0 4 /* GSM TS 09.60 */
......
......@@ -141,6 +141,11 @@ enum {
XFRM_POLICY_MAX = 3
};
enum xfrm_sa_dir {
XFRM_SA_DIR_IN = 1,
XFRM_SA_DIR_OUT = 2
};
enum {
XFRM_SHARE_ANY, /* No limitations */
XFRM_SHARE_SESSION, /* For this session only */
......@@ -315,6 +320,7 @@ enum xfrm_attr_type_t {
XFRMA_SET_MARK_MASK, /* __u32 */
XFRMA_IF_ID, /* __u32 */
XFRMA_MTIMER_THRESH, /* __u32 in seconds for input SA */
XFRMA_SA_DIR, /* __u8 */
__XFRMA_MAX
#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */
......
......@@ -348,7 +348,6 @@ static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb,
__be16 dport)
{
struct udphdr *uh;
__be32 *udpdata32;
unsigned int len;
len = skb->len + esp->tailen - skb_transport_offset(skb);
......@@ -363,12 +362,6 @@ static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb,
*skb_mac_header(skb) = IPPROTO_UDP;
if (encap_type == UDP_ENCAP_ESPINUDP_NON_IKE) {
udpdata32 = (__be32 *)(uh + 1);
udpdata32[0] = udpdata32[1] = 0;
return (struct ip_esp_hdr *)(udpdata32 + 2);
}
return (struct ip_esp_hdr *)(uh + 1);
}
......@@ -424,7 +417,6 @@ static int esp_output_encap(struct xfrm_state *x, struct sk_buff *skb,
switch (encap_type) {
default:
case UDP_ENCAP_ESPINUDP:
case UDP_ENCAP_ESPINUDP_NON_IKE:
esph = esp_output_udp_encap(skb, encap_type, esp, sport, dport);
break;
case TCP_ENCAP_ESPINTCP:
......@@ -776,7 +768,6 @@ int esp_input_done2(struct sk_buff *skb, int err)
source = th->source;
break;
case UDP_ENCAP_ESPINUDP:
case UDP_ENCAP_ESPINUDP_NON_IKE:
source = uh->source;
break;
default:
......@@ -1180,9 +1171,6 @@ static int esp_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
case UDP_ENCAP_ESPINUDP:
x->props.header_len += sizeof(struct udphdr);
break;
case UDP_ENCAP_ESPINUDP_NON_IKE:
x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32);
break;
#ifdef CONFIG_INET_ESPINTCP
case TCP_ENCAP_ESPINTCP:
/* only the length field, TCP encap is done by
......
......@@ -2712,8 +2712,6 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
#ifdef CONFIG_XFRM
case UDP_ENCAP_ESPINUDP:
set_xfrm_gro_udp_encap_rcv(val, sk->sk_family, sk);
fallthrough;
case UDP_ENCAP_ESPINUDP_NON_IKE:
#if IS_ENABLED(CONFIG_IPV6)
if (sk->sk_family == AF_INET6)
WRITE_ONCE(up->encap_rcv,
......
......@@ -113,19 +113,6 @@ static int __xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb, bool pull
/* Must be an IKE packet.. pass it through */
return 1;
break;
case UDP_ENCAP_ESPINUDP_NON_IKE:
/* Check if this is a keepalive packet. If so, eat it. */
if (len == 1 && udpdata[0] == 0xff) {
return -EINVAL;
} else if (len > 2 * sizeof(u32) + sizeof(struct ip_esp_hdr) &&
udpdata32[0] == 0 && udpdata32[1] == 0) {
/* ESP Packet with Non-IKE marker */
len = sizeof(struct udphdr) + 2 * sizeof(u32);
} else
/* Must be an IKE packet.. pass it through */
return 1;
break;
}
/* At this point we are sure that this is an ESPinUDP packet,
......
......@@ -384,7 +384,6 @@ static struct ip_esp_hdr *esp6_output_udp_encap(struct sk_buff *skb,
__be16 dport)
{
struct udphdr *uh;
__be32 *udpdata32;
unsigned int len;
len = skb->len + esp->tailen - skb_transport_offset(skb);
......@@ -399,12 +398,6 @@ static struct ip_esp_hdr *esp6_output_udp_encap(struct sk_buff *skb,
*skb_mac_header(skb) = IPPROTO_UDP;
if (encap_type == UDP_ENCAP_ESPINUDP_NON_IKE) {
udpdata32 = (__be32 *)(uh + 1);
udpdata32[0] = udpdata32[1] = 0;
return (struct ip_esp_hdr *)(udpdata32 + 2);
}
return (struct ip_esp_hdr *)(uh + 1);
}
......@@ -460,7 +453,6 @@ static int esp6_output_encap(struct xfrm_state *x, struct sk_buff *skb,
switch (encap_type) {
default:
case UDP_ENCAP_ESPINUDP:
case UDP_ENCAP_ESPINUDP_NON_IKE:
esph = esp6_output_udp_encap(skb, encap_type, esp, sport, dport);
break;
case TCP_ENCAP_ESPINTCP:
......@@ -823,7 +815,6 @@ int esp6_input_done2(struct sk_buff *skb, int err)
source = th->source;
break;
case UDP_ENCAP_ESPINUDP:
case UDP_ENCAP_ESPINUDP_NON_IKE:
source = uh->source;
break;
default:
......@@ -1233,9 +1224,6 @@ static int esp6_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
case UDP_ENCAP_ESPINUDP:
x->props.header_len += sizeof(struct udphdr);
break;
case UDP_ENCAP_ESPINUDP_NON_IKE:
x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32);
break;
#ifdef CONFIG_INET6_ESPINTCP
case TCP_ENCAP_ESPINTCP:
/* only the length field, TCP encap is done by
......
......@@ -109,19 +109,6 @@ static int __xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb, bool pull
/* Must be an IKE packet.. pass it through */
return 1;
break;
case UDP_ENCAP_ESPINUDP_NON_IKE:
/* Check if this is a keepalive packet. If so, eat it. */
if (len == 1 && udpdata[0] == 0xff) {
return -EINVAL;
} else if (len > 2 * sizeof(u32) + sizeof(struct ip_esp_hdr) &&
udpdata32[0] == 0 && udpdata32[1] == 0) {
/* ESP Packet with Non-IKE marker */
len = sizeof(struct udphdr) + 2 * sizeof(u32);
} else
/* Must be an IKE packet.. pass it through */
return 1;
break;
}
/* At this point we are sure that this is an ESPinUDP packet,
......@@ -279,6 +266,13 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
if (!x)
continue;
if (unlikely(x->dir && x->dir != XFRM_SA_DIR_IN)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEDIRERROR);
xfrm_state_put(x);
x = NULL;
continue;
}
spin_lock(&x->lock);
if ((!i || (x->props.flags & XFRM_STATE_WILDRECV)) &&
......
......@@ -98,6 +98,7 @@ static const int compat_msg_min[XFRM_NR_MSGTYPES] = {
};
static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
[XFRMA_UNSPEC] = { .strict_start_type = XFRMA_SA_DIR },
[XFRMA_SA] = { .len = XMSGSIZE(compat_xfrm_usersa_info)},
[XFRMA_POLICY] = { .len = XMSGSIZE(compat_xfrm_userpolicy_info)},
[XFRMA_LASTUSED] = { .type = NLA_U64},
......@@ -129,6 +130,7 @@ static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
[XFRMA_SET_MARK_MASK] = { .type = NLA_U32 },
[XFRMA_IF_ID] = { .type = NLA_U32 },
[XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
[XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT),
};
static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb,
......@@ -277,9 +279,10 @@ static int xfrm_xlate64_attr(struct sk_buff *dst, const struct nlattr *src)
case XFRMA_SET_MARK_MASK:
case XFRMA_IF_ID:
case XFRMA_MTIMER_THRESH:
case XFRMA_SA_DIR:
return xfrm_nla_cpy(dst, src, nla_len(src));
default:
BUILD_BUG_ON(XFRMA_MAX != XFRMA_MTIMER_THRESH);
BUILD_BUG_ON(XFRMA_MAX != XFRMA_SA_DIR);
pr_warn_once("unsupported nla_type %d\n", src->nla_type);
return -EOPNOTSUPP;
}
......@@ -434,7 +437,7 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
int err;
if (type > XFRMA_MAX) {
BUILD_BUG_ON(XFRMA_MAX != XFRMA_MTIMER_THRESH);
BUILD_BUG_ON(XFRMA_MAX != XFRMA_SA_DIR);
NL_SET_ERR_MSG(extack, "Bad attribute");
return -EOPNOTSUPP;
}
......
......@@ -253,6 +253,12 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
return -EINVAL;
}
if ((xuo->flags & XFRM_OFFLOAD_INBOUND && x->dir == XFRM_SA_DIR_OUT) ||
(!(xuo->flags & XFRM_OFFLOAD_INBOUND) && x->dir == XFRM_SA_DIR_IN)) {
NL_SET_ERR_MSG(extack, "Mismatched SA and offload direction");
return -EINVAL;
}
is_packet_offload = xuo->flags & XFRM_OFFLOAD_PACKET;
/* We don't yet support UDP encapsulation and TFC padding. */
......
......@@ -466,6 +466,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
if (encap_type < 0 || (xo && xo->flags & XFRM_GRO)) {
x = xfrm_input_state(skb);
if (unlikely(x->dir && x->dir != XFRM_SA_DIR_IN)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEDIRERROR);
goto drop;
}
if (unlikely(x->km.state != XFRM_STATE_VALID)) {
if (x->km.state == XFRM_STATE_ACQ)
XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
......@@ -571,6 +576,12 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
goto drop;
}
if (unlikely(x->dir && x->dir != XFRM_SA_DIR_IN)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEDIRERROR);
xfrm_state_put(x);
goto drop;
}
skb->mark = xfrm_smark_get(skb->mark, x);
sp->xvec[sp->len++] = x;
......
......@@ -2489,6 +2489,12 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl,
x = xfrm_state_find(remote, local, fl, tmpl, policy, &error,
family, policy->if_id);
if (x && x->dir && x->dir != XFRM_SA_DIR_OUT) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEDIRERROR);
xfrm_state_put(x);
error = -EINVAL;
goto fail;
}
if (x && x->km.state == XFRM_STATE_VALID) {
xfrm[nx++] = x;
......
......@@ -41,6 +41,8 @@ static const struct snmp_mib xfrm_mib_list[] = {
SNMP_MIB_ITEM("XfrmFwdHdrError", LINUX_MIB_XFRMFWDHDRERROR),
SNMP_MIB_ITEM("XfrmOutStateInvalid", LINUX_MIB_XFRMOUTSTATEINVALID),
SNMP_MIB_ITEM("XfrmAcquireError", LINUX_MIB_XFRMACQUIREERROR),
SNMP_MIB_ITEM("XfrmOutStateDirError", LINUX_MIB_XFRMOUTSTATEDIRERROR),
SNMP_MIB_ITEM("XfrmInStateDirError", LINUX_MIB_XFRMINSTATEDIRERROR),
SNMP_MIB_SENTINEL
};
......
......@@ -778,7 +778,8 @@ int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack)
}
if (x->props.flags & XFRM_STATE_ESN) {
if (replay_esn->replay_window == 0) {
if (replay_esn->replay_window == 0 &&
(!x->dir || x->dir == XFRM_SA_DIR_IN)) {
NL_SET_ERR_MSG(extack, "ESN replay window must be > 0");
return -EINVAL;
}
......
......@@ -1292,6 +1292,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
if (km_query(x, tmpl, pol) == 0) {
spin_lock_bh(&net->xfrm.xfrm_state_lock);
x->km.state = XFRM_STATE_ACQ;
x->dir = XFRM_SA_DIR_OUT;
list_add(&x->km.all, &net->xfrm.state_all);
XFRM_STATE_INSERT(bydst, &x->bydst,
net->xfrm.state_bydst + h,
......@@ -1744,6 +1745,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
x->lastused = orig->lastused;
x->new_mapping = 0;
x->new_mapping_sport = 0;
x->dir = orig->dir;
return x;
......@@ -1864,8 +1866,14 @@ int xfrm_state_update(struct xfrm_state *x)
}
if (x1->km.state == XFRM_STATE_ACQ) {
if (x->dir && x1->dir != x->dir)
goto out;
__xfrm_state_insert(x);
x = NULL;
} else {
if (x1->dir != x->dir)
goto out;
}
err = 0;
......
......@@ -130,7 +130,7 @@ static inline int verify_sec_ctx_len(struct nlattr **attrs, struct netlink_ext_a
}
static inline int verify_replay(struct xfrm_usersa_info *p,
struct nlattr **attrs,
struct nlattr **attrs, u8 sa_dir,
struct netlink_ext_ack *extack)
{
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
......@@ -168,6 +168,30 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
return -EINVAL;
}
if (sa_dir == XFRM_SA_DIR_OUT) {
if (rs->replay_window) {
NL_SET_ERR_MSG(extack, "Replay window should be 0 for output SA");
return -EINVAL;
}
if (rs->seq || rs->seq_hi) {
NL_SET_ERR_MSG(extack,
"Replay seq and seq_hi should be 0 for output SA");
return -EINVAL;
}
if (rs->bmp_len) {
NL_SET_ERR_MSG(extack, "Replay bmp_len should 0 for output SA");
return -EINVAL;
}
}
if (sa_dir == XFRM_SA_DIR_IN) {
if (rs->oseq || rs->oseq_hi) {
NL_SET_ERR_MSG(extack,
"Replay oseq and oseq_hi should be 0 for input SA");
return -EINVAL;
}
}
return 0;
}
......@@ -176,6 +200,7 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
struct netlink_ext_ack *extack)
{
int err;
u8 sa_dir = attrs[XFRMA_SA_DIR] ? nla_get_u8(attrs[XFRMA_SA_DIR]) : 0;
err = -EINVAL;
switch (p->family) {
......@@ -334,7 +359,7 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
goto out;
if ((err = verify_sec_ctx_len(attrs, extack)))
goto out;
if ((err = verify_replay(p, attrs, extack)))
if ((err = verify_replay(p, attrs, sa_dir, extack)))
goto out;
err = -EINVAL;
......@@ -358,6 +383,77 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
err = -EINVAL;
goto out;
}
if (sa_dir == XFRM_SA_DIR_OUT) {
NL_SET_ERR_MSG(extack,
"MTIMER_THRESH attribute should not be set on output SA");
err = -EINVAL;
goto out;
}
}
if (sa_dir == XFRM_SA_DIR_OUT) {
if (p->flags & XFRM_STATE_DECAP_DSCP) {
NL_SET_ERR_MSG(extack, "Flag DECAP_DSCP should not be set for output SA");
err = -EINVAL;
goto out;
}
if (p->flags & XFRM_STATE_ICMP) {
NL_SET_ERR_MSG(extack, "Flag ICMP should not be set for output SA");
err = -EINVAL;
goto out;
}
if (p->flags & XFRM_STATE_WILDRECV) {
NL_SET_ERR_MSG(extack, "Flag WILDRECV should not be set for output SA");
err = -EINVAL;
goto out;
}
if (p->replay_window) {
NL_SET_ERR_MSG(extack, "Replay window should be 0 for output SA");
err = -EINVAL;
goto out;
}
if (attrs[XFRMA_REPLAY_VAL]) {
struct xfrm_replay_state *replay;
replay = nla_data(attrs[XFRMA_REPLAY_VAL]);
if (replay->seq || replay->bitmap) {
NL_SET_ERR_MSG(extack,
"Replay seq and bitmap should be 0 for output SA");
err = -EINVAL;
goto out;
}
}
}
if (sa_dir == XFRM_SA_DIR_IN) {
if (p->flags & XFRM_STATE_NOPMTUDISC) {
NL_SET_ERR_MSG(extack, "Flag NOPMTUDISC should not be set for input SA");
err = -EINVAL;
goto out;
}
if (attrs[XFRMA_SA_EXTRA_FLAGS]) {
u32 xflags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]);
if (xflags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP) {
NL_SET_ERR_MSG(extack, "Flag DONT_ENCAP_DSCP should not be set for input SA");
err = -EINVAL;
goto out;
}
if (xflags & XFRM_SA_XFLAG_OSEQ_MAY_WRAP) {
NL_SET_ERR_MSG(extack, "Flag OSEQ_MAY_WRAP should not be set for input SA");
err = -EINVAL;
goto out;
}
}
}
out:
......@@ -734,6 +830,9 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
if (attrs[XFRMA_IF_ID])
x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
if (attrs[XFRMA_SA_DIR])
x->dir = nla_get_u8(attrs[XFRMA_SA_DIR]);
err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV], extack);
if (err)
goto error;
......@@ -1182,8 +1281,13 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
if (ret)
goto out;
}
if (x->mapping_maxage)
if (x->mapping_maxage) {
ret = nla_put_u32(skb, XFRMA_MTIMER_THRESH, x->mapping_maxage);
if (ret)
goto out;
}
if (x->dir)
ret = nla_put_u8(skb, XFRMA_SA_DIR, x->dir);
out:
return ret;
}
......@@ -1618,6 +1722,9 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
goto out;
if (attrs[XFRMA_SA_DIR])
x->dir = nla_get_u8(attrs[XFRMA_SA_DIR]);
resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
if (IS_ERR(resp_skb)) {
err = PTR_ERR(resp_skb);
......@@ -2402,7 +2509,8 @@ static inline unsigned int xfrm_aevent_msgsize(struct xfrm_state *x)
+ nla_total_size_64bit(sizeof(struct xfrm_lifetime_cur))
+ nla_total_size(sizeof(struct xfrm_mark))
+ nla_total_size(4) /* XFRM_AE_RTHR */
+ nla_total_size(4); /* XFRM_AE_ETHR */
+ nla_total_size(4) /* XFRM_AE_ETHR */
+ nla_total_size(sizeof(x->dir)); /* XFRMA_SA_DIR */
}
static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
......@@ -2459,6 +2567,12 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct
if (err)
goto out_cancel;
if (x->dir) {
err = nla_put_u8(skb, XFRMA_SA_DIR, x->dir);
if (err)
goto out_cancel;
}
nlmsg_end(skb, nlh);
return 0;
......@@ -3018,6 +3132,7 @@ EXPORT_SYMBOL_GPL(xfrm_msg_min);
#undef XMSGSIZE
const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
[XFRMA_UNSPEC] = { .strict_start_type = XFRMA_SA_DIR },
[XFRMA_SA] = { .len = sizeof(struct xfrm_usersa_info)},
[XFRMA_POLICY] = { .len = sizeof(struct xfrm_userpolicy_info)},
[XFRMA_LASTUSED] = { .type = NLA_U64},
......@@ -3049,6 +3164,7 @@ const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
[XFRMA_SET_MARK_MASK] = { .type = NLA_U32 },
[XFRMA_IF_ID] = { .type = NLA_U32 },
[XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
[XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT),
};
EXPORT_SYMBOL_GPL(xfrma_policy);
......@@ -3097,6 +3213,24 @@ static const struct xfrm_link {
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_get_default },
};
static int xfrm_reject_unused_attr(int type, struct nlattr **attrs,
struct netlink_ext_ack *extack)
{
if (attrs[XFRMA_SA_DIR]) {
switch (type) {
case XFRM_MSG_NEWSA:
case XFRM_MSG_UPDSA:
case XFRM_MSG_ALLOCSPI:
break;
default:
NL_SET_ERR_MSG(extack, "Invalid attribute SA_DIR");
return -EINVAL;
}
}
return 0;
}
static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
......@@ -3156,6 +3290,12 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err < 0)
goto err;
if (!link->nla_pol || link->nla_pol == xfrma_policy) {
err = xfrm_reject_unused_attr((type + XFRM_MSG_BASE), attrs, extack);
if (err < 0)
goto err;
}
if (link->doit == NULL) {
err = -EINVAL;
goto err;
......@@ -3189,8 +3329,9 @@ static void xfrm_netlink_rcv(struct sk_buff *skb)
static inline unsigned int xfrm_expire_msgsize(void)
{
return NLMSG_ALIGN(sizeof(struct xfrm_user_expire))
+ nla_total_size(sizeof(struct xfrm_mark));
return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) +
nla_total_size(sizeof(struct xfrm_mark)) +
nla_total_size(sizeof_field(struct xfrm_state, dir));
}
static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
......@@ -3217,6 +3358,12 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct
if (err)
return err;
if (x->dir) {
err = nla_put_u8(skb, XFRMA_SA_DIR, x->dir);
if (err)
return err;
}
nlmsg_end(skb, nlh);
return 0;
}
......@@ -3324,6 +3471,9 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state *x)
if (x->mapping_maxage)
l += nla_total_size(sizeof(x->mapping_maxage));
if (x->dir)
l += nla_total_size(sizeof(x->dir));
return l;
}
......
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