804 lines · 18.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com> */ #include <net/flow.h>#include <net/ip.h> #include "ipvlan.h" static u32 ipvlan_jhash_secret __read_mostly; void ipvlan_init_secret(void){	net_get_random_once(&ipvlan_jhash_secret, sizeof(ipvlan_jhash_secret));} void ipvlan_count_rx(const struct ipvl_dev *ipvlan,			    unsigned int len, bool success, bool mcast){	if (likely(success)) {		struct ipvl_pcpu_stats *pcptr; 		pcptr = this_cpu_ptr(ipvlan->pcpu_stats);		u64_stats_update_begin(&pcptr->syncp);		u64_stats_inc(&pcptr->rx_pkts);		u64_stats_add(&pcptr->rx_bytes, len);		if (mcast)			u64_stats_inc(&pcptr->rx_mcast);		u64_stats_update_end(&pcptr->syncp);	} else {		this_cpu_inc(ipvlan->pcpu_stats->rx_errs);	}}EXPORT_SYMBOL_GPL(ipvlan_count_rx); #if IS_ENABLED(CONFIG_IPV6)static u8 ipvlan_get_v6_hash(const void *iaddr){	const struct in6_addr *ip6_addr = iaddr; 	return __ipv6_addr_jhash(ip6_addr, ipvlan_jhash_secret) &	       IPVLAN_HASH_MASK;}#elsestatic u8 ipvlan_get_v6_hash(const void *iaddr){	return 0;}#endif static u8 ipvlan_get_v4_hash(__be32 addr){	return jhash_1word((__force u32)addr, ipvlan_jhash_secret) &			   IPVLAN_HASH_MASK;} static bool addr_equal(bool is_v6, struct ipvl_addr *addr, const void *iaddr){	if (!is_v6 && addr->atype == IPVL_IPV4) {		struct in_addr *i4addr = (struct in_addr *)iaddr; 		return addr->ip4addr.s_addr == i4addr->s_addr;#if IS_ENABLED(CONFIG_IPV6)	} else if (is_v6 && addr->atype == IPVL_IPV6) {		struct in6_addr *i6addr = (struct in6_addr *)iaddr; 		return ipv6_addr_equal(&addr->ip6addr, i6addr);#endif	} 	return false;} #if IS_ENABLED(CONFIG_IPV6)static struct ipvl_addr *ipvlan_ht_addr_lookup6(const struct ipvl_port *port,						const void *iaddr){	struct ipvl_addr *addr;	u8 hash; 	hash = ipvlan_get_v6_hash(iaddr);	hlist_for_each_entry_rcu(addr, &port->hlhead[hash], hlnode)		if (addr_equal(true, addr, iaddr))			return addr;	return NULL;}#endif static struct ipvl_addr *ipvlan_ht_addr_lookup4(const struct ipvl_port *port,						__be32 addr4){	struct ipvl_addr *addr;	u8 hash; 	hash = ipvlan_get_v4_hash(addr4);	hlist_for_each_entry_rcu(addr, &port->hlhead[hash], hlnode)		if (addr->atype == IPVL_IPV4 && addr->ip4addr.s_addr == addr4)			return addr;	return NULL;} void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr){	struct ipvl_port *port = ipvlan->port;	u8 hash; 	hash = (addr->atype == IPVL_IPV6) ?	       ipvlan_get_v6_hash(&addr->ip6addr) :	       ipvlan_get_v4_hash(addr->ip4addr.s_addr);	if (hlist_unhashed(&addr->hlnode))		hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]);} void ipvlan_ht_addr_del(struct ipvl_addr *addr){	hlist_del_init_rcu(&addr->hlnode);} struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,				   const void *iaddr, bool is_v6){	struct ipvl_addr *addr; 	assert_spin_locked(&ipvlan->port->addrs_lock); 	list_for_each_entry(addr, &ipvlan->addrs, anode) {		if (addr_equal(is_v6, addr, iaddr))			return addr;	}	return NULL;} bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6){	struct ipvl_dev *ipvlan;	bool ret = false; 	rcu_read_lock();	list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) {		if (ipvlan_find_addr(ipvlan, iaddr, is_v6)) {			ret = true;			break;		}	}	rcu_read_unlock();	return ret;} void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type){	void *lyr3h = NULL; 	switch (skb->protocol) {	case htons(ETH_P_ARP): {		struct arphdr *arph; 		if (unlikely(!pskb_may_pull(skb, arp_hdr_len(port->dev))))			return NULL; 		arph = arp_hdr(skb);		*type = IPVL_ARP;		lyr3h = arph;		break;	}	case htons(ETH_P_IP): {		u32 pktlen;		struct iphdr *ip4h; 		if (unlikely(!pskb_may_pull(skb, sizeof(*ip4h))))			return NULL; 		ip4h = ip_hdr(skb);		pktlen = skb_ip_totlen(skb);		if (ip4h->ihl < 5 || ip4h->version != 4)			return NULL;		if (skb->len < pktlen || pktlen < (ip4h->ihl * 4))			return NULL; 		*type = IPVL_IPV4;		lyr3h = ip4h;		break;	}#if IS_ENABLED(CONFIG_IPV6)	case htons(ETH_P_IPV6): {		struct ipv6hdr *ip6h; 		if (unlikely(!pskb_may_pull(skb, sizeof(*ip6h))))			return NULL; 		ip6h = ipv6_hdr(skb);		if (ip6h->version != 6)			return NULL; 		*type = IPVL_IPV6;		lyr3h = ip6h;		/* Only Neighbour Solicitation pkts need different treatment */		if (ipv6_addr_any(&ip6h->saddr) &&		    ip6h->nexthdr == NEXTHDR_ICMP) {			struct icmp6hdr	*icmph; 			if (unlikely(!pskb_may_pull(skb, sizeof(*ip6h) + sizeof(*icmph))))				return NULL; 			ip6h = ipv6_hdr(skb);			icmph = (struct icmp6hdr *)(ip6h + 1); 			if (icmph->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {				/* Need to access the ipv6 address in body */				if (unlikely(!pskb_may_pull(skb, sizeof(*ip6h) + sizeof(*icmph)						+ sizeof(struct in6_addr))))					return NULL; 				ip6h = ipv6_hdr(skb);				icmph = (struct icmp6hdr *)(ip6h + 1);			} 			*type = IPVL_ICMPV6;			lyr3h = icmph;		}		break;	}#endif	default:		return NULL;	} 	return lyr3h;} unsigned int ipvlan_mac_hash(const unsigned char *addr){	u32 hash = jhash_1word(get_unaligned((u32 *)(addr + 2)),			       ipvlan_jhash_secret); 	return hash & IPVLAN_MAC_FILTER_MASK;} void ipvlan_process_multicast(struct work_struct *work){	struct ipvl_port *port = container_of(work, struct ipvl_port, wq);	struct ethhdr *ethh;	struct ipvl_dev *ipvlan;	struct sk_buff *skb, *nskb;	struct sk_buff_head list;	unsigned int len;	unsigned int mac_hash;	int ret;	u8 pkt_type;	bool tx_pkt; 	__skb_queue_head_init(&list); 	spin_lock_bh(&port->backlog.lock);	skb_queue_splice_tail_init(&port->backlog, &list);	spin_unlock_bh(&port->backlog.lock); 	while ((skb = __skb_dequeue(&list)) != NULL) {		struct net_device *dev = skb->dev;		bool consumed = false; 		ethh = eth_hdr(skb);		tx_pkt = IPVL_SKB_CB(skb)->tx_pkt;		mac_hash = ipvlan_mac_hash(ethh->h_dest); 		if (ether_addr_equal(ethh->h_dest, port->dev->broadcast))			pkt_type = PACKET_BROADCAST;		else			pkt_type = PACKET_MULTICAST; 		rcu_read_lock();		list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) {			if (tx_pkt && (ipvlan->dev == skb->dev))				continue;			if (!test_bit(mac_hash, ipvlan->mac_filters))				continue;			if (!(ipvlan->dev->flags & IFF_UP))				continue;			ret = NET_RX_DROP;			len = skb->len + ETH_HLEN;			nskb = skb_clone(skb, GFP_ATOMIC);			local_bh_disable();			if (nskb) {				consumed = true;				nskb->pkt_type = pkt_type;				nskb->dev = ipvlan->dev;				if (tx_pkt)					ret = dev_forward_skb(ipvlan->dev, nskb);				else					ret = netif_receive_skb(nskb);			}			ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true);			local_bh_enable();		}		rcu_read_unlock(); 		if (tx_pkt) {			/* If the packet originated here, send it out. */			skb->dev = port->dev;			skb->pkt_type = pkt_type;			dev_queue_xmit(skb);		} else {			if (consumed)				consume_skb(skb);			else				kfree_skb(skb);		}		dev_put(dev);		cond_resched();	}} static void ipvlan_skb_crossing_ns(struct sk_buff *skb, struct net_device *dev){	bool xnet = true; 	if (dev)		xnet = !net_eq(dev_net(skb->dev), dev_net(dev)); 	skb_scrub_packet(skb, xnet);	if (dev)		skb->dev = dev;} static int ipvlan_rcv_frame(struct ipvl_addr *addr, struct sk_buff **pskb,			    bool local){	struct ipvl_dev *ipvlan = addr->master;	struct net_device *dev = ipvlan->dev;	unsigned int len;	rx_handler_result_t ret = RX_HANDLER_CONSUMED;	bool success = false;	struct sk_buff *skb = *pskb; 	len = skb->len + ETH_HLEN;	/* Only packets exchanged between two local slaves need to have	 * device-up check as well as skb-share check.	 */	if (local) {		if (unlikely(!(dev->flags & IFF_UP))) {			kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);			goto out;		} 		skb = skb_share_check(skb, GFP_ATOMIC);		if (!skb)			goto out; 		*pskb = skb;	} 	if (local) {		skb->pkt_type = PACKET_HOST;		if (dev_forward_skb(ipvlan->dev, skb) == NET_RX_SUCCESS)			success = true;	} else {		skb->dev = dev;		ret = RX_HANDLER_ANOTHER;		success = true;	} out:	ipvlan_count_rx(ipvlan, len, success, false);	return ret;} struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port, void *lyr3h,				     int addr_type, bool use_dest){	struct ipvl_addr *addr = NULL;#if IS_ENABLED(CONFIG_IPV6)	struct in6_addr *i6addr;#endif	__be32 addr4; 	switch (addr_type) {#if IS_ENABLED(CONFIG_IPV6)	case IPVL_IPV6: {		struct ipv6hdr *ip6h; 		ip6h = (struct ipv6hdr *)lyr3h;		i6addr = use_dest ? &ip6h->daddr : &ip6h->saddr;lookup6:		addr = ipvlan_ht_addr_lookup6(port, i6addr);		break;	}	case IPVL_ICMPV6: {		struct nd_msg *ndmh; 		/* Make sure that the NeighborSolicitation ICMPv6 packets		 * are handled to avoid DAD issue.		 */		ndmh = (struct nd_msg *)lyr3h;		if (ndmh->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {			i6addr = &ndmh->target;			goto lookup6;		}		break;	}#endif	case IPVL_IPV4: {		struct iphdr *ip4h; 		ip4h = (struct iphdr *)lyr3h;		addr4 = use_dest ? ip4h->daddr : ip4h->saddr;lookup4:		addr = ipvlan_ht_addr_lookup4(port, addr4);		break;	}	case IPVL_ARP: {		struct arphdr *arph;		unsigned char *arp_ptr; 		arph = (struct arphdr *)lyr3h;		arp_ptr = (unsigned char *)(arph + 1);		if (use_dest)			arp_ptr += (2 * port->dev->addr_len) + 4;		else			arp_ptr += port->dev->addr_len; 		addr4 = get_unaligned((__be32 *)arp_ptr);		goto lookup4;	}	} 	return addr;} static noinline_for_stack int ipvlan_process_v4_outbound(struct sk_buff *skb){	struct net_device *dev = skb->dev;	struct net *net = dev_net(dev);	int err, ret = NET_XMIT_DROP;	const struct iphdr *ip4h;	struct rtable *rt;	struct flowi4 fl4 = {		.flowi4_oif = dev->ifindex,		.flowi4_flags = FLOWI_FLAG_ANYSRC,		.flowi4_mark = skb->mark,	}; 	if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))		goto err; 	ip4h = ip_hdr(skb);	fl4.daddr = ip4h->daddr;	fl4.saddr = ip4h->saddr;	fl4.flowi4_dscp = ip4h_dscp(ip4h); 	rt = ip_route_output_flow(net, &fl4, NULL);	if (IS_ERR(rt))		goto err; 	if (rt->rt_type != RTN_UNICAST && rt->rt_type != RTN_LOCAL) {		ip_rt_put(rt);		goto err;	}	skb_dst_set(skb, &rt->dst); 	memset(IPCB(skb), 0, sizeof(*IPCB(skb))); 	err = ip_local_out(net, NULL, skb);	if (unlikely(net_xmit_eval(err)))		DEV_STATS_INC(dev, tx_errors);	else		ret = NET_XMIT_SUCCESS;	goto out;err:	DEV_STATS_INC(dev, tx_errors);	kfree_skb(skb);out:	return ret;} #if IS_ENABLED(CONFIG_IPV6) static noinline_for_stack intipvlan_route_v6_outbound(struct net_device *dev, struct sk_buff *skb){	const struct ipv6hdr *ip6h = ipv6_hdr(skb);	struct flowi6 fl6 = {		.flowi6_oif = dev->ifindex,		.daddr = ip6h->daddr,		.saddr = ip6h->saddr,		.flowi6_flags = FLOWI_FLAG_ANYSRC,		.flowlabel = ip6_flowinfo(ip6h),		.flowi6_mark = skb->mark,		.flowi6_proto = ip6h->nexthdr,	};	struct dst_entry *dst;	int err; 	dst = ip6_route_output(dev_net(dev), NULL, &fl6);	err = dst->error;	if (err) {		dst_release(dst);		return err;	}	skb_dst_set(skb, dst);	return 0;}

Lines 1–500 of 804.