aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2007-07-11 22:45:24 -0400
committerDavid S. Miller <davem@davemloft.net>2007-07-11 22:45:24 -0400
commit8c979c26a0f093c13290320edda799d8335e50ae (patch)
tree3189e5568583a794aff9d014898ff9a74b79d7cc
parent71bffe556c59a7865bf0b1ecd94530f1e296cdb0 (diff)
[VLAN]: Fix MAC address handling
The VLAN MAC address handling is broken in multiple ways. When the address differs when setting it, the real device is put in promiscous mode twice, but never taken out again. Additionally it doesn't resync when the real device's address is changed and needlessly puts it in promiscous mode when the vlan device is still down. Fix by moving address handling to vlan_dev_open/vlan_dev_stop and properly deal with address changes in the device notifier. Also switch to dev_unicast_add (which needs the exact same handling). Since the set_mac_address handler is identical to the generic ethernet one with these changes, kill it and use ether_setup(). Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/if_vlan.h1
-rw-r--r--net/8021q/vlan.c43
-rw-r--r--net/8021q/vlan.h1
-rw-r--r--net/8021q/vlan_dev.c57
4 files changed, 57 insertions, 45 deletions
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index c7912876a210..61a57dc2ac99 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -135,6 +135,7 @@ struct vlan_dev_info {
135 int old_allmulti; /* similar to above. */ 135 int old_allmulti; /* similar to above. */
136 int old_promiscuity; /* similar to above. */ 136 int old_promiscuity; /* similar to above. */
137 struct net_device *real_dev; /* the underlying device/interface */ 137 struct net_device *real_dev; /* the underlying device/interface */
138 unsigned char real_dev_addr[ETH_ALEN];
138 struct proc_dir_entry *dent; /* Holds the proc data */ 139 struct proc_dir_entry *dent; /* Holds the proc data */
139 unsigned long cnt_inc_headroom_on_tx; /* How many times did we have to grow the skb on TX. */ 140 unsigned long cnt_inc_headroom_on_tx; /* How many times did we have to grow the skb on TX. */
140 unsigned long cnt_encap_on_xmit; /* How many times did we have to encapsulate the skb on TX. */ 141 unsigned long cnt_encap_on_xmit; /* How many times did we have to encapsulate the skb on TX. */
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index e7583eea6fda..b463ba47024d 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -345,12 +345,8 @@ static int vlan_dev_init(struct net_device *dev)
345 (1<<__LINK_STATE_DORMANT))) | 345 (1<<__LINK_STATE_DORMANT))) |
346 (1<<__LINK_STATE_PRESENT); 346 (1<<__LINK_STATE_PRESENT);
347 347
348 /* TODO: maybe just assign it to be ETHERNET? */
349 dev->type = real_dev->type;
350
351 memcpy(dev->broadcast, real_dev->broadcast, real_dev->addr_len); 348 memcpy(dev->broadcast, real_dev->broadcast, real_dev->addr_len);
352 memcpy(dev->dev_addr, real_dev->dev_addr, real_dev->addr_len); 349 memcpy(dev->dev_addr, real_dev->dev_addr, real_dev->addr_len);
353 dev->addr_len = real_dev->addr_len;
354 350
355 if (real_dev->features & NETIF_F_HW_VLAN_TX) { 351 if (real_dev->features & NETIF_F_HW_VLAN_TX) {
356 dev->hard_header = real_dev->hard_header; 352 dev->hard_header = real_dev->hard_header;
@@ -364,6 +360,7 @@ static int vlan_dev_init(struct net_device *dev)
364 dev->rebuild_header = vlan_dev_rebuild_header; 360 dev->rebuild_header = vlan_dev_rebuild_header;
365 } 361 }
366 dev->hard_header_parse = real_dev->hard_header_parse; 362 dev->hard_header_parse = real_dev->hard_header_parse;
363 dev->hard_header_cache = NULL;
367 364
368 lockdep_set_class(&dev->_xmit_lock, &vlan_netdev_xmit_lock_key); 365 lockdep_set_class(&dev->_xmit_lock, &vlan_netdev_xmit_lock_key);
369 return 0; 366 return 0;
@@ -373,6 +370,8 @@ void vlan_setup(struct net_device *new_dev)
373{ 370{
374 SET_MODULE_OWNER(new_dev); 371 SET_MODULE_OWNER(new_dev);
375 372
373 ether_setup(new_dev);
374
376 /* new_dev->ifindex = 0; it will be set when added to 375 /* new_dev->ifindex = 0; it will be set when added to
377 * the global list. 376 * the global list.
378 * iflink is set as well. 377 * iflink is set as well.
@@ -392,7 +391,6 @@ void vlan_setup(struct net_device *new_dev)
392 new_dev->init = vlan_dev_init; 391 new_dev->init = vlan_dev_init;
393 new_dev->open = vlan_dev_open; 392 new_dev->open = vlan_dev_open;
394 new_dev->stop = vlan_dev_stop; 393 new_dev->stop = vlan_dev_stop;
395 new_dev->set_mac_address = vlan_dev_set_mac_address;
396 new_dev->set_multicast_list = vlan_dev_set_multicast_list; 394 new_dev->set_multicast_list = vlan_dev_set_multicast_list;
397 new_dev->destructor = free_netdev; 395 new_dev->destructor = free_netdev;
398 new_dev->do_ioctl = vlan_dev_ioctl; 396 new_dev->do_ioctl = vlan_dev_ioctl;
@@ -592,6 +590,30 @@ out_free_newdev:
592 return err; 590 return err;
593} 591}
594 592
593static void vlan_sync_address(struct net_device *dev,
594 struct net_device *vlandev)
595{
596 struct vlan_dev_info *vlan = VLAN_DEV_INFO(vlandev);
597
598 /* May be called without an actual change */
599 if (!compare_ether_addr(vlan->real_dev_addr, dev->dev_addr))
600 return;
601
602 /* vlan address was different from the old address and is equal to
603 * the new address */
604 if (compare_ether_addr(vlandev->dev_addr, vlan->real_dev_addr) &&
605 !compare_ether_addr(vlandev->dev_addr, dev->dev_addr))
606 dev_unicast_delete(dev, vlandev->dev_addr, ETH_ALEN);
607
608 /* vlan address was equal to the old address and is different from
609 * the new address */
610 if (!compare_ether_addr(vlandev->dev_addr, vlan->real_dev_addr) &&
611 compare_ether_addr(vlandev->dev_addr, dev->dev_addr))
612 dev_unicast_add(dev, vlandev->dev_addr, ETH_ALEN);
613
614 memcpy(vlan->real_dev_addr, dev->dev_addr, ETH_ALEN);
615}
616
595static int vlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr) 617static int vlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
596{ 618{
597 struct net_device *dev = ptr; 619 struct net_device *dev = ptr;
@@ -618,6 +640,17 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
618 } 640 }
619 break; 641 break;
620 642
643 case NETDEV_CHANGEADDR:
644 /* Adjust unicast filters on underlying device */
645 for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
646 vlandev = vlan_group_get_device(grp, i);
647 if (!vlandev)
648 continue;
649
650 vlan_sync_address(dev, vlandev);
651 }
652 break;
653
621 case NETDEV_DOWN: 654 case NETDEV_DOWN:
622 /* Put all VLANs for this dev in the down state too. */ 655 /* Put all VLANs for this dev in the down state too. */
623 for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { 656 for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h
index fe6bb0f7d275..62ce1c519aab 100644
--- a/net/8021q/vlan.h
+++ b/net/8021q/vlan.h
@@ -58,7 +58,6 @@ int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
58int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); 58int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev);
59int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); 59int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, struct net_device *dev);
60int vlan_dev_change_mtu(struct net_device *dev, int new_mtu); 60int vlan_dev_change_mtu(struct net_device *dev, int new_mtu);
61int vlan_dev_set_mac_address(struct net_device *dev, void* addr);
62int vlan_dev_open(struct net_device* dev); 61int vlan_dev_open(struct net_device* dev);
63int vlan_dev_stop(struct net_device* dev); 62int vlan_dev_stop(struct net_device* dev);
64int vlan_dev_ioctl(struct net_device* dev, struct ifreq *ifr, int cmd); 63int vlan_dev_ioctl(struct net_device* dev, struct ifreq *ifr, int cmd);
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 95afe387b952..d4a62d1b52b4 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -612,44 +612,6 @@ void vlan_dev_get_vid(const struct net_device *dev, unsigned short *result)
612 *result = VLAN_DEV_INFO(dev)->vlan_id; 612 *result = VLAN_DEV_INFO(dev)->vlan_id;
613} 613}
614 614
615int vlan_dev_set_mac_address(struct net_device *dev, void *addr_struct_p)
616{
617 struct sockaddr *addr = (struct sockaddr *)(addr_struct_p);
618 int i;
619
620 if (netif_running(dev))
621 return -EBUSY;
622
623 memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
624
625 printk("%s: Setting MAC address to ", dev->name);
626 for (i = 0; i < 6; i++)
627 printk(" %2.2x", dev->dev_addr[i]);
628 printk(".\n");
629
630 if (memcmp(VLAN_DEV_INFO(dev)->real_dev->dev_addr,
631 dev->dev_addr,
632 dev->addr_len) != 0) {
633 if (!(VLAN_DEV_INFO(dev)->real_dev->flags & IFF_PROMISC)) {
634 int flgs = VLAN_DEV_INFO(dev)->real_dev->flags;
635
636 /* Increment our in-use promiscuity counter */
637 dev_set_promiscuity(VLAN_DEV_INFO(dev)->real_dev, 1);
638
639 /* Make PROMISC visible to the user. */
640 flgs |= IFF_PROMISC;
641 printk("VLAN (%s): Setting underlying device (%s) to promiscious mode.\n",
642 dev->name, VLAN_DEV_INFO(dev)->real_dev->name);
643 dev_change_flags(VLAN_DEV_INFO(dev)->real_dev, flgs);
644 }
645 } else {
646 printk("VLAN (%s): Underlying device (%s) has same MAC, not checking promiscious mode.\n",
647 dev->name, VLAN_DEV_INFO(dev)->real_dev->name);
648 }
649
650 return 0;
651}
652
653static inline int vlan_dmi_equals(struct dev_mc_list *dmi1, 615static inline int vlan_dmi_equals(struct dev_mc_list *dmi1,
654 struct dev_mc_list *dmi2) 616 struct dev_mc_list *dmi2)
655{ 617{
@@ -736,15 +698,32 @@ static void vlan_flush_mc_list(struct net_device *dev)
736 698
737int vlan_dev_open(struct net_device *dev) 699int vlan_dev_open(struct net_device *dev)
738{ 700{
739 if (!(VLAN_DEV_INFO(dev)->real_dev->flags & IFF_UP)) 701 struct vlan_dev_info *vlan = VLAN_DEV_INFO(dev);
702 struct net_device *real_dev = vlan->real_dev;
703 int err;
704
705 if (!(real_dev->flags & IFF_UP))
740 return -ENETDOWN; 706 return -ENETDOWN;
741 707
708 if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) {
709 err = dev_unicast_add(real_dev, dev->dev_addr, ETH_ALEN);
710 if (err < 0)
711 return err;
712 }
713 memcpy(vlan->real_dev_addr, real_dev->dev_addr, ETH_ALEN);
714
742 return 0; 715 return 0;
743} 716}
744 717
745int vlan_dev_stop(struct net_device *dev) 718int vlan_dev_stop(struct net_device *dev)
746{ 719{
720 struct net_device *real_dev = VLAN_DEV_INFO(dev)->real_dev;
721
747 vlan_flush_mc_list(dev); 722 vlan_flush_mc_list(dev);
723
724 if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr))
725 dev_unicast_delete(real_dev, dev->dev_addr, dev->addr_len);
726
748 return 0; 727 return 0;
749} 728}
750 729