Skip to content

Commit f9ac30f

Browse files
Eric Biedermandavem330
authored andcommitted
macvlan: Deterministic ingress packet delivery
Changing the mac address when a macvlan device is up will leave the device on the wrong hash chain making it impossible to receive packets. There is no checking of the mac address set on the macvlan. Allowing a misconfiguration to grab packets from the the underlying device or another macvlan. To resolve these problems I update the hash table of macvlans when the mac address of a macvlan changes, and when updating the hash table I verify that the new mac address is usable. The result is well defined and predictable if not perfect handling of mac vlan mac addresses. To keep the code clear I have created a set of hash table maintenance in macvlan so I am not open coding the hash function and the logic needed to update the hash table all over the place. Signed-off-by: Eric Biederman <[email protected]> Acked-by: Patrick McHardy <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent b0832a2 commit f9ac30f

File tree

1 file changed

+60
-13
lines changed

1 file changed

+60
-13
lines changed

drivers/net/macvlan.c

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,47 @@ static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port,
6060
return NULL;
6161
}
6262

63+
static void macvlan_hash_add(struct macvlan_dev *vlan)
64+
{
65+
struct macvlan_port *port = vlan->port;
66+
const unsigned char *addr = vlan->dev->dev_addr;
67+
68+
hlist_add_head_rcu(&vlan->hlist, &port->vlan_hash[addr[5]]);
69+
}
70+
71+
static void macvlan_hash_del(struct macvlan_dev *vlan)
72+
{
73+
hlist_del_rcu(&vlan->hlist);
74+
synchronize_rcu();
75+
}
76+
77+
static void macvlan_hash_change_addr(struct macvlan_dev *vlan,
78+
const unsigned char *addr)
79+
{
80+
macvlan_hash_del(vlan);
81+
/* Now that we are unhashed it is safe to change the device
82+
* address without confusing packet delivery.
83+
*/
84+
memcpy(vlan->dev->dev_addr, addr, ETH_ALEN);
85+
macvlan_hash_add(vlan);
86+
}
87+
88+
static int macvlan_addr_busy(const struct macvlan_port *port,
89+
const unsigned char *addr)
90+
{
91+
/* Test to see if the specified multicast address is
92+
* currently in use by the underlying device or
93+
* another macvlan.
94+
*/
95+
if (memcmp(port->dev->dev_addr, addr, ETH_ALEN) == 0)
96+
return 1;
97+
98+
if (macvlan_hash_lookup(port, addr))
99+
return 1;
100+
101+
return 0;
102+
}
103+
63104
static void macvlan_broadcast(struct sk_buff *skb,
64105
const struct macvlan_port *port)
65106
{
@@ -184,10 +225,13 @@ static const struct header_ops macvlan_hard_header_ops = {
184225
static int macvlan_open(struct net_device *dev)
185226
{
186227
struct macvlan_dev *vlan = netdev_priv(dev);
187-
struct macvlan_port *port = vlan->port;
188228
struct net_device *lowerdev = vlan->lowerdev;
189229
int err;
190230

231+
err = -EBUSY;
232+
if (macvlan_addr_busy(vlan->port, dev->dev_addr))
233+
goto out;
234+
191235
err = dev_unicast_add(lowerdev, dev->dev_addr, ETH_ALEN);
192236
if (err < 0)
193237
goto out;
@@ -196,8 +240,7 @@ static int macvlan_open(struct net_device *dev)
196240
if (err < 0)
197241
goto del_unicast;
198242
}
199-
200-
hlist_add_head_rcu(&vlan->hlist, &port->vlan_hash[dev->dev_addr[5]]);
243+
macvlan_hash_add(vlan);
201244
return 0;
202245

203246
del_unicast:
@@ -217,8 +260,7 @@ static int macvlan_stop(struct net_device *dev)
217260

218261
dev_unicast_delete(lowerdev, dev->dev_addr, ETH_ALEN);
219262

220-
hlist_del_rcu(&vlan->hlist);
221-
synchronize_rcu();
263+
macvlan_hash_del(vlan);
222264
return 0;
223265
}
224266

@@ -232,16 +274,21 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p)
232274
if (!is_valid_ether_addr(addr->sa_data))
233275
return -EADDRNOTAVAIL;
234276

235-
if (!(dev->flags & IFF_UP))
236-
goto out;
277+
if (!(dev->flags & IFF_UP)) {
278+
/* Just copy in the new address */
279+
memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
280+
} else {
281+
/* Rehash and update the device filters */
282+
if (macvlan_addr_busy(vlan->port, addr->sa_data))
283+
return -EBUSY;
237284

238-
err = dev_unicast_add(lowerdev, addr->sa_data, ETH_ALEN);
239-
if (err < 0)
240-
return err;
241-
dev_unicast_delete(lowerdev, dev->dev_addr, ETH_ALEN);
285+
if ((err = dev_unicast_add(lowerdev, addr->sa_data, ETH_ALEN)))
286+
return err;
242287

243-
out:
244-
memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
288+
dev_unicast_delete(lowerdev, dev->dev_addr, ETH_ALEN);
289+
290+
macvlan_hash_change_addr(vlan, addr->sa_data);
291+
}
245292
return 0;
246293
}
247294

0 commit comments

Comments
 (0)