linux/net/sched/act_nat.c
<<
>>
Prefs
   1/*
   2 * Stateless NAT actions
   3 *
   4 * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License as published by the Free
   8 * Software Foundation; either version 2 of the License, or (at your option)
   9 * any later version.
  10 */
  11
  12#include <linux/errno.h>
  13#include <linux/init.h>
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/netfilter.h>
  17#include <linux/rtnetlink.h>
  18#include <linux/skbuff.h>
  19#include <linux/slab.h>
  20#include <linux/spinlock.h>
  21#include <linux/string.h>
  22#include <linux/tc_act/tc_nat.h>
  23#include <net/act_api.h>
  24#include <net/icmp.h>
  25#include <net/ip.h>
  26#include <net/netlink.h>
  27#include <net/tc_act/tc_nat.h>
  28#include <net/tcp.h>
  29#include <net/udp.h>
  30
  31
  32static unsigned int nat_net_id;
  33static struct tc_action_ops act_nat_ops;
  34
  35static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = {
  36        [TCA_NAT_PARMS] = { .len = sizeof(struct tc_nat) },
  37};
  38
  39static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
  40                        struct tc_action **a, int ovr, int bind,
  41                        bool rtnl_held, struct netlink_ext_ack *extack)
  42{
  43        struct tc_action_net *tn = net_generic(net, nat_net_id);
  44        struct nlattr *tb[TCA_NAT_MAX + 1];
  45        struct tc_nat *parm;
  46        int ret = 0, err;
  47        struct tcf_nat *p;
  48
  49        if (nla == NULL)
  50                return -EINVAL;
  51
  52        err = nla_parse_nested(tb, TCA_NAT_MAX, nla, nat_policy, NULL);
  53        if (err < 0)
  54                return err;
  55
  56        if (tb[TCA_NAT_PARMS] == NULL)
  57                return -EINVAL;
  58        parm = nla_data(tb[TCA_NAT_PARMS]);
  59
  60        err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
  61        if (!err) {
  62                ret = tcf_idr_create(tn, parm->index, est, a,
  63                                     &act_nat_ops, bind, false);
  64                if (ret) {
  65                        tcf_idr_cleanup(tn, parm->index);
  66                        return ret;
  67                }
  68                ret = ACT_P_CREATED;
  69        } else if (err > 0) {
  70                if (bind)
  71                        return 0;
  72                if (!ovr) {
  73                        tcf_idr_release(*a, bind);
  74                        return -EEXIST;
  75                }
  76        } else {
  77                return err;
  78        }
  79        p = to_tcf_nat(*a);
  80
  81        spin_lock_bh(&p->tcf_lock);
  82        p->old_addr = parm->old_addr;
  83        p->new_addr = parm->new_addr;
  84        p->mask = parm->mask;
  85        p->flags = parm->flags;
  86
  87        p->tcf_action = parm->action;
  88        spin_unlock_bh(&p->tcf_lock);
  89
  90        if (ret == ACT_P_CREATED)
  91                tcf_idr_insert(tn, *a);
  92
  93        return ret;
  94}
  95
  96static int tcf_nat_act(struct sk_buff *skb, const struct tc_action *a,
  97                       struct tcf_result *res)
  98{
  99        struct tcf_nat *p = to_tcf_nat(a);
 100        struct iphdr *iph;
 101        __be32 old_addr;
 102        __be32 new_addr;
 103        __be32 mask;
 104        __be32 addr;
 105        int egress;
 106        int action;
 107        int ihl;
 108        int noff;
 109
 110        spin_lock(&p->tcf_lock);
 111
 112        tcf_lastuse_update(&p->tcf_tm);
 113        old_addr = p->old_addr;
 114        new_addr = p->new_addr;
 115        mask = p->mask;
 116        egress = p->flags & TCA_NAT_FLAG_EGRESS;
 117        action = p->tcf_action;
 118
 119        bstats_update(&p->tcf_bstats, skb);
 120
 121        spin_unlock(&p->tcf_lock);
 122
 123        if (unlikely(action == TC_ACT_SHOT))
 124                goto drop;
 125
 126        noff = skb_network_offset(skb);
 127        if (!pskb_may_pull(skb, sizeof(*iph) + noff))
 128                goto drop;
 129
 130        iph = ip_hdr(skb);
 131
 132        if (egress)
 133                addr = iph->saddr;
 134        else
 135                addr = iph->daddr;
 136
 137        if (!((old_addr ^ addr) & mask)) {
 138                if (skb_try_make_writable(skb, sizeof(*iph) + noff))
 139                        goto drop;
 140
 141                new_addr &= mask;
 142                new_addr |= addr & ~mask;
 143
 144                /* Rewrite IP header */
 145                iph = ip_hdr(skb);
 146                if (egress)
 147                        iph->saddr = new_addr;
 148                else
 149                        iph->daddr = new_addr;
 150
 151                csum_replace4(&iph->check, addr, new_addr);
 152        } else if ((iph->frag_off & htons(IP_OFFSET)) ||
 153                   iph->protocol != IPPROTO_ICMP) {
 154                goto out;
 155        }
 156
 157        ihl = iph->ihl * 4;
 158
 159        /* It would be nice to share code with stateful NAT. */
 160        switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) {
 161        case IPPROTO_TCP:
 162        {
 163                struct tcphdr *tcph;
 164
 165                if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) ||
 166                    skb_try_make_writable(skb, ihl + sizeof(*tcph) + noff))
 167                        goto drop;
 168
 169                tcph = (void *)(skb_network_header(skb) + ihl);
 170                inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr,
 171                                         true);
 172                break;
 173        }
 174        case IPPROTO_UDP:
 175        {
 176                struct udphdr *udph;
 177
 178                if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) ||
 179                    skb_try_make_writable(skb, ihl + sizeof(*udph) + noff))
 180                        goto drop;
 181
 182                udph = (void *)(skb_network_header(skb) + ihl);
 183                if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
 184                        inet_proto_csum_replace4(&udph->check, skb, addr,
 185                                                 new_addr, true);
 186                        if (!udph->check)
 187                                udph->check = CSUM_MANGLED_0;
 188                }
 189                break;
 190        }
 191        case IPPROTO_ICMP:
 192        {
 193                struct icmphdr *icmph;
 194
 195                if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + noff))
 196                        goto drop;
 197
 198                icmph = (void *)(skb_network_header(skb) + ihl);
 199
 200                if ((icmph->type != ICMP_DEST_UNREACH) &&
 201                    (icmph->type != ICMP_TIME_EXCEEDED) &&
 202                    (icmph->type != ICMP_PARAMETERPROB))
 203                        break;
 204
 205                if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph) +
 206                                        noff))
 207                        goto drop;
 208
 209                icmph = (void *)(skb_network_header(skb) + ihl);
 210                iph = (void *)(icmph + 1);
 211                if (egress)
 212                        addr = iph->daddr;
 213                else
 214                        addr = iph->saddr;
 215
 216                if ((old_addr ^ addr) & mask)
 217                        break;
 218
 219                if (skb_try_make_writable(skb, ihl + sizeof(*icmph) +
 220                                          sizeof(*iph) + noff))
 221                        goto drop;
 222
 223                icmph = (void *)(skb_network_header(skb) + ihl);
 224                iph = (void *)(icmph + 1);
 225
 226                new_addr &= mask;
 227                new_addr |= addr & ~mask;
 228
 229                /* XXX Fix up the inner checksums. */
 230                if (egress)
 231                        iph->daddr = new_addr;
 232                else
 233                        iph->saddr = new_addr;
 234
 235                inet_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr,
 236                                         false);
 237                break;
 238        }
 239        default:
 240                break;
 241        }
 242
 243out:
 244        return action;
 245
 246drop:
 247        spin_lock(&p->tcf_lock);
 248        p->tcf_qstats.drops++;
 249        spin_unlock(&p->tcf_lock);
 250        return TC_ACT_SHOT;
 251}
 252
 253static int tcf_nat_dump(struct sk_buff *skb, struct tc_action *a,
 254                        int bind, int ref)
 255{
 256        unsigned char *b = skb_tail_pointer(skb);
 257        struct tcf_nat *p = to_tcf_nat(a);
 258        struct tc_nat opt = {
 259                .old_addr = p->old_addr,
 260                .new_addr = p->new_addr,
 261                .mask     = p->mask,
 262                .flags    = p->flags,
 263
 264                .index    = p->tcf_index,
 265                .action   = p->tcf_action,
 266                .refcnt   = refcount_read(&p->tcf_refcnt) - ref,
 267                .bindcnt  = atomic_read(&p->tcf_bindcnt) - bind,
 268        };
 269        struct tcf_t t;
 270
 271        if (nla_put(skb, TCA_NAT_PARMS, sizeof(opt), &opt))
 272                goto nla_put_failure;
 273
 274        tcf_tm_dump(&t, &p->tcf_tm);
 275        if (nla_put_64bit(skb, TCA_NAT_TM, sizeof(t), &t, TCA_NAT_PAD))
 276                goto nla_put_failure;
 277
 278        return skb->len;
 279
 280nla_put_failure:
 281        nlmsg_trim(skb, b);
 282        return -1;
 283}
 284
 285static int tcf_nat_walker(struct net *net, struct sk_buff *skb,
 286                          struct netlink_callback *cb, int type,
 287                          const struct tc_action_ops *ops,
 288                          struct netlink_ext_ack *extack)
 289{
 290        struct tc_action_net *tn = net_generic(net, nat_net_id);
 291
 292        return tcf_generic_walker(tn, skb, cb, type, ops, extack);
 293}
 294
 295static int tcf_nat_search(struct net *net, struct tc_action **a, u32 index,
 296                          struct netlink_ext_ack *extack)
 297{
 298        struct tc_action_net *tn = net_generic(net, nat_net_id);
 299
 300        return tcf_idr_search(tn, a, index);
 301}
 302
 303static struct tc_action_ops act_nat_ops = {
 304        .kind           =       "nat",
 305        .type           =       TCA_ACT_NAT,
 306        .owner          =       THIS_MODULE,
 307        .act            =       tcf_nat_act,
 308        .dump           =       tcf_nat_dump,
 309        .init           =       tcf_nat_init,
 310        .walk           =       tcf_nat_walker,
 311        .lookup         =       tcf_nat_search,
 312        .size           =       sizeof(struct tcf_nat),
 313};
 314
 315static __net_init int nat_init_net(struct net *net)
 316{
 317        struct tc_action_net *tn = net_generic(net, nat_net_id);
 318
 319        return tc_action_net_init(tn, &act_nat_ops);
 320}
 321
 322static void __net_exit nat_exit_net(struct list_head *net_list)
 323{
 324        tc_action_net_exit(net_list, nat_net_id);
 325}
 326
 327static struct pernet_operations nat_net_ops = {
 328        .init = nat_init_net,
 329        .exit_batch = nat_exit_net,
 330        .id   = &nat_net_id,
 331        .size = sizeof(struct tc_action_net),
 332};
 333
 334MODULE_DESCRIPTION("Stateless NAT actions");
 335MODULE_LICENSE("GPL");
 336
 337static int __init nat_init_module(void)
 338{
 339        return tcf_register_action(&act_nat_ops, &nat_net_ops);
 340}
 341
 342static void __exit nat_cleanup_module(void)
 343{
 344        tcf_unregister_action(&act_nat_ops, &nat_net_ops);
 345}
 346
 347module_init(nat_init_module);
 348module_exit(nat_cleanup_module);
 349