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