1199 lines · 30.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com> */ #include <linux/ethtool.h>#include <net/netdev_lock.h> #include "ipvlan.h" #if IS_ENABLED(CONFIG_IPVTAP)void (*__ipvtap_dellink_ptr)(struct net *net, struct net_device *dev,			     struct list_head *head);EXPORT_SYMBOL(__ipvtap_dellink_ptr);#endif static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,				struct netlink_ext_ack *extack){	struct ipvl_dev *ipvlan;	unsigned int flags;	int err; 	ASSERT_RTNL();	if (port->mode != nval) {		mutex_lock(&port->pnodes_lock); 		list_for_each_entry(ipvlan, &port->ipvlans, pnode) {			flags = ipvlan->dev->flags;			if (nval == IPVLAN_MODE_L3 || nval == IPVLAN_MODE_L3S) {				err = dev_change_flags(ipvlan->dev,						       flags | IFF_NOARP,						       extack);			} else {				err = dev_change_flags(ipvlan->dev,						       flags & ~IFF_NOARP,						       extack);			}			if (unlikely(err))				goto fail;		}		if (nval == IPVLAN_MODE_L3S) {			/* New mode is L3S */			err = ipvlan_l3s_register(port);			if (err)				goto fail;		} else if (port->mode == IPVLAN_MODE_L3S) {			/* Old mode was L3S */			ipvlan_l3s_unregister(port);		}		port->mode = nval; 		mutex_unlock(&port->pnodes_lock);	}	return 0; fail:	/* Undo the flags changes that have been done so far. */	list_for_each_entry_continue_reverse(ipvlan, &port->ipvlans, pnode) {		flags = ipvlan->dev->flags;		if (port->mode == IPVLAN_MODE_L3 ||		    port->mode == IPVLAN_MODE_L3S)			dev_change_flags(ipvlan->dev, flags | IFF_NOARP,					 NULL);		else			dev_change_flags(ipvlan->dev, flags & ~IFF_NOARP,					 NULL);	} 	mutex_unlock(&port->pnodes_lock); 	return err;} static int ipvlan_port_create(struct net_device *dev){	struct ipvl_port *port;	int err, idx; 	port = kzalloc_obj(struct ipvl_port);	if (!port)		return -ENOMEM; 	write_pnet(&port->pnet, dev_net(dev));	port->dev = dev;	port->mode = IPVLAN_MODE_L3;	INIT_LIST_HEAD(&port->ipvlans);	for (idx = 0; idx < IPVLAN_HASH_SIZE; idx++)		INIT_HLIST_HEAD(&port->hlhead[idx]); 	spin_lock_init(&port->addrs_lock);	mutex_init(&port->pnodes_lock);	skb_queue_head_init(&port->backlog);	INIT_WORK(&port->wq, ipvlan_process_multicast);	ida_init(&port->ida);	port->dev_id_start = 1; 	err = netdev_rx_handler_register(dev, ipvlan_handle_frame, port);	if (err)		goto err; 	netdev_hold(dev, &port->dev_tracker, GFP_KERNEL); 	return 0; err:	kfree(port);	return err;} static void ipvlan_port_destroy(struct ipvl_port *port){	struct net_device *dev = port->dev;	struct sk_buff *skb; 	if (port->mode == IPVLAN_MODE_L3S)		ipvlan_l3s_unregister(port); 	netdev_rx_handler_unregister(dev);	cancel_work_sync(&port->wq);	netdev_put(dev, &port->dev_tracker); 	while ((skb = __skb_dequeue(&port->backlog)) != NULL) {		dev_put(skb->dev);		kfree_skb(skb);	}	ida_destroy(&port->ida);	kfree(port);} static void ipvlan_port_put(struct ipvl_port *port){	if (refcount_dec_and_test(&port->count))		ipvlan_port_destroy(port);} static struct ipvl_port *ipvlan_port_get(struct net_device *dev){	struct ipvl_port *port = NULL; 	rcu_read_lock();	if (netif_is_ipvlan_port(dev)) {		port = ipvlan_port_get_rcu(dev);		if (!refcount_inc_not_zero(&port->count))			port = NULL;	}	rcu_read_unlock(); 	return port;} #define IPVLAN_ALWAYS_ON_OFLOADS \	(NETIF_F_SG | NETIF_F_HW_CSUM | \	 NETIF_F_GSO_ROBUST | NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ENCAP_ALL) #define IPVLAN_ALWAYS_ON \	(IPVLAN_ALWAYS_ON_OFLOADS | NETIF_F_VLAN_CHALLENGED) #define IPVLAN_FEATURES \	(NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \	 NETIF_F_GSO | NETIF_F_ALL_TSO | NETIF_F_GSO_ROBUST | \	 NETIF_F_GRO | NETIF_F_RXCSUM | \	 NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER) 	/* NETIF_F_GSO_ENCAP_ALL NETIF_F_GSO_SOFTWARE Newly added */ #define IPVLAN_STATE_MASK \	((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT)) static int ipvlan_init(struct net_device *dev){	struct ipvl_dev *ipvlan = netdev_priv(dev);	struct net_device *phy_dev = ipvlan->phy_dev;	struct ipvl_port *port;	int err; 	dev->state = (dev->state & ~IPVLAN_STATE_MASK) |		     (phy_dev->state & IPVLAN_STATE_MASK);	dev->features = phy_dev->features & IPVLAN_FEATURES;	dev->features |= IPVLAN_ALWAYS_ON;	dev->vlan_features = phy_dev->vlan_features & IPVLAN_FEATURES;	dev->vlan_features |= IPVLAN_ALWAYS_ON_OFLOADS;	dev->hw_enc_features |= dev->features;	dev->lltx = true;	netif_inherit_tso_max(dev, phy_dev);	dev->hard_header_len = phy_dev->hard_header_len;	dev->needed_headroom = phy_dev->needed_headroom;	dev->needed_tailroom = phy_dev->needed_tailroom; 	netdev_lockdep_set_classes(dev); 	ipvlan->pcpu_stats = netdev_alloc_pcpu_stats(struct ipvl_pcpu_stats);	if (!ipvlan->pcpu_stats)		return -ENOMEM; 	netdev_lock(phy_dev); 	if (!netif_is_ipvlan_port(phy_dev)) {		err = ipvlan_port_create(phy_dev);		if (err < 0) {			netdev_unlock(phy_dev);			free_percpu(ipvlan->pcpu_stats);			return err;		}		port = ipvlan_port_get_rtnl(phy_dev);		refcount_set(&port->count, 1);	} else {		port = ipvlan_port_get_rtnl(phy_dev);		refcount_inc(&port->count);	} 	netdev_unlock(phy_dev); 	ipvlan->port = port; 	return 0;} static void ipvlan_uninit(struct net_device *dev){	struct ipvl_dev *ipvlan = netdev_priv(dev);	netdevice_tracker dev_tracker;	struct net_device *phy_dev; 	free_percpu(ipvlan->pcpu_stats); 	phy_dev = ipvlan->phy_dev;	netdev_hold(phy_dev, &dev_tracker, GFP_KERNEL);	netdev_lock(phy_dev); 	ipvlan_port_put(ipvlan->port); 	netdev_unlock(phy_dev);	netdev_put(phy_dev, &dev_tracker);} static int ipvlan_open(struct net_device *dev){	struct ipvl_dev *ipvlan = netdev_priv(dev);	struct ipvl_port *port = ipvlan->port;	struct ipvl_addr *addr; 	if (ipvlan->port->mode == IPVLAN_MODE_L3 ||	    ipvlan->port->mode == IPVLAN_MODE_L3S)		dev->flags |= IFF_NOARP;	else		dev->flags &= ~IFF_NOARP; 	spin_lock_bh(&port->addrs_lock);	list_for_each_entry(addr, &ipvlan->addrs, anode)		ipvlan_ht_addr_add(ipvlan, addr);	spin_unlock_bh(&port->addrs_lock); 	return 0;} static int ipvlan_stop(struct net_device *dev){	struct ipvl_dev *ipvlan = netdev_priv(dev);	struct net_device *phy_dev = ipvlan->phy_dev;	struct ipvl_addr *addr; 	dev_uc_unsync(phy_dev, dev);	dev_mc_unsync(phy_dev, dev); 	spin_lock_bh(&ipvlan->port->addrs_lock);	list_for_each_entry(addr, &ipvlan->addrs, anode)		ipvlan_ht_addr_del(addr);	spin_unlock_bh(&ipvlan->port->addrs_lock); 	return 0;} static netdev_tx_t ipvlan_start_xmit(struct sk_buff *skb,				     struct net_device *dev){	const struct ipvl_dev *ipvlan = netdev_priv(dev);	int skblen = skb->len;	int ret; 	ret = ipvlan_queue_xmit(skb, dev);	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {		struct ipvl_pcpu_stats *pcptr; 		pcptr = this_cpu_ptr(ipvlan->pcpu_stats); 		u64_stats_update_begin(&pcptr->syncp);		u64_stats_inc(&pcptr->tx_pkts);		u64_stats_add(&pcptr->tx_bytes, skblen);		u64_stats_update_end(&pcptr->syncp);	} else {		this_cpu_inc(ipvlan->pcpu_stats->tx_drps);	}	return ret;} static netdev_features_t ipvlan_fix_features(struct net_device *dev,					     netdev_features_t features){	struct ipvl_dev *ipvlan = netdev_priv(dev); 	features |= NETIF_F_ALL_FOR_ALL;	features &= (ipvlan->sfeatures | ~IPVLAN_FEATURES);	features = netdev_increment_features(ipvlan->phy_dev->features,					     features, features);	features |= IPVLAN_ALWAYS_ON;	features &= (IPVLAN_FEATURES | IPVLAN_ALWAYS_ON); 	return features;} static void ipvlan_change_rx_flags(struct net_device *dev, int change){	struct ipvl_dev *ipvlan = netdev_priv(dev);	struct net_device *phy_dev = ipvlan->phy_dev; 	if (change & IFF_ALLMULTI)		dev_set_allmulti(phy_dev, dev->flags & IFF_ALLMULTI? 1 : -1);} static void ipvlan_set_multicast_mac_filter(struct net_device *dev){	struct ipvl_dev *ipvlan = netdev_priv(dev); 	if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {		bitmap_fill(ipvlan->mac_filters, IPVLAN_MAC_FILTER_SIZE);	} else {		struct netdev_hw_addr *ha;		DECLARE_BITMAP(mc_filters, IPVLAN_MAC_FILTER_SIZE); 		bitmap_zero(mc_filters, IPVLAN_MAC_FILTER_SIZE);		netdev_for_each_mc_addr(ha, dev)			__set_bit(ipvlan_mac_hash(ha->addr), mc_filters); 		/* Turn-on broadcast bit irrespective of address family,		 * since broadcast is deferred to a work-queue, hence no		 * impact on fast-path processing.		 */		__set_bit(ipvlan_mac_hash(dev->broadcast), mc_filters); 		bitmap_copy(ipvlan->mac_filters, mc_filters,			    IPVLAN_MAC_FILTER_SIZE);	}	dev_uc_sync(ipvlan->phy_dev, dev);	dev_mc_sync(ipvlan->phy_dev, dev);} static void ipvlan_get_stats64(struct net_device *dev,			       struct rtnl_link_stats64 *s){	struct ipvl_dev *ipvlan = netdev_priv(dev); 	if (ipvlan->pcpu_stats) {		struct ipvl_pcpu_stats *pcptr;		u64 rx_pkts, rx_bytes, rx_mcast, tx_pkts, tx_bytes;		u32 rx_errs = 0, tx_drps = 0;		u32 strt;		int idx; 		for_each_possible_cpu(idx) {			pcptr = per_cpu_ptr(ipvlan->pcpu_stats, idx);			do {				strt = u64_stats_fetch_begin(&pcptr->syncp);				rx_pkts = u64_stats_read(&pcptr->rx_pkts);				rx_bytes = u64_stats_read(&pcptr->rx_bytes);				rx_mcast = u64_stats_read(&pcptr->rx_mcast);				tx_pkts = u64_stats_read(&pcptr->tx_pkts);				tx_bytes = u64_stats_read(&pcptr->tx_bytes);			} while (u64_stats_fetch_retry(&pcptr->syncp,							   strt)); 			s->rx_packets += rx_pkts;			s->rx_bytes += rx_bytes;			s->multicast += rx_mcast;			s->tx_packets += tx_pkts;			s->tx_bytes += tx_bytes; 			/* u32 values are updated without syncp protection. */			rx_errs += READ_ONCE(pcptr->rx_errs);			tx_drps += READ_ONCE(pcptr->tx_drps);		}		s->rx_errors = rx_errs;		s->rx_dropped = rx_errs;		s->tx_dropped = tx_drps;	}	s->tx_errors = DEV_STATS_READ(dev, tx_errors);} static int ipvlan_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid){	struct ipvl_dev *ipvlan = netdev_priv(dev);	struct net_device *phy_dev = ipvlan->phy_dev; 	return vlan_vid_add(phy_dev, proto, vid);} static int ipvlan_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,				   u16 vid){	struct ipvl_dev *ipvlan = netdev_priv(dev);	struct net_device *phy_dev = ipvlan->phy_dev; 	vlan_vid_del(phy_dev, proto, vid);	return 0;} static int ipvlan_get_iflink(const struct net_device *dev){	struct ipvl_dev *ipvlan = netdev_priv(dev); 	return READ_ONCE(ipvlan->phy_dev->ifindex);} static const struct net_device_ops ipvlan_netdev_ops = {	.ndo_init		= ipvlan_init,	.ndo_uninit		= ipvlan_uninit,	.ndo_open		= ipvlan_open,	.ndo_stop		= ipvlan_stop,	.ndo_start_xmit		= ipvlan_start_xmit,	.ndo_fix_features	= ipvlan_fix_features,	.ndo_change_rx_flags	= ipvlan_change_rx_flags,	.ndo_set_rx_mode	= ipvlan_set_multicast_mac_filter,	.ndo_get_stats64	= ipvlan_get_stats64,	.ndo_vlan_rx_add_vid	= ipvlan_vlan_rx_add_vid,	.ndo_vlan_rx_kill_vid	= ipvlan_vlan_rx_kill_vid,	.ndo_get_iflink		= ipvlan_get_iflink,}; static int ipvlan_hard_header(struct sk_buff *skb, struct net_device *dev,			      unsigned short type, const void *daddr,			      const void *saddr, unsigned len){	const struct ipvl_dev *ipvlan = netdev_priv(dev);	struct net_device *phy_dev = ipvlan->phy_dev; 	/* TODO Probably use a different field than dev_addr so that the	 * mac-address on the virtual device is portable and can be carried	 * while the packets use the mac-addr on the physical device.	 */	return dev_hard_header(skb, phy_dev, type, daddr,			       saddr ? : phy_dev->dev_addr, len);} static const struct header_ops ipvlan_header_ops = {	.create  	= ipvlan_hard_header,	.parse		= eth_header_parse,	.cache		= eth_header_cache,	.cache_update	= eth_header_cache_update,	.parse_protocol	= eth_header_parse_protocol,}; static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev){	ipvlan->dev->mtu = dev->mtu;} static bool netif_is_ipvlan(const struct net_device *dev){	/* both ipvlan and ipvtap devices use the same netdev_ops */	return dev->netdev_ops == &ipvlan_netdev_ops;} static int ipvlan_ethtool_get_link_ksettings(struct net_device *dev,					     struct ethtool_link_ksettings *cmd){	const struct ipvl_dev *ipvlan = netdev_priv(dev); 	return __ethtool_get_link_ksettings(ipvlan->phy_dev, cmd);} static void ipvlan_ethtool_get_drvinfo(struct net_device *dev,				       struct ethtool_drvinfo *drvinfo){	strscpy(drvinfo->driver, IPVLAN_DRV, sizeof(drvinfo->driver));	strscpy(drvinfo->version, IPV_DRV_VER, sizeof(drvinfo->version));} static u32 ipvlan_ethtool_get_msglevel(struct net_device *dev){	const struct ipvl_dev *ipvlan = netdev_priv(dev); 	return ipvlan->msg_enable;} static void ipvlan_ethtool_set_msglevel(struct net_device *dev, u32 value){	struct ipvl_dev *ipvlan = netdev_priv(dev); 	ipvlan->msg_enable = value;} static const struct ethtool_ops ipvlan_ethtool_ops = {	.get_link	= ethtool_op_get_link,	.get_link_ksettings	= ipvlan_ethtool_get_link_ksettings,	.get_drvinfo	= ipvlan_ethtool_get_drvinfo,	.get_msglevel	= ipvlan_ethtool_get_msglevel,	.set_msglevel	= ipvlan_ethtool_set_msglevel,}; static int ipvlan_nl_changelink(struct net_device *dev,				struct nlattr *tb[], struct nlattr *data[],

Lines 1–500 of 1199.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t