Skip to content

Commit f3ef33e

Browse files
liuhangbingregkh
authored andcommitted
ipvlan: call dev_change_flags when ipvlan mode is reset
[ Upstream commit 5dc2d39 ] 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]> Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0bcba95 commit f3ef33e

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
@@ -73,32 +73,52 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval)
7373
{
7474
struct ipvl_dev *ipvlan;
7575
struct net_device *mdev = port->dev;
76-
int err = 0;
76+
unsigned int flags;
77+
int err;
7778

7879
ASSERT_RTNL();
7980
if (port->mode != nval) {
81+
list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
82+
flags = ipvlan->dev->flags;
83+
if (nval == IPVLAN_MODE_L3 || nval == IPVLAN_MODE_L3S) {
84+
err = dev_change_flags(ipvlan->dev,
85+
flags | IFF_NOARP);
86+
} else {
87+
err = dev_change_flags(ipvlan->dev,
88+
flags & ~IFF_NOARP);
89+
}
90+
if (unlikely(err))
91+
goto fail;
92+
}
8093
if (nval == IPVLAN_MODE_L3S) {
8194
/* New mode is L3S */
8295
err = ipvlan_register_nf_hook(read_pnet(&port->pnet));
8396
if (!err) {
8497
mdev->l3mdev_ops = &ipvl_l3mdev_ops;
8598
mdev->priv_flags |= IFF_L3MDEV_MASTER;
8699
} else
87-
return err;
100+
goto fail;
88101
} else if (port->mode == IPVLAN_MODE_L3S) {
89102
/* Old mode was L3S */
90103
mdev->priv_flags &= ~IFF_L3MDEV_MASTER;
91104
ipvlan_unregister_nf_hook(read_pnet(&port->pnet));
92105
mdev->l3mdev_ops = NULL;
93106
}
94-
list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
95-
if (nval == IPVLAN_MODE_L3 || nval == IPVLAN_MODE_L3S)
96-
ipvlan->dev->flags |= IFF_NOARP;
97-
else
98-
ipvlan->dev->flags &= ~IFF_NOARP;
99-
}
100107
port->mode = nval;
101108
}
109+
return 0;
110+
111+
fail:
112+
/* Undo the flags changes that have been done so far. */
113+
list_for_each_entry_continue_reverse(ipvlan, &port->ipvlans, pnode) {
114+
flags = ipvlan->dev->flags;
115+
if (port->mode == IPVLAN_MODE_L3 ||
116+
port->mode == IPVLAN_MODE_L3S)
117+
dev_change_flags(ipvlan->dev, flags | IFF_NOARP);
118+
else
119+
dev_change_flags(ipvlan->dev, flags & ~IFF_NOARP);
120+
}
121+
102122
return err;
103123
}
104124

0 commit comments

Comments
 (0)