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/ip.h> 7 8 /* 'skb' should already be pulled to nh_ofs. */ 9 int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct, 10 enum ip_conntrack_info ctinfo, u16 proto) 11 { 12 const struct nf_conntrack_helper *helper; 13 const struct nf_conn_help *help; 14 unsigned int protoff; 15 int err; 16 17 if (ctinfo == IP_CT_RELATED_REPLY) 18 return NF_ACCEPT; 19 20 help = nfct_help(ct); 21 if (!help) 22 return NF_ACCEPT; 23 24 helper = rcu_dereference(help->helper); 25 if (!helper) 26 return NF_ACCEPT; 27 28 if (helper->tuple.src.l3num != NFPROTO_UNSPEC && 29 helper->tuple.src.l3num != proto) 30 return NF_ACCEPT; 31 32 switch (proto) { 33 case NFPROTO_IPV4: 34 protoff = ip_hdrlen(skb); 35 proto = ip_hdr(skb)->protocol; 36 break; 37 case NFPROTO_IPV6: { 38 u8 nexthdr = ipv6_hdr(skb)->nexthdr; 39 __be16 frag_off; 40 int ofs; 41 42 ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, 43 &frag_off); 44 if (ofs < 0 || (frag_off & htons(~0x7)) != 0) { 45 pr_debug("proto header not found\n"); 46 return NF_ACCEPT; 47 } 48 protoff = ofs; 49 proto = nexthdr; 50 break; 51 } 52 default: 53 WARN_ONCE(1, "helper invoked on non-IP family!"); 54 return NF_DROP; 55 } 56 57 if (helper->tuple.dst.protonum != proto) 58 return NF_ACCEPT; 59 60 err = helper->help(skb, protoff, ct, ctinfo); 61 if (err != NF_ACCEPT) 62 return err; 63 64 /* Adjust seqs after helper. This is needed due to some helpers (e.g., 65 * FTP with NAT) adusting the TCP payload size when mangling IP 66 * addresses and/or port numbers in the text-based control connection. 67 */ 68 if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && 69 !nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) 70 return NF_DROP; 71 return NF_ACCEPT; 72 } 73 EXPORT_SYMBOL_GPL(nf_ct_helper); 74 75 int nf_ct_add_helper(struct nf_conn *ct, const char *name, u8 family, 76 u8 proto, bool nat, struct nf_conntrack_helper **hp) 77 { 78 struct nf_conntrack_helper *helper; 79 struct nf_conn_help *help; 80 int ret = 0; 81 82 helper = nf_conntrack_helper_try_module_get(name, family, proto); 83 if (!helper) 84 return -EINVAL; 85 86 help = nf_ct_helper_ext_add(ct, GFP_KERNEL); 87 if (!help) { 88 nf_conntrack_helper_put(helper); 89 return -ENOMEM; 90 } 91 #if IS_ENABLED(CONFIG_NF_NAT) 92 if (nat) { 93 ret = nf_nat_helper_try_module_get(name, family, proto); 94 if (ret) { 95 nf_conntrack_helper_put(helper); 96 return ret; 97 } 98 } 99 #endif 100 rcu_assign_pointer(help->helper, helper); 101 *hp = helper; 102 return ret; 103 } 104 EXPORT_SYMBOL_GPL(nf_ct_add_helper); 105 106 /* Trim the skb to the length specified by the IP/IPv6 header, 107 * removing any trailing lower-layer padding. This prepares the skb 108 * for higher-layer processing that assumes skb->len excludes padding 109 * (such as nf_ip_checksum). The caller needs to pull the skb to the 110 * network header, and ensure ip_hdr/ipv6_hdr points to valid data. 111 */ 112 int nf_ct_skb_network_trim(struct sk_buff *skb, int family) 113 { 114 unsigned int len; 115 116 switch (family) { 117 case NFPROTO_IPV4: 118 len = skb_ip_totlen(skb); 119 break; 120 case NFPROTO_IPV6: 121 len = sizeof(struct ipv6hdr) 122 + ntohs(ipv6_hdr(skb)->payload_len); 123 break; 124 default: 125 len = skb->len; 126 } 127 128 return pskb_trim_rcsum(skb, len); 129 } 130 EXPORT_SYMBOL_GPL(nf_ct_skb_network_trim); 131