linux/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
<<
>>
Prefs
   1/*
   2 * Copyright (C)2004 USAGI/WIDE Project
   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 * Author:
   9 *      Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
  10 */
  11
  12#include <linux/types.h>
  13#include <linux/ipv6.h>
  14#include <linux/in6.h>
  15#include <linux/netfilter.h>
  16#include <linux/module.h>
  17#include <linux/skbuff.h>
  18#include <linux/icmp.h>
  19#include <net/ipv6.h>
  20#include <net/inet_frag.h>
  21
  22#include <linux/netfilter_bridge.h>
  23#include <linux/netfilter_ipv6.h>
  24#include <linux/netfilter_ipv6/ip6_tables.h>
  25#include <net/netfilter/nf_conntrack.h>
  26#include <net/netfilter/nf_conntrack_helper.h>
  27#include <net/netfilter/nf_conntrack_l4proto.h>
  28#include <net/netfilter/nf_conntrack_l3proto.h>
  29#include <net/netfilter/nf_conntrack_core.h>
  30#include <net/netfilter/nf_conntrack_zones.h>
  31#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
  32#include <net/netfilter/nf_nat_helper.h>
  33#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
  34#include <net/netfilter/nf_log.h>
  35
  36static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
  37                              struct nf_conntrack_tuple *tuple)
  38{
  39        const u_int32_t *ap;
  40        u_int32_t _addrs[8];
  41
  42        ap = skb_header_pointer(skb, nhoff + offsetof(struct ipv6hdr, saddr),
  43                                sizeof(_addrs), _addrs);
  44        if (ap == NULL)
  45                return false;
  46
  47        memcpy(tuple->src.u3.ip6, ap, sizeof(tuple->src.u3.ip6));
  48        memcpy(tuple->dst.u3.ip6, ap + 4, sizeof(tuple->dst.u3.ip6));
  49
  50        return true;
  51}
  52
  53static bool ipv6_invert_tuple(struct nf_conntrack_tuple *tuple,
  54                              const struct nf_conntrack_tuple *orig)
  55{
  56        memcpy(tuple->src.u3.ip6, orig->dst.u3.ip6, sizeof(tuple->src.u3.ip6));
  57        memcpy(tuple->dst.u3.ip6, orig->src.u3.ip6, sizeof(tuple->dst.u3.ip6));
  58
  59        return true;
  60}
  61
  62static int ipv6_print_tuple(struct seq_file *s,
  63                            const struct nf_conntrack_tuple *tuple)
  64{
  65        return seq_printf(s, "src=%pI6 dst=%pI6 ",
  66                          tuple->src.u3.ip6, tuple->dst.u3.ip6);
  67}
  68
  69static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
  70                            unsigned int *dataoff, u_int8_t *protonum)
  71{
  72        unsigned int extoff = nhoff + sizeof(struct ipv6hdr);
  73        __be16 frag_off;
  74        int protoff;
  75        u8 nexthdr;
  76
  77        if (skb_copy_bits(skb, nhoff + offsetof(struct ipv6hdr, nexthdr),
  78                          &nexthdr, sizeof(nexthdr)) != 0) {
  79                pr_debug("ip6_conntrack_core: can't get nexthdr\n");
  80                return -NF_ACCEPT;
  81        }
  82        protoff = ipv6_skip_exthdr(skb, extoff, &nexthdr, &frag_off);
  83        /*
  84         * (protoff == skb->len) means the packet has not data, just
  85         * IPv6 and possibly extensions headers, but it is tracked anyway
  86         */
  87        if (protoff < 0 || (frag_off & htons(~0x7)) != 0) {
  88                pr_debug("ip6_conntrack_core: can't find proto in pkt\n");
  89                return -NF_ACCEPT;
  90        }
  91
  92        *dataoff = protoff;
  93        *protonum = nexthdr;
  94        return NF_ACCEPT;
  95}
  96
  97static unsigned int ipv6_helper(unsigned int hooknum,
  98                                struct sk_buff *skb,
  99                                const struct net_device *in,
 100                                const struct net_device *out,
 101                                int (*okfn)(struct sk_buff *))
 102{
 103        struct nf_conn *ct;
 104        const struct nf_conn_help *help;
 105        const struct nf_conntrack_helper *helper;
 106        enum ip_conntrack_info ctinfo;
 107        __be16 frag_off;
 108        int protoff;
 109        u8 nexthdr;
 110
 111        /* This is where we call the helper: as the packet goes out. */
 112        ct = nf_ct_get(skb, &ctinfo);
 113        if (!ct || ctinfo == IP_CT_RELATED_REPLY)
 114                return NF_ACCEPT;
 115
 116        help = nfct_help(ct);
 117        if (!help)
 118                return NF_ACCEPT;
 119        /* rcu_read_lock()ed by nf_hook_slow */
 120        helper = rcu_dereference(help->helper);
 121        if (!helper)
 122                return NF_ACCEPT;
 123
 124        nexthdr = ipv6_hdr(skb)->nexthdr;
 125        protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
 126                                   &frag_off);
 127        if (protoff < 0 || (frag_off & htons(~0x7)) != 0) {
 128                pr_debug("proto header not found\n");
 129                return NF_ACCEPT;
 130        }
 131
 132        return helper->help(skb, protoff, ct, ctinfo);
 133}
 134
 135static unsigned int ipv6_confirm(unsigned int hooknum,
 136                                 struct sk_buff *skb,
 137                                 const struct net_device *in,
 138                                 const struct net_device *out,
 139                                 int (*okfn)(struct sk_buff *))
 140{
 141        struct nf_conn *ct;
 142        enum ip_conntrack_info ctinfo;
 143        unsigned char pnum = ipv6_hdr(skb)->nexthdr;
 144        int protoff;
 145        __be16 frag_off;
 146
 147        ct = nf_ct_get(skb, &ctinfo);
 148        if (!ct || ctinfo == IP_CT_RELATED_REPLY)
 149                goto out;
 150
 151        protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum,
 152                                   &frag_off);
 153        if (protoff < 0 || (frag_off & htons(~0x7)) != 0) {
 154                pr_debug("proto header not found\n");
 155                goto out;
 156        }
 157
 158        /* adjust seqs for loopback traffic only in outgoing direction */
 159        if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
 160            !nf_is_loopback_packet(skb)) {
 161                typeof(nf_nat_seq_adjust_hook) seq_adjust;
 162
 163                seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook);
 164                if (!seq_adjust ||
 165                    !seq_adjust(skb, ct, ctinfo, protoff)) {
 166                        NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
 167                        return NF_DROP;
 168                }
 169        }
 170out:
 171        /* We've seen it coming out the other side: confirm it */
 172        return nf_conntrack_confirm(skb);
 173}
 174
 175static unsigned int __ipv6_conntrack_in(struct net *net,
 176                                        unsigned int hooknum,
 177                                        struct sk_buff *skb,
 178                                        const struct net_device *in,
 179                                        const struct net_device *out,
 180                                        int (*okfn)(struct sk_buff *))
 181{
 182        struct sk_buff *reasm = skb->nfct_reasm;
 183        const struct nf_conn_help *help;
 184        struct nf_conn *ct;
 185        enum ip_conntrack_info ctinfo;
 186
 187        /* This packet is fragmented and has reassembled packet. */
 188        if (reasm) {
 189                /* Reassembled packet isn't parsed yet ? */
 190                if (!reasm->nfct) {
 191                        unsigned int ret;
 192
 193                        ret = nf_conntrack_in(net, PF_INET6, hooknum, reasm);
 194                        if (ret != NF_ACCEPT)
 195                                return ret;
 196                }
 197
 198                /* Conntrack helpers need the entire reassembled packet in the
 199                 * POST_ROUTING hook. In case of unconfirmed connections NAT
 200                 * might reassign a helper, so the entire packet is also
 201                 * required.
 202                 */
 203                ct = nf_ct_get(reasm, &ctinfo);
 204                if (ct != NULL && !nf_ct_is_untracked(ct)) {
 205                        help = nfct_help(ct);
 206                        if ((help && help->helper) || !nf_ct_is_confirmed(ct)) {
 207                                nf_conntrack_get_reasm(reasm);
 208                                NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm,
 209                                               (struct net_device *)in,
 210                                               (struct net_device *)out,
 211                                               okfn, NF_IP6_PRI_CONNTRACK + 1);
 212                                return NF_DROP_ERR(-ECANCELED);
 213                        }
 214                }
 215
 216                nf_conntrack_get(reasm->nfct);
 217                skb->nfct = reasm->nfct;
 218                skb->nfctinfo = reasm->nfctinfo;
 219                return NF_ACCEPT;
 220        }
 221
 222        return nf_conntrack_in(net, PF_INET6, hooknum, skb);
 223}
 224
 225static unsigned int ipv6_conntrack_in(unsigned int hooknum,
 226                                      struct sk_buff *skb,
 227                                      const struct net_device *in,
 228                                      const struct net_device *out,
 229                                      int (*okfn)(struct sk_buff *))
 230{
 231        return __ipv6_conntrack_in(dev_net(in), hooknum, skb, in, out, okfn);
 232}
 233
 234static unsigned int ipv6_conntrack_local(unsigned int hooknum,
 235                                         struct sk_buff *skb,
 236                                         const struct net_device *in,
 237                                         const struct net_device *out,
 238                                         int (*okfn)(struct sk_buff *))
 239{
 240        /* root is playing with raw sockets. */
 241        if (skb->len < sizeof(struct ipv6hdr)) {
 242                net_notice_ratelimited("ipv6_conntrack_local: packet too short\n");
 243                return NF_ACCEPT;
 244        }
 245        return __ipv6_conntrack_in(dev_net(out), hooknum, skb, in, out, okfn);
 246}
 247
 248static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
 249        {
 250                .hook           = ipv6_conntrack_in,
 251                .owner          = THIS_MODULE,
 252                .pf             = NFPROTO_IPV6,
 253                .hooknum        = NF_INET_PRE_ROUTING,
 254                .priority       = NF_IP6_PRI_CONNTRACK,
 255        },
 256        {
 257                .hook           = ipv6_conntrack_local,
 258                .owner          = THIS_MODULE,
 259                .pf             = NFPROTO_IPV6,
 260                .hooknum        = NF_INET_LOCAL_OUT,
 261                .priority       = NF_IP6_PRI_CONNTRACK,
 262        },
 263        {
 264                .hook           = ipv6_helper,
 265                .owner          = THIS_MODULE,
 266                .pf             = NFPROTO_IPV6,
 267                .hooknum        = NF_INET_POST_ROUTING,
 268                .priority       = NF_IP6_PRI_CONNTRACK_HELPER,
 269        },
 270        {
 271                .hook           = ipv6_confirm,
 272                .owner          = THIS_MODULE,
 273                .pf             = NFPROTO_IPV6,
 274                .hooknum        = NF_INET_POST_ROUTING,
 275                .priority       = NF_IP6_PRI_LAST,
 276        },
 277        {
 278                .hook           = ipv6_helper,
 279                .owner          = THIS_MODULE,
 280                .pf             = NFPROTO_IPV6,
 281                .hooknum        = NF_INET_LOCAL_IN,
 282                .priority       = NF_IP6_PRI_CONNTRACK_HELPER,
 283        },
 284        {
 285                .hook           = ipv6_confirm,
 286                .owner          = THIS_MODULE,
 287                .pf             = NFPROTO_IPV6,
 288                .hooknum        = NF_INET_LOCAL_IN,
 289                .priority       = NF_IP6_PRI_LAST-1,
 290        },
 291};
 292
 293static int
 294ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len)
 295{
 296        const struct inet_sock *inet = inet_sk(sk);
 297        const struct ipv6_pinfo *inet6 = inet6_sk(sk);
 298        const struct nf_conntrack_tuple_hash *h;
 299        struct sockaddr_in6 sin6;
 300        struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 };
 301        struct nf_conn *ct;
 302
 303        tuple.src.u3.in6 = inet6->rcv_saddr;
 304        tuple.src.u.tcp.port = inet->inet_sport;
 305        tuple.dst.u3.in6 = inet6->daddr;
 306        tuple.dst.u.tcp.port = inet->inet_dport;
 307        tuple.dst.protonum = sk->sk_protocol;
 308
 309        if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP)
 310                return -ENOPROTOOPT;
 311
 312        if (*len < 0 || (unsigned int) *len < sizeof(sin6))
 313                return -EINVAL;
 314
 315        h = nf_conntrack_find_get(sock_net(sk), NF_CT_DEFAULT_ZONE, &tuple);
 316        if (!h) {
 317                pr_debug("IP6T_SO_ORIGINAL_DST: Can't find %pI6c/%u-%pI6c/%u.\n",
 318                         &tuple.src.u3.ip6, ntohs(tuple.src.u.tcp.port),
 319                         &tuple.dst.u3.ip6, ntohs(tuple.dst.u.tcp.port));
 320                return -ENOENT;
 321        }
 322
 323        ct = nf_ct_tuplehash_to_ctrack(h);
 324
 325        sin6.sin6_family = AF_INET6;
 326        sin6.sin6_port = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port;
 327        sin6.sin6_flowinfo = inet6->flow_label & IPV6_FLOWINFO_MASK;
 328        memcpy(&sin6.sin6_addr,
 329                &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in6,
 330                                        sizeof(sin6.sin6_addr));
 331
 332        nf_ct_put(ct);
 333        sin6.sin6_scope_id = ipv6_iface_scope_id(&sin6.sin6_addr,
 334                                                 sk->sk_bound_dev_if);
 335        return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0;
 336}
 337
 338#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 339
 340#include <linux/netfilter/nfnetlink.h>
 341#include <linux/netfilter/nfnetlink_conntrack.h>
 342
 343static int ipv6_tuple_to_nlattr(struct sk_buff *skb,
 344                                const struct nf_conntrack_tuple *tuple)
 345{
 346        if (nla_put(skb, CTA_IP_V6_SRC, sizeof(u_int32_t) * 4,
 347                    &tuple->src.u3.ip6) ||
 348            nla_put(skb, CTA_IP_V6_DST, sizeof(u_int32_t) * 4,
 349                    &tuple->dst.u3.ip6))
 350                goto nla_put_failure;
 351        return 0;
 352
 353nla_put_failure:
 354        return -1;
 355}
 356
 357static const struct nla_policy ipv6_nla_policy[CTA_IP_MAX+1] = {
 358        [CTA_IP_V6_SRC] = { .len = sizeof(u_int32_t)*4 },
 359        [CTA_IP_V6_DST] = { .len = sizeof(u_int32_t)*4 },
 360};
 361
 362static int ipv6_nlattr_to_tuple(struct nlattr *tb[],
 363                                struct nf_conntrack_tuple *t)
 364{
 365        if (!tb[CTA_IP_V6_SRC] || !tb[CTA_IP_V6_DST])
 366                return -EINVAL;
 367
 368        memcpy(&t->src.u3.ip6, nla_data(tb[CTA_IP_V6_SRC]),
 369               sizeof(u_int32_t) * 4);
 370        memcpy(&t->dst.u3.ip6, nla_data(tb[CTA_IP_V6_DST]),
 371               sizeof(u_int32_t) * 4);
 372
 373        return 0;
 374}
 375
 376static int ipv6_nlattr_tuple_size(void)
 377{
 378        return nla_policy_len(ipv6_nla_policy, CTA_IP_MAX + 1);
 379}
 380#endif
 381
 382struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
 383        .l3proto                = PF_INET6,
 384        .name                   = "ipv6",
 385        .pkt_to_tuple           = ipv6_pkt_to_tuple,
 386        .invert_tuple           = ipv6_invert_tuple,
 387        .print_tuple            = ipv6_print_tuple,
 388        .get_l4proto            = ipv6_get_l4proto,
 389#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 390        .tuple_to_nlattr        = ipv6_tuple_to_nlattr,
 391        .nlattr_tuple_size      = ipv6_nlattr_tuple_size,
 392        .nlattr_to_tuple        = ipv6_nlattr_to_tuple,
 393        .nla_policy             = ipv6_nla_policy,
 394#endif
 395        .me                     = THIS_MODULE,
 396};
 397
 398MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6));
 399MODULE_LICENSE("GPL");
 400MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
 401
 402static struct nf_sockopt_ops so_getorigdst6 = {
 403        .pf             = NFPROTO_IPV6,
 404        .get_optmin     = IP6T_SO_ORIGINAL_DST,
 405        .get_optmax     = IP6T_SO_ORIGINAL_DST + 1,
 406        .get            = ipv6_getorigdst,
 407        .owner          = THIS_MODULE,
 408};
 409
 410static int ipv6_net_init(struct net *net)
 411{
 412        int ret = 0;
 413
 414        ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp6);
 415        if (ret < 0) {
 416                pr_err("nf_conntrack_tcp6: pernet registration failed\n");
 417                goto out;
 418        }
 419        ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp6);
 420        if (ret < 0) {
 421                pr_err("nf_conntrack_udp6: pernet registration failed\n");
 422                goto cleanup_tcp6;
 423        }
 424        ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmpv6);
 425        if (ret < 0) {
 426                pr_err("nf_conntrack_icmp6: pernet registration failed\n");
 427                goto cleanup_udp6;
 428        }
 429        ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv6);
 430        if (ret < 0) {
 431                pr_err("nf_conntrack_ipv6: pernet registration failed.\n");
 432                goto cleanup_icmpv6;
 433        }
 434        return 0;
 435 cleanup_icmpv6:
 436        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6);
 437 cleanup_udp6:
 438        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6);
 439 cleanup_tcp6:
 440        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6);
 441 out:
 442        return ret;
 443}
 444
 445static void ipv6_net_exit(struct net *net)
 446{
 447        nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv6);
 448        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6);
 449        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6);
 450        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6);
 451}
 452
 453static struct pernet_operations ipv6_net_ops = {
 454        .init = ipv6_net_init,
 455        .exit = ipv6_net_exit,
 456};
 457
 458static int __init nf_conntrack_l3proto_ipv6_init(void)
 459{
 460        int ret = 0;
 461
 462        need_conntrack();
 463        nf_defrag_ipv6_enable();
 464
 465        ret = nf_register_sockopt(&so_getorigdst6);
 466        if (ret < 0) {
 467                pr_err("Unable to register netfilter socket option\n");
 468                return ret;
 469        }
 470
 471        ret = register_pernet_subsys(&ipv6_net_ops);
 472        if (ret < 0)
 473                goto cleanup_sockopt;
 474
 475        ret = nf_register_hooks(ipv6_conntrack_ops,
 476                                ARRAY_SIZE(ipv6_conntrack_ops));
 477        if (ret < 0) {
 478                pr_err("nf_conntrack_ipv6: can't register pre-routing defrag "
 479                       "hook.\n");
 480                goto cleanup_pernet;
 481        }
 482
 483        ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp6);
 484        if (ret < 0) {
 485                pr_err("nf_conntrack_ipv6: can't register tcp6 proto.\n");
 486                goto cleanup_hooks;
 487        }
 488
 489        ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp6);
 490        if (ret < 0) {
 491                pr_err("nf_conntrack_ipv6: can't register udp6 proto.\n");
 492                goto cleanup_tcp6;
 493        }
 494
 495        ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmpv6);
 496        if (ret < 0) {
 497                pr_err("nf_conntrack_ipv6: can't register icmpv6 proto.\n");
 498                goto cleanup_udp6;
 499        }
 500
 501        ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6);
 502        if (ret < 0) {
 503                pr_err("nf_conntrack_ipv6: can't register ipv6 proto.\n");
 504                goto cleanup_icmpv6;
 505        }
 506        return ret;
 507
 508 cleanup_icmpv6:
 509        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
 510 cleanup_udp6:
 511        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6);
 512 cleanup_tcp6:
 513        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
 514 cleanup_hooks:
 515        nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
 516 cleanup_pernet:
 517        unregister_pernet_subsys(&ipv6_net_ops);
 518 cleanup_sockopt:
 519        nf_unregister_sockopt(&so_getorigdst6);
 520        return ret;
 521}
 522
 523static void __exit nf_conntrack_l3proto_ipv6_fini(void)
 524{
 525        synchronize_net();
 526        nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
 527        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
 528        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6);
 529        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
 530        nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
 531        unregister_pernet_subsys(&ipv6_net_ops);
 532        nf_unregister_sockopt(&so_getorigdst6);
 533}
 534
 535module_init(nf_conntrack_l3proto_ipv6_init);
 536module_exit(nf_conntrack_l3proto_ipv6_fini);
 537