2500 lines · 60.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (c) 2007-2014 Nicira, Inc. */ #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 <net/genetlink.h>#include <net/net_namespace.h>#include <net/netns/generic.h> #include "datapath.h"#include "flow.h"#include "flow_table.h"#include "flow_netlink.h"#include "meter.h"#include "vport-internal_dev.h"#include "vport-netdev.h" unsigned int ovs_net_id __read_mostly; static struct genl_family dp_packet_genl_family;static struct genl_family dp_flow_genl_family;static struct genl_family dp_datapath_genl_family; static const struct nla_policy flow_policy[]; static const struct genl_multicast_group ovs_dp_flow_multicast_group = {	.name = OVS_FLOW_MCGROUP,}; static const struct genl_multicast_group ovs_dp_datapath_multicast_group = {	.name = OVS_DATAPATH_MCGROUP,}; static const struct genl_multicast_group ovs_dp_vport_multicast_group = {	.name = OVS_VPORT_MCGROUP,}; /* Check if need to build a reply message. * OVS userspace sets the NLM_F_ECHO flag if it needs the reply. */static bool ovs_must_notify(struct genl_family *family, struct genl_info *info,			    unsigned int group){	return info->nlhdr->nlmsg_flags & NLM_F_ECHO ||	       genl_has_listeners(family, genl_info_net(info), group);} static void ovs_notify(struct genl_family *family,		       struct sk_buff *skb, struct genl_info *info){	genl_notify(family, skb, info, 0, GFP_KERNEL);} /** * DOC: Locking: * * All writes e.g. Writes to device state (add/remove datapath, port, set * operations on vports, etc.), Writes to other state (flow table * modifications, set miscellaneous datapath parameters, etc.) are protected * by ovs_lock. * * 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. * * The RTNL lock nests inside ovs_mutex. */ static DEFINE_MUTEX(ovs_mutex); void ovs_lock(void){	mutex_lock(&ovs_mutex);} void ovs_unlock(void){	mutex_unlock(&ovs_mutex);} #ifdef CONFIG_LOCKDEPint lockdep_ovsl_is_held(void){	if (debug_locks)		return lockdep_is_held(&ovs_mutex);	else		return 1;}#endif static struct vport *new_vport(const struct vport_parms *);static int queue_gso_packets(struct datapath *dp, struct sk_buff *,			     const struct sw_flow_key *,			     const struct dp_upcall_info *,			     uint32_t cutlen);static int queue_userspace_packet(struct datapath *dp, struct sk_buff *,				  const struct sw_flow_key *,				  const struct dp_upcall_info *,				  uint32_t cutlen); /* Must be called with rcu_read_lock or ovs_mutex. */const char *ovs_dp_name(const struct datapath *dp){	struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL);	return ovs_vport_name(vport);} static int get_dpifindex(const struct datapath *dp){	struct vport *local;	int ifindex; 	rcu_read_lock(); 	local = ovs_vport_rcu(dp, OVSP_LOCAL);	if (local)		ifindex = local->dev->ifindex;	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(&dp->table);	free_percpu(dp->stats_percpu);	kfree(dp->ports);	ovs_meters_exit(dp);	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)];} /* Called with ovs_mutex or RCU read lock. */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 ovs_mutex. */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;} void ovs_dp_detach_port(struct vport *p){	ASSERT_OVSL(); 	/* 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_packet(struct sk_buff *skb, struct sw_flow_key *key){	const struct vport *p = OVS_CB(skb)->input_vport;	struct datapath *dp = p->dp;	struct sw_flow *flow;	struct sw_flow_actions *sf_acts;	struct dp_stats_percpu *stats;	u64 *stats_counter;	u32 n_mask_hit; 	stats = this_cpu_ptr(dp->stats_percpu); 	/* Look up flow. */	flow = ovs_flow_tbl_lookup_stats(&dp->table, key, &n_mask_hit);	if (unlikely(!flow)) {		struct dp_upcall_info upcall;		int error; 		memset(&upcall, 0, sizeof(upcall));		upcall.cmd = OVS_PACKET_CMD_MISS;		upcall.portid = ovs_vport_find_upcall_portid(p, skb);		upcall.mru = OVS_CB(skb)->mru;		error = ovs_dp_upcall(dp, skb, key, &upcall, 0);		if (unlikely(error))			kfree_skb(skb);		else			consume_skb(skb);		stats_counter = &stats->n_missed;		goto out;	} 	ovs_flow_stats_update(flow, key->tp.flags, skb);	sf_acts = rcu_dereference(flow->sf_acts);	ovs_execute_actions(dp, skb, sf_acts, key); 	stats_counter = &stats->n_hit; out:	/* Update datapath statistics. */	u64_stats_update_begin(&stats->syncp);	(*stats_counter)++;	stats->n_mask_hit += n_mask_hit;	u64_stats_update_end(&stats->syncp);} int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,		  const struct sw_flow_key *key,		  const struct dp_upcall_info *upcall_info,		  uint32_t cutlen){	struct dp_stats_percpu *stats;	int err; 	if (upcall_info->portid == 0) {		err = -ENOTCONN;		goto err;	} 	if (!skb_is_gso(skb))		err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);	else		err = queue_gso_packets(dp, skb, key, upcall_info, cutlen);	if (err)		goto err; 	return 0; err:	stats = this_cpu_ptr(dp->stats_percpu); 	u64_stats_update_begin(&stats->syncp);	stats->n_lost++;	u64_stats_update_end(&stats->syncp); 	return err;} static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,			     const struct sw_flow_key *key,			     const struct dp_upcall_info *upcall_info,				 uint32_t cutlen){	unsigned int gso_type = skb_shinfo(skb)->gso_type;	struct sw_flow_key later_key;	struct sk_buff *segs, *nskb;	int err; 	BUILD_BUG_ON(sizeof(*OVS_CB(skb)) > SKB_SGO_CB_OFFSET);	segs = __skb_gso_segment(skb, NETIF_F_SG, false);	if (IS_ERR(segs))		return PTR_ERR(segs);	if (segs == NULL)		return -EINVAL; 	if (gso_type & SKB_GSO_UDP) {		/* The initial flow key extracted by ovs_flow_key_extract()		 * in this case is for a first fragment, so we need to		 * properly mark later fragments.		 */		later_key = *key;		later_key.ip.frag = OVS_FRAG_TYPE_LATER;	} 	/* Queue all of the segments. */	skb = segs;	do {		if (gso_type & SKB_GSO_UDP && skb != segs)			key = &later_key; 		err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);		if (err)			break; 	} 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 size_t upcall_msg_size(const struct dp_upcall_info *upcall_info,			      unsigned int hdrlen, int actions_attrlen){	size_t size = NLMSG_ALIGN(sizeof(struct ovs_header))		+ nla_total_size(hdrlen) /* OVS_PACKET_ATTR_PACKET */		+ nla_total_size(ovs_key_attr_size()) /* OVS_PACKET_ATTR_KEY */		+ nla_total_size(sizeof(unsigned int)); /* OVS_PACKET_ATTR_LEN */ 	/* OVS_PACKET_ATTR_USERDATA */	if (upcall_info->userdata)		size += NLA_ALIGN(upcall_info->userdata->nla_len); 	/* OVS_PACKET_ATTR_EGRESS_TUN_KEY */	if (upcall_info->egress_tun_info)		size += nla_total_size(ovs_tun_key_attr_size()); 	/* OVS_PACKET_ATTR_ACTIONS */	if (upcall_info->actions_len)		size += nla_total_size(actions_attrlen); 	/* OVS_PACKET_ATTR_MRU */	if (upcall_info->mru)		size += nla_total_size(sizeof(upcall_info->mru)); 	return size;} static void pad_packet(struct datapath *dp, struct sk_buff *skb){	if (!(dp->user_features & OVS_DP_F_UNALIGNED)) {		size_t plen = NLA_ALIGN(skb->len) - skb->len; 		if (plen > 0)			skb_put_zero(skb, plen);	}} static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,				  const struct sw_flow_key *key,				  const struct dp_upcall_info *upcall_info,				  uint32_t cutlen){	struct ovs_header *upcall;	struct sk_buff *nskb = NULL;	struct sk_buff *user_skb = NULL; /* to be queued to userspace */	struct nlattr *nla;	size_t len;	unsigned int hlen;	int err, dp_ifindex; 	dp_ifindex = get_dpifindex(dp);	if (!dp_ifindex)		return -ENODEV; 	if (skb_vlan_tag_present(skb)) {		nskb = skb_clone(skb, GFP_ATOMIC);		if (!nskb)			return -ENOMEM; 		nskb = __vlan_hwaccel_push_inside(nskb);		if (!nskb)			return -ENOMEM; 		skb = nskb;	} 	if (nla_attr_size(skb->len) > USHRT_MAX) {		err = -EFBIG;		goto out;	} 	/* Complete checksum if needed */	if (skb->ip_summed == CHECKSUM_PARTIAL &&	    (err = skb_csum_hwoffload_help(skb, 0)))		goto out; 	/* Older versions of OVS user space enforce alignment of the last	 * Netlink attribute to NLA_ALIGNTO which would require extensive	 * padding logic. Only perform zerocopy if padding is not required.	 */	if (dp->user_features & OVS_DP_F_UNALIGNED)		hlen = skb_zerocopy_headlen(skb);	else		hlen = skb->len; 	len = upcall_msg_size(upcall_info, hlen - cutlen,			      OVS_CB(skb)->acts_origlen);	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);	if (!upcall) {		err = -EINVAL;		goto out;	}	upcall->dp_ifindex = dp_ifindex; 	err = ovs_nla_put_key(key, key, OVS_PACKET_ATTR_KEY, false, user_skb);	if (err)		goto out; 	if (upcall_info->userdata)		__nla_put(user_skb, OVS_PACKET_ATTR_USERDATA,			  nla_len(upcall_info->userdata),			  nla_data(upcall_info->userdata)); 	if (upcall_info->egress_tun_info) {		nla = nla_nest_start_noflag(user_skb,					    OVS_PACKET_ATTR_EGRESS_TUN_KEY);		if (!nla) {			err = -EMSGSIZE;			goto out;		}		err = ovs_nla_put_tunnel_info(user_skb,					      upcall_info->egress_tun_info);		if (err)			goto out; 		nla_nest_end(user_skb, nla);	} 	if (upcall_info->actions_len) {		nla = nla_nest_start_noflag(user_skb, OVS_PACKET_ATTR_ACTIONS);		if (!nla) {			err = -EMSGSIZE;			goto out;		}		err = ovs_nla_put_actions(upcall_info->actions,					  upcall_info->actions_len,					  user_skb);		if (!err)			nla_nest_end(user_skb, nla);		else			nla_nest_cancel(user_skb, nla);	} 	/* Add OVS_PACKET_ATTR_MRU */	if (upcall_info->mru) {		if (nla_put_u16(user_skb, OVS_PACKET_ATTR_MRU,				upcall_info->mru)) {			err = -ENOBUFS;			goto out;		}		pad_packet(dp, user_skb);	} 	/* Add OVS_PACKET_ATTR_LEN when packet is truncated */	if (cutlen > 0) {		if (nla_put_u32(user_skb, OVS_PACKET_ATTR_LEN,				skb->len)) {			err = -ENOBUFS;			goto out;		}		pad_packet(dp, user_skb);

Lines 1–500 of 2500.