for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) {
dev = vlan->dev;
- if (unlikely(!(dev->flags & IFF_UP)))
- continue;
nskb = skb_clone(skb, GFP_ATOMIC);
if (nskb == NULL) {
}
static int macvlan_hard_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, void *daddr, void *saddr,
- unsigned len)
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned len)
{
const struct macvlan_dev *vlan = netdev_priv(dev);
struct net_device *lowerdev = vlan->lowerdev;
- return lowerdev->hard_header(skb, lowerdev, type, daddr,
- saddr ? : dev->dev_addr, len);
+ return dev_hard_header(skb, lowerdev, type, daddr,
+ saddr ? : dev->dev_addr, len);
}
+static const struct header_ops macvlan_hard_header_ops = {
+ .create = macvlan_hard_header,
+ .rebuild = eth_rebuild_header,
+ .parse = eth_header_parse,
+ .cache = eth_header_cache,
+ .cache_update = eth_header_cache_update,
+};
+
static int macvlan_open(struct net_device *dev)
{
struct macvlan_dev *vlan = netdev_priv(dev);
return 0;
}
+static int macvlan_set_mac_address(struct net_device *dev, void *p)
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+ struct net_device *lowerdev = vlan->lowerdev;
+ struct sockaddr *addr = p;
+ int err;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ if (!(dev->flags & IFF_UP))
+ goto out;
+
+ err = dev_unicast_add(lowerdev, addr->sa_data, ETH_ALEN);
+ if (err < 0)
+ return err;
+ dev_unicast_delete(lowerdev, dev->dev_addr, ETH_ALEN);
+
+out:
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+ return 0;
+}
+
static void macvlan_change_rx_flags(struct net_device *dev, int change)
{
struct macvlan_dev *vlan = netdev_priv(dev);
static const struct ethtool_ops macvlan_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_rx_csum = macvlan_ethtool_get_rx_csum,
- .get_tx_csum = ethtool_op_get_tx_csum,
- .get_tso = ethtool_op_get_tso,
- .get_ufo = ethtool_op_get_ufo,
- .get_sg = ethtool_op_get_sg,
.get_drvinfo = macvlan_ethtool_get_drvinfo,
};
dev->stop = macvlan_stop;
dev->change_mtu = macvlan_change_mtu;
dev->change_rx_flags = macvlan_change_rx_flags;
+ dev->set_mac_address = macvlan_set_mac_address;
dev->set_multicast_list = macvlan_set_multicast_list;
- dev->hard_header = macvlan_hard_header;
dev->hard_start_xmit = macvlan_hard_start_xmit;
dev->destructor = free_netdev;
+ dev->header_ops = &macvlan_hard_header_ops,
dev->ethtool_ops = &macvlan_ethtool_ops;
dev->tx_queue_len = 0;
}
if (!netif_carrier_ok(dev))
netif_carrier_on(dev);
} else {
- if (netif_carrier_ok(lowerdev))
+ if (netif_carrier_ok(dev))
netif_carrier_off(dev);
}
}
if (!tb[IFLA_LINK])
return -EINVAL;
- lowerdev = __dev_get_by_index(nla_get_u32(tb[IFLA_LINK]));
+ lowerdev = __dev_get_by_index(dev->nd_net, nla_get_u32(tb[IFLA_LINK]));
if (lowerdev == NULL)
return -ENODEV;
+ /* Don't allow macvlans on top of other macvlans - its not really
+ * wrong, but lockdep can't handle it and its not useful for anything
+ * you couldn't do directly on top of the real device.
+ */
+ if (lowerdev->rtnl_link_ops == dev->rtnl_link_ops)
+ return -ENODEV;
+
if (!tb[IFLA_MTU])
dev->mtu = lowerdev->mtu;
else if (dev->mtu > lowerdev->mtu)
goto err1;
return 0;
err1:
- macvlan_handle_frame_hook = macvlan_handle_frame;
+ macvlan_handle_frame_hook = NULL;
unregister_netdevice_notifier(&macvlan_notifier_block);
return err;
}