Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bridge: Add vlan filtering infrastructure

Adds an optional infrustructure component to bridge that would allow
native vlan filtering in the bridge. Each bridge port (as well
as the bridge device) now get a VLAN bitmap. Each bit in the bitmap
is associated with a vlan id. This way if the bit corresponding to
the vid is set in the bitmap that the packet with vid is allowed to
enter and exit the port.

Write access the bitmap is protected by RTNL and read access
protected by RCU.

Vlan functionality is disabled by default.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Vlad Yasevich and committed by
David S. Miller
243a2e63 22222997

+296
+14
net/bridge/Kconfig
··· 46 46 Say N to exclude this support and reduce the binary size. 47 47 48 48 If unsure, say Y. 49 + 50 + config BRIDGE_VLAN_FILTERING 51 + bool "VLAN filtering" 52 + depends on BRIDGE 53 + depends on VLAN_8021Q 54 + default n 55 + ---help--- 56 + If you say Y here, then the Ethernet bridge will be able selectively 57 + receive and forward traffic based on VLAN information in the packet 58 + any VLAN information configured on the bridge port or bridge device. 59 + 60 + Say N to exclude this support and reduce the binary size. 61 + 62 + If unsure, say Y.
+2
net/bridge/Makefile
··· 14 14 15 15 bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o 16 16 17 + bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o 18 + 17 19 obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
+1
net/bridge/br_if.c
··· 139 139 140 140 br_ifinfo_notify(RTM_DELLINK, p); 141 141 142 + nbp_vlan_flush(p); 142 143 br_fdb_delete_by_port(br, p, 1); 143 144 144 145 list_del_rcu(&p->list);
+59
net/bridge/br_private.h
··· 18 18 #include <linux/netpoll.h> 19 19 #include <linux/u64_stats_sync.h> 20 20 #include <net/route.h> 21 + #include <linux/if_vlan.h> 21 22 22 23 #define BR_HASH_BITS 8 23 24 #define BR_HASH_SIZE (1 << BR_HASH_BITS) ··· 27 26 28 27 #define BR_PORT_BITS 10 29 28 #define BR_MAX_PORTS (1<<BR_PORT_BITS) 29 + #define BR_VLAN_BITMAP_LEN BITS_TO_LONGS(VLAN_N_VID) 30 30 31 31 #define BR_VERSION "2.3" 32 32 ··· 63 61 #endif 64 62 } u; 65 63 __be16 proto; 64 + }; 65 + 66 + struct net_port_vlans { 67 + u16 port_idx; 68 + union { 69 + struct net_bridge_port *port; 70 + struct net_bridge *br; 71 + } parent; 72 + struct rcu_head rcu; 73 + unsigned long vlan_bitmap[BR_VLAN_BITMAP_LEN]; 66 74 }; 67 75 68 76 struct net_bridge_fdb_entry ··· 167 155 168 156 #ifdef CONFIG_NET_POLL_CONTROLLER 169 157 struct netpoll *np; 158 + #endif 159 + #ifdef CONFIG_BRIDGE_VLAN_FILTERING 160 + struct net_port_vlans __rcu *vlan_info; 170 161 #endif 171 162 }; 172 163 ··· 272 257 struct timer_list topology_change_timer; 273 258 struct timer_list gc_timer; 274 259 struct kobject *ifobj; 260 + #ifdef CONFIG_BRIDGE_VLAN_FILTERING 261 + u8 vlan_enabled; 262 + struct net_port_vlans __rcu *vlan_info; 263 + #endif 275 264 }; 276 265 277 266 struct br_input_skb_cb { ··· 548 529 static inline void br_mdb_uninit(void) 549 530 { 550 531 } 532 + #endif 533 + 534 + /* br_vlan.c */ 535 + #ifdef CONFIG_BRIDGE_VLAN_FILTERING 536 + extern int br_vlan_add(struct net_bridge *br, u16 vid); 537 + extern int br_vlan_delete(struct net_bridge *br, u16 vid); 538 + extern void br_vlan_flush(struct net_bridge *br); 539 + extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); 540 + extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid); 541 + extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); 542 + extern void nbp_vlan_flush(struct net_bridge_port *port); 543 + #else 544 + static inline int br_vlan_add(struct net_bridge *br, u16 vid) 545 + { 546 + return -EOPNOTSUPP; 547 + } 548 + 549 + static inline int br_vlan_delete(struct net_bridge *br, u16 vid) 550 + { 551 + return -EOPNOTSUPP; 552 + } 553 + 554 + static inline void br_vlan_flush(struct net_bridge *br) 555 + { 556 + } 557 + 558 + static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid) 559 + { 560 + return -EOPNOTSUPP; 561 + } 562 + 563 + static inline int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) 564 + { 565 + return -EOPNOTSUPP; 566 + } 567 + 568 + static inline void nbp_vlan_flush(struct net_bridge_port *port) 569 + { 570 + } 571 + 551 572 #endif 552 573 553 574 /* br_netfilter.c */
+21
net/bridge/br_sysfs_br.c
··· 691 691 static DEVICE_ATTR(nf_call_arptables, S_IRUGO | S_IWUSR, 692 692 show_nf_call_arptables, store_nf_call_arptables); 693 693 #endif 694 + #ifdef CONFIG_BRIDGE_VLAN_FILTERING 695 + static ssize_t show_vlan_filtering(struct device *d, 696 + struct device_attribute *attr, 697 + char *buf) 698 + { 699 + struct net_bridge *br = to_bridge(d); 700 + return sprintf(buf, "%d\n", br->vlan_enabled); 701 + } 702 + 703 + static ssize_t store_vlan_filtering(struct device *d, 704 + struct device_attribute *attr, 705 + const char *buf, size_t len) 706 + { 707 + return store_bridge_parm(d, buf, len, br_vlan_filter_toggle); 708 + } 709 + static DEVICE_ATTR(vlan_filtering, S_IRUGO | S_IWUSR, 710 + show_vlan_filtering, store_vlan_filtering); 711 + #endif 694 712 695 713 static struct attribute *bridge_attrs[] = { 696 714 &dev_attr_forward_delay.attr, ··· 749 731 &dev_attr_nf_call_iptables.attr, 750 732 &dev_attr_nf_call_ip6tables.attr, 751 733 &dev_attr_nf_call_arptables.attr, 734 + #endif 735 + #ifdef CONFIG_BRIDGE_VLAN_FILTERING 736 + &dev_attr_vlan_filtering.attr, 752 737 #endif 753 738 NULL 754 739 };
+199
net/bridge/br_vlan.c
··· 1 + #include <linux/kernel.h> 2 + #include <linux/netdevice.h> 3 + #include <linux/rtnetlink.h> 4 + #include <linux/slab.h> 5 + 6 + #include "br_private.h" 7 + 8 + static int __vlan_add(struct net_port_vlans *v, u16 vid) 9 + { 10 + int err; 11 + 12 + if (test_bit(vid, v->vlan_bitmap)) 13 + return -EEXIST; 14 + 15 + if (v->port_idx && vid) { 16 + struct net_device *dev = v->parent.port->dev; 17 + 18 + /* Add VLAN to the device filter if it is supported. 19 + * Stricly speaking, this is not necessary now, since devices 20 + * are made promiscuous by the bridge, but if that ever changes 21 + * this code will allow tagged traffic to enter the bridge. 22 + */ 23 + if (dev->features & NETIF_F_HW_VLAN_FILTER) { 24 + err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid); 25 + if (err) 26 + return err; 27 + } 28 + } 29 + 30 + set_bit(vid, v->vlan_bitmap); 31 + return 0; 32 + } 33 + 34 + static int __vlan_del(struct net_port_vlans *v, u16 vid) 35 + { 36 + if (!test_bit(vid, v->vlan_bitmap)) 37 + return -EINVAL; 38 + 39 + if (v->port_idx && vid) { 40 + struct net_device *dev = v->parent.port->dev; 41 + 42 + if (dev->features & NETIF_F_HW_VLAN_FILTER) 43 + dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid); 44 + } 45 + 46 + clear_bit(vid, v->vlan_bitmap); 47 + if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { 48 + if (v->port_idx) 49 + rcu_assign_pointer(v->parent.port->vlan_info, NULL); 50 + else 51 + rcu_assign_pointer(v->parent.br->vlan_info, NULL); 52 + kfree_rcu(v, rcu); 53 + } 54 + return 0; 55 + } 56 + 57 + static void __vlan_flush(struct net_port_vlans *v) 58 + { 59 + bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN); 60 + if (v->port_idx) 61 + rcu_assign_pointer(v->parent.port->vlan_info, NULL); 62 + else 63 + rcu_assign_pointer(v->parent.br->vlan_info, NULL); 64 + kfree_rcu(v, rcu); 65 + } 66 + 67 + /* Must be protected by RTNL */ 68 + int br_vlan_add(struct net_bridge *br, u16 vid) 69 + { 70 + struct net_port_vlans *pv = NULL; 71 + int err; 72 + 73 + ASSERT_RTNL(); 74 + 75 + pv = rtnl_dereference(br->vlan_info); 76 + if (pv) 77 + return __vlan_add(pv, vid); 78 + 79 + /* Create port vlan infomration 80 + */ 81 + pv = kzalloc(sizeof(*pv), GFP_KERNEL); 82 + if (!pv) 83 + return -ENOMEM; 84 + 85 + pv->parent.br = br; 86 + err = __vlan_add(pv, vid); 87 + if (err) 88 + goto out; 89 + 90 + rcu_assign_pointer(br->vlan_info, pv); 91 + return 0; 92 + out: 93 + kfree(pv); 94 + return err; 95 + } 96 + 97 + /* Must be protected by RTNL */ 98 + int br_vlan_delete(struct net_bridge *br, u16 vid) 99 + { 100 + struct net_port_vlans *pv; 101 + 102 + ASSERT_RTNL(); 103 + 104 + pv = rtnl_dereference(br->vlan_info); 105 + if (!pv) 106 + return -EINVAL; 107 + 108 + __vlan_del(pv, vid); 109 + return 0; 110 + } 111 + 112 + void br_vlan_flush(struct net_bridge *br) 113 + { 114 + struct net_port_vlans *pv; 115 + 116 + ASSERT_RTNL(); 117 + 118 + pv = rtnl_dereference(br->vlan_info); 119 + if (!pv) 120 + return; 121 + 122 + __vlan_flush(pv); 123 + } 124 + 125 + int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) 126 + { 127 + if (!rtnl_trylock()) 128 + return restart_syscall(); 129 + 130 + if (br->vlan_enabled == val) 131 + goto unlock; 132 + 133 + br->vlan_enabled = val; 134 + 135 + unlock: 136 + rtnl_unlock(); 137 + return 0; 138 + } 139 + 140 + /* Must be protected by RTNL */ 141 + int nbp_vlan_add(struct net_bridge_port *port, u16 vid) 142 + { 143 + struct net_port_vlans *pv = NULL; 144 + int err; 145 + 146 + ASSERT_RTNL(); 147 + 148 + pv = rtnl_dereference(port->vlan_info); 149 + if (pv) 150 + return __vlan_add(pv, vid); 151 + 152 + /* Create port vlan infomration 153 + */ 154 + pv = kzalloc(sizeof(*pv), GFP_KERNEL); 155 + if (!pv) { 156 + err = -ENOMEM; 157 + goto clean_up; 158 + } 159 + 160 + pv->port_idx = port->port_no; 161 + pv->parent.port = port; 162 + err = __vlan_add(pv, vid); 163 + if (err) 164 + goto clean_up; 165 + 166 + rcu_assign_pointer(port->vlan_info, pv); 167 + return 0; 168 + 169 + clean_up: 170 + kfree(pv); 171 + return err; 172 + } 173 + 174 + /* Must be protected by RTNL */ 175 + int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) 176 + { 177 + struct net_port_vlans *pv; 178 + 179 + ASSERT_RTNL(); 180 + 181 + pv = rtnl_dereference(port->vlan_info); 182 + if (!pv) 183 + return -EINVAL; 184 + 185 + return __vlan_del(pv, vid); 186 + } 187 + 188 + void nbp_vlan_flush(struct net_bridge_port *port) 189 + { 190 + struct net_port_vlans *pv; 191 + 192 + ASSERT_RTNL(); 193 + 194 + pv = rtnl_dereference(port->vlan_info); 195 + if (!pv) 196 + return; 197 + 198 + __vlan_flush(pv); 199 + }