diff options
Diffstat (limited to 'net/openvswitch/conntrack.c')
| -rw-r--r-- | net/openvswitch/conntrack.c | 251 | 
1 files changed, 19 insertions, 232 deletions
| diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index c8eaf4234b2e..c8b137649ca4 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -434,65 +434,6 @@ static int ovs_ct_set_labels(struct nf_conn *ct, struct sw_flow_key *key,  	return 0;  } -/* 'skb' should already be pulled to nh_ofs. */ -static int ovs_ct_helper(struct sk_buff *skb, u16 proto) -{ -	const struct nf_conntrack_helper *helper; -	const struct nf_conn_help *help; -	enum ip_conntrack_info ctinfo; -	unsigned int protoff; -	struct nf_conn *ct; -	int err; - -	ct = nf_ct_get(skb, &ctinfo); -	if (!ct || ctinfo == IP_CT_RELATED_REPLY) -		return NF_ACCEPT; - -	help = nfct_help(ct); -	if (!help) -		return NF_ACCEPT; - -	helper = rcu_dereference(help->helper); -	if (!helper) -		return NF_ACCEPT; - -	switch (proto) { -	case NFPROTO_IPV4: -		protoff = ip_hdrlen(skb); -		break; -	case NFPROTO_IPV6: { -		u8 nexthdr = ipv6_hdr(skb)->nexthdr; -		__be16 frag_off; -		int ofs; - -		ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, -				       &frag_off); -		if (ofs < 0 || (frag_off & htons(~0x7)) != 0) { -			pr_debug("proto header not found\n"); -			return NF_ACCEPT; -		} -		protoff = ofs; -		break; -	} -	default: -		WARN_ONCE(1, "helper invoked on non-IP family!"); -		return NF_DROP; -	} - -	err = helper->help(skb, protoff, ct, ctinfo); -	if (err != NF_ACCEPT) -		return err; - -	/* Adjust seqs after helper.  This is needed due to some helpers (e.g., -	 * FTP with NAT) adusting the TCP payload size when mangling IP -	 * addresses and/or port numbers in the text-based control connection. -	 */ -	if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && -	    !nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) -		return NF_DROP; -	return NF_ACCEPT; -} -  /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero   * value if 'skb' is freed.   */ @@ -785,147 +726,27 @@ static void ovs_nat_update_key(struct sw_flow_key *key,  	}  } -/* Modelled after nf_nat_ipv[46]_fn(). - * range is only used for new, uninitialized NAT state. - * Returns either NF_ACCEPT or NF_DROP. - */ -static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, -			      enum ip_conntrack_info ctinfo, -			      const struct nf_nat_range2 *range, -			      enum nf_nat_manip_type maniptype, struct sw_flow_key *key) -{ -	int hooknum, nh_off, err = NF_ACCEPT; - -	nh_off = skb_network_offset(skb); -	skb_pull_rcsum(skb, nh_off); - -	/* See HOOK2MANIP(). */ -	if (maniptype == NF_NAT_MANIP_SRC) -		hooknum = NF_INET_LOCAL_IN; /* Source NAT */ -	else -		hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */ - -	switch (ctinfo) { -	case IP_CT_RELATED: -	case IP_CT_RELATED_REPLY: -		if (IS_ENABLED(CONFIG_NF_NAT) && -		    skb->protocol == htons(ETH_P_IP) && -		    ip_hdr(skb)->protocol == IPPROTO_ICMP) { -			if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, -							   hooknum)) -				err = NF_DROP; -			goto push; -		} else if (IS_ENABLED(CONFIG_IPV6) && -			   skb->protocol == htons(ETH_P_IPV6)) { -			__be16 frag_off; -			u8 nexthdr = ipv6_hdr(skb)->nexthdr; -			int hdrlen = ipv6_skip_exthdr(skb, -						      sizeof(struct ipv6hdr), -						      &nexthdr, &frag_off); - -			if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { -				if (!nf_nat_icmpv6_reply_translation(skb, ct, -								     ctinfo, -								     hooknum, -								     hdrlen)) -					err = NF_DROP; -				goto push; -			} -		} -		/* Non-ICMP, fall thru to initialize if needed. */ -		fallthrough; -	case IP_CT_NEW: -		/* Seen it before?  This can happen for loopback, retrans, -		 * or local packets. -		 */ -		if (!nf_nat_initialized(ct, maniptype)) { -			/* Initialize according to the NAT action. */ -			err = (range && range->flags & NF_NAT_RANGE_MAP_IPS) -				/* Action is set up to establish a new -				 * mapping. -				 */ -				? nf_nat_setup_info(ct, range, maniptype) -				: nf_nat_alloc_null_binding(ct, hooknum); -			if (err != NF_ACCEPT) -				goto push; -		} -		break; - -	case IP_CT_ESTABLISHED: -	case IP_CT_ESTABLISHED_REPLY: -		break; - -	default: -		err = NF_DROP; -		goto push; -	} - -	err = nf_nat_packet(ct, ctinfo, hooknum, skb); -push: -	skb_push_rcsum(skb, nh_off); - -	/* Update the flow key if NAT successful. */ -	if (err == NF_ACCEPT) -		ovs_nat_update_key(key, skb, maniptype); - -	return err; -} -  /* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */  static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,  		      const struct ovs_conntrack_info *info,  		      struct sk_buff *skb, struct nf_conn *ct,  		      enum ip_conntrack_info ctinfo)  { -	enum nf_nat_manip_type maniptype; -	int err; +	int err, action = 0; -	/* Add NAT extension if not confirmed yet. */ -	if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) -		return NF_ACCEPT;   /* Can't NAT. */ +	if (!(info->nat & OVS_CT_NAT)) +		return NF_ACCEPT; +	if (info->nat & OVS_CT_SRC_NAT) +		action |= BIT(NF_NAT_MANIP_SRC); +	if (info->nat & OVS_CT_DST_NAT) +		action |= BIT(NF_NAT_MANIP_DST); -	/* Determine NAT type. -	 * Check if the NAT type can be deduced from the tracked connection. -	 * Make sure new expected connections (IP_CT_RELATED) are NATted only -	 * when committing. -	 */ -	if (info->nat & OVS_CT_NAT && ctinfo != IP_CT_NEW && -	    ct->status & IPS_NAT_MASK && -	    (ctinfo != IP_CT_RELATED || info->commit)) { -		/* NAT an established or related connection like before. */ -		if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) -			/* This is the REPLY direction for a connection -			 * for which NAT was applied in the forward -			 * direction.  Do the reverse NAT. -			 */ -			maniptype = ct->status & IPS_SRC_NAT -				? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC; -		else -			maniptype = ct->status & IPS_SRC_NAT -				? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST; -	} else if (info->nat & OVS_CT_SRC_NAT) { -		maniptype = NF_NAT_MANIP_SRC; -	} else if (info->nat & OVS_CT_DST_NAT) { -		maniptype = NF_NAT_MANIP_DST; -	} else { -		return NF_ACCEPT; /* Connection is not NATed. */ -	} -	err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype, key); - -	if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { -		if (ct->status & IPS_SRC_NAT) { -			if (maniptype == NF_NAT_MANIP_SRC) -				maniptype = NF_NAT_MANIP_DST; -			else -				maniptype = NF_NAT_MANIP_SRC; - -			err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, -						 maniptype, key); -		} else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { -			err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL, -						 NF_NAT_MANIP_SRC, key); -		} -	} +	err = nf_ct_nat(skb, ct, ctinfo, &action, &info->range, info->commit); + +	if (action & BIT(NF_NAT_MANIP_SRC)) +		ovs_nat_update_key(key, skb, NF_NAT_MANIP_SRC); +	if (action & BIT(NF_NAT_MANIP_DST)) +		ovs_nat_update_key(key, skb, NF_NAT_MANIP_DST);  	return err;  } @@ -1038,7 +859,7 @@ static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key,  		 */  		if ((nf_ct_is_confirmed(ct) ? !cached || add_helper :  					      info->commit) && -		    ovs_ct_helper(skb, info->family) != NF_ACCEPT) { +		    nf_ct_helper(skb, ct, ctinfo, info->family) != NF_ACCEPT) {  			return -EINVAL;  		} @@ -1350,43 +1171,6 @@ int ovs_ct_clear(struct sk_buff *skb, struct sw_flow_key *key)  	return 0;  } -static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name, -			     const struct sw_flow_key *key, bool log) -{ -	struct nf_conntrack_helper *helper; -	struct nf_conn_help *help; -	int ret = 0; - -	helper = nf_conntrack_helper_try_module_get(name, info->family, -						    key->ip.proto); -	if (!helper) { -		OVS_NLERR(log, "Unknown helper \"%s\"", name); -		return -EINVAL; -	} - -	help = nf_ct_helper_ext_add(info->ct, GFP_KERNEL); -	if (!help) { -		nf_conntrack_helper_put(helper); -		return -ENOMEM; -	} - -#if IS_ENABLED(CONFIG_NF_NAT) -	if (info->nat) { -		ret = nf_nat_helper_try_module_get(name, info->family, -						   key->ip.proto); -		if (ret) { -			nf_conntrack_helper_put(helper); -			OVS_NLERR(log, "Failed to load \"%s\" NAT helper, error: %d", -				  name, ret); -			return ret; -		} -	} -#endif -	rcu_assign_pointer(help->helper, helper); -	info->helper = helper; -	return ret; -} -  #if IS_ENABLED(CONFIG_NF_NAT)  static int parse_nat(const struct nlattr *attr,  		     struct ovs_conntrack_info *info, bool log) @@ -1720,9 +1504,12 @@ int ovs_ct_copy_action(struct net *net, const struct nlattr *attr,  	}  	if (helper) { -		err = ovs_ct_add_helper(&ct_info, helper, key, log); -		if (err) +		err = nf_ct_add_helper(ct_info.ct, helper, ct_info.family, +				       key->ip.proto, ct_info.nat, &ct_info.helper); +		if (err) { +			OVS_NLERR(log, "Failed to add %s helper %d", helper, err);  			goto err_free_ct; +		}  	}  	err = ovs_nla_add_action(sfa, OVS_ACTION_ATTR_CT, &ct_info, |