| @@ -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 | |
| | 17 | struct 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 | |
| | 30 | struct netkit_link { |
| | 31 | struct bpf_link link; |
| | 32 | struct net_device *dev; |
| | 33 | u32 location; |
| | 34 | }; |
| | 35 | |
| | 36 | static __always_inline int |
| | 37 | netkit_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 | |
| | 52 | static 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 | |
| | 59 | static struct netkit *netkit_priv(const struct net_device *dev) |
| | 60 | { |
| | 61 | return netdev_priv(dev); |
| | 62 | } |
| | 63 | |
| | 64 | static 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: |
| | 95 | drop: |
| | 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 | |
| | 105 | static 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 | |
| | 119 | static 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 | |
| | 130 | static 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 | |
| | 144 | static void netkit_set_multicast(struct net_device *dev) |
| | 145 | { |
| | 146 | /* Nothing to do, we receive whatever gets pushed to us! */ |
| | 147 | } |
| | 148 | |
| | 149 | static 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; |
| | 168 | out: |
| | 169 | rcu_read_unlock(); |
| | 170 | } |
| | 171 | |
| | 172 | static struct net_device *netkit_peer_dev(struct net_device *dev) |
| | 173 | { |
| | 174 | return rcu_dereference(netkit_priv(dev)->peer); |
| | 175 | } |
| | 176 | |
| | 177 | static void netkit_uninit(struct net_device *dev); |
| | 178 | |
| | 179 | static 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 | |
| | 191 | static 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 | |
| | 197 | static const struct ethtool_ops netkit_ethtool_ops = { |
| | 198 | .get_drvinfo = netkit_get_drvinfo, |
| | 199 | }; |
| | 200 | |
| | 201 | static 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 | |
| | 242 | static 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 | |
| | 250 | static 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 | |
| | 264 | static 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 | |
| | 278 | static 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 | |
| | 290 | static struct rtnl_link_ops netkit_link_ops; |
| | 291 | |
| | 292 | static 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; |
| | 414 | err_configure_peer: |
| | 415 | unregister_netdevice(peer); |
| | 416 | return err; |
| | 417 | err_register_peer: |
| | 418 | free_netdev(peer); |
| | 419 | return err; |
| | 420 | } |
| | 421 | |
| | 422 | static 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 | |
| | 437 | static 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 | |
| | 446 | static void netkit_entry_sync(void) |
| | 447 | { |
| | 448 | synchronize_rcu(); |
| | 449 | } |
| | 450 | |
| | 451 | static 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 | |
| | 483 | int 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 | } |
| | 517 | out: |
| | 518 | if (replace_prog) |
| | 519 | bpf_prog_put(replace_prog); |
| | 520 | rtnl_unlock(); |
| | 521 | return ret; |
| | 522 | } |
| | 523 | |
| | 524 | int 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 | } |
| | 551 | out: |
| | 552 | rtnl_unlock(); |
| | 553 | return ret; |
| | 554 | } |
| | 555 | |
| | 556 | int 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)); |
| | 570 | out: |
| | 571 | rtnl_unlock(); |
| | 572 | return ret; |
| | 573 | } |
| | 574 | |
| | 575 | static struct netkit_link *netkit_link(const struct bpf_link *link) |
| | 576 | { |
| | 577 | return container_of(link, struct netkit_link, link); |
| | 578 | } |
| | 579 | |
| | 580 | static 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 | |
| | 602 | static 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 | } |
| | 627 | out: |
| | 628 | WARN_ON_ONCE(ret); |
| | 629 | rtnl_unlock(); |
| | 630 | } |
| | 631 | |
| | 632 | static 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 | } |
| | 669 | out: |
| | 670 | rtnl_unlock(); |
| | 671 | return ret; |
| | 672 | } |
| | 673 | |
| | 674 | static void netkit_link_dealloc(struct bpf_link *link) |
| | 675 | { |
| | 676 | kfree(netkit_link(link)); |
| | 677 | } |
| | 678 | |
| | 679 | static 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 | |
| | 695 | static 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 | |
| | 711 | static int netkit_link_detach(struct bpf_link *link) |
| | 712 | { |
| | 713 | netkit_link_release(link); |
| | 714 | return 0; |
| | 715 | } |
| | 716 | |
| | 717 | static 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 | |
| | 726 | static 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 | |
| | 739 | int 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); |
| | 774 | out: |
| | 775 | rtnl_unlock(); |
| | 776 | return ret; |
| | 777 | } |
| | 778 | |
| | 779 | static 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 | |
| | 799 | static void netkit_uninit(struct net_device *dev) |
| | 800 | { |
| | 801 | netkit_release_all(dev); |
| | 802 | } |
| | 803 | |
| | 804 | static 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 | |
| | 818 | static 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 | |
| | 864 | static 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 | |
| | 873 | static 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 | |
| | 894 | static 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 | |
| | 903 | static 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 | |
| | 918 | static __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 | |
| | 928 | static __exit void netkit_exit(void) |
| | 929 | { |
| | 930 | rtnl_link_unregister(&netkit_link_ops); |
| | 931 | } |
| | 932 | |
| | 933 | module_init(netkit_init); |
| | 934 | module_exit(netkit_exit); |
| | 935 | |
| | 936 | MODULE_DESCRIPTION("BPF-programmable network device"); |
| | 937 | MODULE_AUTHOR("Daniel Borkmann <daniel@iogearbox.net>"); |
| | 938 | MODULE_AUTHOR("Nikolay Aleksandrov <razor@blackwall.org>"); |
| | 939 | MODULE_LICENSE("GPL"); |
| | 940 | MODULE_ALIAS_RTNL_LINK(DRV_NAME); |