1724 lines · 43.2 KB · C
RawDownload
/* * Copyright (c) 2007 Patrick McHardy <kaber@trash.net> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * The code this is based on carried the following copyright notice: * --- * (C) Copyright 2001-2006 * Alex Zeffertt, Cambridge Broadband Ltd, ajz@cambridgebroadband.com * Re-worked by Ben Greear <greearb@candelatech.com> * --- */#include <linux/kernel.h>#include <linux/types.h>#include <linux/module.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/rculist.h>#include <linux/notifier.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/if_arp.h>#include <linux/if_vlan.h>#include <linux/if_link.h>#include <linux/if_macvlan.h>#include <linux/hash.h>#include <linux/workqueue.h>#include <net/rtnetlink.h>#include <net/xfrm.h>#include <linux/netpoll.h> #define MACVLAN_HASH_BITS	8#define MACVLAN_HASH_SIZE	(1<<MACVLAN_HASH_BITS)#define MACVLAN_BC_QUEUE_LEN	1000 #define MACVLAN_F_PASSTHRU	1#define MACVLAN_F_ADDRCHANGE	2 struct macvlan_port {	struct net_device	*dev;	struct hlist_head	vlan_hash[MACVLAN_HASH_SIZE];	struct list_head	vlans;	struct sk_buff_head	bc_queue;	struct work_struct	bc_work;	u32			flags;	int			count;	struct hlist_head	vlan_source_hash[MACVLAN_HASH_SIZE];	DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);	unsigned char           perm_addr[ETH_ALEN];}; struct macvlan_source_entry {	struct hlist_node	hlist;	struct macvlan_dev	*vlan;	unsigned char		addr[6+2] __aligned(sizeof(u16));	struct rcu_head		rcu;}; struct macvlan_skb_cb {	const struct macvlan_dev *src;}; #define MACVLAN_SKB_CB(__skb) ((struct macvlan_skb_cb *)&((__skb)->cb[0])) static void macvlan_port_destroy(struct net_device *dev); static inline bool macvlan_passthru(const struct macvlan_port *port){	return port->flags & MACVLAN_F_PASSTHRU;} static inline void macvlan_set_passthru(struct macvlan_port *port){	port->flags |= MACVLAN_F_PASSTHRU;} static inline bool macvlan_addr_change(const struct macvlan_port *port){	return port->flags & MACVLAN_F_ADDRCHANGE;} static inline void macvlan_set_addr_change(struct macvlan_port *port){	port->flags |= MACVLAN_F_ADDRCHANGE;} static inline void macvlan_clear_addr_change(struct macvlan_port *port){	port->flags &= ~MACVLAN_F_ADDRCHANGE;} /* Hash Ethernet address */static u32 macvlan_eth_hash(const unsigned char *addr){	u64 value = get_unaligned((u64 *)addr); 	/* only want 6 bytes */#ifdef __BIG_ENDIAN	value >>= 16;#else	value <<= 16;#endif	return hash_64(value, MACVLAN_HASH_BITS);} static struct macvlan_port *macvlan_port_get_rcu(const struct net_device *dev){	return rcu_dereference(dev->rx_handler_data);} static struct macvlan_port *macvlan_port_get_rtnl(const struct net_device *dev){	return rtnl_dereference(dev->rx_handler_data);} #define macvlan_port_exists(dev) (dev->priv_flags & IFF_MACVLAN_PORT) static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port,					       const unsigned char *addr){	struct macvlan_dev *vlan;	u32 idx = macvlan_eth_hash(addr); 	hlist_for_each_entry_rcu(vlan, &port->vlan_hash[idx], hlist) {		if (ether_addr_equal_64bits(vlan->dev->dev_addr, addr))			return vlan;	}	return NULL;} static struct macvlan_source_entry *macvlan_hash_lookup_source(	const struct macvlan_dev *vlan,	const unsigned char *addr){	struct macvlan_source_entry *entry;	u32 idx = macvlan_eth_hash(addr);	struct hlist_head *h = &vlan->port->vlan_source_hash[idx]; 	hlist_for_each_entry_rcu(entry, h, hlist) {		if (ether_addr_equal_64bits(entry->addr, addr) &&		    entry->vlan == vlan)			return entry;	}	return NULL;} static int macvlan_hash_add_source(struct macvlan_dev *vlan,				   const unsigned char *addr){	struct macvlan_port *port = vlan->port;	struct macvlan_source_entry *entry;	struct hlist_head *h; 	entry = macvlan_hash_lookup_source(vlan, addr);	if (entry)		return 0; 	entry = kmalloc(sizeof(*entry), GFP_KERNEL);	if (!entry)		return -ENOMEM; 	ether_addr_copy(entry->addr, addr);	entry->vlan = vlan;	h = &port->vlan_source_hash[macvlan_eth_hash(addr)];	hlist_add_head_rcu(&entry->hlist, h);	vlan->macaddr_count++; 	return 0;} static void macvlan_hash_add(struct macvlan_dev *vlan){	struct macvlan_port *port = vlan->port;	const unsigned char *addr = vlan->dev->dev_addr;	u32 idx = macvlan_eth_hash(addr); 	hlist_add_head_rcu(&vlan->hlist, &port->vlan_hash[idx]);} static void macvlan_hash_del_source(struct macvlan_source_entry *entry){	hlist_del_rcu(&entry->hlist);	kfree_rcu(entry, rcu);} static void macvlan_hash_del(struct macvlan_dev *vlan, bool sync){	hlist_del_rcu(&vlan->hlist);	if (sync)		synchronize_rcu();} static void macvlan_hash_change_addr(struct macvlan_dev *vlan,					const unsigned char *addr){	macvlan_hash_del(vlan, true);	/* Now that we are unhashed it is safe to change the device	 * address without confusing packet delivery.	 */	memcpy(vlan->dev->dev_addr, addr, ETH_ALEN);	macvlan_hash_add(vlan);} static bool macvlan_addr_busy(const struct macvlan_port *port,			      const unsigned char *addr){	/* Test to see if the specified address is	 * currently in use by the underlying device or	 * another macvlan.	 */	if (!macvlan_passthru(port) && !macvlan_addr_change(port) &&	    ether_addr_equal_64bits(port->dev->dev_addr, addr))		return true; 	if (macvlan_hash_lookup(port, addr))		return true; 	return false;}  static int macvlan_broadcast_one(struct sk_buff *skb,				 const struct macvlan_dev *vlan,				 const struct ethhdr *eth, bool local){	struct net_device *dev = vlan->dev; 	if (local)		return __dev_forward_skb(dev, skb); 	skb->dev = dev;	if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))		skb->pkt_type = PACKET_BROADCAST;	else		skb->pkt_type = PACKET_MULTICAST; 	return 0;} static u32 macvlan_hash_mix(const struct macvlan_dev *vlan){	return (u32)(((unsigned long)vlan) >> L1_CACHE_SHIFT);}  static unsigned int mc_hash(const struct macvlan_dev *vlan,			    const unsigned char *addr){	u32 val = __get_unaligned_cpu32(addr + 2); 	val ^= macvlan_hash_mix(vlan);	return hash_32(val, MACVLAN_MC_FILTER_BITS);} static void macvlan_broadcast(struct sk_buff *skb,			      const struct macvlan_port *port,			      struct net_device *src,			      enum macvlan_mode mode){	const struct ethhdr *eth = eth_hdr(skb);	const struct macvlan_dev *vlan;	struct sk_buff *nskb;	unsigned int i;	int err;	unsigned int hash; 	if (skb->protocol == htons(ETH_P_PAUSE))		return; 	for (i = 0; i < MACVLAN_HASH_SIZE; i++) {		hlist_for_each_entry_rcu(vlan, &port->vlan_hash[i], hlist) {			if (vlan->dev == src || !(vlan->mode & mode))				continue; 			hash = mc_hash(vlan, eth->h_dest);			if (!test_bit(hash, vlan->mc_filter))				continue; 			err = NET_RX_DROP;			nskb = skb_clone(skb, GFP_ATOMIC);			if (likely(nskb))				err = macvlan_broadcast_one(					nskb, vlan, eth,					mode == MACVLAN_MODE_BRIDGE) ?:				      netif_rx_ni(nskb);			macvlan_count_rx(vlan, skb->len + ETH_HLEN,					 err == NET_RX_SUCCESS, true);		}	}} static void macvlan_process_broadcast(struct work_struct *w){	struct macvlan_port *port = container_of(w, struct macvlan_port,						 bc_work);	struct sk_buff *skb;	struct sk_buff_head list; 	__skb_queue_head_init(&list); 	spin_lock_bh(&port->bc_queue.lock);	skb_queue_splice_tail_init(&port->bc_queue, &list);	spin_unlock_bh(&port->bc_queue.lock); 	while ((skb = __skb_dequeue(&list))) {		const struct macvlan_dev *src = MACVLAN_SKB_CB(skb)->src; 		rcu_read_lock(); 		if (!src)			/* frame comes from an external address */			macvlan_broadcast(skb, port, NULL,					  MACVLAN_MODE_PRIVATE |					  MACVLAN_MODE_VEPA    |					  MACVLAN_MODE_PASSTHRU|					  MACVLAN_MODE_BRIDGE);		else if (src->mode == MACVLAN_MODE_VEPA)			/* flood to everyone except source */			macvlan_broadcast(skb, port, src->dev,					  MACVLAN_MODE_VEPA |					  MACVLAN_MODE_BRIDGE);		else			/*			 * flood only to VEPA ports, bridge ports			 * already saw the frame on the way out.			 */			macvlan_broadcast(skb, port, src->dev,					  MACVLAN_MODE_VEPA); 		rcu_read_unlock(); 		if (src)			dev_put(src->dev);		kfree_skb(skb);	}} static void macvlan_broadcast_enqueue(struct macvlan_port *port,				      const struct macvlan_dev *src,				      struct sk_buff *skb){	struct sk_buff *nskb;	int err = -ENOMEM; 	nskb = skb_clone(skb, GFP_ATOMIC);	if (!nskb)		goto err; 	MACVLAN_SKB_CB(nskb)->src = src; 	spin_lock(&port->bc_queue.lock);	if (skb_queue_len(&port->bc_queue) < MACVLAN_BC_QUEUE_LEN) {		if (src)			dev_hold(src->dev);		__skb_queue_tail(&port->bc_queue, nskb);		err = 0;	}	spin_unlock(&port->bc_queue.lock); 	if (err)		goto free_nskb; 	schedule_work(&port->bc_work);	return; free_nskb:	kfree_skb(nskb);err:	atomic_long_inc(&skb->dev->rx_dropped);} static void macvlan_flush_sources(struct macvlan_port *port,				  struct macvlan_dev *vlan){	int i; 	for (i = 0; i < MACVLAN_HASH_SIZE; i++) {		struct hlist_node *h, *n; 		hlist_for_each_safe(h, n, &port->vlan_source_hash[i]) {			struct macvlan_source_entry *entry; 			entry = hlist_entry(h, struct macvlan_source_entry,					    hlist);			if (entry->vlan == vlan)				macvlan_hash_del_source(entry);		}	}	vlan->macaddr_count = 0;} static void macvlan_forward_source_one(struct sk_buff *skb,				       struct macvlan_dev *vlan){	struct sk_buff *nskb;	struct net_device *dev;	int len;	int ret; 	dev = vlan->dev;	if (unlikely(!(dev->flags & IFF_UP)))		return; 	nskb = skb_clone(skb, GFP_ATOMIC);	if (!nskb)		return; 	len = nskb->len + ETH_HLEN;	nskb->dev = dev;	nskb->pkt_type = PACKET_HOST; 	ret = netif_rx(nskb);	macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, false);} static void macvlan_forward_source(struct sk_buff *skb,				   struct macvlan_port *port,				   const unsigned char *addr){	struct macvlan_source_entry *entry;	u32 idx = macvlan_eth_hash(addr);	struct hlist_head *h = &port->vlan_source_hash[idx]; 	hlist_for_each_entry_rcu(entry, h, hlist) {		if (ether_addr_equal_64bits(entry->addr, addr))			macvlan_forward_source_one(skb, entry->vlan);	}} /* called under rcu_read_lock() from netif_receive_skb */static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb){	struct macvlan_port *port;	struct sk_buff *skb = *pskb;	const struct ethhdr *eth = eth_hdr(skb);	const struct macvlan_dev *vlan;	const struct macvlan_dev *src;	struct net_device *dev;	unsigned int len = 0;	int ret;	rx_handler_result_t handle_res; 	port = macvlan_port_get_rcu(skb->dev);	if (is_multicast_ether_addr(eth->h_dest)) {		unsigned int hash; 		skb = ip_check_defrag(dev_net(skb->dev), skb, IP_DEFRAG_MACVLAN);		if (!skb)			return RX_HANDLER_CONSUMED;		*pskb = skb;		eth = eth_hdr(skb);		macvlan_forward_source(skb, port, eth->h_source);		src = macvlan_hash_lookup(port, eth->h_source);		if (src && src->mode != MACVLAN_MODE_VEPA &&		    src->mode != MACVLAN_MODE_BRIDGE) {			/* forward to original port. */			vlan = src;			ret = macvlan_broadcast_one(skb, vlan, eth, 0) ?:			      netif_rx(skb);			handle_res = RX_HANDLER_CONSUMED;			goto out;		} 		hash = mc_hash(NULL, eth->h_dest);		if (test_bit(hash, port->mc_filter))			macvlan_broadcast_enqueue(port, src, skb); 		return RX_HANDLER_PASS;	} 	macvlan_forward_source(skb, port, eth->h_source);	if (macvlan_passthru(port))		vlan = list_first_or_null_rcu(&port->vlans,					      struct macvlan_dev, list);	else		vlan = macvlan_hash_lookup(port, eth->h_dest);	if (vlan == NULL)		return RX_HANDLER_PASS; 	dev = vlan->dev;	if (unlikely(!(dev->flags & IFF_UP))) {		kfree_skb(skb);		return RX_HANDLER_CONSUMED;	}	len = skb->len + ETH_HLEN;	skb = skb_share_check(skb, GFP_ATOMIC);	if (!skb) {		ret = NET_RX_DROP;		handle_res = RX_HANDLER_CONSUMED;		goto out;	} 	*pskb = skb;	skb->dev = dev;

Lines 1–500 of 1724.