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