1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Support ct functions for openvswitch and used by OVS and TC conntrack. */
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>
9 #include <linux/netfilter_ipv6.h>
11 /* 'skb' should already be pulled to nh_ofs. */
12 int nf_ct_helper(struct sk_buff
*skb
, struct nf_conn
*ct
,
13 enum ip_conntrack_info ctinfo
, u16 proto
)
15 const struct nf_conntrack_helper
*helper
;
16 const struct nf_conn_help
*help
;
20 if (ctinfo
== IP_CT_RELATED_REPLY
)
27 helper
= rcu_dereference(help
->helper
);
31 if (helper
->tuple
.src
.l3num
!= NFPROTO_UNSPEC
&&
32 helper
->tuple
.src
.l3num
!= proto
)
37 protoff
= ip_hdrlen(skb
);
38 proto
= ip_hdr(skb
)->protocol
;
41 u8 nexthdr
= ipv6_hdr(skb
)->nexthdr
;
45 ofs
= ipv6_skip_exthdr(skb
, sizeof(struct ipv6hdr
), &nexthdr
,
47 if (ofs
< 0 || (frag_off
& htons(~0x7)) != 0) {
48 pr_debug("proto header not found\n");
56 WARN_ONCE(1, "helper invoked on non-IP family!");
60 if (helper
->tuple
.dst
.protonum
!= proto
)
63 err
= helper
->help(skb
, protoff
, ct
, ctinfo
);
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.
71 if (test_bit(IPS_SEQ_ADJUST_BIT
, &ct
->status
) &&
72 !nf_ct_seq_adjust(skb
, ct
, ctinfo
, protoff
))
76 EXPORT_SYMBOL_GPL(nf_ct_helper
);
78 int nf_ct_add_helper(struct nf_conn
*ct
, const char *name
, u8 family
,
79 u8 proto
, bool nat
, struct nf_conntrack_helper
**hp
)
81 struct nf_conntrack_helper
*helper
;
82 struct nf_conn_help
*help
;
85 helper
= nf_conntrack_helper_try_module_get(name
, family
, proto
);
89 help
= nf_ct_helper_ext_add(ct
, GFP_KERNEL
);
91 nf_conntrack_helper_put(helper
);
94 #if IS_ENABLED(CONFIG_NF_NAT)
96 ret
= nf_nat_helper_try_module_get(name
, family
, proto
);
98 nf_conntrack_helper_put(helper
);
103 rcu_assign_pointer(help
->helper
, helper
);
107 EXPORT_SYMBOL_GPL(nf_ct_add_helper
);
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.
115 int nf_ct_skb_network_trim(struct sk_buff
*skb
, int family
)
121 len
= skb_ip_totlen(skb
);
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
);
131 len
+= sizeof(struct ipv6hdr
);
137 return pskb_trim_rcsum(skb
, len
);
139 EXPORT_SYMBOL_GPL(nf_ct_skb_network_trim
);
141 /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
142 * value if 'skb' is freed.
144 int nf_ct_handle_fragments(struct net
*net
, struct sk_buff
*skb
,
145 u16 zone
, u8 family
, u8
*proto
, u16
*mru
)
149 if (family
== NFPROTO_IPV4
) {
150 enum ip_defrag_users user
= IP_DEFRAG_CONNTRACK_IN
+ zone
;
152 memset(IPCB(skb
), 0, sizeof(struct inet_skb_parm
));
154 err
= ip_defrag(net
, skb
, user
);
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
;
164 memset(IP6CB(skb
), 0, sizeof(struct inet6_skb_parm
));
165 err
= nf_ct_frag6_gather(net
, skb
, user
);
167 if (err
!= -EINPROGRESS
)
172 *proto
= ipv6_hdr(skb
)->nexthdr
;
173 *mru
= IP6CB(skb
)->frag_max_size
;
177 return -EPFNOSUPPORT
;
185 EXPORT_SYMBOL_GPL(nf_ct_handle_fragments
);