History

netkit: Add single device mode for netkit

Add a single device mode for netkit instead of netkit pairs. The primary target for the paired devices is to connect network namespaces, of course, and support has been implemented in projects like Cilium [0]. For the rxq leasing the plan is to support two main scenarios related to single device mode: * For the use-case of io_uring zero-copy, the control plane can either set up a netkit pair where the peer device can perform rxq leasing which is then tied to the lifetime of the peer device, or the control plane can use a regular netkit pair to connect the hostns to a Pod/container and dynamically add/remove rxq leasing through a single device without having to interrupt the device pair. In the case of io_uring, the memory pool is used as skb non-linear pages, and thus the skb will go its way through the regular stack into netkit. Things like the netkit policy when no BPF is attached or skb scrubbing etc apply as-is in case the paired devices are used, or if the backend memory is tied to the single device and traffic goes through a paired device. * For the use-case of AF_XDP, the control plane needs to use netkit in the single device mode. The single device mode currently enforces only a pass policy when no BPF is attached, and does not yet support BPF link attachments for AF_XDP. skbs sent to that device get dropped at the moment. Given AF_XDP operates at a lower layer of the stack tying this to the netkit pair did not make sense. In future, the plan is to allow BPF at the XDP layer which can: i) process traffic coming from the AF_XDP application (e.g. QEMU with AF_XDP backend) to filter egress traffic or to push selected egress traffic up to the single netkit device to the local stack (e.g. DHCP requests), and ii) vice-versa skbs sent to the single netkit into the AF_XDP application (e.g. DHCP replies). Also, the control-plane can dynamically manage rxq leasing for the single netkit device without having to interrupt (e.g. down/up cycle) the main netkit pair for the Pod which has traffic going in and out.

Daniel Borkmann committed 10 months ago

Commit b5c3fa41 parent 0073d2f2 files · +76 −40Browse files

Unified · Split

2 files
  1. drivers/net/netkit.c +70 −40
  2. include/uapi/linux/if_link.h +6 −0
