2081 lines · 54.9 KB · C
RawDownload
/* * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include "flow.h"#include "datapath.h"#include <linux/uaccess.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/if_ether.h>#include <linux/if_vlan.h>#include <net/llc_pdu.h>#include <linux/kernel.h>#include <linux/jhash.h>#include <linux/jiffies.h>#include <linux/llc.h>#include <linux/module.h>#include <linux/in.h>#include <linux/rcupdate.h>#include <linux/if_arp.h>#include <linux/ip.h>#include <linux/ipv6.h>#include <linux/sctp.h>#include <linux/tcp.h>#include <linux/udp.h>#include <linux/icmp.h>#include <linux/icmpv6.h>#include <linux/rculist.h>#include <net/ip.h>#include <net/ip_tunnels.h>#include <net/ipv6.h>#include <net/ndisc.h> static struct kmem_cache *flow_cache; static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask,		struct sw_flow_key_range *range, u8 val); static void update_range__(struct sw_flow_match *match,			  size_t offset, size_t size, bool is_mask){	struct sw_flow_key_range *range = NULL;	size_t start = rounddown(offset, sizeof(long));	size_t end = roundup(offset + size, sizeof(long)); 	if (!is_mask)		range = &match->range;	else if (match->mask)		range = &match->mask->range; 	if (!range)		return; 	if (range->start == range->end) {		range->start = start;		range->end = end;		return;	} 	if (range->start > start)		range->start = start; 	if (range->end < end)		range->end = end;} #define SW_FLOW_KEY_PUT(match, field, value, is_mask) \	do { \		update_range__(match, offsetof(struct sw_flow_key, field),  \				     sizeof((match)->key->field), is_mask); \		if (is_mask) {						    \			if ((match)->mask)				    \				(match)->mask->key.field = value;	    \		} else {                                                    \			(match)->key->field = value;		            \		}                                                           \	} while (0) #define SW_FLOW_KEY_MEMCPY(match, field, value_p, len, is_mask) \	do { \		update_range__(match, offsetof(struct sw_flow_key, field),  \				len, is_mask);                              \		if (is_mask) {						    \			if ((match)->mask)				    \				memcpy(&(match)->mask->key.field, value_p, len);\		} else {                                                    \			memcpy(&(match)->key->field, value_p, len);         \		}                                                           \	} while (0) static u16 range_n_bytes(const struct sw_flow_key_range *range){	return range->end - range->start;} void ovs_match_init(struct sw_flow_match *match,		    struct sw_flow_key *key,		    struct sw_flow_mask *mask){	memset(match, 0, sizeof(*match));	match->key = key;	match->mask = mask; 	memset(key, 0, sizeof(*key)); 	if (mask) {		memset(&mask->key, 0, sizeof(mask->key));		mask->range.start = mask->range.end = 0;	}} static bool ovs_match_validate(const struct sw_flow_match *match,		u64 key_attrs, u64 mask_attrs){	u64 key_expected = 1 << OVS_KEY_ATTR_ETHERNET;	u64 mask_allowed = key_attrs;  /* At most allow all key attributes */ 	/* The following mask attributes allowed only if they	 * pass the validation tests. */	mask_allowed &= ~((1 << OVS_KEY_ATTR_IPV4)			| (1 << OVS_KEY_ATTR_IPV6)			| (1 << OVS_KEY_ATTR_TCP)			| (1 << OVS_KEY_ATTR_UDP)			| (1 << OVS_KEY_ATTR_SCTP)			| (1 << OVS_KEY_ATTR_ICMP)			| (1 << OVS_KEY_ATTR_ICMPV6)			| (1 << OVS_KEY_ATTR_ARP)			| (1 << OVS_KEY_ATTR_ND)); 	/* Always allowed mask fields. */	mask_allowed |= ((1 << OVS_KEY_ATTR_TUNNEL)		       | (1 << OVS_KEY_ATTR_IN_PORT)		       | (1 << OVS_KEY_ATTR_ETHERTYPE)); 	/* Check key attributes. */	if (match->key->eth.type == htons(ETH_P_ARP)			|| match->key->eth.type == htons(ETH_P_RARP)) {		key_expected |= 1 << OVS_KEY_ATTR_ARP;		if (match->mask && (match->mask->key.eth.type == htons(0xffff)))			mask_allowed |= 1 << OVS_KEY_ATTR_ARP;	} 	if (match->key->eth.type == htons(ETH_P_IP)) {		key_expected |= 1 << OVS_KEY_ATTR_IPV4;		if (match->mask && (match->mask->key.eth.type == htons(0xffff)))			mask_allowed |= 1 << OVS_KEY_ATTR_IPV4; 		if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {			if (match->key->ip.proto == IPPROTO_UDP) {				key_expected |= 1 << OVS_KEY_ATTR_UDP;				if (match->mask && (match->mask->key.ip.proto == 0xff))					mask_allowed |= 1 << OVS_KEY_ATTR_UDP;			} 			if (match->key->ip.proto == IPPROTO_SCTP) {				key_expected |= 1 << OVS_KEY_ATTR_SCTP;				if (match->mask && (match->mask->key.ip.proto == 0xff))					mask_allowed |= 1 << OVS_KEY_ATTR_SCTP;			} 			if (match->key->ip.proto == IPPROTO_TCP) {				key_expected |= 1 << OVS_KEY_ATTR_TCP;				if (match->mask && (match->mask->key.ip.proto == 0xff))					mask_allowed |= 1 << OVS_KEY_ATTR_TCP;			} 			if (match->key->ip.proto == IPPROTO_ICMP) {				key_expected |= 1 << OVS_KEY_ATTR_ICMP;				if (match->mask && (match->mask->key.ip.proto == 0xff))					mask_allowed |= 1 << OVS_KEY_ATTR_ICMP;			}		}	} 	if (match->key->eth.type == htons(ETH_P_IPV6)) {		key_expected |= 1 << OVS_KEY_ATTR_IPV6;		if (match->mask && (match->mask->key.eth.type == htons(0xffff)))			mask_allowed |= 1 << OVS_KEY_ATTR_IPV6; 		if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {			if (match->key->ip.proto == IPPROTO_UDP) {				key_expected |= 1 << OVS_KEY_ATTR_UDP;				if (match->mask && (match->mask->key.ip.proto == 0xff))					mask_allowed |= 1 << OVS_KEY_ATTR_UDP;			} 			if (match->key->ip.proto == IPPROTO_SCTP) {				key_expected |= 1 << OVS_KEY_ATTR_SCTP;				if (match->mask && (match->mask->key.ip.proto == 0xff))					mask_allowed |= 1 << OVS_KEY_ATTR_SCTP;			} 			if (match->key->ip.proto == IPPROTO_TCP) {				key_expected |= 1 << OVS_KEY_ATTR_TCP;				if (match->mask && (match->mask->key.ip.proto == 0xff))					mask_allowed |= 1 << OVS_KEY_ATTR_TCP;			} 			if (match->key->ip.proto == IPPROTO_ICMPV6) {				key_expected |= 1 << OVS_KEY_ATTR_ICMPV6;				if (match->mask && (match->mask->key.ip.proto == 0xff))					mask_allowed |= 1 << OVS_KEY_ATTR_ICMPV6; 				if (match->key->ipv6.tp.src ==						htons(NDISC_NEIGHBOUR_SOLICITATION) ||				    match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {					key_expected |= 1 << OVS_KEY_ATTR_ND;					if (match->mask && (match->mask->key.ipv6.tp.src == htons(0xffff)))						mask_allowed |= 1 << OVS_KEY_ATTR_ND;				}			}		}	} 	if ((key_attrs & key_expected) != key_expected) {		/* Key attributes check failed. */		OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n",				key_attrs, key_expected);		return false;	} 	if ((mask_attrs & mask_allowed) != mask_attrs) {		/* Mask attributes check failed. */		OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n",				mask_attrs, mask_allowed);		return false;	} 	return true;} static int check_header(struct sk_buff *skb, int len){	if (unlikely(skb->len < len))		return -EINVAL;	if (unlikely(!pskb_may_pull(skb, len)))		return -ENOMEM;	return 0;} static bool arphdr_ok(struct sk_buff *skb){	return pskb_may_pull(skb, skb_network_offset(skb) +				  sizeof(struct arp_eth_header));} static int check_iphdr(struct sk_buff *skb){	unsigned int nh_ofs = skb_network_offset(skb);	unsigned int ip_len;	int err; 	err = check_header(skb, nh_ofs + sizeof(struct iphdr));	if (unlikely(err))		return err; 	ip_len = ip_hdrlen(skb);	if (unlikely(ip_len < sizeof(struct iphdr) ||		     skb->len < nh_ofs + ip_len))		return -EINVAL; 	skb_set_transport_header(skb, nh_ofs + ip_len);	return 0;} static bool tcphdr_ok(struct sk_buff *skb){	int th_ofs = skb_transport_offset(skb);	int tcp_len; 	if (unlikely(!pskb_may_pull(skb, th_ofs + sizeof(struct tcphdr))))		return false; 	tcp_len = tcp_hdrlen(skb);	if (unlikely(tcp_len < sizeof(struct tcphdr) ||		     skb->len < th_ofs + tcp_len))		return false; 	return true;} static bool udphdr_ok(struct sk_buff *skb){	return pskb_may_pull(skb, skb_transport_offset(skb) +				  sizeof(struct udphdr));} static bool sctphdr_ok(struct sk_buff *skb){	return pskb_may_pull(skb, skb_transport_offset(skb) +				  sizeof(struct sctphdr));} static bool icmphdr_ok(struct sk_buff *skb){	return pskb_may_pull(skb, skb_transport_offset(skb) +				  sizeof(struct icmphdr));} u64 ovs_flow_used_time(unsigned long flow_jiffies){	struct timespec cur_ts;	u64 cur_ms, idle_ms; 	ktime_get_ts(&cur_ts);	idle_ms = jiffies_to_msecs(jiffies - flow_jiffies);	cur_ms = (u64)cur_ts.tv_sec * MSEC_PER_SEC +		 cur_ts.tv_nsec / NSEC_PER_MSEC; 	return cur_ms - idle_ms;} static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key){	unsigned int nh_ofs = skb_network_offset(skb);	unsigned int nh_len;	int payload_ofs;	struct ipv6hdr *nh;	uint8_t nexthdr;	__be16 frag_off;	int err; 	err = check_header(skb, nh_ofs + sizeof(*nh));	if (unlikely(err))		return err; 	nh = ipv6_hdr(skb);	nexthdr = nh->nexthdr;	payload_ofs = (u8 *)(nh + 1) - skb->data; 	key->ip.proto = NEXTHDR_NONE;	key->ip.tos = ipv6_get_dsfield(nh);	key->ip.ttl = nh->hop_limit;	key->ipv6.label = *(__be32 *)nh & htonl(IPV6_FLOWINFO_FLOWLABEL);	key->ipv6.addr.src = nh->saddr;	key->ipv6.addr.dst = nh->daddr; 	payload_ofs = ipv6_skip_exthdr(skb, payload_ofs, &nexthdr, &frag_off);	if (unlikely(payload_ofs < 0))		return -EINVAL; 	if (frag_off) {		if (frag_off & htons(~0x7))			key->ip.frag = OVS_FRAG_TYPE_LATER;		else			key->ip.frag = OVS_FRAG_TYPE_FIRST;	} 	nh_len = payload_ofs - nh_ofs;	skb_set_transport_header(skb, nh_ofs + nh_len);	key->ip.proto = nexthdr;	return nh_len;} static bool icmp6hdr_ok(struct sk_buff *skb){	return pskb_may_pull(skb, skb_transport_offset(skb) +				  sizeof(struct icmp6hdr));} void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src,		       const struct sw_flow_mask *mask){	const long *m = (long *)((u8 *)&mask->key + mask->range.start);	const long *s = (long *)((u8 *)src + mask->range.start);	long *d = (long *)((u8 *)dst + mask->range.start);	int i; 	/* The memory outside of the 'mask->range' are not set since	 * further operations on 'dst' only uses contents within	 * 'mask->range'.	 */	for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long))		*d++ = *s++ & *m++;} #define TCP_FLAGS_OFFSET 13#define TCP_FLAG_MASK 0x3f void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb){	u8 tcp_flags = 0; 	if ((flow->key.eth.type == htons(ETH_P_IP) ||	     flow->key.eth.type == htons(ETH_P_IPV6)) &&	    flow->key.ip.proto == IPPROTO_TCP &&	    likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) {		u8 *tcp = (u8 *)tcp_hdr(skb);		tcp_flags = *(tcp + TCP_FLAGS_OFFSET) & TCP_FLAG_MASK;	} 	spin_lock(&flow->lock);	flow->used = jiffies;	flow->packet_count++;	flow->byte_count += skb->len;	flow->tcp_flags |= tcp_flags;	spin_unlock(&flow->lock);} struct sw_flow_actions *ovs_flow_actions_alloc(int size){	struct sw_flow_actions *sfa; 	if (size > MAX_ACTIONS_BUFSIZE)		return ERR_PTR(-EINVAL); 	sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL);	if (!sfa)		return ERR_PTR(-ENOMEM); 	sfa->actions_len = 0;	return sfa;} struct sw_flow *ovs_flow_alloc(void){	struct sw_flow *flow; 	flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);	if (!flow)		return ERR_PTR(-ENOMEM); 	spin_lock_init(&flow->lock);	flow->sf_acts = NULL;	flow->mask = NULL; 	return flow;} static struct hlist_head *find_bucket(struct flow_table *table, u32 hash){	hash = jhash_1word(hash, table->hash_seed);	return flex_array_get(table->buckets,				(hash & (table->n_buckets - 1)));} static struct flex_array *alloc_buckets(unsigned int n_buckets){	struct flex_array *buckets;	int i, err; 	buckets = flex_array_alloc(sizeof(struct hlist_head),				   n_buckets, GFP_KERNEL);	if (!buckets)		return NULL; 	err = flex_array_prealloc(buckets, 0, n_buckets, GFP_KERNEL);	if (err) {		flex_array_free(buckets);		return NULL;	} 	for (i = 0; i < n_buckets; i++)		INIT_HLIST_HEAD((struct hlist_head *)					flex_array_get(buckets, i)); 	return buckets;} static void free_buckets(struct flex_array *buckets){	flex_array_free(buckets);} static struct flow_table *__flow_tbl_alloc(int new_size){	struct flow_table *table = kmalloc(sizeof(*table), GFP_KERNEL); 	if (!table)		return NULL; 	table->buckets = alloc_buckets(new_size); 	if (!table->buckets) {		kfree(table);		return NULL;	}	table->n_buckets = new_size;	table->count = 0;	table->node_ver = 0;	table->keep_flows = false;	get_random_bytes(&table->hash_seed, sizeof(u32));	table->mask_list = NULL; 	return table;}

Lines 1–500 of 2081.