File Index
Files and directories
Sign in
FILE

nf_conntrack_ovs.c

File preview with syntax highlighting.

linux-6.19.14/net/netfilter/nf_conntrack_ovs.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/* Support ct functions for openvswitch and used by OVS and TC conntrack. */
  3
  4#include <net/netfilter/nf_conntrack_helper.h>
  5#include <net/netfilter/nf_conntrack_seqadj.h>
  6#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
  7#include <net/ipv6_frag.h>
  8#include <net/ip.h>
  9#include <linux/netfilter_ipv6.h>
 10
 11/* 'skb' should already be pulled to nh_ofs. */
 12int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct,
 13		 enum ip_conntrack_info ctinfo, u16 proto)
 14{
 15	const struct nf_conntrack_helper *helper;
 16	const struct nf_conn_help *help;
 17	unsigned int protoff;
 18	int err;
 19
 20	if (ctinfo == IP_CT_RELATED_REPLY)
 21		return NF_ACCEPT;
 22
 23	help = nfct_help(ct);
 24	if (!help)
 25		return NF_ACCEPT;
 26
 27	helper = rcu_dereference(help->helper);
 28	if (!helper)
 29		return NF_ACCEPT;
 30
 31	if (helper->tuple.src.l3num != NFPROTO_UNSPEC &&
 32	    helper->tuple.src.l3num != proto)
 33		return NF_ACCEPT;
 34
 35	switch (proto) {
 36	case NFPROTO_IPV4:
 37		protoff = ip_hdrlen(skb);
 38		proto = ip_hdr(skb)->protocol;
 39		break;
 40	case NFPROTO_IPV6: {
 41		u8 nexthdr = ipv6_hdr(skb)->nexthdr;
 42		__be16 frag_off;
 43		int ofs;
 44
 45		ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
 46				       &frag_off);
 47		if (ofs < 0 || (frag_off & htons(~0x7)) != 0) {
 48			pr_debug("proto header not found\n");
 49			return NF_ACCEPT;
 50		}
 51		protoff = ofs;
 52		proto = nexthdr;
 53		break;
 54	}
 55	default:
 56		WARN_ONCE(1, "helper invoked on non-IP family!");
 57		return NF_DROP;
 58	}
 59
 60	if (helper->tuple.dst.protonum != proto)
 61		return NF_ACCEPT;
 62
 63	err = helper->help(skb, protoff, ct, ctinfo);
 64	if (err != NF_ACCEPT)
 65		return err;
 66
 67	/* Adjust seqs after helper.  This is needed due to some helpers (e.g.,
 68	 * FTP with NAT) adusting the TCP payload size when mangling IP
 69	 * addresses and/or port numbers in the text-based control connection.
 70	 */
 71	if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
 72	    !nf_ct_seq_adjust(skb, ct, ctinfo, protoff))
 73		return NF_DROP;
 74	return NF_ACCEPT;
 75}
 76EXPORT_SYMBOL_GPL(nf_ct_helper);
 77
 78int nf_ct_add_helper(struct nf_conn *ct, const char *name, u8 family,
 79		     u8 proto, bool nat, struct nf_conntrack_helper **hp)
 80{
 81	struct nf_conntrack_helper *helper;
 82	struct nf_conn_help *help;
 83	int ret = 0;
 84
 85	helper = nf_conntrack_helper_try_module_get(name, family, proto);
 86	if (!helper)
 87		return -EINVAL;
 88
 89	help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
 90	if (!help) {
 91		nf_conntrack_helper_put(helper);
 92		return -ENOMEM;
 93	}
 94#if IS_ENABLED(CONFIG_NF_NAT)
 95	if (nat) {
 96		ret = nf_nat_helper_try_module_get(name, family, proto);
 97		if (ret) {
 98			nf_conntrack_helper_put(helper);
 99			return ret;
100		}
101	}
102#endif
103	rcu_assign_pointer(help->helper, helper);
104	*hp = helper;
105	return ret;
106}
107EXPORT_SYMBOL_GPL(nf_ct_add_helper);
108
109/* Trim the skb to the length specified by the IP/IPv6 header,
110 * removing any trailing lower-layer padding. This prepares the skb
111 * for higher-layer processing that assumes skb->len excludes padding
112 * (such as nf_ip_checksum). The caller needs to pull the skb to the
113 * network header, and ensure ip_hdr/ipv6_hdr points to valid data.
114 */
115int nf_ct_skb_network_trim(struct sk_buff *skb, int family)
116{
117	unsigned int len;
118
119	switch (family) {
120	case NFPROTO_IPV4:
121		len = skb_ip_totlen(skb);
122		break;
123	case NFPROTO_IPV6:
124		len = ntohs(ipv6_hdr(skb)->payload_len);
125		if (ipv6_hdr(skb)->nexthdr == NEXTHDR_HOP) {
126			int err = nf_ip6_check_hbh_len(skb, &len);
127
128			if (err)
129				return err;
130		}
131		len += sizeof(struct ipv6hdr);
132		break;
133	default:
134		len = skb->len;
135	}
136
137	return pskb_trim_rcsum(skb, len);
138}
139EXPORT_SYMBOL_GPL(nf_ct_skb_network_trim);
140
141/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
142 * value if 'skb' is freed.
143 */
144int nf_ct_handle_fragments(struct net *net, struct sk_buff *skb,
145			   u16 zone, u8 family, u8 *proto, u16 *mru)
146{
147	int err;
148
149	if (family == NFPROTO_IPV4) {
150		enum ip_defrag_users user = IP_DEFRAG_CONNTRACK_IN + zone;
151
152		memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
153		local_bh_disable();
154		err = ip_defrag(net, skb, user);
155		local_bh_enable();
156		if (err)
157			return err;
158
159		*mru = IPCB(skb)->frag_max_size;
160#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
161	} else if (family == NFPROTO_IPV6) {
162		enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone;
163
164		memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
165		err = nf_ct_frag6_gather(net, skb, user);
166		if (err) {
167			if (err != -EINPROGRESS)
168				kfree_skb(skb);
169			return err;
170		}
171
172		*proto = ipv6_hdr(skb)->nexthdr;
173		*mru = IP6CB(skb)->frag_max_size;
174#endif
175	} else {
176		kfree_skb(skb);
177		return -EPFNOSUPPORT;
178	}
179
180	skb_clear_hash(skb);
181	skb->ignore_df = 1;
182
183	return 0;
184}
185EXPORT_SYMBOL_GPL(nf_ct_handle_fragments);
~/ Read-only file preview.