@@ -302,30 +302,37 @@ static int macvlan_open(struct net_device *dev)
struct net_device *lowerdev = vlan->lowerdev;
int err;
- if (vlan->port->passthru) {
- dev_set_promiscuity(lowerdev, 1);
- goto hash_add;
- }
+ if (!vlan->port->passthru) {
+ err = -EBUSY;
+ if (macvlan_addr_busy(vlan->port, dev->dev_addr))
+ goto out;
- err = -EBUSY;
- if (macvlan_addr_busy(vlan->port, dev->dev_addr))
- goto out;
+ err = dev_uc_add(lowerdev, dev->dev_addr);
+ if (err < 0)
+ goto out;
+ }
- err = dev_uc_add(lowerdev, dev->dev_addr);
- if (err < 0)
- goto out;
if (dev->flags & IFF_ALLMULTI) {
err = dev_set_allmulti(lowerdev, 1);
if (err < 0)
goto del_unicast;
}
-hash_add:
+ if (dev->flags & IFF_PROMISC) {
+ err = dev_set_promiscuity(lowerdev, 1);
+ if (err < 0)
+ goto unset_allmulti;
+ }
+
macvlan_hash_add(vlan);
return 0;
+unset_allmulti:
+ dev_set_allmulti(lowerdev, -1);
+
del_unicast:
- dev_uc_del(lowerdev, dev->dev_addr);
+ if (!vlan->port->passthru)
+ dev_uc_del(lowerdev, dev->dev_addr);
out:
return err;
}
@@ -335,18 +342,16 @@ static int macvlan_stop(struct net_device *dev)
struct macvlan_dev *vlan = netdev_priv(dev);
struct net_device *lowerdev = vlan->lowerdev;
- if (vlan->port->passthru) {
- dev_set_promiscuity(lowerdev, -1);
- goto hash_del;
- }
-
+ dev_uc_unsync(lowerdev, dev);
dev_mc_unsync(lowerdev, dev);
if (dev->flags & IFF_ALLMULTI)
dev_set_allmulti(lowerdev, -1);
+ if (dev->flags & IFF_PROMISC)
+ dev_set_promiscuity(lowerdev, -1);
- dev_uc_del(lowerdev, dev->dev_addr);
+ if (!vlan->port->passthru)
+ dev_uc_del(lowerdev, dev->dev_addr);
-hash_del:
macvlan_hash_del(vlan, !dev->dismantle);
return 0;
}
@@ -387,12 +392,16 @@ static void macvlan_change_rx_flags(struct net_device *dev, int change)
if (change & IFF_ALLMULTI)
dev_set_allmulti(lowerdev, dev->flags & IFF_ALLMULTI ? 1 : -1);
+ if (change & IFF_PROMISC)
+ dev_set_promiscuity(lowerdev,
+ dev->flags & IFF_PROMISC ? 1 : -1);
}
-static void macvlan_set_multicast_list(struct net_device *dev)
+static void macvlan_set_rx_mode(struct net_device *dev)
{
struct macvlan_dev *vlan = netdev_priv(dev);
+ dev_uc_sync(vlan->lowerdev, dev);
dev_mc_sync(vlan->lowerdev, dev);
}
@@ -535,6 +544,257 @@ static void macvlan_vlan_rx_kill_vid(struct net_device *dev,
ops->ndo_vlan_rx_kill_vid(lowerdev, vid);
}
+static inline void macvlan_set_filter_vlan(struct net_device *dev, int vid)
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+
+ set_bit(vid, vlan->vlan_filter);
+ macvlan_vlan_rx_add_vid(dev, vid);
+}
+
+static inline void macvlan_clear_filter_vlan(struct net_device *dev, int vid)
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+
+ clear_bit(vid, vlan->vlan_filter);
+ macvlan_vlan_rx_kill_vid(dev, vid);
+}
+
+static int macvlan_set_rx_filter_vlan_passthru(struct net_device *dev, int vf,
+ struct nlattr *tb[])
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+ struct net_device *lowerdev = vlan->lowerdev;
+ const struct net_device_ops *ops = lowerdev->netdev_ops;
+ unsigned long *vlans;
+ u16 vid;
+
+ if (ops->ndo_set_rx_filter_vlan)
+ return ops->ndo_set_rx_filter_vlan(dev, vf, tb);
+
+ if (!tb[IFLA_RX_FILTER_VLAN_BITMAP])
+ return -EINVAL;
+
+ vlans = nla_data(tb[IFLA_RX_FILTER_VLAN_BITMAP]);
+
+ /*
+ * Clear vlans that are not present in the new filter
+ */
+ for_each_set_bit(vid, vlan->vlan_filter, VLAN_N_VID) {
+ if (!test_bit(vid, vlans))
+ macvlan_clear_filter_vlan(dev, vid);
+ }
+
+ /*
+ * Set new vlans that came in the filter
+ */
+ for_each_set_bit(vid, vlans, VLAN_N_VID) {
+ if (!test_bit(vid, vlan->vlan_filter))
+ macvlan_set_filter_vlan(dev, vid);
+ }
+
+ return 0;
+}
+
+static int macvlan_set_rx_filter_vlan(struct net_device *dev, int vf,
+ struct nlattr *tb[])
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+ int err;
+
+ if (vf != SELF_VF)
+ return -EINVAL;
+
+ switch (vlan->mode) {
+ case MACVLAN_MODE_PASSTHRU:
+ return macvlan_set_rx_filter_vlan_passthru(dev, vf, tb);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int macvlan_addr_in_hw_list(struct netdev_hw_addr_list *list,
+ u8 *addr, int addrlen)
+{
+ struct netdev_hw_addr *ha;
+
+ netdev_hw_addr_list_for_each(ha, list) {
+ if (!memcmp(ha->addr, addr, addrlen))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int macvlan_addr_in_attrs(struct nlattr *addr_list, u8 *addr,
+ int addrlen)
+{
+ struct nlattr *addr_attr;
+ int addr_rem;
+
+ nla_for_each_nested(addr_attr, addr_list, addr_rem) {
+ if (!memcmp(nla_data(addr_attr), addr, addrlen))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int macvlan_update_hw_addr_list(struct net_device *dev,
+ struct netdev_hw_addr_list *curr_addr_list,
+ int addr_list_type,
+ struct nlattr *new_addr_attrs)
+{
+ struct nlattr *addr_attr;
+ int addr_rem;
+ u8 *addr;
+ int alen, i;
+ int err = 0;
+
+ if (!netdev_hw_addr_list_empty(curr_addr_list)) {
+ struct netdev_hw_addr *ha;
+ u8 *del_addrlist;
+ int del_addr_count = 0;
+
+ alen = ETH_ALEN * netdev_hw_addr_list_count(curr_addr_list);
+ del_addrlist = kmalloc(alen, GFP_KERNEL);
+ if (!del_addrlist) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ /*
+ * Get the addresses that need to be deleted
+ */
+ netdev_hw_addr_list_for_each(ha, curr_addr_list) {
+ if (!macvlan_addr_in_attrs(new_addr_attrs, ha->addr,
+ ETH_ALEN))
+ memcpy(del_addrlist + (del_addr_count++ *
+ ETH_ALEN), ha->addr, ETH_ALEN);
+ }
+
+ /*
+ * Delete addresses
+ */
+ for (i = 0, addr = del_addrlist; i < del_addr_count && addr;
+ i++, addr += ETH_ALEN) {
+ if (addr_list_type == NETDEV_HW_ADDR_T_UNICAST)
+ dev_uc_del(dev, addr);
+ else if (addr_list_type == NETDEV_HW_ADDR_T_MULTICAST)
+ dev_mc_del(dev, addr);
+ }
+ kfree(del_addrlist);
+ }
+
+ /* Add new addresses */
+ nla_for_each_nested(addr_attr, new_addr_attrs, addr_rem) {
+ if (!macvlan_addr_in_hw_list(curr_addr_list,
+ nla_data(addr_attr), ETH_ALEN)) {
+ if (addr_list_type == NETDEV_HW_ADDR_T_UNICAST)
+ dev_uc_add(dev, nla_data(addr_attr));
+ else if (addr_list_type == NETDEV_HW_ADDR_T_MULTICAST)
+ dev_mc_add(dev, nla_data(addr_attr));
+ }
+ }
+
+ return 0;
+
+err_out:
+ return err;
+}
+
+static int macvlan_set_rx_filter_addr_passthru(struct net_device *dev,
+ int vf, struct nlattr *tb[])
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+ struct net_device *lowerdev = vlan->lowerdev;
+ const struct net_device_ops *ops = lowerdev->netdev_ops;
+ unsigned int flags, flags_changed;
+ int err;
+
+ if (ops->ndo_set_rx_filter_addr)
+ return ops->ndo_set_rx_filter_addr(vlan->lowerdev, vf, tb);
+
+ if (tb[IFLA_RX_FILTER_ADDR_FLAGS]) {
+ flags = nla_get_u32(tb[IFLA_RX_FILTER_ADDR_FLAGS]);
+
+ flags_changed = (dev->flags ^ flags) & RX_FILTER_FLAGS;
+ if (flags_changed)
+ dev_change_flags(dev, dev->flags ^ flags_changed);
+ }
+
+ if (tb[IFLA_RX_FILTER_ADDR_UC_LIST]) {
+ err = macvlan_update_hw_addr_list(dev, &dev->uc,
+ NETDEV_HW_ADDR_T_UNICAST,
+ tb[IFLA_RX_FILTER_ADDR_UC_LIST]);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_RX_FILTER_ADDR_MC_LIST]) {
+ err = macvlan_update_hw_addr_list(dev, &dev->mc,
+ NETDEV_HW_ADDR_T_MULTICAST,
+ tb[IFLA_RX_FILTER_ADDR_MC_LIST]);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int macvlan_validate_rx_filter_addr(struct net_device *dev, int vf,
+ struct nlattr *tb[])
+{
+ struct nlattr *addr_attr;
+ int addr_rem;
+
+ if (vf != SELF_VF)
+ return -EINVAL;
+
+ if (tb[IFLA_RX_FILTER_ADDR_UC_LIST]) {
+ nla_for_each_nested(addr_attr, tb[IFLA_RX_FILTER_ADDR_UC_LIST],
+ addr_rem) {
+ if ((nla_type(addr_attr) != IFLA_ADDR_LIST_ENTRY) ||
+ !is_unicast_ether_addr(nla_data(addr_attr)))
+ return -EINVAL;
+ }
+ }
+
+ if (tb[IFLA_RX_FILTER_ADDR_MC_LIST]) {
+ nla_for_each_nested(addr_attr, tb[IFLA_RX_FILTER_ADDR_MC_LIST],
+ addr_rem) {
+ if ((nla_type(addr_attr) != IFLA_ADDR_LIST_ENTRY) ||
+ !is_multicast_ether_addr(nla_data(addr_attr)))
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int macvlan_set_rx_filter_addr(struct net_device *dev, int vf,
+ struct nlattr *tb[])
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+ int err;
+
+ err = macvlan_validate_rx_filter_addr(dev, vf, tb);
+ if (err)
+ return err;
+
+ switch (vlan->mode) {
+ case MACVLAN_MODE_PASSTHRU:
+ return macvlan_set_rx_filter_addr_passthru(dev, vf, tb);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static void macvlan_ethtool_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *drvinfo)
{
@@ -557,19 +817,21 @@ static const struct ethtool_ops macvlan_ethtool_ops = {
};
static const struct net_device_ops macvlan_netdev_ops = {
- .ndo_init = macvlan_init,
- .ndo_uninit = macvlan_uninit,
- .ndo_open = macvlan_open,
- .ndo_stop = macvlan_stop,
- .ndo_start_xmit = macvlan_start_xmit,
- .ndo_change_mtu = macvlan_change_mtu,
- .ndo_change_rx_flags = macvlan_change_rx_flags,
- .ndo_set_mac_address = macvlan_set_mac_address,
- .ndo_set_rx_mode = macvlan_set_multicast_list,
- .ndo_get_stats64 = macvlan_dev_get_stats64,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_vlan_rx_add_vid = macvlan_vlan_rx_add_vid,
- .ndo_vlan_rx_kill_vid = macvlan_vlan_rx_kill_vid,
+ .ndo_init = macvlan_init,
+ .ndo_uninit = macvlan_uninit,
+ .ndo_open = macvlan_open,
+ .ndo_stop = macvlan_stop,
+ .ndo_start_xmit = macvlan_start_xmit,
+ .ndo_change_mtu = macvlan_change_mtu,
+ .ndo_change_rx_flags = macvlan_change_rx_flags,
+ .ndo_set_mac_address = macvlan_set_mac_address,
+ .ndo_set_rx_mode = macvlan_set_rx_mode,
+ .ndo_get_stats64 = macvlan_dev_get_stats64,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_vlan_rx_add_vid = macvlan_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = macvlan_vlan_rx_kill_vid,
+ .ndo_set_rx_filter_addr = macvlan_set_rx_filter_addr,
+ .ndo_set_rx_filter_vlan = macvlan_set_rx_filter_vlan,
};
void macvlan_common_setup(struct net_device *dev)
@@ -577,6 +839,7 @@ void macvlan_common_setup(struct net_device *dev)
ether_setup(dev);
dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
+ dev->priv_flags |= IFF_UNICAST_FLT;
dev->netdev_ops = &macvlan_netdev_ops;
dev->destructor = free_netdev;
dev->header_ops = &macvlan_hard_header_ops,
@@ -704,6 +967,8 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
if (data && data[IFLA_MACVLAN_MODE])
vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
+ memset(vlan->vlan_filter, 0, VLAN_BITMAP_SIZE);
+
if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
if (port->count)
return -EINVAL;
@@ -7,6 +7,7 @@
#include <linux/netlink.h>
#include <net/netlink.h>
#include <linux/u64_stats_sync.h>
+#include <linux/if_vlan.h>
#if defined(CONFIG_MACVTAP) || defined(CONFIG_MACVTAP_MODULE)
struct socket *macvtap_get_socket(struct file *);
@@ -65,6 +66,7 @@ struct macvlan_dev {
struct macvtap_queue *taps[MAX_MACVTAP_QUEUES];
int numvtaps;
int minor;
+ unsigned long vlan_filter[BITS_TO_LONGS(VLAN_N_VID)];
};
static inline void macvlan_count_rx(const struct macvlan_dev *vlan,