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