Skip to content

Commit abc340b

Browse files
ebirgerklassert
authored andcommitted
xfrm: interface: support collect metadata mode
This commit adds support for 'collect_md' mode on xfrm interfaces. Each net can have one collect_md device, created by providing the IFLA_XFRM_COLLECT_METADATA flag at creation. This device cannot be altered and has no if_id or link device attributes. On transmit to this device, the if_id is fetched from the attached dst metadata on the skb. If exists, the link property is also fetched from the metadata. The dst metadata type used is METADATA_XFRM which holds these properties. On the receive side, xfrmi_rcv_cb() populates a dst metadata for each packet received and attaches it to the skb. The if_id used in this case is fetched from the xfrm state, and the link is fetched from the incoming device. This information can later be used by upper layers such as tc, ebpf, and ip rules. Because the skb is scrubed in xfrmi_rcv_cb(), the attachment of the dst metadata is postponed until after scrubing. Similarly, xfrm_input() is adapted to avoid dropping metadata dsts by only dropping 'valid' (skb_valid_dst(skb) == true) dsts. Policy matching on packets arriving from collect_md xfrmi devices is done by using the xfrm state existing in the skb's sec_path. The xfrm_if_cb.decode_cb() interface implemented by xfrmi_decode_session() is changed to keep the details of the if_id extraction tucked away in xfrm_interface.c. Reviewed-by: Nicolas Dichtel <[email protected]> Reviewed-by: Nikolay Aleksandrov <[email protected]> Signed-off-by: Eyal Birger <[email protected]> Signed-off-by: Steffen Klassert <[email protected]>
1 parent 5182a5d commit abc340b

File tree

5 files changed

+121
-29
lines changed

5 files changed

+121
-29
lines changed

include/net/xfrm.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,15 @@ struct km_event {
312312
struct net *net;
313313
};
314314

315+
struct xfrm_if_decode_session_result {
316+
struct net *net;
317+
u32 if_id;
318+
};
319+
315320
struct xfrm_if_cb {
316-
struct xfrm_if *(*decode_session)(struct sk_buff *skb,
317-
unsigned short family);
321+
bool (*decode_session)(struct sk_buff *skb,
322+
unsigned short family,
323+
struct xfrm_if_decode_session_result *res);
318324
};
319325

320326
void xfrm_if_register_cb(const struct xfrm_if_cb *ifcb);
@@ -985,6 +991,7 @@ void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
985991
struct xfrm_if_parms {
986992
int link; /* ifindex of underlying L2 interface */
987993
u32 if_id; /* interface identifyer */
994+
bool collect_md;
988995
};
989996

990997
struct xfrm_if {

include/uapi/linux/if_link.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ enum {
694694
IFLA_XFRM_UNSPEC,
695695
IFLA_XFRM_LINK,
696696
IFLA_XFRM_IF_ID,
697+
IFLA_XFRM_COLLECT_METADATA,
697698
__IFLA_XFRM_MAX
698699
};
699700

net/xfrm/xfrm_input.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <net/xfrm.h>
2121
#include <net/ip_tunnels.h>
2222
#include <net/ip6_tunnel.h>
23+
#include <net/dst_metadata.h>
2324

2425
#include "xfrm_inout.h"
2526

@@ -720,7 +721,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
720721
sp = skb_sec_path(skb);
721722
if (sp)
722723
sp->olen = 0;
723-
skb_dst_drop(skb);
724+
if (skb_valid_dst(skb))
725+
skb_dst_drop(skb);
724726
gro_cells_receive(&gro_cells, skb);
725727
return 0;
726728
} else {
@@ -738,7 +740,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
738740
sp = skb_sec_path(skb);
739741
if (sp)
740742
sp->olen = 0;
741-
skb_dst_drop(skb);
743+
if (skb_valid_dst(skb))
744+
skb_dst_drop(skb);
742745
gro_cells_receive(&gro_cells, skb);
743746
return err;
744747
}

net/xfrm/xfrm_interface.c

Lines changed: 101 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include <net/addrconf.h>
4242
#include <net/xfrm.h>
4343
#include <net/net_namespace.h>
44+
#include <net/dst_metadata.h>
4445
#include <net/netns/generic.h>
4546
#include <linux/etherdevice.h>
4647

@@ -56,6 +57,7 @@ static const struct net_device_ops xfrmi_netdev_ops;
5657
struct xfrmi_net {
5758
/* lists for storing interfaces in use */
5859
struct xfrm_if __rcu *xfrmi[XFRMI_HASH_SIZE];
60+
struct xfrm_if __rcu *collect_md_xfrmi;
5961
};
6062

6163
#define for_each_xfrmi_rcu(start, xi) \
@@ -77,17 +79,23 @@ static struct xfrm_if *xfrmi_lookup(struct net *net, struct xfrm_state *x)
7779
return xi;
7880
}
7981

82+
xi = rcu_dereference(xfrmn->collect_md_xfrmi);
83+
if (xi && (xi->dev->flags & IFF_UP))
84+
return xi;
85+
8086
return NULL;
8187
}
8288

