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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'bridge-handle-changes-in-vlan_flag_bridge_binding'

Petr Machata says:

====================
bridge: Handle changes in VLAN_FLAG_BRIDGE_BINDING

When bridge binding is enabled on a VLAN netdevice, its link state should
track bridge ports that are members of the corresponding VLAN. This works
for a newly-added netdevices. However toggling the option does not have the
effect of enabling or disabling the behavior as appropriate.

In this patchset, have bridge react to bridge_binding toggles on VLAN
uppers.

There has been another attempt at supporting this behavior in 2022 by
Sevinj Aghayeva [0]. A discussion ensued that informed how this new
patchset is constructed, namely that the logic is in the bridge as opposed
to the 8021q driver, and the bridge reacts to NETDEV_CHANGE events on the
8021q upper.

Patches #1 and #2 contain the implementation, patches #3 and #4 a
selftest.

[0] https://lore.kernel.org/netdev/cover.1660100506.git.sevinj.aghayeva@gmail.com/
====================

Link: https://patch.msgid.link/cover.1734540770.git.petrm@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+340 -8
+7
net/bridge/br.c
··· 51 51 } 52 52 } 53 53 54 + if (is_vlan_dev(dev)) { 55 + struct net_device *real_dev = vlan_dev_real_dev(dev); 56 + 57 + if (netif_is_bridge_master(real_dev)) 58 + br_vlan_vlan_upper_event(real_dev, dev, event); 59 + } 60 + 54 61 /* not a port of a bridge */ 55 62 p = br_port_get_rtnl(dev); 56 63 if (!p)
+9
net/bridge/br_private.h
··· 1571 1571 void br_vlan_port_event(struct net_bridge_port *p, unsigned long event); 1572 1572 int br_vlan_bridge_event(struct net_device *dev, unsigned long event, 1573 1573 void *ptr); 1574 + void br_vlan_vlan_upper_event(struct net_device *br_dev, 1575 + struct net_device *vlan_dev, 1576 + unsigned long event); 1574 1577 int br_vlan_rtnl_init(void); 1575 1578 void br_vlan_rtnl_uninit(void); 1576 1579 void br_vlan_notify(const struct net_bridge *br, ··· 1803 1800 unsigned long event, void *ptr) 1804 1801 { 1805 1802 return 0; 1803 + } 1804 + 1805 + static inline void br_vlan_vlan_upper_event(struct net_device *br_dev, 1806 + struct net_device *vlan_dev, 1807 + unsigned long event) 1808 + { 1806 1809 } 1807 1810 1808 1811 static inline int br_vlan_rtnl_init(void)
+38 -6
net/bridge/br_vlan.c
··· 1664 1664 } 1665 1665 } 1666 1666 1667 + static void br_vlan_toggle_bridge_binding(struct net_device *br_dev, 1668 + bool enable) 1669 + { 1670 + struct net_bridge *br = netdev_priv(br_dev); 1671 + 1672 + if (enable) 1673 + br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, true); 1674 + else 1675 + br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, 1676 + br_vlan_has_upper_bind_vlan_dev(br_dev)); 1677 + } 1678 + 1667 1679 static void br_vlan_upper_change(struct net_device *dev, 1668 1680 struct net_device *upper_dev, 1669 1681 bool linking) ··· 1685 1673 if (!br_vlan_is_bind_vlan_dev(upper_dev)) 1686 1674 return; 1687 1675 1688 - if (linking) { 1676 + br_vlan_toggle_bridge_binding(dev, linking); 1677 + if (linking) 1689 1678 br_vlan_set_vlan_dev_state(br, upper_dev); 1690 - br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, true); 1691 - } else { 1692 - br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, 1693 - br_vlan_has_upper_bind_vlan_dev(dev)); 1694 - } 1695 1679 } 1696 1680 1697 1681 struct br_vlan_link_state_walk_data { ··· 1770 1762 br_vlan_notify(br, NULL, br->default_pvid, 0, vlcmd); 1771 1763 1772 1764 return ret; 1765 + } 1766 + 1767 + void br_vlan_vlan_upper_event(struct net_device *br_dev, 1768 + struct net_device *vlan_dev, 1769 + unsigned long event) 1770 + { 1771 + struct vlan_dev_priv *vlan = vlan_dev_priv(vlan_dev); 1772 + struct net_bridge *br = netdev_priv(br_dev); 1773 + bool bridge_binding; 1774 + 1775 + switch (event) { 1776 + case NETDEV_CHANGE: 1777 + case NETDEV_UP: 1778 + break; 1779 + default: 1780 + return; 1781 + } 1782 + 1783 + bridge_binding = vlan->flags & VLAN_FLAG_BRIDGE_BINDING; 1784 + br_vlan_toggle_bridge_binding(br_dev, bridge_binding); 1785 + if (bridge_binding) 1786 + br_vlan_set_vlan_dev_state(br, vlan_dev); 1787 + else if (!bridge_binding && netif_carrier_ok(br_dev)) 1788 + netif_carrier_on(vlan_dev); 1773 1789 } 1774 1790 1775 1791 /* Must be protected by RTNL. */
+1
tools/testing/selftests/net/Makefile
··· 96 96 TEST_PROGS += fdb_flush.sh fdb_notify.sh 97 97 TEST_PROGS += fq_band_pktlimit.sh 98 98 TEST_PROGS += vlan_hw_filter.sh 99 + TEST_PROGS += vlan_bridge_binding.sh 99 100 TEST_PROGS += bpf_offload.py 100 101 TEST_PROGS += ipv6_route_update_soft_lockup.sh 101 102 TEST_PROGS += busy_poll_test.sh
+29 -2
tools/testing/selftests/net/lib.sh
··· 477 477 defer ip link set dev "$name" address "$old_addr" 478 478 } 479 479 480 + ip_link_is_up() 481 + { 482 + local name=$1; shift 483 + 484 + local state=$(ip -j link show "$name" | 485 + jq -r '(.[].flags[] | select(. == "UP")) // "DOWN"') 486 + [[ $state == "UP" ]] 487 + } 488 + 480 489 ip_link_set_up() 481 490 { 482 491 local name=$1; shift 483 492 484 - ip link set dev "$name" up 485 - defer ip link set dev "$name" down 493 + if ! ip_link_is_up "$name"; then 494 + ip link set dev "$name" up 495 + defer ip link set dev "$name" down 496 + fi 497 + } 498 + 499 + ip_link_set_down() 500 + { 501 + local name=$1; shift 502 + 503 + if ip_link_is_up "$name"; then 504 + ip link set dev "$name" down 505 + defer ip link set dev "$name" up 506 + fi 486 507 } 487 508 488 509 ip_addr_add() ··· 518 497 { 519 498 ip route add "$@" 520 499 defer ip route del "$@" 500 + } 501 + 502 + bridge_vlan_add() 503 + { 504 + bridge vlan add "$@" 505 + defer bridge vlan del "$@" 521 506 }
+256
tools/testing/selftests/net/vlan_bridge_binding.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + source lib.sh 5 + 6 + ALL_TESTS=" 7 + test_binding_on 8 + test_binding_off 9 + test_binding_toggle_on 10 + test_binding_toggle_off 11 + test_binding_toggle_on_when_upper_down 12 + test_binding_toggle_off_when_upper_down 13 + test_binding_toggle_on_when_lower_down 14 + test_binding_toggle_off_when_lower_down 15 + " 16 + 17 + setup_prepare() 18 + { 19 + local port 20 + 21 + ip_link_add br up type bridge vlan_filtering 1 22 + 23 + for port in d1 d2 d3; do 24 + ip_link_add $port type veth peer name r$port 25 + ip_link_set_up $port 26 + ip_link_set_up r$port 27 + ip_link_set_master $port br 28 + done 29 + 30 + bridge_vlan_add vid 11 dev br self 31 + bridge_vlan_add vid 11 dev d1 master 32 + 33 + bridge_vlan_add vid 12 dev br self 34 + bridge_vlan_add vid 12 dev d2 master 35 + 36 + bridge_vlan_add vid 13 dev br self 37 + bridge_vlan_add vid 13 dev d1 master 38 + bridge_vlan_add vid 13 dev d2 master 39 + 40 + bridge_vlan_add vid 14 dev br self 41 + bridge_vlan_add vid 14 dev d1 master 42 + bridge_vlan_add vid 14 dev d2 master 43 + bridge_vlan_add vid 14 dev d3 master 44 + } 45 + 46 + operstate_is() 47 + { 48 + local dev=$1; shift 49 + local expect=$1; shift 50 + 51 + local operstate=$(ip -j link show $dev | jq -r .[].operstate) 52 + if [[ $operstate == UP ]]; then 53 + operstate=1 54 + elif [[ $operstate == DOWN || $operstate == LOWERLAYERDOWN ]]; then 55 + operstate=0 56 + fi 57 + echo -n $operstate 58 + [[ $operstate == $expect ]] 59 + } 60 + 61 + check_operstate() 62 + { 63 + local dev=$1; shift 64 + local expect=$1; shift 65 + local operstate 66 + 67 + operstate=$(busywait 1000 \ 68 + operstate_is "$dev" "$expect") 69 + check_err $? "Got operstate of $operstate, expected $expect" 70 + } 71 + 72 + add_one_vlan() 73 + { 74 + local link=$1; shift 75 + local id=$1; shift 76 + 77 + ip_link_add $link.$id link $link type vlan id $id "$@" 78 + } 79 + 80 + add_vlans() 81 + { 82 + add_one_vlan br 11 "$@" 83 + add_one_vlan br 12 "$@" 84 + add_one_vlan br 13 "$@" 85 + add_one_vlan br 14 "$@" 86 + } 87 + 88 + set_vlans() 89 + { 90 + ip link set dev br.11 "$@" 91 + ip link set dev br.12 "$@" 92 + ip link set dev br.13 "$@" 93 + ip link set dev br.14 "$@" 94 + } 95 + 96 + down_netdevs() 97 + { 98 + local dev 99 + 100 + for dev in "$@"; do 101 + ip_link_set_down $dev 102 + done 103 + } 104 + 105 + check_operstates() 106 + { 107 + local opst_11=$1; shift 108 + local opst_12=$1; shift 109 + local opst_13=$1; shift 110 + local opst_14=$1; shift 111 + 112 + check_operstate br.11 $opst_11 113 + check_operstate br.12 $opst_12 114 + check_operstate br.13 $opst_13 115 + check_operstate br.14 $opst_14 116 + } 117 + 118 + do_test_binding() 119 + { 120 + local inject=$1; shift 121 + local what=$1; shift 122 + local opsts_d1=$1; shift 123 + local opsts_d2=$1; shift 124 + local opsts_d12=$1; shift 125 + local opsts_d123=$1; shift 126 + 127 + RET=0 128 + 129 + defer_scope_push 130 + down_netdevs d1 131 + $inject 132 + check_operstates $opsts_d1 133 + defer_scope_pop 134 + 135 + defer_scope_push 136 + down_netdevs d2 137 + $inject 138 + check_operstates $opsts_d2 139 + defer_scope_pop 140 + 141 + defer_scope_push 142 + down_netdevs d1 d2 143 + $inject 144 + check_operstates $opsts_d12 145 + defer_scope_pop 146 + 147 + defer_scope_push 148 + down_netdevs d1 d2 d3 149 + $inject 150 + check_operstates $opsts_d123 151 + defer_scope_pop 152 + 153 + log_test "Test bridge_binding $what" 154 + } 155 + 156 + do_test_binding_on() 157 + { 158 + local inject=$1; shift 159 + local what=$1; shift 160 + 161 + do_test_binding "$inject" "$what" \ 162 + "0 1 1 1" \ 163 + "1 0 1 1" \ 164 + "0 0 0 1" \ 165 + "0 0 0 0" 166 + } 167 + 168 + do_test_binding_off() 169 + { 170 + local inject=$1; shift 171 + local what=$1; shift 172 + 173 + do_test_binding "$inject" "$what" \ 174 + "1 1 1 1" \ 175 + "1 1 1 1" \ 176 + "1 1 1 1" \ 177 + "0 0 0 0" 178 + } 179 + 180 + test_binding_on() 181 + { 182 + add_vlans bridge_binding on 183 + set_vlans up 184 + do_test_binding_on : "on" 185 + } 186 + 187 + test_binding_off() 188 + { 189 + add_vlans bridge_binding off 190 + set_vlans up 191 + do_test_binding_off : "off" 192 + } 193 + 194 + test_binding_toggle_on() 195 + { 196 + add_vlans bridge_binding off 197 + set_vlans up 198 + set_vlans type vlan bridge_binding on 199 + do_test_binding_on : "off->on" 200 + } 201 + 202 + test_binding_toggle_off() 203 + { 204 + add_vlans bridge_binding on 205 + set_vlans up 206 + set_vlans type vlan bridge_binding off 207 + do_test_binding_off : "on->off" 208 + } 209 + 210 + dfr_set_binding_on() 211 + { 212 + set_vlans type vlan bridge_binding on 213 + defer set_vlans type vlan bridge_binding off 214 + } 215 + 216 + dfr_set_binding_off() 217 + { 218 + set_vlans type vlan bridge_binding off 219 + defer set_vlans type vlan bridge_binding on 220 + } 221 + 222 + test_binding_toggle_on_when_lower_down() 223 + { 224 + add_vlans bridge_binding off 225 + set_vlans up 226 + do_test_binding_on dfr_set_binding_on "off->on when lower down" 227 + } 228 + 229 + test_binding_toggle_off_when_lower_down() 230 + { 231 + add_vlans bridge_binding on 232 + set_vlans up 233 + do_test_binding_off dfr_set_binding_off "on->off when lower down" 234 + } 235 + 236 + test_binding_toggle_on_when_upper_down() 237 + { 238 + add_vlans bridge_binding off 239 + set_vlans type vlan bridge_binding on 240 + set_vlans up 241 + do_test_binding_on : "off->on when upper down" 242 + } 243 + 244 + test_binding_toggle_off_when_upper_down() 245 + { 246 + add_vlans bridge_binding on 247 + set_vlans type vlan bridge_binding off 248 + set_vlans up 249 + do_test_binding_off : "on->off when upper down" 250 + } 251 + 252 + trap defer_scopes_cleanup EXIT 253 + setup_prepare 254 + tests_run 255 + 256 + exit $EXIT_STATUS