History

netkit, bpf: Add bpf programmable net device

This work adds a new, minimal BPF-programmable device called "netkit" (former PoC code-name "meta") we recently presented at LSF/MM/BPF. The core idea is that BPF programs are executed within the drivers xmit routine and therefore e.g. in case of containers/Pods moving BPF processing closer to the source. One of the goals was that in case of Pod egress traffic, this allows to move BPF programs from hostns tcx ingress into the device itself, providing earlier drop or forward mechanisms, for example, if the BPF program determines that the skb must be sent out of the node, then a redirect to the physical device can take place directly without going through per-CPU backlog queue. This helps to shift processing for such traffic from softirq to process context, leading to better scheduling decisions/performance (see measurements in the slides). In this initial version, the netkit device ships as a pair, but we plan to extend this further so it can also operate in single device mode. The pair comes with a primary and a peer device. Only the primary device, typically residing in hostns, can manage BPF programs for itself and its peer. The peer device is designated for containers/Pods and cannot attach/detach BPF programs. Upon the device creation, the user can set the default policy to 'pass' or 'drop' for the case when no BPF program is attached. Additionally, the device can be operated in L3 (default) or L2 mode. The management of BPF programs is done via bpf_mprog, so that multi-attach is supported right from the beginning with similar API and dependency controls as tcx. For details on the latter see commit 053c8e1f235d ("bpf: Add generic attach/detach/query API for multi-progs"). tc BPF compatibility is provided, so that existing programs can be easily migrated. Going forward, we plan to use netkit devices in Cilium as the main device type for connecting Pods. They will be operated in L3 mode in order to simplify a Pod's neighbor management and the peer will operate in default drop mode, so that no traffic is leaving between the time when a Pod is brought up by the CNI plugin and programs attached by the agent. Additionally, the programs we attach via tcx on the physical devices are using bpf_redirect_peer() for inbound traffic into netkit device, hence the latter is also supporting the ndo_get_peer_dev callback. Similarly, we use bpf_redirect_neigh() for the way out, pushing from netkit peer to phys device directly. Also, BIG TCP is supported on netkit device. For the follow-up work in single device mode, we plan to convert Cilium's cilium_host/_net devices into a single one. An extensive test suite for checking device operations and the BPF program and link management API comes as BPF selftests in this series.

Daniel Borkmann committed 3 years ago

Commit 35dfaad1 parent 42d31dd9 files · +1074 −5Browse files

Unified · Split

9 files
  1. MAINTAINERS +9 −0
  2. drivers/net/Kconfig +9 −0
  3. drivers/net/Makefile +1 −0
  4. drivers/net/netkit.c +940 −0
  5. include/net/netkit.h +38 −0
  6. include/uapi/linux/bpf.h +14 −0
  7. include/uapi/linux/if_link.h +24 −0
  8. kernel/bpf/syscall.c +25 −5
  9. tools/include/uapi/linux/bpf.h +14 −0
