| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * INET 802.1Q VLAN |
|---|
| 3 | 4 | * Ethernet-type device handling. |
|---|
| .. | .. |
|---|
| 11 | 12 | * Add HW acceleration hooks - David S. Miller <davem@redhat.com>; |
|---|
| 12 | 13 | * Correct all the locking - David S. Miller <davem@redhat.com>; |
|---|
| 13 | 14 | * Use hash table for VLAN groups - David S. Miller <davem@redhat.com> |
|---|
| 14 | | - * |
|---|
| 15 | | - * This program is free software; you can redistribute it and/or |
|---|
| 16 | | - * modify it under the terms of the GNU General Public License |
|---|
| 17 | | - * as published by the Free Software Foundation; either version |
|---|
| 18 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 19 | 15 | */ |
|---|
| 20 | 16 | |
|---|
| 21 | 17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| .. | .. |
|---|
| 55 | 51 | __be16 vlan_proto, u16 vlan_id) |
|---|
| 56 | 52 | { |
|---|
| 57 | 53 | struct net_device **array; |
|---|
| 58 | | - unsigned int pidx, vidx; |
|---|
| 54 | + unsigned int vidx; |
|---|
| 59 | 55 | unsigned int size; |
|---|
| 56 | + int pidx; |
|---|
| 60 | 57 | |
|---|
| 61 | 58 | ASSERT_RTNL(); |
|---|
| 62 | 59 | |
|---|
| 63 | 60 | pidx = vlan_proto_idx(vlan_proto); |
|---|
| 61 | + if (pidx < 0) |
|---|
| 62 | + return -EINVAL; |
|---|
| 63 | + |
|---|
| 64 | 64 | vidx = vlan_id / VLAN_GROUP_ARRAY_PART_LEN; |
|---|
| 65 | 65 | array = vg->vlan_devices_arrays[pidx][vidx]; |
|---|
| 66 | 66 | if (array != NULL) |
|---|
| .. | .. |
|---|
| 73 | 73 | |
|---|
| 74 | 74 | vg->vlan_devices_arrays[pidx][vidx] = array; |
|---|
| 75 | 75 | return 0; |
|---|
| 76 | +} |
|---|
| 77 | + |
|---|
| 78 | +static void vlan_stacked_transfer_operstate(const struct net_device *rootdev, |
|---|
| 79 | + struct net_device *dev, |
|---|
| 80 | + struct vlan_dev_priv *vlan) |
|---|
| 81 | +{ |
|---|
| 82 | + if (!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING)) |
|---|
| 83 | + netif_stacked_transfer_operstate(rootdev, dev); |
|---|
| 76 | 84 | } |
|---|
| 77 | 85 | |
|---|
| 78 | 86 | void unregister_vlan_dev(struct net_device *dev, struct list_head *head) |
|---|
| .. | .. |
|---|
| 112 | 120 | } |
|---|
| 113 | 121 | |
|---|
| 114 | 122 | vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); |
|---|
| 115 | | - |
|---|
| 116 | | - /* Get rid of the vlan's reference to real_dev */ |
|---|
| 117 | | - dev_put(real_dev); |
|---|
| 118 | 123 | } |
|---|
| 119 | 124 | |
|---|
| 120 | 125 | int vlan_check_real_dev(struct net_device *real_dev, |
|---|
| .. | .. |
|---|
| 168 | 173 | if (err < 0) |
|---|
| 169 | 174 | goto out_uninit_mvrp; |
|---|
| 170 | 175 | |
|---|
| 171 | | - vlan->nest_level = dev_get_nest_level(real_dev) + 1; |
|---|
| 172 | 176 | err = register_netdevice(dev); |
|---|
| 173 | 177 | if (err < 0) |
|---|
| 174 | 178 | goto out_uninit_mvrp; |
|---|
| .. | .. |
|---|
| 177 | 181 | if (err) |
|---|
| 178 | 182 | goto out_unregister_netdev; |
|---|
| 179 | 183 | |
|---|
| 180 | | - /* Account for reference in struct vlan_dev_priv */ |
|---|
| 181 | | - dev_hold(real_dev); |
|---|
| 182 | | - |
|---|
| 183 | | - netif_stacked_transfer_operstate(real_dev, dev); |
|---|
| 184 | + vlan_stacked_transfer_operstate(real_dev, dev, vlan); |
|---|
| 184 | 185 | linkwatch_fire_event(dev); /* _MUST_ call rfc2863_policy() */ |
|---|
| 185 | 186 | |
|---|
| 186 | 187 | /* So, got the sucker initialized, now lets place |
|---|
| .. | .. |
|---|
| 277 | 278 | return 0; |
|---|
| 278 | 279 | |
|---|
| 279 | 280 | out_free_newdev: |
|---|
| 280 | | - if (new_dev->reg_state == NETREG_UNINITIALIZED || |
|---|
| 281 | | - new_dev->reg_state == NETREG_UNREGISTERED) |
|---|
| 282 | | - free_netdev(new_dev); |
|---|
| 281 | + free_netdev(new_dev); |
|---|
| 283 | 282 | return err; |
|---|
| 284 | 283 | } |
|---|
| 285 | 284 | |
|---|
| .. | .. |
|---|
| 331 | 330 | |
|---|
| 332 | 331 | vlandev->priv_flags &= ~IFF_XMIT_DST_RELEASE; |
|---|
| 333 | 332 | vlandev->priv_flags |= (vlan->real_dev->priv_flags & IFF_XMIT_DST_RELEASE); |
|---|
| 333 | + vlandev->hw_enc_features = vlan_tnl_features(vlan->real_dev); |
|---|
| 334 | 334 | |
|---|
| 335 | 335 | netdev_update_features(vlandev); |
|---|
| 336 | 336 | } |
|---|
| .. | .. |
|---|
| 358 | 358 | static int vlan_device_event(struct notifier_block *unused, unsigned long event, |
|---|
| 359 | 359 | void *ptr) |
|---|
| 360 | 360 | { |
|---|
| 361 | + struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr); |
|---|
| 361 | 362 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
|---|
| 362 | 363 | struct vlan_group *grp; |
|---|
| 363 | 364 | struct vlan_info *vlan_info; |
|---|
| .. | .. |
|---|
| 398 | 399 | case NETDEV_CHANGE: |
|---|
| 399 | 400 | /* Propagate real device state to vlan devices */ |
|---|
| 400 | 401 | vlan_group_for_each_dev(grp, i, vlandev) |
|---|
| 401 | | - netif_stacked_transfer_operstate(dev, vlandev); |
|---|
| 402 | + vlan_stacked_transfer_operstate(dev, vlandev, |
|---|
| 403 | + vlan_dev_priv(vlandev)); |
|---|
| 402 | 404 | break; |
|---|
| 403 | 405 | |
|---|
| 404 | 406 | case NETDEV_CHANGEADDR: |
|---|
| .. | .. |
|---|
| 445 | 447 | dev_close_many(&close_list, false); |
|---|
| 446 | 448 | |
|---|
| 447 | 449 | list_for_each_entry_safe(vlandev, tmp, &close_list, close_list) { |
|---|
| 448 | | - netif_stacked_transfer_operstate(dev, vlandev); |
|---|
| 450 | + vlan_stacked_transfer_operstate(dev, vlandev, |
|---|
| 451 | + vlan_dev_priv(vlandev)); |
|---|
| 449 | 452 | list_del_init(&vlandev->close_list); |
|---|
| 450 | 453 | } |
|---|
| 451 | 454 | list_del(&close_list); |
|---|
| .. | .. |
|---|
| 460 | 463 | |
|---|
| 461 | 464 | vlan = vlan_dev_priv(vlandev); |
|---|
| 462 | 465 | if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) |
|---|
| 463 | | - dev_change_flags(vlandev, flgs | IFF_UP); |
|---|
| 464 | | - netif_stacked_transfer_operstate(dev, vlandev); |
|---|
| 466 | + dev_change_flags(vlandev, flgs | IFF_UP, |
|---|
| 467 | + extack); |
|---|
| 468 | + vlan_stacked_transfer_operstate(dev, vlandev, vlan); |
|---|
| 465 | 469 | } |
|---|
| 466 | 470 | break; |
|---|
| 467 | 471 | |
|---|
| .. | .. |
|---|
| 648 | 652 | return err; |
|---|
| 649 | 653 | } |
|---|
| 650 | 654 | |
|---|
| 651 | | -static struct sk_buff *vlan_gro_receive(struct list_head *head, |
|---|
| 652 | | - struct sk_buff *skb) |
|---|
| 653 | | -{ |
|---|
| 654 | | - const struct packet_offload *ptype; |
|---|
| 655 | | - unsigned int hlen, off_vlan; |
|---|
| 656 | | - struct sk_buff *pp = NULL; |
|---|
| 657 | | - struct vlan_hdr *vhdr; |
|---|
| 658 | | - struct sk_buff *p; |
|---|
| 659 | | - __be16 type; |
|---|
| 660 | | - int flush = 1; |
|---|
| 661 | | - |
|---|
| 662 | | - off_vlan = skb_gro_offset(skb); |
|---|
| 663 | | - hlen = off_vlan + sizeof(*vhdr); |
|---|
| 664 | | - vhdr = skb_gro_header_fast(skb, off_vlan); |
|---|
| 665 | | - if (skb_gro_header_hard(skb, hlen)) { |
|---|
| 666 | | - vhdr = skb_gro_header_slow(skb, hlen, off_vlan); |
|---|
| 667 | | - if (unlikely(!vhdr)) |
|---|
| 668 | | - goto out; |
|---|
| 669 | | - } |
|---|
| 670 | | - |
|---|
| 671 | | - type = vhdr->h_vlan_encapsulated_proto; |
|---|
| 672 | | - |
|---|
| 673 | | - rcu_read_lock(); |
|---|
| 674 | | - ptype = gro_find_receive_by_type(type); |
|---|
| 675 | | - if (!ptype) |
|---|
| 676 | | - goto out_unlock; |
|---|
| 677 | | - |
|---|
| 678 | | - flush = 0; |
|---|
| 679 | | - |
|---|
| 680 | | - list_for_each_entry(p, head, list) { |
|---|
| 681 | | - struct vlan_hdr *vhdr2; |
|---|
| 682 | | - |
|---|
| 683 | | - if (!NAPI_GRO_CB(p)->same_flow) |
|---|
| 684 | | - continue; |
|---|
| 685 | | - |
|---|
| 686 | | - vhdr2 = (struct vlan_hdr *)(p->data + off_vlan); |
|---|
| 687 | | - if (compare_vlan_header(vhdr, vhdr2)) |
|---|
| 688 | | - NAPI_GRO_CB(p)->same_flow = 0; |
|---|
| 689 | | - } |
|---|
| 690 | | - |
|---|
| 691 | | - skb_gro_pull(skb, sizeof(*vhdr)); |
|---|
| 692 | | - skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr)); |
|---|
| 693 | | - pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb); |
|---|
| 694 | | - |
|---|
| 695 | | -out_unlock: |
|---|
| 696 | | - rcu_read_unlock(); |
|---|
| 697 | | -out: |
|---|
| 698 | | - skb_gro_flush_final(skb, pp, flush); |
|---|
| 699 | | - |
|---|
| 700 | | - return pp; |
|---|
| 701 | | -} |
|---|
| 702 | | - |
|---|
| 703 | | -static int vlan_gro_complete(struct sk_buff *skb, int nhoff) |
|---|
| 704 | | -{ |
|---|
| 705 | | - struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff); |
|---|
| 706 | | - __be16 type = vhdr->h_vlan_encapsulated_proto; |
|---|
| 707 | | - struct packet_offload *ptype; |
|---|
| 708 | | - int err = -ENOENT; |
|---|
| 709 | | - |
|---|
| 710 | | - rcu_read_lock(); |
|---|
| 711 | | - ptype = gro_find_complete_by_type(type); |
|---|
| 712 | | - if (ptype) |
|---|
| 713 | | - err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(*vhdr)); |
|---|
| 714 | | - |
|---|
| 715 | | - rcu_read_unlock(); |
|---|
| 716 | | - return err; |
|---|
| 717 | | -} |
|---|
| 718 | | - |
|---|
| 719 | | -static struct packet_offload vlan_packet_offloads[] __read_mostly = { |
|---|
| 720 | | - { |
|---|
| 721 | | - .type = cpu_to_be16(ETH_P_8021Q), |
|---|
| 722 | | - .priority = 10, |
|---|
| 723 | | - .callbacks = { |
|---|
| 724 | | - .gro_receive = vlan_gro_receive, |
|---|
| 725 | | - .gro_complete = vlan_gro_complete, |
|---|
| 726 | | - }, |
|---|
| 727 | | - }, |
|---|
| 728 | | - { |
|---|
| 729 | | - .type = cpu_to_be16(ETH_P_8021AD), |
|---|
| 730 | | - .priority = 10, |
|---|
| 731 | | - .callbacks = { |
|---|
| 732 | | - .gro_receive = vlan_gro_receive, |
|---|
| 733 | | - .gro_complete = vlan_gro_complete, |
|---|
| 734 | | - }, |
|---|
| 735 | | - }, |
|---|
| 736 | | -}; |
|---|
| 737 | | - |
|---|
| 738 | 655 | static int __net_init vlan_init_net(struct net *net) |
|---|
| 739 | 656 | { |
|---|
| 740 | 657 | struct vlan_net *vn = net_generic(net, vlan_net_id); |
|---|
| .. | .. |
|---|
| 762 | 679 | static int __init vlan_proto_init(void) |
|---|
| 763 | 680 | { |
|---|
| 764 | 681 | int err; |
|---|
| 765 | | - unsigned int i; |
|---|
| 766 | 682 | |
|---|
| 767 | 683 | pr_info("%s v%s\n", vlan_fullname, vlan_version); |
|---|
| 768 | 684 | |
|---|
| .. | .. |
|---|
| 786 | 702 | if (err < 0) |
|---|
| 787 | 703 | goto err5; |
|---|
| 788 | 704 | |
|---|
| 789 | | - for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++) |
|---|
| 790 | | - dev_add_offload(&vlan_packet_offloads[i]); |
|---|
| 791 | | - |
|---|
| 792 | 705 | vlan_ioctl_set(vlan_ioctl_handler); |
|---|
| 793 | 706 | return 0; |
|---|
| 794 | 707 | |
|---|
| .. | .. |
|---|
| 806 | 719 | |
|---|
| 807 | 720 | static void __exit vlan_cleanup_module(void) |
|---|
| 808 | 721 | { |
|---|
| 809 | | - unsigned int i; |
|---|
| 810 | | - |
|---|
| 811 | 722 | vlan_ioctl_set(NULL); |
|---|
| 812 | | - |
|---|
| 813 | | - for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++) |
|---|
| 814 | | - dev_remove_offload(&vlan_packet_offloads[i]); |
|---|
| 815 | 723 | |
|---|
| 816 | 724 | vlan_netlink_fini(); |
|---|
| 817 | 725 | |
|---|