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
26 26
27 __cacheline_group_begin(netkit_slowpath);27 __cacheline_group_begin(netkit_slowpath);
28 enum netkit_mode mode;28 enum netkit_mode mode;
29 enum netkit_pairing pair;
29 bool primary;30 bool primary;
30 u32 headroom;31 u32 headroom;
31 __cacheline_group_end(netkit_slowpath);32 __cacheline_group_end(netkit_slowpath);
@@ -135,6 +136,10 @@ static int netkit_open(struct net_device *dev) · 103 unmodified lines
135 struct netkit *nk = netkit_priv(dev);136 struct netkit *nk = netkit_priv(dev);
136 struct net_device *peer = rtnl_dereference(nk->peer);137 struct net_device *peer = rtnl_dereference(nk->peer);
137 138
139 if (nk->pair == NETKIT_DEVICE_SINGLE) {
140 netif_carrier_on(dev);
141 return 0;
142 }
138 if (!peer)143 if (!peer)
139 return -ENOTCONN;144 return -ENOTCONN;
140 if (peer->flags & IFF_UP) {145 if (peer->flags & IFF_UP) {
@@ -335,6 +340,7 @@ static int netkit_new_link(struct net_device *dev, · 194 unmodified lines
335 enum netkit_scrub scrub_prim = NETKIT_SCRUB_DEFAULT;340 enum netkit_scrub scrub_prim = NETKIT_SCRUB_DEFAULT;
336 enum netkit_scrub scrub_peer = NETKIT_SCRUB_DEFAULT;341 enum netkit_scrub scrub_peer = NETKIT_SCRUB_DEFAULT;
337 struct nlattr *peer_tb[IFLA_MAX + 1], **tbp, *attr;342 struct nlattr *peer_tb[IFLA_MAX + 1], **tbp, *attr;
343 enum netkit_pairing pair = NETKIT_DEVICE_PAIR;
338 enum netkit_action policy_prim = NETKIT_PASS;344 enum netkit_action policy_prim = NETKIT_PASS;
339 enum netkit_action policy_peer = NETKIT_PASS;345 enum netkit_action policy_peer = NETKIT_PASS;
340 struct nlattr **data = params->data;346 struct nlattr **data = params->data;
@@ -343,7 +349,8 @@ static int netkit_new_link(struct net_device *dev, · 2 unmodified lines
343 struct nlattr **tb = params->tb;349 struct nlattr **tb = params->tb;
344 u16 headroom = 0, tailroom = 0;350 u16 headroom = 0, tailroom = 0;
345 struct ifinfomsg *ifmp = NULL;351 struct ifinfomsg *ifmp = NULL;
346 struct net_device *peer;352 struct net_device *peer = NULL;
353 bool seen_peer = false;
347 char ifname[IFNAMSIZ];354 char ifname[IFNAMSIZ];
348 struct netkit *nk;355 struct netkit *nk;
349 int err;356 int err;
@@ -380,6 +387,12 @@ static int netkit_new_link(struct net_device *dev, · 30 unmodified lines
380 headroom = nla_get_u16(data[IFLA_NETKIT_HEADROOM]);387 headroom = nla_get_u16(data[IFLA_NETKIT_HEADROOM]);
381 if (data[IFLA_NETKIT_TAILROOM])388 if (data[IFLA_NETKIT_TAILROOM])
382 tailroom = nla_get_u16(data[IFLA_NETKIT_TAILROOM]);389 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];
383 }396 }
384 397
385 if (ifmp && tbp[IFLA_IFNAME]) {398 if (ifmp && tbp[IFLA_IFNAME]) {
@@ -392,45 +405,46 @@ static int netkit_new_link(struct net_device *dev, · 6 unmodified lines
392 if (mode != NETKIT_L2 &&405 if (mode != NETKIT_L2 &&
393 (tb[IFLA_ADDRESS] || tbp[IFLA_ADDRESS]))406 (tb[IFLA_ADDRESS] || tbp[IFLA_ADDRESS]))
394 return -EOPNOTSUPP;407 return -EOPNOTSUPP;
408 if (pair == NETKIT_DEVICE_SINGLE &&
409 (tb != tbp || seen_peer || policy_prim != NETKIT_PASS))
410 return -EOPNOTSUPP;
395 411
396 peer = rtnl_create_link(peer_net, ifname, ifname_assign_type,412 if (pair == NETKIT_DEVICE_PAIR) {
397 &netkit_link_ops, tbp, extack);413 peer = rtnl_create_link(peer_net, ifname, ifname_assign_type,
398 if (IS_ERR(peer))414 &netkit_link_ops, tbp, extack);
399 return PTR_ERR(peer);415 if (IS_ERR(peer))
400 416 return PTR_ERR(peer);
401 netif_inherit_tso_max(peer, dev);417
402 if (headroom) {418 netif_inherit_tso_max(peer, dev);
403 peer->needed_headroom = headroom;419 if (headroom)
404 dev->needed_headroom = headroom;420 peer->needed_headroom = headroom;
405 }421 if (tailroom)
406 if (tailroom) {422 peer->needed_tailroom = tailroom;
407 peer->needed_tailroom = tailroom;423 if (mode == NETKIT_L2 && !(ifmp && tbp[IFLA_ADDRESS]))
408 dev->needed_tailroom = tailroom;424 eth_hw_addr_random(peer);
409 }425 if (ifmp && dev->ifindex)
410 426 peer->ifindex = ifmp->ifi_index;
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);
423 427
424 err = register_netdevice(peer);428 nk = netkit_priv(peer);
425 if (err < 0)429 nk->primary = false;
426 goto err_register_peer;430 nk->policy = policy_peer;
427 netif_carrier_off(peer);431 nk->scrub = scrub_peer;
428 if (mode == NETKIT_L2)432 nk->mode = mode;
429 dev_change_flags(peer, peer->flags & ~IFF_NOARP, NULL);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);
430 443
431 err = rtnl_configure_link(peer, NULL, 0, NULL);444 err = rtnl_configure_link(peer, NULL, 0, NULL);
432 if (err < 0)445 if (err < 0)
433 goto err_configure_peer;446 goto err_configure_peer;
447 }
434 448
435 if (mode == NETKIT_L2 && !tb[IFLA_ADDRESS])449 if (mode == NETKIT_L2 && !tb[IFLA_ADDRESS])
436 eth_hw_addr_random(dev);450 eth_hw_addr_random(dev);
@@ -438,12 +452,17 @@ static int netkit_new_link(struct net_device *dev, · 1 unmodified line
438 nla_strscpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);452 nla_strscpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);
439 else453 else
440 strscpy(dev->name, "nk%d", IFNAMSIZ);454 strscpy(dev->name, "nk%d", IFNAMSIZ);
455 if (headroom)
456 dev->needed_headroom = headroom;
457 if (tailroom)
458 dev->needed_tailroom = tailroom;
441 459
442 nk = netkit_priv(dev);460 nk = netkit_priv(dev);
443 nk->primary = true;461 nk->primary = true;
444 nk->policy = policy_prim;462 nk->policy = policy_prim;
445 nk->scrub = scrub_prim;463 nk->scrub = scrub_prim;
446 nk->mode = mode;464 nk->mode = mode;
465 nk->pair = pair;
447 nk->headroom = headroom;466 nk->headroom = headroom;
448 bpf_mprog_bundle_init(&nk->bundle);467 bpf_mprog_bundle_init(&nk->bundle);
449 468
@@ -455,10 +474,12 @@ static int netkit_new_link(struct net_device *dev, · 5 unmodified lines
455 dev_change_flags(dev, dev->flags & ~IFF_NOARP, NULL);474 dev_change_flags(dev, dev->flags & ~IFF_NOARP, NULL);
456 475
457 rcu_assign_pointer(netkit_priv(dev)->peer, peer);476 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);
459 return 0;479 return 0;
460err_configure_peer:480err_configure_peer:
461 unregister_netdevice(peer);481 if (peer)
482 unregister_netdevice(peer);
462 return err;483 return err;
463err_register_peer:484err_register_peer:
464 free_netdev(peer);485 free_netdev(peer);
@@ -518,6 +539,8 @@ static struct net_device *netkit_dev_fetch(struct net *net, u32 ifindex, u32 whi · 53 unmodified lines
518 nk = netkit_priv(dev);539 nk = netkit_priv(dev);
519 if (!nk->primary)540 if (!nk->primary)
520 return ERR_PTR(-EACCES);541 return ERR_PTR(-EACCES);
542 if (nk->pair == NETKIT_DEVICE_SINGLE)
543 return ERR_PTR(-EOPNOTSUPP);
521 if (which == BPF_NETKIT_PEER) {544 if (which == BPF_NETKIT_PEER) {
522 dev = rcu_dereference_rtnl(nk->peer);545 dev = rcu_dereference_rtnl(nk->peer);
523 if (!dev)546 if (!dev)
@@ -879,6 +902,7 @@ static int netkit_change_link(struct net_device *dev, struct nlattr *tb[], · 355 unmodified lines
879 { IFLA_NETKIT_PEER_INFO, "peer info" },902 { IFLA_NETKIT_PEER_INFO, "peer info" },
880 { IFLA_NETKIT_HEADROOM, "headroom" },903 { IFLA_NETKIT_HEADROOM, "headroom" },
881 { IFLA_NETKIT_TAILROOM, "tailroom" },904 { IFLA_NETKIT_TAILROOM, "tailroom" },
905 { IFLA_NETKIT_PAIRING, "pairing" },
882 };906 };
883 907
884 if (!nk->primary) {908 if (!nk->primary) {
@@ -898,9 +922,11 @@ static int netkit_change_link(struct net_device *dev, struct nlattr *tb[], · 13 unmodified lines
898 }922 }
899 923
900 if (data[IFLA_NETKIT_POLICY]) {924 if (data[IFLA_NETKIT_POLICY]) {
925 err = -EOPNOTSUPP;
901 attr = data[IFLA_NETKIT_POLICY];926 attr = data[IFLA_NETKIT_POLICY];
902 policy = nla_get_u32(attr);927 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);
904 if (err)930 if (err)
905 return err;931 return err;
906 WRITE_ONCE(nk->policy, policy);932 WRITE_ONCE(nk->policy, policy);
@@ -931,6 +957,7 @@ static size_t netkit_get_size(const struct net_device *dev) · 24 unmodified lines
931 nla_total_size(sizeof(u8)) + /* IFLA_NETKIT_PRIMARY */957 nla_total_size(sizeof(u8)) + /* IFLA_NETKIT_PRIMARY */
932 nla_total_size(sizeof(u16)) + /* IFLA_NETKIT_HEADROOM */958 nla_total_size(sizeof(u16)) + /* IFLA_NETKIT_HEADROOM */
933 nla_total_size(sizeof(u16)) + /* IFLA_NETKIT_TAILROOM */959 nla_total_size(sizeof(u16)) + /* IFLA_NETKIT_TAILROOM */
960 nla_total_size(sizeof(u32)) + /* IFLA_NETKIT_PAIRING */
934 0;961 0;
935}962}
936 963
@@ -951,6 +978,8 @@ static int netkit_fill_info(struct sk_buff *skb, const struct net_device *dev) · 14 unmodified lines
951 return -EMSGSIZE;978 return -EMSGSIZE;
952 if (nla_put_u16(skb, IFLA_NETKIT_TAILROOM, dev->needed_tailroom))979 if (nla_put_u16(skb, IFLA_NETKIT_TAILROOM, dev->needed_tailroom))
953 return -EMSGSIZE;980 return -EMSGSIZE;
981 if (nla_put_u32(skb, IFLA_NETKIT_PAIRING, nk->pair))
982 return -EMSGSIZE;
954 983
955 if (peer) {984 if (peer) {
956 nk = netkit_priv(peer);985 nk = netkit_priv(peer);
@@ -972,6 +1001,7 @@ static const struct nla_policy netkit_policy[IFLA_NETKIT_MAX + 1] = { · 15 unmodified lines
972 [IFLA_NETKIT_TAILROOM] = { .type = NLA_U16 },1001 [IFLA_NETKIT_TAILROOM] = { .type = NLA_U16 },
973 [IFLA_NETKIT_SCRUB] = NLA_POLICY_MAX(NLA_U32, NETKIT_SCRUB_DEFAULT),1002 [IFLA_NETKIT_SCRUB] = NLA_POLICY_MAX(NLA_U32, NETKIT_SCRUB_DEFAULT),
974 [IFLA_NETKIT_PEER_SCRUB] = NLA_POLICY_MAX(NLA_U32, NETKIT_SCRUB_DEFAULT),1003 [IFLA_NETKIT_PEER_SCRUB] = NLA_POLICY_MAX(NLA_U32, NETKIT_SCRUB_DEFAULT),
1004 [IFLA_NETKIT_PAIRING] = NLA_POLICY_MAX(NLA_U32, NETKIT_DEVICE_SINGLE),
975 [IFLA_NETKIT_PRIMARY] = { .type = NLA_REJECT,1005 [IFLA_NETKIT_PRIMARY] = { .type = NLA_REJECT,
976 .reject_message = "Primary attribute is read-only" },1006 .reject_message = "Primary attribute is read-only" },
977};1007};
include/uapi/linux/if_link.h+6−0
@@ -1296,6 +1296,11 @@ enum netkit_mode { · 1295 unmodified lines
1296 NETKIT_L3,1296 NETKIT_L3,
1297};1297};
1298 1298
1299enum netkit_pairing {
1300 NETKIT_DEVICE_PAIR,
1301 NETKIT_DEVICE_SINGLE,
1302};
1303
1299/* NETKIT_SCRUB_NONE leaves clearing skb->{mark,priority} up to1304/* NETKIT_SCRUB_NONE leaves clearing skb->{mark,priority} up to
1300 * the BPF program if attached. This also means the latter can1305 * the BPF program if attached. This also means the latter can
1301 * consume the two fields if they were populated earlier.1306 * consume the two fields if they were populated earlier.
@@ -1320,6 +1325,7 @@ enum { · 18 unmodified lines
1320 IFLA_NETKIT_PEER_SCRUB,1325 IFLA_NETKIT_PEER_SCRUB,
1321 IFLA_NETKIT_HEADROOM,1326 IFLA_NETKIT_HEADROOM,
1322 IFLA_NETKIT_TAILROOM,1327 IFLA_NETKIT_TAILROOM,
1328 IFLA_NETKIT_PAIRING,
1323 __IFLA_NETKIT_MAX,1329 __IFLA_NETKIT_MAX,
1324};1330};
1325#define IFLA_NETKIT_MAX (__IFLA_NETKIT_MAX - 1)1331#define IFLA_NETKIT_MAX (__IFLA_NETKIT_MAX - 1)