@@ -131,6 +131,7 @@ extern struct net_device *__vlan_find_dev_deep_rcu(struct net_device *real_dev,
extern int vlan_for_each(struct net_device *dev,
int (*action)(struct net_device *dev, int vid,
void *arg), void *arg);
+extern u16 vlan_dev_get_addr_vid(struct net_device *dev, const u8 *addr);
extern struct net_device *vlan_dev_real_dev(const struct net_device *dev);
extern u16 vlan_dev_vlan_id(const struct net_device *dev);
extern __be16 vlan_dev_vlan_proto(const struct net_device *dev);
@@ -6,6 +6,8 @@
#include <linux/u64_stats_sync.h>
#include <linux/list.h>
+#define NET_8021Q_VID_TSIZE 2
+
/* if this changes, algorithm will have to be reworked because this
* depends on completely exhausting the VLAN identifier space. Thus
* it gives constant time look-up, but in many cases it wastes memory.
@@ -453,6 +453,19 @@ bool vlan_uses_dev(const struct net_device *dev)
}
EXPORT_SYMBOL(vlan_uses_dev);
+u16 vlan_dev_get_addr_vid(struct net_device *dev, const u8 *addr)
+{
+ u16 vid = 0;
+
+ if (dev->vid_len != NET_8021Q_VID_TSIZE)
+ return vid;
+
+ vid = addr[dev->addr_len];
+ vid |= (addr[dev->addr_len + 1] & 0xf) << 8;
+ return vid;
+}
+EXPORT_SYMBOL(vlan_dev_get_addr_vid);
+
static struct sk_buff *vlan_gro_receive(struct list_head *head,
struct sk_buff *skb)
{
@@ -244,6 +244,14 @@ void vlan_dev_get_realdev_name(const struct net_device *dev, char *result)
strncpy(result, vlan_dev_priv(dev)->real_dev->name, 23);
}
+static void vlan_dev_set_addr_vid(struct net_device *vlan_dev, u8 *addr)
+{
+ u16 vid = vlan_dev_vlan_id(vlan_dev);
+
+ addr[vlan_dev->addr_len] = vid & 0xff;
+ addr[vlan_dev->addr_len + 1] = (vid >> 8) & 0xf;
+}
+
bool vlan_dev_inherit_address(struct net_device *dev,
struct net_device *real_dev)
{
@@ -482,8 +490,26 @@ static void vlan_dev_change_rx_flags(struct net_device *dev, int change)
}
}
+static void vlan_dev_align_addr_vid(struct net_device *vlan_dev)
+{
+ struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
+ struct netdev_hw_addr *ha;
+
+ if (!real_dev->vid_len)
+ return;
+
+ netdev_for_each_mc_addr(ha, vlan_dev)
+ if (!ha->sync_cnt)
+ vlan_dev_set_addr_vid(vlan_dev, ha->addr);
+
+ netdev_for_each_uc_addr(ha, vlan_dev)
+ if (!ha->sync_cnt)
+ vlan_dev_set_addr_vid(vlan_dev, ha->addr);
+}
+
static void vlan_dev_set_rx_mode(struct net_device *vlan_dev)
{
+ vlan_dev_align_addr_vid(vlan_dev);
dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
}