drivers/net/netkit.c+70−40
@@ -26,6 +26,7 @@ struct netkit { · 25 unmodified lines
2626
2727 __cacheline_group_begin(netkit_slowpath);
2828 enum netkit_mode mode;
29+ enum netkit_pairing pair;
2930 bool primary;
3031 u32 headroom;
3132 __cacheline_group_end(netkit_slowpath);
@@ -135,6 +136,10 @@ static int netkit_open(struct net_device *dev) · 103 unmodified lines
135136 struct netkit *nk = netkit_priv(dev);
136137 struct net_device *peer = rtnl_dereference(nk->peer);
137138
139+ if (nk->pair == NETKIT_DEVICE_SINGLE) {
140+ netif_carrier_on(dev);
141+ return 0;
142+ }
138143 if (!peer)
139144 return -ENOTCONN;
140145 if (peer->flags & IFF_UP) {
@@ -335,6 +340,7 @@ static int netkit_new_link(struct net_device *dev, · 194 unmodified lines
335340 enum netkit_scrub scrub_prim = NETKIT_SCRUB_DEFAULT;
336341 enum netkit_scrub scrub_peer = NETKIT_SCRUB_DEFAULT;
337342 struct nlattr *peer_tb[IFLA_MAX + 1], **tbp, *attr;
343+ enum netkit_pairing pair = NETKIT_DEVICE_PAIR;
338344 enum netkit_action policy_prim = NETKIT_PASS;
339345 enum netkit_action policy_peer = NETKIT_PASS;
340346 struct nlattr **data = params->data;
@@ -343,7 +349,8 @@ static int netkit_new_link(struct net_device *dev, · 2 unmodified lines
343349 struct nlattr **tb = params->tb;
344350 u16 headroom = 0, tailroom = 0;
345351 struct ifinfomsg *ifmp = NULL;
346− struct net_device *peer;
352+ struct net_device *peer = NULL;
353+ bool seen_peer = false;
347354 char ifname[IFNAMSIZ];
348355 struct netkit *nk;
349356 int err;
@@ -380,6 +387,12 @@ static int netkit_new_link(struct net_device *dev, · 30 unmodified lines
380387 headroom = nla_get_u16(data[IFLA_NETKIT_HEADROOM]);
381388 if (data[IFLA_NETKIT_TAILROOM])
382389 tailroom = nla_get_u16(data[IFLA_NETKIT_TAILROOM]);
390+ if (data[IFLA_NETKIT_PAIRING])
391+ pair = nla_get_u32(data[IFLA_NETKIT_PAIRING]);
392+
393+ seen_peer = data[IFLA_NETKIT_PEER_INFO] ||
394+ data[IFLA_NETKIT_PEER_SCRUB] ||
395+ data[IFLA_NETKIT_PEER_POLICY];
383396 }
384397
385398 if (ifmp && tbp[IFLA_IFNAME]) {
@@ -392,45 +405,46 @@ static int netkit_new_link(struct net_device *dev, · 6 unmodified lines
392405 if (mode != NETKIT_L2 &&
393406 (tb[IFLA_ADDRESS] || tbp[IFLA_ADDRESS]))
394407 return -EOPNOTSUPP;
408+ if (pair == NETKIT_DEVICE_SINGLE &&
409+ (tb != tbp || seen_peer || policy_prim != NETKIT_PASS))
410+ return -EOPNOTSUPP;
395411
396− peer = rtnl_create_link(peer_net, ifname, ifname_assign_type,
397− &netkit_link_ops, tbp, extack);
398− if (IS_ERR(peer))
399− return PTR_ERR(peer);
400−
401− netif_inherit_tso_max(peer, dev);
402− if (headroom) {
403− peer->needed_headroom = headroom;
404− dev->needed_headroom = headroom;
405− }
406− if (tailroom) {
407− peer->needed_tailroom = tailroom;
408− dev->needed_tailroom = tailroom;
409− }
410−
411− if (mode == NETKIT_L2 && !(ifmp && tbp[IFLA_ADDRESS]))
412− eth_hw_addr_random(peer);
413− if (ifmp && dev->ifindex)
414− peer->ifindex = ifmp->ifi_index;
415−
416− nk = netkit_priv(peer);
417− nk->primary = false;
418− nk->policy = policy_peer;
419− nk->scrub = scrub_peer;
420− nk->mode = mode;
421− nk->headroom = headroom;
422− bpf_mprog_bundle_init(&nk->bundle);
412+ if (pair == NETKIT_DEVICE_PAIR) {
413+ peer = rtnl_create_link(peer_net, ifname, ifname_assign_type,
414+ &netkit_link_ops, tbp, extack);
415+ if (IS_ERR(peer))
416+ return PTR_ERR(peer);
417+
418+ netif_inherit_tso_max(peer, dev);
419+ if (headroom)
420+ peer->needed_headroom = headroom;
421+ if (tailroom)
422+ peer->needed_tailroom = tailroom;
423+ if (mode == NETKIT_L2 && !(ifmp && tbp[IFLA_ADDRESS]))
424+ eth_hw_addr_random(peer);
425+ if (ifmp && dev->ifindex)
426+ peer->ifindex = ifmp->ifi_index;
423427
424− err = register_netdevice(peer);
425− if (err < 0)
426− goto err_register_peer;
427− netif_carrier_off(peer);
428− if (mode == NETKIT_L2)
429− dev_change_flags(peer, peer->flags & ~IFF_NOARP, NULL);
428+ nk = netkit_priv(peer);
429+ nk->primary = false;
430+ nk->policy = policy_peer;
431+ nk->scrub = scrub_peer;
432+ nk->mode = mode;
433+ nk->pair = pair;
434+ nk->headroom = headroom;
435+ bpf_mprog_bundle_init(&nk->bundle);
436+
437+ err = register_netdevice(peer);
438+ if (err < 0)
439+ goto err_register_peer;
440+ netif_carrier_off(peer);
441+ if (mode == NETKIT_L2)
442+ dev_change_flags(peer, peer->flags & ~IFF_NOARP, NULL);
430443
431− err = rtnl_configure_link(peer, NULL, 0, NULL);
432− if (err < 0)
433− goto err_configure_peer;
444+ err = rtnl_configure_link(peer, NULL, 0, NULL);
445+ if (err < 0)
446+ goto err_configure_peer;
447+ }
434448
435449 if (mode == NETKIT_L2 && !tb[IFLA_ADDRESS])
436450 eth_hw_addr_random(dev);
@@ -438,12 +452,17 @@ static int netkit_new_link(struct net_device *dev, · 1 unmodified line
438452 nla_strscpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);
439453 else
440454 strscpy(dev->name, "nk%d", IFNAMSIZ);
455+ if (headroom)
456+ dev->needed_headroom = headroom;
457+ if (tailroom)
458+ dev->needed_tailroom = tailroom;
441459
442460 nk = netkit_priv(dev);
443461 nk->primary = true;
444462 nk->policy = policy_prim;
445463 nk->scrub = scrub_prim;
446464 nk->mode = mode;
465+ nk->pair = pair;
447466 nk->headroom = headroom;
448467 bpf_mprog_bundle_init(&nk->bundle);
449468
@@ -455,10 +474,12 @@ static int netkit_new_link(struct net_device *dev, · 5 unmodified lines
455474 dev_change_flags(dev, dev->flags & ~IFF_NOARP, NULL);
456475
457476 rcu_assign_pointer(netkit_priv(dev)->peer, peer);
458− rcu_assign_pointer(netkit_priv(peer)->peer, dev);
477+ if (peer)
478+ rcu_assign_pointer(netkit_priv(peer)->peer, dev);
459479 return 0;
460480 err_configure_peer:
461− unregister_netdevice(peer);
481+ if (peer)
482+ unregister_netdevice(peer);
462483 return err;
463484 err_register_peer:
464485 free_netdev(peer);
@@ -518,6 +539,8 @@ static struct net_device *netkit_dev_fetch(struct net *net, u32 ifindex, u32 whi · 53 unmodified lines
518539 nk = netkit_priv(dev);
519540 if (!nk->primary)
520541 return ERR_PTR(-EACCES);
542+ if (nk->pair == NETKIT_DEVICE_SINGLE)
543+ return ERR_PTR(-EOPNOTSUPP);
521544 if (which == BPF_NETKIT_PEER) {
522545 dev = rcu_dereference_rtnl(nk->peer);
523546 if (!dev)
@@ -879,6 +902,7 @@ static int netkit_change_link(struct net_device *dev, struct nlattr *tb[], · 355 unmodified lines
879902 { IFLA_NETKIT_PEER_INFO, "peer info" },
880903 { IFLA_NETKIT_HEADROOM, "headroom" },
881904 { IFLA_NETKIT_TAILROOM, "tailroom" },
905+ { IFLA_NETKIT_PAIRING, "pairing" },
882906 };
883907
884908 if (!nk->primary) {
@@ -898,9 +922,11 @@ static int netkit_change_link(struct net_device *dev, struct nlattr *tb[], · 13 unmodified lines
898922 }
899923
900924 if (data[IFLA_NETKIT_POLICY]) {
925+ err = -EOPNOTSUPP;
901926 attr = data[IFLA_NETKIT_POLICY];
902927 policy = nla_get_u32(attr);
903− err = netkit_check_policy(policy, attr, extack);
928+ if (nk->pair == NETKIT_DEVICE_PAIR)
929+ err = netkit_check_policy(policy, attr, extack);
904930 if (err)
905931 return err;
906932 WRITE_ONCE(nk->policy, policy);
@@ -931,6 +957,7 @@ static size_t netkit_get_size(const struct net_device *dev) · 24 unmodified lines
931957 nla_total_size(sizeof(u8)) + /* IFLA_NETKIT_PRIMARY */
932958 nla_total_size(sizeof(u16)) + /* IFLA_NETKIT_HEADROOM */
933959 nla_total_size(sizeof(u16)) + /* IFLA_NETKIT_TAILROOM */
960+ nla_total_size(sizeof(u32)) + /* IFLA_NETKIT_PAIRING */
934961 0;
935962 }
936963
@@ -951,6 +978,8 @@ static int netkit_fill_info(struct sk_buff *skb, const struct net_device *dev) · 14 unmodified lines
951978 return -EMSGSIZE;
952979 if (nla_put_u16(skb, IFLA_NETKIT_TAILROOM, dev->needed_tailroom))
953980 return -EMSGSIZE;
981+ if (nla_put_u32(skb, IFLA_NETKIT_PAIRING, nk->pair))
982+ return -EMSGSIZE;
954983
955984 if (peer) {
956985 nk = netkit_priv(peer);
@@ -972,6 +1001,7 @@ static const struct nla_policy netkit_policy[IFLA_NETKIT_MAX + 1] = { · 15 unmodified lines
9721001 [IFLA_NETKIT_TAILROOM] = { .type = NLA_U16 },
9731002 [IFLA_NETKIT_SCRUB] = NLA_POLICY_MAX(NLA_U32, NETKIT_SCRUB_DEFAULT),
9741003 [IFLA_NETKIT_PEER_SCRUB] = NLA_POLICY_MAX(NLA_U32, NETKIT_SCRUB_DEFAULT),
1004+ [IFLA_NETKIT_PAIRING] = NLA_POLICY_MAX(NLA_U32, NETKIT_DEVICE_SINGLE),
9751005 [IFLA_NETKIT_PRIMARY] = { .type = NLA_REJECT,
9761006 .reject_message = "Primary attribute is read-only" },
9771007 };
include/uapi/linux/if_link.h+6−0
@@ -1296,6 +1296,11 @@ enum netkit_mode { · 1295 unmodified lines
12961296 NETKIT_L3,
12971297 };
12981298
1299+enum netkit_pairing {
1300+ NETKIT_DEVICE_PAIR,
1301+ NETKIT_DEVICE_SINGLE,
1302+};
1303+
12991304 /* NETKIT_SCRUB_NONE leaves clearing skb->{mark,priority} up to
13001305 * the BPF program if attached. This also means the latter can
13011306 * consume the two fields if they were populated earlier.
@@ -1320,6 +1325,7 @@ enum { · 18 unmodified lines
13201325 IFLA_NETKIT_PEER_SCRUB,
13211326 IFLA_NETKIT_HEADROOM,
13221327 IFLA_NETKIT_TAILROOM,
1328+ IFLA_NETKIT_PAIRING,
13231329 __IFLA_NETKIT_MAX,
13241330 };
13251331 #define IFLA_NETKIT_MAX (__IFLA_NETKIT_MAX - 1)