int old_allmulti;               /* similar to above. */
        int old_promiscuity;            /* similar to above. */
        struct net_device *real_dev;    /* the underlying device/interface */
+       unsigned char real_dev_addr[ETH_ALEN];
        struct proc_dir_entry *dent;    /* Holds the proc data */
        unsigned long cnt_inc_headroom_on_tx; /* How many times did we have to grow the skb on TX. */
        unsigned long cnt_encap_on_xmit;      /* How many times did we have to encapsulate the skb on TX. */
 
                                          (1<<__LINK_STATE_DORMANT))) |
                      (1<<__LINK_STATE_PRESENT);
 
-       /* TODO: maybe just assign it to be ETHERNET? */
-       dev->type = real_dev->type;
-
        memcpy(dev->broadcast, real_dev->broadcast, real_dev->addr_len);
        memcpy(dev->dev_addr, real_dev->dev_addr, real_dev->addr_len);
-       dev->addr_len = real_dev->addr_len;
 
        if (real_dev->features & NETIF_F_HW_VLAN_TX) {
                dev->hard_header     = real_dev->hard_header;
                dev->rebuild_header  = vlan_dev_rebuild_header;
        }
        dev->hard_header_parse = real_dev->hard_header_parse;
+       dev->hard_header_cache = NULL;
 
        lockdep_set_class(&dev->_xmit_lock, &vlan_netdev_xmit_lock_key);
        return 0;
 {
        SET_MODULE_OWNER(new_dev);
 
+       ether_setup(new_dev);
+
        /* new_dev->ifindex = 0;  it will be set when added to
         * the global list.
         * iflink is set as well.
        new_dev->init = vlan_dev_init;
        new_dev->open = vlan_dev_open;
        new_dev->stop = vlan_dev_stop;
-       new_dev->set_mac_address = vlan_dev_set_mac_address;
        new_dev->set_multicast_list = vlan_dev_set_multicast_list;
        new_dev->destructor = free_netdev;
        new_dev->do_ioctl = vlan_dev_ioctl;
        return err;
 }
 
+static void vlan_sync_address(struct net_device *dev,
+                             struct net_device *vlandev)
+{
+       struct vlan_dev_info *vlan = VLAN_DEV_INFO(vlandev);
+
+       /* May be called without an actual change */
+       if (!compare_ether_addr(vlan->real_dev_addr, dev->dev_addr))
+               return;
+
+       /* vlan address was different from the old address and is equal to
+        * the new address */
+       if (compare_ether_addr(vlandev->dev_addr, vlan->real_dev_addr) &&
+           !compare_ether_addr(vlandev->dev_addr, dev->dev_addr))
+               dev_unicast_delete(dev, vlandev->dev_addr, ETH_ALEN);
+
+       /* vlan address was equal to the old address and is different from
+        * the new address */
+       if (!compare_ether_addr(vlandev->dev_addr, vlan->real_dev_addr) &&
+           compare_ether_addr(vlandev->dev_addr, dev->dev_addr))
+               dev_unicast_add(dev, vlandev->dev_addr, ETH_ALEN);
+
+       memcpy(vlan->real_dev_addr, dev->dev_addr, ETH_ALEN);
+}
+
 static int vlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
 {
        struct net_device *dev = ptr;
                }
                break;
 
+       case NETDEV_CHANGEADDR:
+               /* Adjust unicast filters on underlying device */
+               for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
+                       vlandev = vlan_group_get_device(grp, i);
+                       if (!vlandev)
+                               continue;
+
+                       vlan_sync_address(dev, vlandev);
+               }
+               break;
+
        case NETDEV_DOWN:
                /* Put all VLANs for this dev in the down state too.  */
                for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
 
 int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev);
 int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, struct net_device *dev);
 int vlan_dev_change_mtu(struct net_device *dev, int new_mtu);
-int vlan_dev_set_mac_address(struct net_device *dev, void* addr);
 int vlan_dev_open(struct net_device* dev);
 int vlan_dev_stop(struct net_device* dev);
 int vlan_dev_ioctl(struct net_device* dev, struct ifreq *ifr, int cmd);
 
        *result = VLAN_DEV_INFO(dev)->vlan_id;
 }
 
-int vlan_dev_set_mac_address(struct net_device *dev, void *addr_struct_p)
-{
-       struct sockaddr *addr = (struct sockaddr *)(addr_struct_p);
-       int i;
-
-       if (netif_running(dev))
-               return -EBUSY;
-
-       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
-
-       printk("%s: Setting MAC address to ", dev->name);
-       for (i = 0; i < 6; i++)
-               printk(" %2.2x", dev->dev_addr[i]);
-       printk(".\n");
-
-       if (memcmp(VLAN_DEV_INFO(dev)->real_dev->dev_addr,
-                  dev->dev_addr,
-                  dev->addr_len) != 0) {
-               if (!(VLAN_DEV_INFO(dev)->real_dev->flags & IFF_PROMISC)) {
-                       int flgs = VLAN_DEV_INFO(dev)->real_dev->flags;
-
-                       /* Increment our in-use promiscuity counter */
-                       dev_set_promiscuity(VLAN_DEV_INFO(dev)->real_dev, 1);
-
-                       /* Make PROMISC visible to the user. */
-                       flgs |= IFF_PROMISC;
-                       printk("VLAN (%s):  Setting underlying device (%s) to promiscious mode.\n",
-                              dev->name, VLAN_DEV_INFO(dev)->real_dev->name);
-                       dev_change_flags(VLAN_DEV_INFO(dev)->real_dev, flgs);
-               }
-       } else {
-               printk("VLAN (%s):  Underlying device (%s) has same MAC, not checking promiscious mode.\n",
-                      dev->name, VLAN_DEV_INFO(dev)->real_dev->name);
-       }
-
-       return 0;
-}
-
 static inline int vlan_dmi_equals(struct dev_mc_list *dmi1,
                                  struct dev_mc_list *dmi2)
 {
 
 int vlan_dev_open(struct net_device *dev)
 {
-       if (!(VLAN_DEV_INFO(dev)->real_dev->flags & IFF_UP))
+       struct vlan_dev_info *vlan = VLAN_DEV_INFO(dev);
+       struct net_device *real_dev = vlan->real_dev;
+       int err;
+
+       if (!(real_dev->flags & IFF_UP))
                return -ENETDOWN;
 
+       if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) {
+               err = dev_unicast_add(real_dev, dev->dev_addr, ETH_ALEN);
+               if (err < 0)
+                       return err;
+       }
+       memcpy(vlan->real_dev_addr, real_dev->dev_addr, ETH_ALEN);
+
        return 0;
 }
 
 int vlan_dev_stop(struct net_device *dev)
 {
+       struct net_device *real_dev = VLAN_DEV_INFO(dev)->real_dev;
+
        vlan_flush_mc_list(dev);
+
+       if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr))
+               dev_unicast_delete(real_dev, dev->dev_addr, dev->addr_len);
+
        return 0;
 }