83-
static struct xfrm_if *xfrmi_decode_session(struct sk_buff *skb,
84-
unsigned short family)
89+
static bool xfrmi_decode_session(struct sk_buff *skb,
90+
unsigned short family,
91+
struct xfrm_if_decode_session_result *res)
8592
{
8693
struct net_device *dev;
94+
struct xfrm_if *xi;
8795
int ifindex = 0;
8896

8997
if (!secpath_exists(skb) || !skb->dev)
90-
return NULL;
98+
return false;
9199

92100
switch (family) {
93101
case AF_INET6:
@@ -107,11 +115,18 @@ static struct xfrm_if *xfrmi_decode_session(struct sk_buff *skb,
107115
}
108116

109117
if (!dev || !(dev->flags & IFF_UP))
110-
return NULL;
118+
return false;
111119
if (dev->netdev_ops != &xfrmi_netdev_ops)
112-
return NULL;
120+
return false;
113121

114-
return netdev_priv(dev);
122+
xi = netdev_priv(dev);
123+
res->net = xi->net;
124+
125+
if (xi->p.collect_md)
126+
res->if_id = xfrm_input_state(skb)->if_id;
127+
else
128+
res->if_id = xi->p.if_id;
129+
return true;
115130
}
116131

117132
static void xfrmi_link(struct xfrmi_net *xfrmn, struct xfrm_if *xi)
@@ -157,7 +172,10 @@ static int xfrmi_create(struct net_device *dev)
157172
if (err < 0)
158173
goto out;
159174

160-
xfrmi_link(xfrmn, xi);
175+
if (xi->p.collect_md)
176+
rcu_assign_pointer(xfrmn->collect_md_xfrmi, xi);
177+
else
178+
xfrmi_link(xfrmn, xi);
161179

162180
return 0;
163181

@@ -185,7 +203,10 @@ static void xfrmi_dev_uninit(struct net_device *dev)
185203
struct xfrm_if *xi = netdev_priv(dev);
186204
struct xfrmi_net *xfrmn = net_generic(xi->net, xfrmi_net_id);
187205

188-
xfrmi_unlink(xfrmn, xi);
206+
if (xi->p.collect_md)
207+
RCU_INIT_POINTER(xfrmn->collect_md_xfrmi, NULL);
208+
else
209+
xfrmi_unlink(xfrmn, xi);
189210
}
190211

191212
static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet)
@@ -214,6 +235,7 @@ static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
214235
struct xfrm_state *x;
215236
struct xfrm_if *xi;
216237
bool xnet;
238+
int link;
217239

218240
if (err && !secpath_exists(skb))
219241
return 0;
@@ -224,6 +246,7 @@ static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
224246
if (!xi)
225247
return 1;
226248

249+
link = skb->dev->ifindex;
227250
dev = xi->dev;
228251
skb->dev = dev;
229252

@@ -254,6 +277,17 @@ static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
254277
}
255278

256279
xfrmi_scrub_packet(skb, xnet);
280+
if (xi->p.collect_md) {
281+
struct metadata_dst *md_dst;
282+
283+
md_dst = metadata_dst_alloc(0, METADATA_XFRM, GFP_ATOMIC);
284+
if (!md_dst)
285+
return -ENOMEM;
286+
287+
md_dst->u.xfrm_info.if_id = x->if_id;
288+
md_dst->u.xfrm_info.link = link;
289+
skb_dst_set(skb, (struct dst_entry *)md_dst);
290+
}
257291
dev_sw_netstats_rx_add(dev, skb->len);
258292

259293
return 0;
@@ -269,10 +303,23 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
269303
struct net_device *tdev;
270304
struct xfrm_state *x;
271305
int err = -1;
306+
u32 if_id;
272307
int mtu;
273308

