Skip to content

Commit 5dc2d39

Browse files
liuhangbindavem330
authored andcommitted
ipvlan: call dev_change_flags when ipvlan mode is reset
After we change the ipvlan mode from l3 to l2, or vice versa, we only reset IFF_NOARP flag, but don't flush the ARP table cache, which will cause eth->h_dest to be equal to eth->h_source in ipvlan_xmit_mode_l2(). Then the message will not come out of host. Here is the reproducer on local host: ip link set eth1 up ip addr add 192.168.1.1/24 dev eth1 ip link add link eth1 ipvlan1 type ipvlan mode l3 ip netns add net1 ip link set ipvlan1 netns net1 ip netns exec net1 ip link set ipvlan1 up ip netns exec net1 ip addr add 192.168.2.1/24 dev ipvlan1 ip route add 192.168.2.0/24 via 192.168.1.2 ping 192.168.2.2 -c 2 ip netns exec net1 ip link set ipvlan1 type ipvlan mode l2 ping 192.168.2.2 -c 2 Add the same configuration on remote host. After we set the mode to l2, we could find that the src/dst MAC addresses are the same on eth1: 21:26:06.648565 00:b7:13:ad:d3:05 > 00:b7:13:ad:d3:05, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 58356, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.2.1 > 192.168.2.2: ICMP echo request, id 22686, seq 1, length 64 Fix this by calling dev_change_flags(), which will call netdevice notifier with flag change info. v2: a) As pointed out by Wang Cong, check return value for dev_change_flags() when change dev flags. b) As suggested by Stefano and Sabrina, move flags setting before l3mdev_ops. So we don't need to redo ipvlan_{, un}register_nf_hook() again in err path. Reported-by: Jianlin Shi <[email protected]> Reviewed-by: Stefano Brivio <[email protected]> Reviewed-by: Sabrina Dubroca <[email protected]> Fixes: 2ad7bf3 ("ipvlan: Initial check-in of the IPVLAN driver.") Signed-off-by: Hangbin Liu <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent fc9c202 commit 5dc2d39

File tree

1 file changed

+28
-8
lines changed

1 file changed

+28
-8
lines changed

drivers/net/ipvlan/ipvlan_main.c

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,32 +75,52 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval)
7575
{
7676
struct ipvl_dev *ipvlan;
7777
struct net_device *mdev = port->dev;
78-
int err = 0;
78+
unsigned int flags;
79+
int err;
7980

8081
ASSERT_RTNL();
8182
if (port->mode != nval) {
83+
list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
84+
flags = ipvlan->dev->flags;
85+
if (nval == IPVLAN_MODE_L3 || nval == IPVLAN_MODE_L3S) {
86+
err = dev_change_flags(ipvlan->dev,
87+
flags | IFF_NOARP);
88+
} else {
89+
err = dev_change_flags(ipvlan->dev,
90+
flags & ~IFF_NOARP);
91+
}
92+
if (unlikely(err))
93+
goto fail;
94+
}
8295
if (nval == IPVLAN_MODE_L3S) {
8396
/* New mode is L3S */
8497
err = ipvlan_register_nf_hook(read_pnet(&port->pnet));
8598
if (!err) {
8699
mdev->l3mdev_ops = &ipvl_l3mdev_ops;
87100
mdev->priv_flags |= IFF_L3MDEV_MASTER;
88101
} else
89-
return err;
102+
goto fail;
90103
} else if (port->mode == IPVLAN_MODE_L3S) {
91104
/* Old mode was L3S */
92105
mdev->priv_flags &= ~IFF_L3MDEV_MASTER;
93106
ipvlan_unregister_nf_hook(read_pnet(&port->pnet));
94107
mdev->l3mdev_ops = NULL;
95108
}
96-
list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
97-
if (nval == IPVLAN_MODE_L3 || nval == IPVLAN_MODE_L3S)
98-
ipvlan->dev->flags |= IFF_NOARP;
99-
else
100-
ipvlan->dev->flags &= ~IFF_NOARP;
101-
}
102109
port->mode = nval;
103110
}
111+
return 0;
112+
113+
fail:
114+
/* Undo the flags changes that have been done so far. */
115+
list_for_each_entry_continue_reverse(ipvlan, &port->ipvlans, pnode) {
116+
flags = ipvlan->dev->flags;
117+
if (port->mode == IPVLAN_MODE_L3 ||
118+
port->mode == IPVLAN_MODE_L3S)
119+
dev_change_flags(ipvlan->dev, flags | IFF_NOARP);
120+
else
121+
dev_change_flags(ipvlan->dev, flags & ~IFF_NOARP);
122+
}
123+
104124
return err;
105125
}
106126

0 commit comments

Comments
 (0)