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