309+
if (xi->p.collect_md) {
310+
struct xfrm_md_info *md_info = skb_xfrm_md_info(skb);
311+
312+
if (unlikely(!md_info))
313+
return -EINVAL;
314+
315+
if_id = md_info->if_id;
316+
fl->flowi_oif = md_info->link;
317+
} else {
318+
if_id = xi->p.if_id;
319+
}
320+
274321
dst_hold(dst);
275-
dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, xi->p.if_id);
322+
dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, if_id);
276323
if (IS_ERR(dst)) {
277324
err = PTR_ERR(dst);
278325
dst = NULL;
@@ -283,7 +330,7 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
283330
if (!x)
284331
goto tx_err_link_failure;
285332

286-
if (x->if_id != xi->p.if_id)
333+
if (x->if_id != if_id)
287334
goto tx_err_link_failure;
288335

289336
tdev = dst->dev;
@@ -633,6 +680,9 @@ static void xfrmi_netlink_parms(struct nlattr *data[],
633680

634681
if (data[IFLA_XFRM_IF_ID])
635682
parms->if_id = nla_get_u32(data[IFLA_XFRM_IF_ID]);
683+
684+
if (data[IFLA_XFRM_COLLECT_METADATA])
685+
parms->collect_md = true;
636686
}
637687

638688
static int xfrmi_newlink(struct net *src_net, struct net_device *dev,
@@ -645,14 +695,27 @@ static int xfrmi_newlink(struct net *src_net, struct net_device *dev,
645695
int err;
646696

647697
xfrmi_netlink_parms(data, &p);
648-
if (!p.if_id) {
649-
NL_SET_ERR_MSG(extack, "if_id must be non zero");
650-
return -EINVAL;
651-
}
698+
if (p.collect_md) {
699+
struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
652700

653-
xi = xfrmi_locate(net, &p);
654-
if (xi)
655-
return -EEXIST;
701+
if (p.link || p.if_id) {
702+
NL_SET_ERR_MSG(extack, "link and if_id must be zero");
703+
return -EINVAL;
704+
}
705+
706+
if (rtnl_dereference(xfrmn->collect_md_xfrmi))
707+
return -EEXIST;
708+
709+
} else {
710+
if (!p.if_id) {
711+
NL_SET_ERR_MSG(extack, "if_id must be non zero");
712+
return -EINVAL;
713+
}
714+
715+
xi = xfrmi_locate(net, &p);
716+
if (xi)
717+
return -EEXIST;
718+
}
656719

657720
xi = netdev_priv(dev);
658721
xi->p = p;
@@ -682,12 +745,22 @@ static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[],
682745
return -EINVAL;
683746
}
684747

748+
if (p.collect_md) {
749+
NL_SET_ERR_MSG(extack, "collect_md can't be changed");
750+
return -EINVAL;
751+
}
752+
685753
xi = xfrmi_locate(net, &p);
686754
if (!xi) {
687755
xi = netdev_priv(dev);
688756
} else {
689757
if (xi->dev != dev)
690758
return -EEXIST;
759+
if (xi->p.collect_md) {
760+
NL_SET_ERR_MSG(extack,
761+
"device can't be changed to collect_md");
762+
return -EINVAL;
763+
}
691764
}
692765

693766
return xfrmi_update(xi, &p);
@@ -700,6 +773,8 @@ static size_t xfrmi_get_size(const struct net_device *dev)
700773
nla_total_size(4) +
701774
/* IFLA_XFRM_IF_ID */
702775
nla_total_size(4) +
776+
/* IFLA_XFRM_COLLECT_METADATA */
777+
nla_total_size(0) +
703778
0;
704779
}
705780

@@ -709,7 +784,8 @@ static int xfrmi_fill_info(struct sk_buff *skb, const struct net_device *dev)
709784
struct xfrm_if_parms *parm = &xi->p;
710785

711786
if (nla_put_u32(skb, IFLA_XFRM_LINK, parm->link) ||
712-
nla_put_u32(skb, IFLA_XFRM_IF_ID, parm->if_id))
787+
nla_put_u32(skb, IFLA_XFRM_IF_ID, parm->if_id) ||
788+
(xi->p.collect_md && nla_put_flag(skb, IFLA_XFRM_COLLECT_METADATA)))
713789
goto nla_put_failure;
714790
return 0;
715791

@@ -725,8 +801,10 @@ static struct net *xfrmi_get_link_net(const struct net_device *dev)
725801
}
726802

727803
static const struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = {
728-
[IFLA_XFRM_LINK] = { .type = NLA_U32 },
729-
[IFLA_XFRM_IF_ID] = { .type = NLA_U32 },
804+
[IFLA_XFRM_UNSPEC] = { .strict_start_type = IFLA_XFRM_COLLECT_METADATA },
805+
[IFLA_XFRM_LINK] = { .type = NLA_U32 },
806+
[IFLA_XFRM_IF_ID] = { .type = NLA_U32 },
807+
[IFLA_XFRM_COLLECT_METADATA] = { .type = NLA_FLAG },
730808
};
731809

732810
static struct rtnl_link_ops xfrmi_link_ops __read_mostly = {
@@ -762,6 +840,9 @@ static void __net_exit xfrmi_exit_batch_net(struct list_head *net_exit_list)
762840
xip = &xi->next)
763841
unregister_netdevice_queue(xi->dev, &list);
764842
}
843+
xi = rtnl_dereference(xfrmn->collect_md_xfrmi);
844+
if (xi)
845+
unregister_netdevice_queue(xi->dev, &list);
765846
}
766847
unregister_netdevice_many(&list);
767848
rtnl_unlock();

net/xfrm/xfrm_policy.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3515,17 +3515,17 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
35153515
int xerr_idx = -1;
35163516
const struct xfrm_if_cb *ifcb;
35173517
struct sec_path *sp;
3518-
struct xfrm_if *xi;
35193518
u32 if_id = 0;
35203519

35213520
rcu_read_lock();
35223521
ifcb = xfrm_if_get_cb();
35233522

35243523
if (ifcb) {
3525-
xi = ifcb->decode_session(skb, family);
3526-
if (xi) {
3527-
if_id = xi->p.if_id;
3528-
net = xi->net;
3524+
struct xfrm_if_decode_session_result r;
3525+
3526+
if (ifcb->decode_session(skb, family, &r)) {
3527+
if_id = r.if_id;
3528+
net = r.net;
35293529
}
35303530
}
35313531
rcu_read_unlock();

0 commit comments

Comments
 (0)