linux/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * Development of IPv6 NAT funded by Astaro.
   9 */
  10#include <linux/types.h>
  11#include <linux/module.h>
  12#include <linux/skbuff.h>
  13#include <linux/ipv6.h>
  14#include <linux/netfilter.h>
  15#include <linux/netfilter_ipv6.h>
  16#include <net/secure_seq.h>
  17#include <net/checksum.h>
  18#include <net/ip6_checksum.h>
  19#include <net/ip6_route.h>
  20#include <net/ipv6.h>
  21
  22#include <net/netfilter/nf_conntrack_core.h>
  23#include <net/netfilter/nf_conntrack.h>
  24#include <net/netfilter/nf_nat_core.h>
  25#include <net/netfilter/nf_nat_l3proto.h>
  26#include <net/netfilter/nf_nat_l4proto.h>
  27
  28static const struct nf_nat_l3proto nf_nat_l3proto_ipv6;
  29
  30#ifdef CONFIG_XFRM
  31static void nf_nat_ipv6_decode_session(struct sk_buff *skb,
  32                                       const struct nf_conn *ct,
  33                                       enum ip_conntrack_dir dir,
  34                                       unsigned long statusbit,
  35                                       struct flowi *fl)
  36{
  37        const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple;
  38        struct flowi6 *fl6 = &fl->u.ip6;
  39
  40        if (ct->status & statusbit) {
  41                fl6->daddr = t->dst.u3.in6;
  42                if (t->dst.protonum == IPPROTO_TCP ||
  43                    t->dst.protonum == IPPROTO_UDP ||
  44                    t->dst.protonum == IPPROTO_UDPLITE ||
  45                    t->dst.protonum == IPPROTO_DCCP ||
  46                    t->dst.protonum == IPPROTO_SCTP)
  47                        fl6->fl6_dport = t->dst.u.all;
  48        }
  49
  50        statusbit ^= IPS_NAT_MASK;
  51
  52        if (ct->status & statusbit) {
  53                fl6->saddr = t->src.u3.in6;
  54                if (t->dst.protonum == IPPROTO_TCP ||
  55                    t->dst.protonum == IPPROTO_UDP ||
  56                    t->dst.protonum == IPPROTO_UDPLITE ||
  57                    t->dst.protonum == IPPROTO_DCCP ||
  58                    t->dst.protonum == IPPROTO_SCTP)
  59                        fl6->fl6_sport = t->src.u.all;
  60        }
  61}
  62#endif
  63
  64static bool nf_nat_ipv6_in_range(const struct nf_conntrack_tuple *t,
  65                                 const struct nf_nat_range *range)
  66{
  67        return ipv6_addr_cmp(&t->src.u3.in6, &range->min_addr.in6) >= 0 &&
  68               ipv6_addr_cmp(&t->src.u3.in6, &range->max_addr.in6) <= 0;
  69}
  70
  71static u32 nf_nat_ipv6_secure_port(const struct nf_conntrack_tuple *t,
  72                                   __be16 dport)
  73{
  74        return secure_ipv6_port_ephemeral(t->src.u3.ip6, t->dst.u3.ip6, dport);
  75}
  76
  77static bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb,
  78                                  unsigned int iphdroff,
  79                                  const struct nf_nat_l4proto *l4proto,
  80                                  const struct nf_conntrack_tuple *target,
  81                                  enum nf_nat_manip_type maniptype)
  82{
  83        struct ipv6hdr *ipv6h;
  84        __be16 frag_off;
  85        int hdroff;
  86        u8 nexthdr;
  87
  88        if (!skb_make_writable(skb, iphdroff + sizeof(*ipv6h)))
  89                return false;
  90
  91        ipv6h = (void *)skb->data + iphdroff;
  92        nexthdr = ipv6h->nexthdr;
  93        hdroff = ipv6_skip_exthdr(skb, iphdroff + sizeof(*ipv6h),
  94                                  &nexthdr, &frag_off);
  95        if (hdroff < 0)
  96                goto manip_addr;
  97
  98        if ((frag_off & htons(~0x7)) == 0 &&
  99            !l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv6, iphdroff, hdroff,
 100                                target, maniptype))
 101                return false;
 102manip_addr:
 103        if (maniptype == NF_NAT_MANIP_SRC)
 104                ipv6h->saddr = target->src.u3.in6;
 105        else
 106                ipv6h->daddr = target->dst.u3.in6;
 107
 108        return true;
 109}
 110
 111static void nf_nat_ipv6_csum_update(struct sk_buff *skb,
 112                                    unsigned int iphdroff, __sum16 *check,
 113                                    const struct nf_conntrack_tuple *t,
 114                                    enum nf_nat_manip_type maniptype)
 115{
 116        const struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + iphdroff);
 117        const struct in6_addr *oldip, *newip;
 118
 119        if (maniptype == NF_NAT_MANIP_SRC) {
 120                oldip = &ipv6h->saddr;
 121                newip = &t->src.u3.in6;
 122        } else {
 123                oldip = &ipv6h->daddr;
 124                newip = &t->dst.u3.in6;
 125        }
 126        inet_proto_csum_replace16(check, skb, oldip->s6_addr32,
 127                                  newip->s6_addr32, true);
 128}
 129
 130static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
 131                                    u8 proto, void *data, __sum16 *check,
 132                                    int datalen, int oldlen)
 133{
 134        if (skb->ip_summed != CHECKSUM_PARTIAL) {
 135                const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
 136
 137                skb->ip_summed = CHECKSUM_PARTIAL;
 138                skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
 139                        (data - (void *)skb->data);
 140                skb->csum_offset = (void *)check - data;
 141                *check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
 142                                          datalen, proto, 0);
 143        } else
 144                inet_proto_csum_replace2(check, skb,
 145                                         htons(oldlen), htons(datalen), true);
 146}
 147
 148#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 149static int nf_nat_ipv6_nlattr_to_range(struct nlattr *tb[],
 150                                       struct nf_nat_range *range)
 151{
 152        if (tb[CTA_NAT_V6_MINIP]) {
 153                nla_memcpy(&range->min_addr.ip6, tb[CTA_NAT_V6_MINIP],
 154                           sizeof(struct in6_addr));
 155                range->flags |= NF_NAT_RANGE_MAP_IPS;
 156        }
 157
 158        if (tb[CTA_NAT_V6_MAXIP])
 159                nla_memcpy(&range->max_addr.ip6, tb[CTA_NAT_V6_MAXIP],
 160                           sizeof(struct in6_addr));
 161        else
 162                range->max_addr = range->min_addr;
 163
 164        return 0;
 165}
 166#endif
 167
 168static const struct nf_nat_l3proto nf_nat_l3proto_ipv6 = {
 169        .l3proto                = NFPROTO_IPV6,
 170        .secure_port            = nf_nat_ipv6_secure_port,
 171        .in_range               = nf_nat_ipv6_in_range,
 172        .manip_pkt              = nf_nat_ipv6_manip_pkt,
 173        .csum_update            = nf_nat_ipv6_csum_update,
 174        .csum_recalc            = nf_nat_ipv6_csum_recalc,
 175#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 176        .nlattr_to_range        = nf_nat_ipv6_nlattr_to_range,
 177#endif
 178#ifdef CONFIG_XFRM
 179        .decode_session = nf_nat_ipv6_decode_session,
 180#endif
 181};
 182
 183int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
 184                                    struct nf_conn *ct,
 185                                    enum ip_conntrack_info ctinfo,
 186                                    unsigned int hooknum,
 187                                    unsigned int hdrlen)
 188{
 189        struct {
 190                struct icmp6hdr icmp6;
 191                struct ipv6hdr  ip6;
 192        } *inside;
 193        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 194        enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
 195        const struct nf_nat_l4proto *l4proto;
 196        struct nf_conntrack_tuple target;
 197        unsigned long statusbit;
 198
 199        NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY);
 200
 201        if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
 202                return 0;
 203        if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6))
 204                return 0;
 205
 206        inside = (void *)skb->data + hdrlen;
 207        if (inside->icmp6.icmp6_type == NDISC_REDIRECT) {
 208                if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
 209                        return 0;
 210                if (ct->status & IPS_NAT_MASK)
 211                        return 0;
 212        }
 213
 214        if (manip == NF_NAT_MANIP_SRC)
 215                statusbit = IPS_SRC_NAT;
 216        else
 217                statusbit = IPS_DST_NAT;
 218
 219        /* Invert if this is reply direction */
 220        if (dir == IP_CT_DIR_REPLY)
 221                statusbit ^= IPS_NAT_MASK;
 222
 223        if (!(ct->status & statusbit))
 224                return 1;
 225
 226        l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, inside->ip6.nexthdr);
 227        if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6),
 228                                   l4proto, &ct->tuplehash[!dir].tuple, !manip))
 229                return 0;
 230
 231        if (skb->ip_summed != CHECKSUM_PARTIAL) {
 232                struct ipv6hdr *ipv6h = ipv6_hdr(skb);
 233                inside = (void *)skb->data + hdrlen;
 234                inside->icmp6.icmp6_cksum = 0;
 235                inside->icmp6.icmp6_cksum =
 236                        csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
 237                                        skb->len - hdrlen, IPPROTO_ICMPV6,
 238                                        skb_checksum(skb, hdrlen,
 239                                                     skb->len - hdrlen, 0));
 240        }
 241
 242        nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
 243        l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, IPPROTO_ICMPV6);
 244        if (!nf_nat_ipv6_manip_pkt(skb, 0, l4proto, &target, manip))
 245                return 0;
 246
 247        return 1;
 248}
 249EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation);
 250
 251unsigned int
 252nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
 253               const struct nf_hook_state *state,
 254               unsigned int (*do_chain)(void *priv,
 255                                        struct sk_buff *skb,
 256                                        const struct nf_hook_state *state,
 257                                        struct nf_conn *ct))
 258{
 259        struct nf_conn *ct;
 260        enum ip_conntrack_info ctinfo;
 261        struct nf_conn_nat *nat;
 262        enum nf_nat_manip_type maniptype = HOOK2MANIP(state->hook);
 263        __be16 frag_off;
 264        int hdrlen;
 265        u8 nexthdr;
 266
 267        ct = nf_ct_get(skb, &ctinfo);
 268        /* Can't track?  It's not due to stress, or conntrack would
 269         * have dropped it.  Hence it's the user's responsibilty to
 270         * packet filter it out, or implement conntrack/NAT for that
 271         * protocol. 8) --RR
 272         */
 273        if (!ct)
 274                return NF_ACCEPT;
 275
 276        nat = nfct_nat(ct);
 277
 278        switch (ctinfo) {
 279        case IP_CT_RELATED:
 280        case IP_CT_RELATED_REPLY:
 281                nexthdr = ipv6_hdr(skb)->nexthdr;
 282                hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
 283                                          &nexthdr, &frag_off);
 284
 285                if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
 286                        if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
 287                                                             state->hook,
 288                                                             hdrlen))
 289                                return NF_DROP;
 290                        else
 291                                return NF_ACCEPT;
 292                }
 293                /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
 294        case IP_CT_NEW:
 295                /* Seen it before?  This can happen for loopback, retrans,
 296                 * or local packets.
 297                 */
 298                if (!nf_nat_initialized(ct, maniptype)) {
 299                        unsigned int ret;
 300
 301                        ret = do_chain(priv, skb, state, ct);
 302                        if (ret != NF_ACCEPT)
 303                                return ret;
 304
 305                        if (nf_nat_initialized(ct, HOOK2MANIP(state->hook)))
 306                                break;
 307
 308                        ret = nf_nat_alloc_null_binding(ct, state->hook);
 309                        if (ret != NF_ACCEPT)
 310                                return ret;
 311                } else {
 312                        pr_debug("Already setup manip %s for ct %p\n",
 313                                 maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
 314                                 ct);
 315                        if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
 316                                goto oif_changed;
 317                }
 318                break;
 319
 320        default:
 321                /* ESTABLISHED */
 322                NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
 323                             ctinfo == IP_CT_ESTABLISHED_REPLY);
 324                if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
 325                        goto oif_changed;
 326        }
 327
 328        return nf_nat_packet(ct, ctinfo, state->hook, skb);
 329
 330oif_changed:
 331        nf_ct_kill_acct(ct, ctinfo, skb);
 332        return NF_DROP;
 333}
 334EXPORT_SYMBOL_GPL(nf_nat_ipv6_fn);
 335
 336unsigned int
 337nf_nat_ipv6_in(void *priv, struct sk_buff *skb,
 338               const struct nf_hook_state *state,
 339               unsigned int (*do_chain)(void *priv,
 340                                        struct sk_buff *skb,
 341                                        const struct nf_hook_state *state,
 342                                        struct nf_conn *ct))
 343{
 344        unsigned int ret;
 345        struct in6_addr daddr = ipv6_hdr(skb)->daddr;
 346
 347        ret = nf_nat_ipv6_fn(priv, skb, state, do_chain);
 348        if (ret != NF_DROP && ret != NF_STOLEN &&
 349            ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
 350                skb_dst_drop(skb);
 351
 352        return ret;
 353}
 354EXPORT_SYMBOL_GPL(nf_nat_ipv6_in);
 355
 356unsigned int
 357nf_nat_ipv6_out(void *priv, struct sk_buff *skb,
 358                const struct nf_hook_state *state,
 359                unsigned int (*do_chain)(void *priv,
 360                                         struct sk_buff *skb,
 361                                         const struct nf_hook_state *state,
 362                                         struct nf_conn *ct))
 363{
 364#ifdef CONFIG_XFRM
 365        const struct nf_conn *ct;
 366        enum ip_conntrack_info ctinfo;
 367        int err;
 368#endif
 369        unsigned int ret;
 370
 371        /* root is playing with raw sockets. */
 372        if (skb->len < sizeof(struct ipv6hdr))
 373                return NF_ACCEPT;
 374
 375        ret = nf_nat_ipv6_fn(priv, skb, state, do_chain);
 376#ifdef CONFIG_XFRM
 377        if (ret != NF_DROP && ret != NF_STOLEN &&
 378            !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
 379            (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
 380                enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 381
 382                if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
 383                                      &ct->tuplehash[!dir].tuple.dst.u3) ||
 384                    (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
 385                     ct->tuplehash[dir].tuple.src.u.all !=
 386                     ct->tuplehash[!dir].tuple.dst.u.all)) {
 387                        err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
 388                        if (err < 0)
 389                                ret = NF_DROP_ERR(err);
 390                }
 391        }
 392#endif
 393        return ret;
 394}
 395EXPORT_SYMBOL_GPL(nf_nat_ipv6_out);
 396
 397unsigned int
 398nf_nat_ipv6_local_fn(void *priv, struct sk_buff *skb,
 399                     const struct nf_hook_state *state,
 400                     unsigned int (*do_chain)(void *priv,
 401                                              struct sk_buff *skb,
 402                                              const struct nf_hook_state *state,
 403                                              struct nf_conn *ct))
 404{
 405        const struct nf_conn *ct;
 406        enum ip_conntrack_info ctinfo;
 407        unsigned int ret;
 408        int err;
 409
 410        /* root is playing with raw sockets. */
 411        if (skb->len < sizeof(struct ipv6hdr))
 412                return NF_ACCEPT;
 413
 414        ret = nf_nat_ipv6_fn(priv, skb, state, do_chain);
 415        if (ret != NF_DROP && ret != NF_STOLEN &&
 416            (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
 417                enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 418
 419                if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
 420                                      &ct->tuplehash[!dir].tuple.src.u3)) {
 421                        err = ip6_route_me_harder(state->net, skb);
 422                        if (err < 0)
 423                                ret = NF_DROP_ERR(err);
 424                }
 425#ifdef CONFIG_XFRM
 426                else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
 427                         ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
 428                         ct->tuplehash[dir].tuple.dst.u.all !=
 429                         ct->tuplehash[!dir].tuple.src.u.all) {
 430                        err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
 431                        if (err < 0)
 432                                ret = NF_DROP_ERR(err);
 433                }
 434#endif
 435        }
 436        return ret;
 437}
 438EXPORT_SYMBOL_GPL(nf_nat_ipv6_local_fn);
 439
 440static int __init nf_nat_l3proto_ipv6_init(void)
 441{
 442        int err;
 443
 444        err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
 445        if (err < 0)
 446                goto err1;
 447        err = nf_nat_l3proto_register(&nf_nat_l3proto_ipv6);
 448        if (err < 0)
 449                goto err2;
 450        return err;
 451
 452err2:
 453        nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
 454err1:
 455        return err;
 456}
 457
 458static void __exit nf_nat_l3proto_ipv6_exit(void)
 459{
 460        nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv6);
 461        nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
 462}
 463
 464MODULE_LICENSE("GPL");
 465MODULE_ALIAS("nf-nat-" __stringify(AF_INET6));
 466
 467module_init(nf_nat_l3proto_ipv6_init);
 468module_exit(nf_nat_l3proto_ipv6_exit);
 469