1122 lines · 29.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* -*- linux-c -*- * INET		802.1Q VLAN *		Ethernet-type device handling. * * Authors:	Ben Greear <greearb@candelatech.com> *              Please send support related email to: netdev@vger.kernel.org *              VLAN Home Page: http://www.candelatech.com/~greear/vlan.html * * Fixes:       Mar 22 2001: Martin Bokaemper <mbokaemper@unispherenetworks.com> *                - reset skb->pkt_type on incoming packets when MAC was changed *                - see that changed MAC is saddr for outgoing packets *              Oct 20, 2001:  Ard van Breeman: *                - Fix MC-list, finally. *                - Flush MC-list on VLAN destroy. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h>#include <linux/slab.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/net_tstamp.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/phy.h>#include <net/arp.h>#include <net/macsec.h>#include <net/netdev_lock.h> #include "vlan.h"#include "vlanproc.h"#include <linux/if_vlan.h>#include <linux/netpoll.h> /* *	Create the VLAN header for an arbitrary protocol layer * *	saddr=NULL	means use device source address *	daddr=NULL	means leave destination address (eg unresolved arp) * *  This is called when the SKB is moving down the stack towards the *  physical devices. */static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,				unsigned short type,				const void *daddr, const void *saddr,				unsigned int len){	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);	struct vlan_hdr *vhdr;	unsigned int vhdrlen = 0;	u16 vlan_tci = 0;	int rc; 	if (!(vlan->flags & VLAN_FLAG_REORDER_HDR)) {		vhdr = skb_push(skb, VLAN_HLEN); 		vlan_tci = vlan->vlan_id;		vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority);		vhdr->h_vlan_TCI = htons(vlan_tci); 		/*		 *  Set the protocol type. For a packet of type ETH_P_802_3/2 we		 *  put the length in here instead.		 */		if (type != ETH_P_802_3 && type != ETH_P_802_2)			vhdr->h_vlan_encapsulated_proto = htons(type);		else			vhdr->h_vlan_encapsulated_proto = htons(len); 		skb->protocol = vlan->vlan_proto;		type = ntohs(vlan->vlan_proto);		vhdrlen = VLAN_HLEN;	} 	/* Before delegating work to the lower layer, enter our MAC-address */	if (saddr == NULL)		saddr = dev->dev_addr; 	/* Now make the underlying real hard header */	dev = vlan->real_dev;	rc = dev_hard_header(skb, dev, type, daddr, saddr, len + vhdrlen);	if (rc > 0)		rc += vhdrlen;	return rc;} static inline netdev_tx_t vlan_netpoll_send_skb(struct vlan_dev_priv *vlan, struct sk_buff *skb){#ifdef CONFIG_NET_POLL_CONTROLLER	return netpoll_send_skb(vlan->netpoll, skb);#else	BUG();	return NETDEV_TX_OK;#endif} static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,					    struct net_device *dev){	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);	unsigned int len;	int ret; 	/* Handle non-VLAN frames if they are sent to us, for example by DHCP.	 *	 * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING	 * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs...	 */	if (vlan->flags & VLAN_FLAG_REORDER_HDR ||	    veth->h_vlan_proto != vlan->vlan_proto) {		u16 vlan_tci;		vlan_tci = vlan->vlan_id;		vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority);		__vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci);	} 	skb->dev = vlan->real_dev;	len = skb->len;	if (unlikely(netpoll_tx_running(dev)))		return vlan_netpoll_send_skb(vlan, skb); 	ret = dev_queue_xmit(skb); 	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {		struct vlan_pcpu_stats *stats; 		stats = this_cpu_ptr(vlan->vlan_pcpu_stats);		u64_stats_update_begin(&stats->syncp);		u64_stats_inc(&stats->tx_packets);		u64_stats_add(&stats->tx_bytes, len);		u64_stats_update_end(&stats->syncp);	} else {		this_cpu_inc(vlan->vlan_pcpu_stats->tx_dropped);	} 	return ret;} static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;	unsigned int max_mtu = real_dev->mtu; 	if (netif_reduces_vlan_mtu(real_dev))		max_mtu -= VLAN_HLEN;	if (max_mtu < new_mtu)		return -ERANGE; 	WRITE_ONCE(dev->mtu, new_mtu); 	return 0;} void vlan_dev_set_ingress_priority(const struct net_device *dev,				   u32 skb_prio, u16 vlan_prio){	struct vlan_dev_priv *vlan = vlan_dev_priv(dev); 	if (vlan->ingress_priority_map[vlan_prio & 0x7] && !skb_prio)		vlan->nr_ingress_mappings--;	else if (!vlan->ingress_priority_map[vlan_prio & 0x7] && skb_prio)		vlan->nr_ingress_mappings++; 	vlan->ingress_priority_map[vlan_prio & 0x7] = skb_prio;} int vlan_dev_set_egress_priority(const struct net_device *dev,				 u32 skb_prio, u16 vlan_prio){	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);	struct vlan_priority_tci_mapping __rcu **mpp;	struct vlan_priority_tci_mapping *mp;	struct vlan_priority_tci_mapping *np;	u32 bucket = skb_prio & 0xF;	u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK; 	/* See if a priority mapping exists.. */	mpp = &vlan->egress_priority_map[bucket];	mp = rtnl_dereference(*mpp);	while (mp) {		if (mp->priority == skb_prio) {			if (!vlan_qos) {				rcu_assign_pointer(*mpp, rtnl_dereference(mp->next));				vlan->nr_egress_mappings--;				kfree_rcu(mp, rcu);			} else {				WRITE_ONCE(mp->vlan_qos, vlan_qos);			}			return 0;		}		mpp = &mp->next;		mp = rtnl_dereference(*mpp);	} 	/* Create a new mapping then. */	if (!vlan_qos)		return 0; 	np = kmalloc_obj(struct vlan_priority_tci_mapping);	if (!np)		return -ENOBUFS; 	np->priority = skb_prio;	np->vlan_qos = vlan_qos;	RCU_INIT_POINTER(np->next, rtnl_dereference(vlan->egress_priority_map[bucket]));	rcu_assign_pointer(vlan->egress_priority_map[bucket], np);	if (vlan_qos)		vlan->nr_egress_mappings++;	return 0;} /* Flags are defined in the vlan_flags enum in * include/uapi/linux/if_vlan.h file. */int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask){	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);	u32 old_flags = vlan->flags; 	if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP |		     VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP |		     VLAN_FLAG_BRIDGE_BINDING))		return -EINVAL; 	vlan->flags = (old_flags & ~mask) | (flags & mask); 	if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_GVRP) {		if (vlan->flags & VLAN_FLAG_GVRP)			vlan_gvrp_request_join(dev);		else			vlan_gvrp_request_leave(dev);	} 	if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_MVRP) {		if (vlan->flags & VLAN_FLAG_MVRP)			vlan_mvrp_request_join(dev);		else			vlan_mvrp_request_leave(dev);	}	return 0;} void vlan_dev_get_realdev_name(const struct net_device *dev, char *result, size_t size){	strscpy_pad(result, vlan_dev_priv(dev)->real_dev->name, size);} bool vlan_dev_inherit_address(struct net_device *dev,			      struct net_device *real_dev){	if (dev->addr_assign_type != NET_ADDR_STOLEN)		return false; 	eth_hw_addr_set(dev, real_dev->dev_addr);	call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);	return true;} static int vlan_dev_open(struct net_device *dev){	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);	struct net_device *real_dev = vlan->real_dev;	int err; 	if (!(real_dev->flags & IFF_UP) &&	    !(vlan->flags & VLAN_FLAG_LOOSE_BINDING))		return -ENETDOWN; 	/* The explicit open supersedes any deferred link-state sync */	netdev_work_cancel(dev, VLAN_WORK_LINK_STATE); 	if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr) &&	    !vlan_dev_inherit_address(dev, real_dev)) {		err = dev_uc_add(real_dev, dev->dev_addr);		if (err < 0)			goto out;	} 	ether_addr_copy(vlan->real_dev_addr, real_dev->dev_addr); 	if (vlan->flags & VLAN_FLAG_GVRP)		vlan_gvrp_request_join(dev); 	if (vlan->flags & VLAN_FLAG_MVRP)		vlan_mvrp_request_join(dev); 	if (netif_carrier_ok(real_dev) &&	    !(vlan->flags & VLAN_FLAG_BRIDGE_BINDING))		netif_carrier_on(dev);	return 0; out:	netif_carrier_off(dev);	return err;} static int vlan_dev_stop(struct net_device *dev){	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);	struct net_device *real_dev = vlan->real_dev; 	/* The explicit close supersedes any deferred link-state sync */	netdev_work_cancel(dev, VLAN_WORK_LINK_STATE); 	dev_mc_unsync(real_dev, dev);	dev_uc_unsync(real_dev, dev); 	if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr))		dev_uc_del(real_dev, dev->dev_addr); 	if (!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING))		netif_carrier_off(dev);	return 0;} static int vlan_dev_set_mac_address(struct net_device *dev, void *p){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;	struct sockaddr *addr = p;	int err; 	if (!is_valid_ether_addr(addr->sa_data))		return -EADDRNOTAVAIL; 	if (!(dev->flags & IFF_UP))		goto out; 	if (!ether_addr_equal(addr->sa_data, real_dev->dev_addr)) {		err = dev_uc_add(real_dev, addr->sa_data);		if (err < 0)			return err;	} 	if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr))		dev_uc_del(real_dev, dev->dev_addr); out:	eth_hw_addr_set(dev, addr->sa_data);	return 0;} static int vlan_hwtstamp_get(struct net_device *dev,			     struct kernel_hwtstamp_config *cfg){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; 	return generic_hwtstamp_get_lower(real_dev, cfg);} static int vlan_hwtstamp_set(struct net_device *dev,			     struct kernel_hwtstamp_config *cfg,			     struct netlink_ext_ack *extack){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; 	if (!net_eq(dev_net(dev), dev_net(real_dev)))		return -EOPNOTSUPP; 	return generic_hwtstamp_set_lower(real_dev, cfg, extack);} static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;	struct ifreq ifrr;	int err = -EOPNOTSUPP; 	strscpy_pad(ifrr.ifr_name, real_dev->name, IFNAMSIZ);	ifrr.ifr_ifru = ifr->ifr_ifru; 	switch (cmd) {	case SIOCGMIIPHY:	case SIOCGMIIREG:	case SIOCSMIIREG:		err = dev_eth_ioctl(real_dev, &ifrr, cmd);		break;	} 	if (!err)		ifr->ifr_ifru = ifrr.ifr_ifru; 	return err;} static int vlan_dev_neigh_setup(struct net_device *dev, struct neigh_parms *pa){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;	const struct net_device_ops *ops = real_dev->netdev_ops;	int err = 0; 	if (netif_device_present(real_dev) && ops->ndo_neigh_setup)		err = ops->ndo_neigh_setup(real_dev, pa); 	return err;} #if IS_ENABLED(CONFIG_FCOE)static int vlan_dev_fcoe_ddp_setup(struct net_device *dev, u16 xid,				   struct scatterlist *sgl, unsigned int sgc){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;	const struct net_device_ops *ops = real_dev->netdev_ops;	int rc = 0; 	if (ops->ndo_fcoe_ddp_setup)		rc = ops->ndo_fcoe_ddp_setup(real_dev, xid, sgl, sgc); 	return rc;} static int vlan_dev_fcoe_ddp_done(struct net_device *dev, u16 xid){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;	const struct net_device_ops *ops = real_dev->netdev_ops;	int len = 0; 	if (ops->ndo_fcoe_ddp_done)		len = ops->ndo_fcoe_ddp_done(real_dev, xid); 	return len;} static int vlan_dev_fcoe_enable(struct net_device *dev){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;	const struct net_device_ops *ops = real_dev->netdev_ops;	int rc = -EINVAL; 	if (ops->ndo_fcoe_enable)		rc = ops->ndo_fcoe_enable(real_dev);	return rc;} static int vlan_dev_fcoe_disable(struct net_device *dev){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;	const struct net_device_ops *ops = real_dev->netdev_ops;	int rc = -EINVAL; 	if (ops->ndo_fcoe_disable)		rc = ops->ndo_fcoe_disable(real_dev);	return rc;} static int vlan_dev_fcoe_ddp_target(struct net_device *dev, u16 xid,				    struct scatterlist *sgl, unsigned int sgc){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;	const struct net_device_ops *ops = real_dev->netdev_ops;	int rc = 0; 	if (ops->ndo_fcoe_ddp_target)		rc = ops->ndo_fcoe_ddp_target(real_dev, xid, sgl, sgc); 	return rc;}#endif #ifdef NETDEV_FCOE_WWNNstatic int vlan_dev_fcoe_get_wwn(struct net_device *dev, u64 *wwn, int type){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;	const struct net_device_ops *ops = real_dev->netdev_ops;	int rc = -EINVAL; 	if (ops->ndo_fcoe_get_wwn)		rc = ops->ndo_fcoe_get_wwn(real_dev, wwn, type);	return rc;}#endif static void vlan_dev_change_rx_flags(struct net_device *dev, int change){	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; 	if (change & IFF_ALLMULTI)		dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1);	if (change & IFF_PROMISC)		dev_set_promiscuity(real_dev, dev->flags & IFF_PROMISC ? 1 : -1);} static void vlan_dev_set_rx_mode(struct net_device *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);} static __be16 vlan_parse_protocol(const struct sk_buff *skb){	struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data); 	return __vlan_get_protocol(skb, veth->h_vlan_proto, NULL);} static const struct header_ops vlan_header_ops = {	.create	 = vlan_dev_hard_header,

Lines 1–500 of 1122.

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