1 /* nf_nat_helper.c - generic support functions for NAT helpers 2 * 3 * (C) 2000-2002 Harald Welte <laforge@netfilter.org> 4 * (C) 2003-2006 Netfilter Core Team <coreteam@netfilter.org> 5 * (C) 2007-2012 Patrick McHardy <kaber@trash.net> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 #include <linux/module.h> 12 #include <linux/gfp.h> 13 #include <linux/types.h> 14 #include <linux/skbuff.h> 15 #include <linux/tcp.h> 16 #include <linux/udp.h> 17 #include <net/tcp.h> 18 19 #include <net/netfilter/nf_conntrack.h> 20 #include <net/netfilter/nf_conntrack_helper.h> 21 #include <net/netfilter/nf_conntrack_ecache.h> 22 #include <net/netfilter/nf_conntrack_expect.h> 23 #include <net/netfilter/nf_conntrack_seqadj.h> 24 #include <net/netfilter/nf_nat.h> 25 #include <net/netfilter/nf_nat_helper.h> 26 27 /* Frobs data inside this packet, which is linear. */ 28 static void mangle_contents(struct sk_buff *skb, 29 unsigned int dataoff, 30 unsigned int match_offset, 31 unsigned int match_len, 32 const char *rep_buffer, 33 unsigned int rep_len) 34 { 35 unsigned char *data; 36 37 SKB_LINEAR_ASSERT(skb); 38 data = skb_network_header(skb) + dataoff; 39 40 /* move post-replacement */ 41 memmove(data + match_offset + rep_len, 42 data + match_offset + match_len, 43 skb_tail_pointer(skb) - (skb_network_header(skb) + dataoff + 44 match_offset + match_len)); 45 46 /* insert data from buffer */ 47 memcpy(data + match_offset, rep_buffer, rep_len); 48 49 /* update skb info */ 50 if (rep_len > match_len) { 51 pr_debug("nf_nat_mangle_packet: Extending packet by " 52 "%u from %u bytes\n", rep_len - match_len, skb->len); 53 skb_put(skb, rep_len - match_len); 54 } else { 55 pr_debug("nf_nat_mangle_packet: Shrinking packet from " 56 "%u from %u bytes\n", match_len - rep_len, skb->len); 57 __skb_trim(skb, skb->len + rep_len - match_len); 58 } 59 60 if (nf_ct_l3num((struct nf_conn *)skb_nfct(skb)) == NFPROTO_IPV4) { 61 /* fix IP hdr checksum information */ 62 ip_hdr(skb)->tot_len = htons(skb->len); 63 ip_send_check(ip_hdr(skb)); 64 } else 65 ipv6_hdr(skb)->payload_len = 66 htons(skb->len - sizeof(struct ipv6hdr)); 67 } 68 69 /* Unusual, but possible case. */ 70 static bool enlarge_skb(struct sk_buff *skb, unsigned int extra) 71 { 72 if (skb->len + extra > 65535) 73 return false; 74 75 if (pskb_expand_head(skb, 0, extra - skb_tailroom(skb), GFP_ATOMIC)) 76 return false; 77 78 return true; 79 } 80 81 /* Generic function for mangling variable-length address changes inside 82 * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX 83 * command in FTP). 84 * 85 * Takes care about all the nasty sequence number changes, checksumming, 86 * skb enlargement, ... 87 * 88 * */ 89 bool __nf_nat_mangle_tcp_packet(struct sk_buff *skb, 90 struct nf_conn *ct, 91 enum ip_conntrack_info ctinfo, 92 unsigned int protoff, 93 unsigned int match_offset, 94 unsigned int match_len, 95 const char *rep_buffer, 96 unsigned int rep_len, bool adjust) 97 { 98 struct tcphdr *tcph; 99 int oldlen, datalen; 100 101 if (!skb_make_writable(skb, skb->len)) 102 return false; 103 104 if (rep_len > match_len && 105 rep_len - match_len > skb_tailroom(skb) && 106 !enlarge_skb(skb, rep_len - match_len)) 107 return false; 108 109 tcph = (void *)skb->data + protoff; 110 111 oldlen = skb->len - protoff; 112 mangle_contents(skb, protoff + tcph->doff*4, 113 match_offset, match_len, rep_buffer, rep_len); 114 115 datalen = skb->len - protoff; 116 117 nf_nat_csum_recalc(skb, nf_ct_l3num(ct), IPPROTO_TCP, 118 tcph, &tcph->check, datalen, oldlen); 119 120 if (adjust && rep_len != match_len) 121 nf_ct_seqadj_set(ct, ctinfo, tcph->seq, 122 (int)rep_len - (int)match_len); 123 124 return true; 125 } 126 EXPORT_SYMBOL(__nf_nat_mangle_tcp_packet); 127 128 /* Generic function for mangling variable-length address changes inside 129 * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX 130 * command in the Amanda protocol) 131 * 132 * Takes care about all the nasty sequence number changes, checksumming, 133 * skb enlargement, ... 134 * 135 * XXX - This function could be merged with nf_nat_mangle_tcp_packet which 136 * should be fairly easy to do. 137 */ 138 bool 139 nf_nat_mangle_udp_packet(struct sk_buff *skb, 140 struct nf_conn *ct, 141 enum ip_conntrack_info ctinfo, 142 unsigned int protoff, 143 unsigned int match_offset, 144 unsigned int match_len, 145 const char *rep_buffer, 146 unsigned int rep_len) 147 { 148 struct udphdr *udph; 149 int datalen, oldlen; 150 151 if (!skb_make_writable(skb, skb->len)) 152 return false; 153 154 if (rep_len > match_len && 155 rep_len - match_len > skb_tailroom(skb) && 156 !enlarge_skb(skb, rep_len - match_len)) 157 return false; 158 159 udph = (void *)skb->data + protoff; 160 161 oldlen = skb->len - protoff; 162 mangle_contents(skb, protoff + sizeof(*udph), 163 match_offset, match_len, rep_buffer, rep_len); 164 165 /* update the length of the UDP packet */ 166 datalen = skb->len - protoff; 167 udph->len = htons(datalen); 168 169 /* fix udp checksum if udp checksum was previously calculated */ 170 if (!udph->check && skb->ip_summed != CHECKSUM_PARTIAL) 171 return true; 172 173 nf_nat_csum_recalc(skb, nf_ct_l3num(ct), IPPROTO_UDP, 174 udph, &udph->check, datalen, oldlen); 175 176 return true; 177 } 178 EXPORT_SYMBOL(nf_nat_mangle_udp_packet); 179 180 /* Setup NAT on this expected conntrack so it follows master. */ 181 /* If we fail to get a free NAT slot, we'll get dropped on confirm */ 182 void nf_nat_follow_master(struct nf_conn *ct, 183 struct nf_conntrack_expect *exp) 184 { 185 struct nf_nat_range2 range; 186 187 /* This must be a fresh one. */ 188 BUG_ON(ct->status & IPS_NAT_DONE_MASK); 189 190 /* Change src to where master sends to */ 191 range.flags = NF_NAT_RANGE_MAP_IPS; 192 range.min_addr = range.max_addr 193 = ct->master->tuplehash[!exp->dir].tuple.dst.u3; 194 nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); 195 196 /* For DST manip, map port here to where it's expected. */ 197 range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); 198 range.min_proto = range.max_proto = exp->saved_proto; 199 range.min_addr = range.max_addr 200 = ct->master->tuplehash[!exp->dir].tuple.src.u3; 201 nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); 202 } 203 EXPORT_SYMBOL(nf_nat_follow_master); 204