diff options
-rw-r--r-- | drivers/net/macvlan.c | 73 |
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 | ||
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 | |||
63 | static void macvlan_broadcast(struct sk_buff *skb, | 104 | static 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 = { | |||
184 | static int macvlan_open(struct net_device *dev) | 225 | static 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 | ||
203 | del_unicast: | 246 | del_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 | ||
243 | out: | 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 | ||