MAINTAINERS+9−0
@@ -3795,6 +3795,15 @@ L: bpf@vger.kernel.org · 3794 unmodified lines
3795S: Odd Fixes3795S: Odd Fixes
3796K: (?:\b|_)bpf(?:\b|_)3796K: (?:\b|_)bpf(?:\b|_)
3797 3797
3798BPF [NETKIT] (BPF-programmable network device)
3799M: Daniel Borkmann <daniel@iogearbox.net>
3800M: Nikolay Aleksandrov <razor@blackwall.org>
3801L: bpf@vger.kernel.org
3802L: netdev@vger.kernel.org
3803S: Supported
3804F: drivers/net/netkit.c
3805F: include/net/netkit.h
3806
3798BPF [NETWORKING] (struct_ops, reuseport)3807BPF [NETWORKING] (struct_ops, reuseport)
3799M: Martin KaFai Lau <martin.lau@linux.dev>3808M: Martin KaFai Lau <martin.lau@linux.dev>
3800L: bpf@vger.kernel.org3809L: bpf@vger.kernel.org
drivers/net/Kconfig+9−0
@@ -448,6 +448,15 @@ config NLMON · 447 unmodified lines
448 diagnostics, etc. This is mostly intended for developers or support448 diagnostics, etc. This is mostly intended for developers or support
449 to debug netlink issues. If unsure, say N.449 to debug netlink issues. If unsure, say N.
450 450
451config NETKIT
452 bool "BPF-programmable network device"
453 depends on BPF_SYSCALL
454 help
455 The netkit device is a virtual networking device where BPF programs
456 can be attached to the device(s) transmission routine in order to
457 implement the driver's internal logic. The device can be configured
458 to operate in L3 or L2 mode. If unsure, say N.
459
451config NET_VRF460config NET_VRF
452 tristate "Virtual Routing and Forwarding (Lite)"461 tristate "Virtual Routing and Forwarding (Lite)"
453 depends on IP_MULTIPLE_TABLES462 depends on IP_MULTIPLE_TABLES
drivers/net/Makefile+1−0
@@ -22,6 +22,7 @@ obj-$(CONFIG_MDIO) += mdio.o · 21 unmodified lines
22obj-$(CONFIG_NET) += loopback.o22obj-$(CONFIG_NET) += loopback.o
23obj-$(CONFIG_NETDEV_LEGACY_INIT) += Space.o23obj-$(CONFIG_NETDEV_LEGACY_INIT) += Space.o
24obj-$(CONFIG_NETCONSOLE) += netconsole.o24obj-$(CONFIG_NETCONSOLE) += netconsole.o
25obj-$(CONFIG_NETKIT) += netkit.o
25obj-y += phy/26obj-y += phy/
26obj-y += pse-pd/27obj-y += pse-pd/
27obj-y += mdio/28obj-y += mdio/
drivers/net/netkit.c+940−0
@@ -0,0 +1,940 @@
1// SPDX-License-Identifier: GPL-2.0-only
2/* Copyright (c) 2023 Isovalent */
3
4#include <linux/netdevice.h>
5#include <linux/ethtool.h>
6#include <linux/etherdevice.h>
7#include <linux/filter.h>
8#include <linux/netfilter_netdev.h>
9#include <linux/bpf_mprog.h>
10
11#include <net/netkit.h>
12#include <net/dst.h>
13#include <net/tcx.h>
14
15#define DRV_NAME "netkit"
16
17struct netkit {
18 /* Needed in fast-path */
19 struct net_device __rcu *peer;
20 struct bpf_mprog_entry __rcu *active;
21 enum netkit_action policy;
22 struct bpf_mprog_bundle bundle;
23
24 /* Needed in slow-path */
25 enum netkit_mode mode;
26 bool primary;
27 u32 headroom;
28};
29
30struct netkit_link {
31 struct bpf_link link;
32 struct net_device *dev;
33 u32 location;
34};
35
36static __always_inline int
37netkit_run(const struct bpf_mprog_entry *entry, struct sk_buff *skb,
38 enum netkit_action ret)
39{
40 const struct bpf_mprog_fp *fp;
41 const struct bpf_prog *prog;
42
43 bpf_mprog_foreach_prog(entry, fp, prog) {
44 bpf_compute_data_pointers(skb);
45 ret = bpf_prog_run(prog, skb);
46 if (ret != NETKIT_NEXT)
47 break;
48 }
49 return ret;
50}
51
52static void netkit_prep_forward(struct sk_buff *skb, bool xnet)
53{
54 skb_scrub_packet(skb, xnet);
55 skb->priority = 0;
56 nf_skip_egress(skb, true);
57}
58
59static struct netkit *netkit_priv(const struct net_device *dev)
60{
61 return netdev_priv(dev);
62}
63
64static netdev_tx_t netkit_xmit(struct sk_buff *skb, struct net_device *dev)
65{
66 struct netkit *nk = netkit_priv(dev);
67 enum netkit_action ret = READ_ONCE(nk->policy);
68 netdev_tx_t ret_dev = NET_XMIT_SUCCESS;
69 const struct bpf_mprog_entry *entry;
70 struct net_device *peer;
71
72 rcu_read_lock();
73 peer = rcu_dereference(nk->peer);
74 if (unlikely(!peer || !(peer->flags & IFF_UP) ||
75 !pskb_may_pull(skb, ETH_HLEN) ||
76 skb_orphan_frags(skb, GFP_ATOMIC)))
77 goto drop;
78 netkit_prep_forward(skb, !net_eq(dev_net(dev), dev_net(peer)));
79 skb->dev = peer;
80 entry = rcu_dereference(nk->active);
81 if (entry)
82 ret = netkit_run(entry, skb, ret);
83 switch (ret) {
84 case NETKIT_NEXT:
85 case NETKIT_PASS:
86 skb->protocol = eth_type_trans(skb, skb->dev);
87 skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
88 __netif_rx(skb);
89 break;
90 case NETKIT_REDIRECT:
91 skb_do_redirect(skb);
92 break;
93 case NETKIT_DROP:
94 default:
95drop:
96 kfree_skb(skb);
97 dev_core_stats_tx_dropped_inc(dev);
98 ret_dev = NET_XMIT_DROP;
99 break;
100 }
101 rcu_read_unlock();
102 return ret_dev;
103}
104
105static int netkit_open(struct net_device *dev)
106{
107 struct netkit *nk = netkit_priv(dev);
108 struct net_device *peer = rtnl_dereference(nk->peer);
109
110 if (!peer)
111 return -ENOTCONN;
112 if (peer->flags & IFF_UP) {
113 netif_carrier_on(dev);
114 netif_carrier_on(peer);
115 }
116 return 0;
117}
118
119static int netkit_close(struct net_device *dev)
120{
121 struct netkit *nk = netkit_priv(dev);
122 struct net_device *peer = rtnl_dereference(nk->peer);
123
124 netif_carrier_off(dev);
125 if (peer)
126 netif_carrier_off(peer);
127 return 0;
128}
129
130static int netkit_get_iflink(const struct net_device *dev)
131{
132 struct netkit *nk = netkit_priv(dev);
133 struct net_device *peer;
134 int iflink = 0;
135
136 rcu_read_lock();
137 peer = rcu_dereference(nk->peer);
138 if (peer)
139 iflink = peer->ifindex;
140 rcu_read_unlock();
141 return iflink;
142}
143
144static void netkit_set_multicast(struct net_device *dev)
145{
146 /* Nothing to do, we receive whatever gets pushed to us! */
147}
148
149static void netkit_set_headroom(struct net_device *dev, int headroom)
150{
151 struct netkit *nk = netkit_priv(dev), *nk2;
152 struct net_device *peer;
153
154 if (headroom < 0)
155 headroom = NET_SKB_PAD;
156
157 rcu_read_lock();
158 peer = rcu_dereference(nk->peer);
159 if (unlikely(!peer))
160 goto out;
161
162 nk2 = netkit_priv(peer);
163 nk->headroom = headroom;
164 headroom = max(nk->headroom, nk2->headroom);
165
166 peer->needed_headroom = headroom;
167 dev->needed_headroom = headroom;
168out:
169 rcu_read_unlock();
170}
171
172static struct net_device *netkit_peer_dev(struct net_device *dev)
173{
174 return rcu_dereference(netkit_priv(dev)->peer);
175}
176
177static void netkit_uninit(struct net_device *dev);
178
179static const struct net_device_ops netkit_netdev_ops = {
180 .ndo_open = netkit_open,
181 .ndo_stop = netkit_close,
182 .ndo_start_xmit = netkit_xmit,
183 .ndo_set_rx_mode = netkit_set_multicast,
184 .ndo_set_rx_headroom = netkit_set_headroom,
185 .ndo_get_iflink = netkit_get_iflink,
186 .ndo_get_peer_dev = netkit_peer_dev,
187 .ndo_uninit = netkit_uninit,
188 .ndo_features_check = passthru_features_check,
189};
190
191static void netkit_get_drvinfo(struct net_device *dev,
192 struct ethtool_drvinfo *info)
193{
194 strscpy(info->driver, DRV_NAME, sizeof(info->driver));
195}
196
197static const struct ethtool_ops netkit_ethtool_ops = {
198 .get_drvinfo = netkit_get_drvinfo,
199};
200
201static void netkit_setup(struct net_device *dev)
202{
203 static const netdev_features_t netkit_features_hw_vlan =
204 NETIF_F_HW_VLAN_CTAG_TX |
205 NETIF_F_HW_VLAN_CTAG_RX |
206 NETIF_F_HW_VLAN_STAG_TX |
207 NETIF_F_HW_VLAN_STAG_RX;
208 static const netdev_features_t netkit_features =
209 netkit_features_hw_vlan |
210 NETIF_F_SG |
211 NETIF_F_FRAGLIST |
212 NETIF_F_HW_CSUM |
213 NETIF_F_RXCSUM |
214 NETIF_F_SCTP_CRC |
215 NETIF_F_HIGHDMA |
216 NETIF_F_GSO_SOFTWARE |
217 NETIF_F_GSO_ENCAP_ALL;
218
219 ether_setup(dev);
220 dev->max_mtu = ETH_MAX_MTU;
221
222 dev->flags |= IFF_NOARP;
223 dev->priv_flags &= ~IFF_TX_SKB_SHARING;
224 dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
225 dev->priv_flags |= IFF_PHONY_HEADROOM;
226 dev->priv_flags |= IFF_NO_QUEUE;
227
228 dev->ethtool_ops = &netkit_ethtool_ops;
229 dev->netdev_ops = &netkit_netdev_ops;
230
231 dev->features |= netkit_features | NETIF_F_LLTX;
232 dev->hw_features = netkit_features;
233 dev->hw_enc_features = netkit_features;
234 dev->mpls_features = NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE;
235 dev->vlan_features = dev->features & ~netkit_features_hw_vlan;
236
237 dev->needs_free_netdev = true;
238
239 netif_set_tso_max_size(dev, GSO_MAX_SIZE);
240}
241
242static struct net *netkit_get_link_net(const struct net_device *dev)
243{
244 struct netkit *nk = netkit_priv(dev);
245 struct net_device *peer = rtnl_dereference(nk->peer);
246
247 return peer ? dev_net(peer) : dev_net(dev);
248}
249
250static int netkit_check_policy(int policy, struct nlattr *tb,
251 struct netlink_ext_ack *extack)
252{
253 switch (policy) {
254 case NETKIT_PASS:
255 case NETKIT_DROP:
256 return 0;
257 default:
258 NL_SET_ERR_MSG_ATTR(extack, tb,
259 "Provided default xmit policy not supported");
260 return -EINVAL;
261 }
262}
263
264static int netkit_check_mode(int mode, struct nlattr *tb,
265 struct netlink_ext_ack *extack)
266{
267 switch (mode) {
268 case NETKIT_L2:
269 case NETKIT_L3:
270 return 0;
271 default:
272 NL_SET_ERR_MSG_ATTR(extack, tb,
273 "Provided device mode can only be L2 or L3");
274 return -EINVAL;
275 }
276}
277
278static int netkit_validate(struct nlattr *tb[], struct nlattr *data[],
279 struct netlink_ext_ack *extack)
280{
281 struct nlattr *attr = tb[IFLA_ADDRESS];
282
283 if (!attr)
284 return 0;
285 NL_SET_ERR_MSG_ATTR(extack, attr,
286 "Setting Ethernet address is not supported");
287 return -EOPNOTSUPP;
288}
289
290static struct rtnl_link_ops netkit_link_ops;
291
292static int netkit_new_link(struct net *src_net, struct net_device *dev,
293 struct nlattr *tb[], struct nlattr *data[],
294 struct netlink_ext_ack *extack)
295{
296 struct nlattr *peer_tb[IFLA_MAX + 1], **tbp = tb, *attr;
297 enum netkit_action default_prim = NETKIT_PASS;
298 enum netkit_action default_peer = NETKIT_PASS;
299 enum netkit_mode mode = NETKIT_L3;
300 unsigned char ifname_assign_type;
301 struct ifinfomsg *ifmp = NULL;
302 struct net_device *peer;
303 char ifname[IFNAMSIZ];
304 struct netkit *nk;
305 struct net *net;
306 int err;
307
308 if (data) {
309 if (data[IFLA_NETKIT_MODE]) {
310 attr = data[IFLA_NETKIT_MODE];
311 mode = nla_get_u32(attr);
312 err = netkit_check_mode(mode, attr, extack);
313 if (err < 0)
314 return err;
315 }
316 if (data[IFLA_NETKIT_PEER_INFO]) {
317 attr = data[IFLA_NETKIT_PEER_INFO];
318 ifmp = nla_data(attr);
319 err = rtnl_nla_parse_ifinfomsg(peer_tb, attr, extack);
320 if (err < 0)
321 return err;
322 err = netkit_validate(peer_tb, NULL, extack);
323 if (err < 0)
324 return err;
325 tbp = peer_tb;
326 }
327 if (data[IFLA_NETKIT_POLICY]) {
328 attr = data[IFLA_NETKIT_POLICY];
329 default_prim = nla_get_u32(attr);
330 err = netkit_check_policy(default_prim, attr, extack);
331 if (err < 0)
332 return err;
333 }
334 if (data[IFLA_NETKIT_PEER_POLICY]) {
335 attr = data[IFLA_NETKIT_PEER_POLICY];
336 default_peer = nla_get_u32(attr);
337 err = netkit_check_policy(default_peer, attr, extack);
338 if (err < 0)
339 return err;
340 }
341 }
342
343 if (ifmp && tbp[IFLA_IFNAME]) {
344 nla_strscpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ);
345 ifname_assign_type = NET_NAME_USER;
346 } else {
347 strscpy(ifname, "nk%d", IFNAMSIZ);
348 ifname_assign_type = NET_NAME_ENUM;
349 }
350
351 net = rtnl_link_get_net(src_net, tbp);
352 if (IS_ERR(net))
353 return PTR_ERR(net);
354
355 peer = rtnl_create_link(net, ifname, ifname_assign_type,
356 &netkit_link_ops, tbp, extack);
357 if (IS_ERR(peer)) {
358 put_net(net);
359 return PTR_ERR(peer);
360 }
361
362 netif_inherit_tso_max(peer, dev);
363
364 if (mode == NETKIT_L2)
365 eth_hw_addr_random(peer);
366 if (ifmp && dev->ifindex)
367 peer->ifindex = ifmp->ifi_index;
368
369 nk = netkit_priv(peer);
370 nk->primary = false;
371 nk->policy = default_peer;
372 nk->mode = mode;
373 bpf_mprog_bundle_init(&nk->bundle);
374 RCU_INIT_POINTER(nk->active, NULL);
375 RCU_INIT_POINTER(nk->peer, NULL);
376
377 err = register_netdevice(peer);
378 put_net(net);
379 if (err < 0)
380 goto err_register_peer;
381 netif_carrier_off(peer);
382 if (mode == NETKIT_L2)
383 dev_change_flags(peer, peer->flags & ~IFF_NOARP, NULL);
384
385 err = rtnl_configure_link(peer, NULL, 0, NULL);
386 if (err < 0)
387 goto err_configure_peer;
388
389 if (mode == NETKIT_L2)
390 eth_hw_addr_random(dev);
391 if (tb[IFLA_IFNAME])
392 nla_strscpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);
393 else
394 strscpy(dev->name, "nk%d", IFNAMSIZ);
395
396 nk = netkit_priv(dev);
397 nk->primary = true;
398 nk->policy = default_prim;
399 nk->mode = mode;
400 bpf_mprog_bundle_init(&nk->bundle);
401 RCU_INIT_POINTER(nk->active, NULL);
402 RCU_INIT_POINTER(nk->peer, NULL);
403
404 err = register_netdevice(dev);
405 if (err < 0)
406 goto err_configure_peer;
407 netif_carrier_off(dev);
408 if (mode == NETKIT_L2)
409 dev_change_flags(dev, dev->flags & ~IFF_NOARP, NULL);
410
411 rcu_assign_pointer(netkit_priv(dev)->peer, peer);
412 rcu_assign_pointer(netkit_priv(peer)->peer, dev);
413 return 0;
414err_configure_peer:
415 unregister_netdevice(peer);
416 return err;
417err_register_peer:
418 free_netdev(peer);
419 return err;
420}
421
422static struct bpf_mprog_entry *netkit_entry_fetch(struct net_device *dev,
423 bool bundle_fallback)
424{
425 struct netkit *nk = netkit_priv(dev);
426 struct bpf_mprog_entry *entry;
427
428 ASSERT_RTNL();
429 entry = rcu_dereference_rtnl(nk->active);
430 if (entry)
431 return entry;
432 if (bundle_fallback)
433 return &nk->bundle.a;
434 return NULL;
435}
436
437static void netkit_entry_update(struct net_device *dev,
438 struct bpf_mprog_entry *entry)
439{
440 struct netkit *nk = netkit_priv(dev);
441
442 ASSERT_RTNL();
443 rcu_assign_pointer(nk->active, entry);
444}
445
446static void netkit_entry_sync(void)
447{
448 synchronize_rcu();
449}
450
451static struct net_device *netkit_dev_fetch(struct net *net, u32 ifindex, u32 which)
452{
453 struct net_device *dev;
454 struct netkit *nk;
455
456 ASSERT_RTNL();
457
458 switch (which) {
459 case BPF_NETKIT_PRIMARY:
460 case BPF_NETKIT_PEER:
461 break;
462 default:
463 return ERR_PTR(-EINVAL);
464 }
465
466 dev = __dev_get_by_index(net, ifindex);
467 if (!dev)
468 return ERR_PTR(-ENODEV);
469 if (dev->netdev_ops != &netkit_netdev_ops)
470 return ERR_PTR(-ENXIO);
471
472 nk = netkit_priv(dev);
473 if (!nk->primary)
474 return ERR_PTR(-EACCES);
475 if (which == BPF_NETKIT_PEER) {
476 dev = rcu_dereference_rtnl(nk->peer);
477 if (!dev)
478 return ERR_PTR(-ENODEV);
479 }
480 return dev;
481}
482
483int netkit_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog)
484{
485 struct bpf_mprog_entry *entry, *entry_new;
486 struct bpf_prog *replace_prog = NULL;
487 struct net_device *dev;
488 int ret;
489
490 rtnl_lock();
491 dev = netkit_dev_fetch(current->nsproxy->net_ns, attr->target_ifindex,
492 attr->attach_type);
493 if (IS_ERR(dev)) {
494 ret = PTR_ERR(dev);
495 goto out;
496 }
497 entry = netkit_entry_fetch(dev, true);
498 if (attr->attach_flags & BPF_F_REPLACE) {
499 replace_prog = bpf_prog_get_type(attr->replace_bpf_fd,
500 prog->type);
501 if (IS_ERR(replace_prog)) {
502 ret = PTR_ERR(replace_prog);
503 replace_prog = NULL;
504 goto out;
505 }
506 }
507 ret = bpf_mprog_attach(entry, &entry_new, prog, NULL, replace_prog,
508 attr->attach_flags, attr->relative_fd,
509 attr->expected_revision);
510 if (!ret) {
511 if (entry != entry_new) {
512 netkit_entry_update(dev, entry_new);
513 netkit_entry_sync();
514 }
515 bpf_mprog_commit(entry);
516 }
517out:
518 if (replace_prog)
519 bpf_prog_put(replace_prog);
520 rtnl_unlock();
521 return ret;
522}
523
524int netkit_prog_detach(const union bpf_attr *attr, struct bpf_prog *prog)
525{
526 struct bpf_mprog_entry *entry, *entry_new;
527 struct net_device *dev;
528 int ret;
529
530 rtnl_lock();
531 dev = netkit_dev_fetch(current->nsproxy->net_ns, attr->target_ifindex,
532 attr->attach_type);
533 if (IS_ERR(dev)) {
534 ret = PTR_ERR(dev);
535 goto out;
536 }
537 entry = netkit_entry_fetch(dev, false);
538 if (!entry) {
539 ret = -ENOENT;
540 goto out;
541 }
542 ret = bpf_mprog_detach(entry, &entry_new, prog, NULL, attr->attach_flags,
543 attr->relative_fd, attr->expected_revision);
544 if (!ret) {
545 if (!bpf_mprog_total(entry_new))
546 entry_new = NULL;
547 netkit_entry_update(dev, entry_new);
548 netkit_entry_sync();
549 bpf_mprog_commit(entry);
550 }
551out:
552 rtnl_unlock();
553 return ret;
554}
555
556int netkit_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr)
557{
558 struct net_device *dev;
559 int ret;
560
561 rtnl_lock();
562 dev = netkit_dev_fetch(current->nsproxy->net_ns,
563 attr->query.target_ifindex,
564 attr->query.attach_type);
565 if (IS_ERR(dev)) {
566 ret = PTR_ERR(dev);
567 goto out;
568 }
569 ret = bpf_mprog_query(attr, uattr, netkit_entry_fetch(dev, false));
570out:
571 rtnl_unlock();
572 return ret;
573}
574
575static struct netkit_link *netkit_link(const struct bpf_link *link)
576{
577 return container_of(link, struct netkit_link, link);
578}
579
580static int netkit_link_prog_attach(struct bpf_link *link, u32 flags,
581 u32 id_or_fd, u64 revision)
582{
583 struct netkit_link *nkl = netkit_link(link);
584 struct bpf_mprog_entry *entry, *entry_new;
585 struct net_device *dev = nkl->dev;
586 int ret;
587
588 ASSERT_RTNL();
589 entry = netkit_entry_fetch(dev, true);
590 ret = bpf_mprog_attach(entry, &entry_new, link->prog, link, NULL, flags,
591 id_or_fd, revision);
592 if (!ret) {
593 if (entry != entry_new) {
594 netkit_entry_update(dev, entry_new);
595 netkit_entry_sync();
596 }
597 bpf_mprog_commit(entry);
598 }
599 return ret;
600}
601
602static void netkit_link_release(struct bpf_link *link)
603{
604 struct netkit_link *nkl = netkit_link(link);
605 struct bpf_mprog_entry *entry, *entry_new;
606 struct net_device *dev;
607 int ret = 0;
608
609 rtnl_lock();
610 dev = nkl->dev;
611 if (!dev)
612 goto out;
613 entry = netkit_entry_fetch(dev, false);
614 if (!entry) {
615 ret = -ENOENT;
616 goto out;
617 }
618 ret = bpf_mprog_detach(entry, &entry_new, link->prog, link, 0, 0, 0);
619 if (!ret) {
620 if (!bpf_mprog_total(entry_new))
621 entry_new = NULL;
622 netkit_entry_update(dev, entry_new);
623 netkit_entry_sync();
624 bpf_mprog_commit(entry);
625 nkl->dev = NULL;
626 }
627out:
628 WARN_ON_ONCE(ret);
629 rtnl_unlock();
630}
631
632static int netkit_link_update(struct bpf_link *link, struct bpf_prog *nprog,
633 struct bpf_prog *oprog)
634{
635 struct netkit_link *nkl = netkit_link(link);
636 struct bpf_mprog_entry *entry, *entry_new;
637 struct net_device *dev;
638 int ret = 0;
639
640 rtnl_lock();
641 dev = nkl->dev;
642 if (!dev) {
643 ret = -ENOLINK;
644 goto out;
645 }
646 if (oprog && link->prog != oprog) {
647 ret = -EPERM;
648 goto out;
649 }
650 oprog = link->prog;
651 if (oprog == nprog) {
652 bpf_prog_put(nprog);
653 goto out;
654 }
655 entry = netkit_entry_fetch(dev, false);
656 if (!entry) {
657 ret = -ENOENT;
658 goto out;
659 }
660 ret = bpf_mprog_attach(entry, &entry_new, nprog, link, oprog,
661 BPF_F_REPLACE | BPF_F_ID,
662 link->prog->aux->id, 0);
663 if (!ret) {
664 WARN_ON_ONCE(entry != entry_new);
665 oprog = xchg(&link->prog, nprog);
666 bpf_prog_put(oprog);
667 bpf_mprog_commit(entry);
668 }
669out:
670 rtnl_unlock();
671 return ret;
672}
673
674static void netkit_link_dealloc(struct bpf_link *link)
675{
676 kfree(netkit_link(link));
677}
678
679static void netkit_link_fdinfo(const struct bpf_link *link, struct seq_file *seq)
680{
681 const struct netkit_link *nkl = netkit_link(link);
682 u32 ifindex = 0;
683
684 rtnl_lock();
685 if (nkl->dev)
686 ifindex = nkl->dev->ifindex;
687 rtnl_unlock();
688
689 seq_printf(seq, "ifindex:\t%u\n", ifindex);
690 seq_printf(seq, "attach_type:\t%u (%s)\n",
691 nkl->location,
692 nkl->location == BPF_NETKIT_PRIMARY ? "primary" : "peer");
693}
694
695static int netkit_link_fill_info(const struct bpf_link *link,
696 struct bpf_link_info *info)
697{
698 const struct netkit_link *nkl = netkit_link(link);
699 u32 ifindex = 0;
700
701 rtnl_lock();
702 if (nkl->dev)
703 ifindex = nkl->dev->ifindex;
704 rtnl_unlock();
705
706 info->netkit.ifindex = ifindex;
707 info->netkit.attach_type = nkl->location;
708 return 0;
709}
710
711static int netkit_link_detach(struct bpf_link *link)
712{
713 netkit_link_release(link);
714 return 0;
715}
716
717static const struct bpf_link_ops netkit_link_lops = {
718 .release = netkit_link_release,
719 .detach = netkit_link_detach,
720 .dealloc = netkit_link_dealloc,
721 .update_prog = netkit_link_update,
722 .show_fdinfo = netkit_link_fdinfo,
723 .fill_link_info = netkit_link_fill_info,
724};
725
726static int netkit_link_init(struct netkit_link *nkl,
727 struct bpf_link_primer *link_primer,
728 const union bpf_attr *attr,
729 struct net_device *dev,
730 struct bpf_prog *prog)
731{
732 bpf_link_init(&nkl->link, BPF_LINK_TYPE_NETKIT,
733 &netkit_link_lops, prog);
734 nkl->location = attr->link_create.attach_type;
735 nkl->dev = dev;
736 return bpf_link_prime(&nkl->link, link_primer);
737}
738
739int netkit_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
740{
741 struct bpf_link_primer link_primer;
742 struct netkit_link *nkl;
743 struct net_device *dev;
744 int ret;
745
746 rtnl_lock();
747 dev = netkit_dev_fetch(current->nsproxy->net_ns,
748 attr->link_create.target_ifindex,
749 attr->link_create.attach_type);
750 if (IS_ERR(dev)) {
751 ret = PTR_ERR(dev);
752 goto out;
753 }
754 nkl = kzalloc(sizeof(*nkl), GFP_KERNEL_ACCOUNT);
755 if (!nkl) {
756 ret = -ENOMEM;
757 goto out;
758 }
759 ret = netkit_link_init(nkl, &link_primer, attr, dev, prog);
760 if (ret) {
761 kfree(nkl);
762 goto out;
763 }
764 ret = netkit_link_prog_attach(&nkl->link,
765 attr->link_create.flags,
766 attr->link_create.netkit.relative_fd,
767 attr->link_create.netkit.expected_revision);
768 if (ret) {
769 nkl->dev = NULL;
770 bpf_link_cleanup(&link_primer);
771 goto out;
772 }
773 ret = bpf_link_settle(&link_primer);
774out:
775 rtnl_unlock();
776 return ret;
777}
778
779static void netkit_release_all(struct net_device *dev)
780{
781 struct bpf_mprog_entry *entry;
782 struct bpf_tuple tuple = {};
783 struct bpf_mprog_fp *fp;
784 struct bpf_mprog_cp *cp;
785
786 entry = netkit_entry_fetch(dev, false);
787 if (!entry)
788 return;
789 netkit_entry_update(dev, NULL);
790 netkit_entry_sync();
791 bpf_mprog_foreach_tuple(entry, fp, cp, tuple) {
792 if (tuple.link)
793 netkit_link(tuple.link)->dev = NULL;
794 else
795 bpf_prog_put(tuple.prog);
796 }
797}
798
799static void netkit_uninit(struct net_device *dev)
800{
801 netkit_release_all(dev);
802}
803
804static void netkit_del_link(struct net_device *dev, struct list_head *head)
805{
806 struct netkit *nk = netkit_priv(dev);
807 struct net_device *peer = rtnl_dereference(nk->peer);
808
809 RCU_INIT_POINTER(nk->peer, NULL);
810 unregister_netdevice_queue(dev, head);
811 if (peer) {
812 nk = netkit_priv(peer);
813 RCU_INIT_POINTER(nk->peer, NULL);
814 unregister_netdevice_queue(peer, head);
815 }
816}
817
818static int netkit_change_link(struct net_device *dev, struct nlattr *tb[],
819 struct nlattr *data[],
820 struct netlink_ext_ack *extack)
821{
822 struct netkit *nk = netkit_priv(dev);
823 struct net_device *peer = rtnl_dereference(nk->peer);
824 enum netkit_action policy;
825 struct nlattr *attr;
826 int err;
827
828 if (!nk->primary) {
829 NL_SET_ERR_MSG(extack,
830 "netkit link settings can be changed only through the primary device");
831 return -EACCES;
832 }
833
834 if (data[IFLA_NETKIT_MODE]) {
835 NL_SET_ERR_MSG_ATTR(extack, data[IFLA_NETKIT_MODE],
836 "netkit link operating mode cannot be changed after device creation");
837 return -EACCES;
838 }
839
840 if (data[IFLA_NETKIT_POLICY]) {
841 attr = data[IFLA_NETKIT_POLICY];
842 policy = nla_get_u32(attr);
843 err = netkit_check_policy(policy, attr, extack);
844 if (err)
845 return err;
846 WRITE_ONCE(nk->policy, policy);
847 }
848
849 if (data[IFLA_NETKIT_PEER_POLICY]) {
850 err = -EOPNOTSUPP;
851 attr = data[IFLA_NETKIT_PEER_POLICY];
852 policy = nla_get_u32(attr);
853 if (peer)
854 err = netkit_check_policy(policy, attr, extack);
855 if (err)
856 return err;
857 nk = netkit_priv(peer);
858 WRITE_ONCE(nk->policy, policy);
859 }
860
861 return 0;
862}
863
864static size_t netkit_get_size(const struct net_device *dev)
865{
866 return nla_total_size(sizeof(u32)) + /* IFLA_NETKIT_POLICY */
867 nla_total_size(sizeof(u32)) + /* IFLA_NETKIT_PEER_POLICY */
868 nla_total_size(sizeof(u8)) + /* IFLA_NETKIT_PRIMARY */
869 nla_total_size(sizeof(u32)) + /* IFLA_NETKIT_MODE */
870 0;
871}
872
873static int netkit_fill_info(struct sk_buff *skb, const struct net_device *dev)
874{
875 struct netkit *nk = netkit_priv(dev);
876 struct net_device *peer = rtnl_dereference(nk->peer);
877
878 if (nla_put_u8(skb, IFLA_NETKIT_PRIMARY, nk->primary))
879 return -EMSGSIZE;
880 if (nla_put_u32(skb, IFLA_NETKIT_POLICY, nk->policy))
881 return -EMSGSIZE;
882 if (nla_put_u32(skb, IFLA_NETKIT_MODE, nk->mode))
883 return -EMSGSIZE;
884
885 if (peer) {
886 nk = netkit_priv(peer);
887 if (nla_put_u32(skb, IFLA_NETKIT_PEER_POLICY, nk->policy))
888 return -EMSGSIZE;
889 }
890
891 return 0;
892}
893
894static const struct nla_policy netkit_policy[IFLA_NETKIT_MAX + 1] = {
895 [IFLA_NETKIT_PEER_INFO] = { .len = sizeof(struct ifinfomsg) },
896 [IFLA_NETKIT_POLICY] = { .type = NLA_U32 },
897 [IFLA_NETKIT_MODE] = { .type = NLA_U32 },
898 [IFLA_NETKIT_PEER_POLICY] = { .type = NLA_U32 },
899 [IFLA_NETKIT_PRIMARY] = { .type = NLA_REJECT,
900 .reject_message = "Primary attribute is read-only" },
901};
902
903static struct rtnl_link_ops netkit_link_ops = {
904 .kind = DRV_NAME,
905 .priv_size = sizeof(struct netkit),
906 .setup = netkit_setup,
907 .newlink = netkit_new_link,
908 .dellink = netkit_del_link,
909 .changelink = netkit_change_link,
910 .get_link_net = netkit_get_link_net,
911 .get_size = netkit_get_size,
912 .fill_info = netkit_fill_info,
913 .policy = netkit_policy,
914 .validate = netkit_validate,
915 .maxtype = IFLA_NETKIT_MAX,
916};
917
918static __init int netkit_init(void)
919{
920 BUILD_BUG_ON((int)NETKIT_NEXT != (int)TCX_NEXT ||
921 (int)NETKIT_PASS != (int)TCX_PASS ||
922 (int)NETKIT_DROP != (int)TCX_DROP ||
923 (int)NETKIT_REDIRECT != (int)TCX_REDIRECT);
924
925 return rtnl_link_register(&netkit_link_ops);
926}
927
928static __exit void netkit_exit(void)
929{
930 rtnl_link_unregister(&netkit_link_ops);
931}
932
933module_init(netkit_init);
934module_exit(netkit_exit);
935
936MODULE_DESCRIPTION("BPF-programmable network device");
937MODULE_AUTHOR("Daniel Borkmann <daniel@iogearbox.net>");
938MODULE_AUTHOR("Nikolay Aleksandrov <razor@blackwall.org>");
939MODULE_LICENSE("GPL");
940MODULE_ALIAS_RTNL_LINK(DRV_NAME);
include/net/netkit.h+38−0
@@ -0,0 +1,38 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/* Copyright (c) 2023 Isovalent */
3#ifndef __NET_NETKIT_H
4#define __NET_NETKIT_H
5
6#include <linux/bpf.h>
7
8#ifdef CONFIG_NETKIT
9int netkit_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog);
10int netkit_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
11int netkit_prog_detach(const union bpf_attr *attr, struct bpf_prog *prog);
12int netkit_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr);
13#else
14static inline int netkit_prog_attach(const union bpf_attr *attr,
15 struct bpf_prog *prog)
16{
17 return -EINVAL;
18}
19
20static inline int netkit_link_attach(const union bpf_attr *attr,
21 struct bpf_prog *prog)
22{
23 return -EINVAL;
24}
25
26static inline int netkit_prog_detach(const union bpf_attr *attr,
27 struct bpf_prog *prog)
28{
29 return -EINVAL;
30}
31
32static inline int netkit_prog_query(const union bpf_attr *attr,
33 union bpf_attr __user *uattr)
34{
35 return -EINVAL;
36}
37#endif /* CONFIG_NETKIT */
38#endif /* __NET_NETKIT_H */
include/uapi/linux/bpf.h+14−0
@@ -1052,6 +1052,8 @@ enum bpf_attach_type { · 1051 unmodified lines
1052 BPF_CGROUP_UNIX_RECVMSG,1052 BPF_CGROUP_UNIX_RECVMSG,
1053 BPF_CGROUP_UNIX_GETPEERNAME,1053 BPF_CGROUP_UNIX_GETPEERNAME,
1054 BPF_CGROUP_UNIX_GETSOCKNAME,1054 BPF_CGROUP_UNIX_GETSOCKNAME,
1055 BPF_NETKIT_PRIMARY,
1056 BPF_NETKIT_PEER,
1055 __MAX_BPF_ATTACH_TYPE1057 __MAX_BPF_ATTACH_TYPE
1056};1058};
1057 1059
@@ -1071,6 +1073,7 @@ enum bpf_link_type { · 13 unmodified lines
1071 BPF_LINK_TYPE_NETFILTER = 10,1073 BPF_LINK_TYPE_NETFILTER = 10,
1072 BPF_LINK_TYPE_TCX = 11,1074 BPF_LINK_TYPE_TCX = 11,
1073 BPF_LINK_TYPE_UPROBE_MULTI = 12,1075 BPF_LINK_TYPE_UPROBE_MULTI = 12,
1076 BPF_LINK_TYPE_NETKIT = 13,
1074 MAX_BPF_LINK_TYPE,1077 MAX_BPF_LINK_TYPE,
1075};1078};
1076 1079
@@ -1656,6 +1659,13 @@ union bpf_attr { · 579 unmodified lines
1656 __u32 flags;1659 __u32 flags;
1657 __u32 pid;1660 __u32 pid;
1658 } uprobe_multi;1661 } uprobe_multi;
1662 struct {
1663 union {
1664 __u32 relative_fd;
1665 __u32 relative_id;
1666 };
1667 __u64 expected_revision;
1668 } netkit;
1659 };1669 };
1660 } link_create;1670 } link_create;
1661 1671
@@ -6576,6 +6586,10 @@ struct bpf_link_info { · 4914 unmodified lines
6576 __u32 ifindex;6586 __u32 ifindex;
6577 __u32 attach_type;6587 __u32 attach_type;
6578 } tcx;6588 } tcx;
6589 struct {
6590 __u32 ifindex;
6591 __u32 attach_type;
6592 } netkit;
6579 };6593 };
6580} __attribute__((aligned(8)));6594} __attribute__((aligned(8)));
6581 6595
include/uapi/linux/if_link.h+24−0
@@ -756,6 +756,30 @@ struct tunnel_msg { · 755 unmodified lines
756 __u32 ifindex;756 __u32 ifindex;
757};757};
758 758
759/* netkit section */
760enum netkit_action {
761 NETKIT_NEXT = -1,
762 NETKIT_PASS = 0,
763 NETKIT_DROP = 2,
764 NETKIT_REDIRECT = 7,
765};
766
767enum netkit_mode {
768 NETKIT_L2,
769 NETKIT_L3,
770};
771
772enum {
773 IFLA_NETKIT_UNSPEC,
774 IFLA_NETKIT_PEER_INFO,
775 IFLA_NETKIT_PRIMARY,
776 IFLA_NETKIT_POLICY,
777 IFLA_NETKIT_PEER_POLICY,
778 IFLA_NETKIT_MODE,
779 __IFLA_NETKIT_MAX,
780};
781#define IFLA_NETKIT_MAX (__IFLA_NETKIT_MAX - 1)
782
759/* VXLAN section */783/* VXLAN section */
760 784
761/* include statistics in the dump */785/* include statistics in the dump */
kernel/bpf/syscall.c+25−5
@@ -35,8 +35,9 @@ · 34 unmodified lines
35#include <linux/rcupdate_trace.h>35#include <linux/rcupdate_trace.h>
36#include <linux/memcontrol.h>36#include <linux/memcontrol.h>
37#include <linux/trace_events.h>37#include <linux/trace_events.h>
38#include <net/netfilter/nf_bpf_link.h>
39 38
39#include <net/netfilter/nf_bpf_link.h>
40#include <net/netkit.h>
40#include <net/tcx.h>41#include <net/tcx.h>
41 42
42#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \43#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
@@ -3730,6 +3731,8 @@ attach_type_to_prog_type(enum bpf_attach_type attach_type) · 3687 unmodified lines
3730 return BPF_PROG_TYPE_LSM;3731 return BPF_PROG_TYPE_LSM;
3731 case BPF_TCX_INGRESS:3732 case BPF_TCX_INGRESS:
3732 case BPF_TCX_EGRESS:3733 case BPF_TCX_EGRESS:
3734 case BPF_NETKIT_PRIMARY:
3735 case BPF_NETKIT_PEER:
3733 return BPF_PROG_TYPE_SCHED_CLS;3736 return BPF_PROG_TYPE_SCHED_CLS;
3734 default:3737 default:
3735 return BPF_PROG_TYPE_UNSPEC;3738 return BPF_PROG_TYPE_UNSPEC;
@@ -3781,7 +3784,9 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, · 45 unmodified lines
3781 return 0;3784 return 0;
3782 case BPF_PROG_TYPE_SCHED_CLS:3785 case BPF_PROG_TYPE_SCHED_CLS:
3783 if (attach_type != BPF_TCX_INGRESS &&3786 if (attach_type != BPF_TCX_INGRESS &&
3784 attach_type != BPF_TCX_EGRESS)3787 attach_type != BPF_TCX_EGRESS &&
3788 attach_type != BPF_NETKIT_PRIMARY &&
3789 attach_type != BPF_NETKIT_PEER)
3785 return -EINVAL;3790 return -EINVAL;
3786 return 0;3791 return 0;
3787 default:3792 default:
@@ -3864,7 +3869,11 @@ static int bpf_prog_attach(const union bpf_attr *attr) · 76 unmodified lines
3864 ret = cgroup_bpf_prog_attach(attr, ptype, prog);3869 ret = cgroup_bpf_prog_attach(attr, ptype, prog);
3865 break;3870 break;
3866 case BPF_PROG_TYPE_SCHED_CLS:3871 case BPF_PROG_TYPE_SCHED_CLS:
3867 ret = tcx_prog_attach(attr, prog);3872 if (attr->attach_type == BPF_TCX_INGRESS ||
3873 attr->attach_type == BPF_TCX_EGRESS)
3874 ret = tcx_prog_attach(attr, prog);
3875 else
3876 ret = netkit_prog_attach(attr, prog);
3868 break;3877 break;
3869 default:3878 default:
3870 ret = -EINVAL;3879 ret = -EINVAL;
@@ -3925,7 +3934,11 @@ static int bpf_prog_detach(const union bpf_attr *attr) · 54 unmodified lines
3925 ret = cgroup_bpf_prog_detach(attr, ptype);3934 ret = cgroup_bpf_prog_detach(attr, ptype);
3926 break;3935 break;
3927 case BPF_PROG_TYPE_SCHED_CLS:3936 case BPF_PROG_TYPE_SCHED_CLS:
3928 ret = tcx_prog_detach(attr, prog);3937 if (attr->attach_type == BPF_TCX_INGRESS ||
3938 attr->attach_type == BPF_TCX_EGRESS)
3939 ret = tcx_prog_detach(attr, prog);
3940 else
3941 ret = netkit_prog_detach(attr, prog);
3929 break;3942 break;
3930 default:3943 default:
3931 ret = -EINVAL;3944 ret = -EINVAL;
@@ -3992,6 +4005,9 @@ static int bpf_prog_query(const union bpf_attr *attr, · 60 unmodified lines
3992 case BPF_TCX_INGRESS:4005 case BPF_TCX_INGRESS:
3993 case BPF_TCX_EGRESS:4006 case BPF_TCX_EGRESS:
3994 return tcx_prog_query(attr, uattr);4007 return tcx_prog_query(attr, uattr);
4008 case BPF_NETKIT_PRIMARY:
4009 case BPF_NETKIT_PEER:
4010 return netkit_prog_query(attr, uattr);
3995 default:4011 default:
3996 return -EINVAL;4012 return -EINVAL;
3997 }4013 }
@@ -4973,7 +4989,11 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr) · 975 unmodified lines
4973 ret = bpf_xdp_link_attach(attr, prog);4989 ret = bpf_xdp_link_attach(attr, prog);
4974 break;4990 break;
4975 case BPF_PROG_TYPE_SCHED_CLS:4991 case BPF_PROG_TYPE_SCHED_CLS:
4976 ret = tcx_link_attach(attr, prog);4992 if (attr->link_create.attach_type == BPF_TCX_INGRESS ||
4993 attr->link_create.attach_type == BPF_TCX_EGRESS)
4994 ret = tcx_link_attach(attr, prog);
4995 else
4996 ret = netkit_link_attach(attr, prog);
4977 break;4997 break;
4978 case BPF_PROG_TYPE_NETFILTER:4998 case BPF_PROG_TYPE_NETFILTER:
4979 ret = bpf_nf_link_attach(attr, prog);4999 ret = bpf_nf_link_attach(attr, prog);
tools/include/uapi/linux/bpf.h+14−0
@@ -1052,6 +1052,8 @@ enum bpf_attach_type { · 1051 unmodified lines
1052 BPF_CGROUP_UNIX_RECVMSG,1052 BPF_CGROUP_UNIX_RECVMSG,
1053 BPF_CGROUP_UNIX_GETPEERNAME,1053 BPF_CGROUP_UNIX_GETPEERNAME,
1054 BPF_CGROUP_UNIX_GETSOCKNAME,1054 BPF_CGROUP_UNIX_GETSOCKNAME,
1055 BPF_NETKIT_PRIMARY,
1056 BPF_NETKIT_PEER,
1055 __MAX_BPF_ATTACH_TYPE1057 __MAX_BPF_ATTACH_TYPE
1056};1058};
1057 1059
@@ -1071,6 +1073,7 @@ enum bpf_link_type { · 13 unmodified lines
1071 BPF_LINK_TYPE_NETFILTER = 10,1073 BPF_LINK_TYPE_NETFILTER = 10,
1072 BPF_LINK_TYPE_TCX = 11,1074 BPF_LINK_TYPE_TCX = 11,
1073 BPF_LINK_TYPE_UPROBE_MULTI = 12,1075 BPF_LINK_TYPE_UPROBE_MULTI = 12,
1076 BPF_LINK_TYPE_NETKIT = 13,
1074 MAX_BPF_LINK_TYPE,1077 MAX_BPF_LINK_TYPE,
1075};1078};
1076 1079
@@ -1656,6 +1659,13 @@ union bpf_attr { · 579 unmodified lines
1656 __u32 flags;1659 __u32 flags;
1657 __u32 pid;1660 __u32 pid;
1658 } uprobe_multi;1661 } uprobe_multi;
1662 struct {
1663 union {
1664 __u32 relative_fd;
1665 __u32 relative_id;
1666 };
1667 __u64 expected_revision;
1668 } netkit;
1659 };1669 };
1660 } link_create;1670 } link_create;
1661 1671
@@ -6576,6 +6586,10 @@ struct bpf_link_info { · 4914 unmodified lines
6576 __u32 ifindex;6586 __u32 ifindex;
6577 __u32 attach_type;6587 __u32 attach_type;
6578 } tcx;6588 } tcx;
6589 struct {
6590 __u32 ifindex;
6591 __u32 attach_type;
6592 } netkit;
6579 };6593 };
6580} __attribute__((aligned(8)));6594} __attribute__((aligned(8)));
6581 6595

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t