2056 lines · 48.8 KB · C
RawDownload
/* * Copyright (c) 2007-2012 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 */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/init.h>#include <linux/module.h>#include <linux/if_arp.h>#include <linux/if_vlan.h>#include <linux/in.h>#include <linux/ip.h>#include <linux/jhash.h>#include <linux/delay.h>#include <linux/time.h>#include <linux/etherdevice.h>#include <linux/genetlink.h>#include <linux/kernel.h>#include <linux/kthread.h>#include <linux/mutex.h>#include <linux/percpu.h>#include <linux/rcupdate.h>#include <linux/tcp.h>#include <linux/udp.h>#include <linux/ethtool.h>#include <linux/wait.h>#include <asm/div64.h>#include <linux/highmem.h>#include <linux/netfilter_bridge.h>#include <linux/netfilter_ipv4.h>#include <linux/inetdevice.h>#include <linux/list.h>#include <linux/openvswitch.h>#include <linux/rculist.h>#include <linux/dmi.h>#include <linux/workqueue.h>#include <net/genetlink.h>#include <net/net_namespace.h>#include <net/netns/generic.h> #include "datapath.h"#include "flow.h"#include "vport-internal_dev.h" /** * struct ovs_net - Per net-namespace data for ovs. * @dps: List of datapaths to enable dumping them all out. * Protected by genl_mutex. */struct ovs_net {	struct list_head dps;}; static int ovs_net_id __read_mostly; #define REHASH_FLOW_INTERVAL (10 * 60 * HZ)static void rehash_flow_table(struct work_struct *work);static DECLARE_DELAYED_WORK(rehash_flow_wq, rehash_flow_table); /** * DOC: Locking: * * Writes to device state (add/remove datapath, port, set operations on vports, * etc.) are protected by RTNL. * * Writes to other state (flow table modifications, set miscellaneous datapath * parameters, etc.) are protected by genl_mutex.  The RTNL lock nests inside * genl_mutex. * * Reads are protected by RCU. * * There are a few special cases (mostly stats) that have their own * synchronization but they nest under all of above and don't interact with * each other. */ static struct vport *new_vport(const struct vport_parms *);static int queue_gso_packets(struct net *, int dp_ifindex, struct sk_buff *,			     const struct dp_upcall_info *);static int queue_userspace_packet(struct net *, int dp_ifindex,				  struct sk_buff *,				  const struct dp_upcall_info *); /* Must be called with rcu_read_lock, genl_mutex, or RTNL lock. */static struct datapath *get_dp(struct net *net, int dp_ifindex){	struct datapath *dp = NULL;	struct net_device *dev; 	rcu_read_lock();	dev = dev_get_by_index_rcu(net, dp_ifindex);	if (dev) {		struct vport *vport = ovs_internal_dev_get_vport(dev);		if (vport)			dp = vport->dp;	}	rcu_read_unlock(); 	return dp;} /* Must be called with rcu_read_lock or RTNL lock. */const char *ovs_dp_name(const struct datapath *dp){	struct vport *vport = ovs_vport_rtnl_rcu(dp, OVSP_LOCAL);	return vport->ops->get_name(vport);} static int get_dpifindex(struct datapath *dp){	struct vport *local;	int ifindex; 	rcu_read_lock(); 	local = ovs_vport_rcu(dp, OVSP_LOCAL);	if (local)		ifindex = local->ops->get_ifindex(local);	else		ifindex = 0; 	rcu_read_unlock(); 	return ifindex;} static void destroy_dp_rcu(struct rcu_head *rcu){	struct datapath *dp = container_of(rcu, struct datapath, rcu); 	ovs_flow_tbl_destroy((__force struct flow_table *)dp->table);	free_percpu(dp->stats_percpu);	release_net(ovs_dp_get_net(dp));	kfree(dp->ports);	kfree(dp);} static struct hlist_head *vport_hash_bucket(const struct datapath *dp,					    u16 port_no){	return &dp->ports[port_no & (DP_VPORT_HASH_BUCKETS - 1)];} struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no){	struct vport *vport;	struct hlist_head *head; 	head = vport_hash_bucket(dp, port_no);	hlist_for_each_entry_rcu(vport, head, dp_hash_node) {		if (vport->port_no == port_no)			return vport;	}	return NULL;} /* Called with RTNL lock and genl_lock. */static struct vport *new_vport(const struct vport_parms *parms){	struct vport *vport; 	vport = ovs_vport_add(parms);	if (!IS_ERR(vport)) {		struct datapath *dp = parms->dp;		struct hlist_head *head = vport_hash_bucket(dp, vport->port_no); 		hlist_add_head_rcu(&vport->dp_hash_node, head);	} 	return vport;} /* Called with RTNL lock. */void ovs_dp_detach_port(struct vport *p){	ASSERT_RTNL(); 	/* First drop references to device. */	hlist_del_rcu(&p->dp_hash_node); 	/* Then destroy it. */	ovs_vport_del(p);} /* Must be called with rcu_read_lock. */void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb){	struct datapath *dp = p->dp;	struct sw_flow *flow;	struct dp_stats_percpu *stats;	struct sw_flow_key key;	u64 *stats_counter;	int error;	int key_len; 	stats = this_cpu_ptr(dp->stats_percpu); 	/* Extract flow from 'skb' into 'key'. */	error = ovs_flow_extract(skb, p->port_no, &key, &key_len);	if (unlikely(error)) {		kfree_skb(skb);		return;	} 	/* Look up flow. */	flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table), &key, key_len);	if (unlikely(!flow)) {		struct dp_upcall_info upcall; 		upcall.cmd = OVS_PACKET_CMD_MISS;		upcall.key = &key;		upcall.userdata = NULL;		upcall.portid = p->upcall_portid;		ovs_dp_upcall(dp, skb, &upcall);		consume_skb(skb);		stats_counter = &stats->n_missed;		goto out;	} 	OVS_CB(skb)->flow = flow; 	stats_counter = &stats->n_hit;	ovs_flow_used(OVS_CB(skb)->flow, skb);	ovs_execute_actions(dp, skb); out:	/* Update datapath statistics. */	u64_stats_update_begin(&stats->sync);	(*stats_counter)++;	u64_stats_update_end(&stats->sync);} static struct genl_family dp_packet_genl_family = {	.id = GENL_ID_GENERATE,	.hdrsize = sizeof(struct ovs_header),	.name = OVS_PACKET_FAMILY,	.version = OVS_PACKET_VERSION,	.maxattr = OVS_PACKET_ATTR_MAX,	.netnsok = true}; int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,		  const struct dp_upcall_info *upcall_info){	struct dp_stats_percpu *stats;	int dp_ifindex;	int err; 	if (upcall_info->portid == 0) {		err = -ENOTCONN;		goto err;	} 	dp_ifindex = get_dpifindex(dp);	if (!dp_ifindex) {		err = -ENODEV;		goto err;	} 	if (!skb_is_gso(skb))		err = queue_userspace_packet(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info);	else		err = queue_gso_packets(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info);	if (err)		goto err; 	return 0; err:	stats = this_cpu_ptr(dp->stats_percpu); 	u64_stats_update_begin(&stats->sync);	stats->n_lost++;	u64_stats_update_end(&stats->sync); 	return err;} static int queue_gso_packets(struct net *net, int dp_ifindex,			     struct sk_buff *skb,			     const struct dp_upcall_info *upcall_info){	unsigned short gso_type = skb_shinfo(skb)->gso_type;	struct dp_upcall_info later_info;	struct sw_flow_key later_key;	struct sk_buff *segs, *nskb;	int err; 	segs = __skb_gso_segment(skb, NETIF_F_SG | NETIF_F_HW_CSUM, false);	if (IS_ERR(segs))		return PTR_ERR(segs); 	/* Queue all of the segments. */	skb = segs;	do {		err = queue_userspace_packet(net, dp_ifindex, skb, upcall_info);		if (err)			break; 		if (skb == segs && gso_type & SKB_GSO_UDP) {			/* The initial flow key extracted by ovs_flow_extract()			 * in this case is for a first fragment, so we need to			 * properly mark later fragments.			 */			later_key = *upcall_info->key;			later_key.ip.frag = OVS_FRAG_TYPE_LATER; 			later_info = *upcall_info;			later_info.key = &later_key;			upcall_info = &later_info;		}	} while ((skb = skb->next)); 	/* Free all of the segments. */	skb = segs;	do {		nskb = skb->next;		if (err)			kfree_skb(skb);		else			consume_skb(skb);	} while ((skb = nskb));	return err;} static int queue_userspace_packet(struct net *net, int dp_ifindex,				  struct sk_buff *skb,				  const struct dp_upcall_info *upcall_info){	struct ovs_header *upcall;	struct sk_buff *nskb = NULL;	struct sk_buff *user_skb; /* to be queued to userspace */	struct nlattr *nla;	unsigned int len;	int err; 	if (vlan_tx_tag_present(skb)) {		nskb = skb_clone(skb, GFP_ATOMIC);		if (!nskb)			return -ENOMEM; 		nskb = __vlan_put_tag(nskb, vlan_tx_tag_get(nskb));		if (!nskb)			return -ENOMEM; 		nskb->vlan_tci = 0;		skb = nskb;	} 	if (nla_attr_size(skb->len) > USHRT_MAX) {		err = -EFBIG;		goto out;	} 	len = sizeof(struct ovs_header);	len += nla_total_size(skb->len);	len += nla_total_size(FLOW_BUFSIZE);	if (upcall_info->cmd == OVS_PACKET_CMD_ACTION)		len += nla_total_size(8); 	user_skb = genlmsg_new(len, GFP_ATOMIC);	if (!user_skb) {		err = -ENOMEM;		goto out;	} 	upcall = genlmsg_put(user_skb, 0, 0, &dp_packet_genl_family,			     0, upcall_info->cmd);	upcall->dp_ifindex = dp_ifindex; 	nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY);	ovs_flow_to_nlattrs(upcall_info->key, user_skb);	nla_nest_end(user_skb, nla); 	if (upcall_info->userdata)		nla_put_u64(user_skb, OVS_PACKET_ATTR_USERDATA,			    nla_get_u64(upcall_info->userdata)); 	nla = __nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, skb->len); 	skb_copy_and_csum_dev(skb, nla_data(nla)); 	genlmsg_end(user_skb, upcall);	err = genlmsg_unicast(net, user_skb, upcall_info->portid); out:	kfree_skb(nskb);	return err;} /* Called with genl_mutex. */static int flush_flows(struct datapath *dp){	struct flow_table *old_table;	struct flow_table *new_table; 	old_table = genl_dereference(dp->table);	new_table = ovs_flow_tbl_alloc(TBL_MIN_BUCKETS);	if (!new_table)		return -ENOMEM; 	rcu_assign_pointer(dp->table, new_table); 	ovs_flow_tbl_deferred_destroy(old_table);	return 0;} static int validate_actions(const struct nlattr *attr,				const struct sw_flow_key *key, int depth); static int validate_sample(const struct nlattr *attr,				const struct sw_flow_key *key, int depth){	const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];	const struct nlattr *probability, *actions;	const struct nlattr *a;	int rem; 	memset(attrs, 0, sizeof(attrs));	nla_for_each_nested(a, attr, rem) {		int type = nla_type(a);		if (!type || type > OVS_SAMPLE_ATTR_MAX || attrs[type])			return -EINVAL;		attrs[type] = a;	}	if (rem)		return -EINVAL; 	probability = attrs[OVS_SAMPLE_ATTR_PROBABILITY];	if (!probability || nla_len(probability) != sizeof(u32))		return -EINVAL; 	actions = attrs[OVS_SAMPLE_ATTR_ACTIONS];	if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN))		return -EINVAL;	return validate_actions(actions, key, depth + 1);} static int validate_tp_port(const struct sw_flow_key *flow_key){	if (flow_key->eth.type == htons(ETH_P_IP)) {		if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst)			return 0;	} else if (flow_key->eth.type == htons(ETH_P_IPV6)) {		if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst)			return 0;	} 	return -EINVAL;} static int validate_set(const struct nlattr *a,			const struct sw_flow_key *flow_key){	const struct nlattr *ovs_key = nla_data(a);	int key_type = nla_type(ovs_key); 	/* There can be only one key in a action */	if (nla_total_size(nla_len(ovs_key)) != nla_len(a))		return -EINVAL; 	if (key_type > OVS_KEY_ATTR_MAX ||	    nla_len(ovs_key) != ovs_key_lens[key_type])		return -EINVAL; 	switch (key_type) {	const struct ovs_key_ipv4 *ipv4_key;	const struct ovs_key_ipv6 *ipv6_key; 	case OVS_KEY_ATTR_PRIORITY:	case OVS_KEY_ATTR_SKB_MARK:	case OVS_KEY_ATTR_ETHERNET:		break; 	case OVS_KEY_ATTR_IPV4:		if (flow_key->eth.type != htons(ETH_P_IP))			return -EINVAL; 		if (!flow_key->ip.proto)			return -EINVAL; 		ipv4_key = nla_data(ovs_key);		if (ipv4_key->ipv4_proto != flow_key->ip.proto)			return -EINVAL; 		if (ipv4_key->ipv4_frag != flow_key->ip.frag)

Lines 1–500 of 2056.