aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Biederman <ebiederm@aristanetworks.com>2009-03-13 16:16:13 -0400
committerDavid S. Miller <davem@davemloft.net>2009-03-13 16:16:13 -0400
commitf9ac30f080d23ef0a2d4a1b7c6806c9a21c0f324 (patch)
treed10817f083028f8be53d48693f7cf3fadd24a3e0
parentb0832a2961022a076c812384435b5f0290b3fc91 (diff)
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 <ebiederm@aristanetworks.com> Acked-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/macvlan.c73
1 files changed, 60 insertions, 13 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index b5241fc0f51..70d3ef4a2c5 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -60,6 +60,47 @@ static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port,
60 return NULL; 60 return NULL;
61} 61}
62 62
63static 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
71static void macvlan_hash_del(struct macvlan_dev *vlan)
72{
73 hlist_del_rcu(&vlan->hlist);
74 synchronize_rcu();
75}
76
77static 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
88static 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
63static void macvlan_broadcast(struct sk_buff *skb, 104static void macvlan_broadcast(struct sk_buff *skb,
64 const struct macvlan_port *port) 105 const struct macvlan_port *port)
65{ 106{
@@ -184,10 +225,13 @@ static const struct header_ops macvlan_hard_header_ops = {
184static int macvlan_open(struct net_device *dev) 225static int macvlan_open(struct net_device *dev)
185{ 226{
186 struct macvlan_dev *vlan = netdev_priv(dev); 227 struct macvlan_dev *vlan = netdev_priv(dev);
187 struct macvlan_port *port = vlan->port;
188 struct net_device *lowerdev = vlan->lowerdev; 228 struct net_device *lowerdev = vlan->lowerdev;
189 int err; 229 int err;
190 230
231 err = -EBUSY;
232 if (macvlan_addr_busy(vlan->port, dev->dev_addr))
233 goto out;
234
191 err = dev_unicast_add(lowerdev, dev->dev_addr, ETH_ALEN); 235 err = dev_unicast_add(lowerdev, dev->dev_addr, ETH_ALEN);
192 if (err < 0) 236 if (err < 0)
193 goto out; 237 goto out;
@@ -196,8 +240,7 @@ static int macvlan_open(struct net_device *dev)
196 if (err < 0) 240 if (err < 0)
197 goto del_unicast; 241 goto del_unicast;
198 } 242 }
199 243 macvlan_hash_add(vlan);
200 hlist_add_head_rcu(&vlan->hlist, &port->vlan_hash[dev->dev_addr[5]]);
201 return 0; 244 return 0;
202 245
203del_unicast: 246del_unicast:
@@ -217,8 +260,7 @@ static int macvlan_stop(struct net_device *dev)
217 260
218 dev_unicast_delete(lowerdev, dev->dev_addr, ETH_ALEN); 261 dev_unicast_delete(lowerdev, dev->dev_addr, ETH_ALEN);
219 262
220 hlist_del_rcu(&vlan->hlist); 263 macvlan_hash_del(vlan);
221 synchronize_rcu();
222 return 0; 264 return 0;
223} 265}
224 266
@@ -232,16 +274,21 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p)
232 if (!is_valid_ether_addr(addr->sa_data)) 274 if (!is_valid_ether_addr(addr->sa_data))
233 return -EADDRNOTAVAIL; 275 return -EADDRNOTAVAIL;
234 276
235 if (!(dev->flags & IFF_UP)) 277 if (!(dev->flags & IFF_UP)) {
236 goto out; 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;
237 284
238 err = dev_unicast_add(lowerdev, addr->sa_data, ETH_ALEN); 285 if ((err = dev_unicast_add(lowerdev, addr->sa_data, ETH_ALEN)))
239 if (err < 0) 286 return err;
240 return err;
241 dev_unicast_delete(lowerdev, dev->dev_addr, ETH_ALEN);
242 287
243out: 288 dev_unicast_delete(lowerdev, dev->dev_addr, ETH_ALEN);
244 memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); 289
290 macvlan_hash_change_addr(vlan, addr->sa_data);
291 }
245 return 0; 292 return 0;
246} 293}
247 294