1345 lines · 34.9 KB · C
RawDownload
/* * Copyright (c) 2007-2011 Nicira Networks. * * 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/tcp.h>#include <linux/udp.h>#include <linux/icmp.h>#include <linux/icmpv6.h>#include <linux/rculist.h>#include <net/ip.h>#include <net/ipv6.h>#include <net/ndisc.h> static struct kmem_cache *flow_cache; 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 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;} #define SW_FLOW_KEY_OFFSET(field)		\	(offsetof(struct sw_flow_key, field) +	\	 FIELD_SIZEOF(struct sw_flow_key, field)) static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key,			 int *key_lenp){	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; 	*key_lenp = SW_FLOW_KEY_OFFSET(ipv6.label); 	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));} #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.ip.proto == IPPROTO_TCP) {		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(const struct nlattr *actions){	int actions_len = nla_len(actions);	struct sw_flow_actions *sfa; 	/* At least DP_MAX_PORTS actions are required to be able to flood a	 * packet to every port.  Factor of 2 allows for setting VLAN tags,	 * etc. */	if (actions_len > 2 * DP_MAX_PORTS * nla_total_size(4))		return ERR_PTR(-EINVAL); 	sfa = kmalloc(sizeof(*sfa) + actions_len, GFP_KERNEL);	if (!sfa)		return ERR_PTR(-ENOMEM); 	sfa->actions_len = actions_len;	memcpy(sfa->actions, nla_data(actions), actions_len);	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; 	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);} struct flow_table *ovs_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)); 	return table;} void ovs_flow_tbl_destroy(struct flow_table *table){	int i; 	if (!table)		return; 	if (table->keep_flows)		goto skip_flows; 	for (i = 0; i < table->n_buckets; i++) {		struct sw_flow *flow;		struct hlist_head *head = flex_array_get(table->buckets, i);		struct hlist_node *node, *n;		int ver = table->node_ver; 		hlist_for_each_entry_safe(flow, node, n, head, hash_node[ver]) {			hlist_del_rcu(&flow->hash_node[ver]);			ovs_flow_free(flow);		}	} skip_flows:	free_buckets(table->buckets);	kfree(table);} static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu){	struct flow_table *table = container_of(rcu, struct flow_table, rcu); 	ovs_flow_tbl_destroy(table);} void ovs_flow_tbl_deferred_destroy(struct flow_table *table){	if (!table)		return; 	call_rcu(&table->rcu, flow_tbl_destroy_rcu_cb);} struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *last){	struct sw_flow *flow;	struct hlist_head *head;	struct hlist_node *n;	int ver;	int i; 	ver = table->node_ver;	while (*bucket < table->n_buckets) {		i = 0;		head = flex_array_get(table->buckets, *bucket);		hlist_for_each_entry_rcu(flow, n, head, hash_node[ver]) {			if (i < *last) {				i++;				continue;			}			*last = i + 1;			return flow;		}		(*bucket)++;		*last = 0;	} 	return NULL;} static void flow_table_copy_flows(struct flow_table *old, struct flow_table *new){	int old_ver;	int i; 	old_ver = old->node_ver;	new->node_ver = !old_ver; 	/* Insert in new table. */	for (i = 0; i < old->n_buckets; i++) {		struct sw_flow *flow;		struct hlist_head *head;		struct hlist_node *n; 		head = flex_array_get(old->buckets, i); 		hlist_for_each_entry(flow, n, head, hash_node[old_ver])			ovs_flow_tbl_insert(new, flow);	}	old->keep_flows = true;} static struct flow_table *__flow_tbl_rehash(struct flow_table *table, int n_buckets){	struct flow_table *new_table; 	new_table = ovs_flow_tbl_alloc(n_buckets);	if (!new_table)		return ERR_PTR(-ENOMEM); 	flow_table_copy_flows(table, new_table); 	return new_table;} struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table){	return __flow_tbl_rehash(table, table->n_buckets);} struct flow_table *ovs_flow_tbl_expand(struct flow_table *table){	return __flow_tbl_rehash(table, table->n_buckets * 2);} void ovs_flow_free(struct sw_flow *flow){	if (unlikely(!flow))		return; 	kfree((struct sf_flow_acts __force *)flow->sf_acts);	kmem_cache_free(flow_cache, flow);} /* RCU callback used by ovs_flow_deferred_free. */static void rcu_free_flow_callback(struct rcu_head *rcu){	struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu); 	ovs_flow_free(flow);} /* Schedules 'flow' to be freed after the next RCU grace period. * The caller must hold rcu_read_lock for this to be sensible. */void ovs_flow_deferred_free(struct sw_flow *flow){	call_rcu(&flow->rcu, rcu_free_flow_callback);} /* RCU callback used by ovs_flow_deferred_free_acts. */static void rcu_free_acts_callback(struct rcu_head *rcu){	struct sw_flow_actions *sf_acts = container_of(rcu,			struct sw_flow_actions, rcu);	kfree(sf_acts);} /* Schedules 'sf_acts' to be freed after the next RCU grace period. * The caller must hold rcu_read_lock for this to be sensible. */void ovs_flow_deferred_free_acts(struct sw_flow_actions *sf_acts){	call_rcu(&sf_acts->rcu, rcu_free_acts_callback);} static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key){	struct qtag_prefix {		__be16 eth_type; /* ETH_P_8021Q */		__be16 tci;	};	struct qtag_prefix *qp; 	if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16)))		return 0; 	if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) +					 sizeof(__be16))))		return -ENOMEM; 	qp = (struct qtag_prefix *) skb->data;	key->eth.tci = qp->tci | htons(VLAN_TAG_PRESENT);	__skb_pull(skb, sizeof(struct qtag_prefix)); 	return 0;} static __be16 parse_ethertype(struct sk_buff *skb){	struct llc_snap_hdr {		u8  dsap;  /* Always 0xAA */		u8  ssap;  /* Always 0xAA */		u8  ctrl;		u8  oui[3];		__be16 ethertype;	};	struct llc_snap_hdr *llc;	__be16 proto; 	proto = *(__be16 *) skb->data;	__skb_pull(skb, sizeof(__be16)); 	if (ntohs(proto) >= 1536)		return proto; 	if (skb->len < sizeof(struct llc_snap_hdr))		return htons(ETH_P_802_2); 	if (unlikely(!pskb_may_pull(skb, sizeof(struct llc_snap_hdr))))		return htons(0); 	llc = (struct llc_snap_hdr *) skb->data;	if (llc->dsap != LLC_SAP_SNAP ||	    llc->ssap != LLC_SAP_SNAP ||	    (llc->oui[0] | llc->oui[1] | llc->oui[2]) != 0)		return htons(ETH_P_802_2); 	__skb_pull(skb, sizeof(struct llc_snap_hdr));	return llc->ethertype;} static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,			int *key_lenp, int nh_len)

Lines 1–500 of 1345.