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(void *priv,
  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(void *priv,
 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(void *priv,
 169                                      struct sk_buff *skb,
 170                                      const struct nf_hook_state *state)
 171{
 172        return nf_conntrack_in(state->net, PF_INET6, state->hook, skb);
 173}
 174
 175static unsigned int ipv6_conntrack_local(void *priv,
 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(state->net, PF_INET6, state->hook, skb);
 185}
 186
 187static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
 188        {
 189                .hook           = ipv6_conntrack_in,
 190                .pf             = NFPROTO_IPV6,
 191                .hooknum        = NF_INET_PRE_ROUTING,
 192                .priority       = NF_IP6_PRI_CONNTRACK,
 193        },
 194        {
 195                .hook           = ipv6_conntrack_local,
 196                .pf             = NFPROTO_IPV6,
 197                .hooknum        = NF_INET_LOCAL_OUT,
 198                .priority       = NF_IP6_PRI_CONNTRACK,
 199        },
 200        {
 201                .hook           = ipv6_helper,
 202                .pf             = NFPROTO_IPV6,
 203                .hooknum        = NF_INET_POST_ROUTING,
 204                .priority       = NF_IP6_PRI_CONNTRACK_HELPER,
 205        },
 206        {
 207                .hook           = ipv6_confirm,
 208                .pf             = NFPROTO_IPV6,
 209                .hooknum        = NF_INET_POST_ROUTING,
 210                .priority       = NF_IP6_PRI_LAST,
 211        },
 212        {
 213                .hook           = ipv6_helper,
 214                .pf             = NFPROTO_IPV6,
 215                .hooknum        = NF_INET_LOCAL_IN,
 216                .priority       = NF_IP6_PRI_CONNTRACK_HELPER,
 217        },
 218        {
 219                .hook           = ipv6_confirm,
 220                .pf             = NFPROTO_IPV6,
 221                .hooknum        = NF_INET_LOCAL_IN,
 222                .priority       = NF_IP6_PRI_LAST-1,
 223        },
 224};
 225
 226static int
 227ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len)
 228{
 229        const struct inet_sock *inet = inet_sk(sk);
 230        const struct ipv6_pinfo *inet6 = inet6_sk(sk);
 231        const struct nf_conntrack_tuple_hash *h;
 232        struct sockaddr_in6 sin6;
 233        struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 };
 234        struct nf_conn *ct;
 235
 236        tuple.src.u3.in6 = sk->sk_v6_rcv_saddr;
 237        tuple.src.u.tcp.port = inet->inet_sport;
 238        tuple.dst.u3.in6 = sk->sk_v6_daddr;
 239        tuple.dst.u.tcp.port = inet->inet_dport;
 240        tuple.dst.protonum = sk->sk_protocol;
 241
 242        if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP)
 243                return -ENOPROTOOPT;
 244
 245        if (*len < 0 || (unsigned int) *len < sizeof(sin6))
 246                return -EINVAL;
 247
 248        h = nf_conntrack_find_get(sock_net(sk), &nf_ct_zone_dflt, &tuple);
 249        if (!h) {
 250                pr_debug("IP6T_SO_ORIGINAL_DST: Can't find %pI6c/%u-%pI6c/%u.\n",
 251                         &tuple.src.u3.ip6, ntohs(tuple.src.u.tcp.port),
 252                         &tuple.dst.u3.ip6, ntohs(tuple.dst.u.tcp.port));
 253                return -ENOENT;
 254        }
 255
 256        ct = nf_ct_tuplehash_to_ctrack(h);
 257
 258        sin6.sin6_family = AF_INET6;
 259        sin6.sin6_port = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port;
 260        sin6.sin6_flowinfo = inet6->flow_label & IPV6_FLOWINFO_MASK;
 261        memcpy(&sin6.sin6_addr,
 262                &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in6,
 263                                        sizeof(sin6.sin6_addr));
 264
 265        nf_ct_put(ct);
 266        sin6.sin6_scope_id = ipv6_iface_scope_id(&sin6.sin6_addr,
 267                                                 sk->sk_bound_dev_if);
 268        return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0;
 269}
 270
 271#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 272
 273#include <linux/netfilter/nfnetlink.h>
 274#include <linux/netfilter/nfnetlink_conntrack.h>
 275
 276static int ipv6_tuple_to_nlattr(struct sk_buff *skb,
 277                                const struct nf_conntrack_tuple *tuple)
 278{
 279        if (nla_put_in6_addr(skb, CTA_IP_V6_SRC, &tuple->src.u3.in6) ||
 280            nla_put_in6_addr(skb, CTA_IP_V6_DST, &tuple->dst.u3.in6))
 281                goto nla_put_failure;
 282        return 0;
 283
 284nla_put_failure:
 285        return -1;
 286}
 287
 288static const struct nla_policy ipv6_nla_policy[CTA_IP_MAX+1] = {
 289        [CTA_IP_V6_SRC] = { .len = sizeof(u_int32_t)*4 },
 290        [CTA_IP_V6_DST] = { .len = sizeof(u_int32_t)*4 },
 291};
 292
 293static int ipv6_nlattr_to_tuple(struct nlattr *tb[],
 294                                struct nf_conntrack_tuple *t)
 295{
 296        if (!tb[CTA_IP_V6_SRC] || !tb[CTA_IP_V6_DST])
 297                return -EINVAL;
 298
 299        t->src.u3.in6 = nla_get_in6_addr(tb[CTA_IP_V6_SRC]);
 300        t->dst.u3.in6 = nla_get_in6_addr(tb[CTA_IP_V6_DST]);
 301
 302        return 0;
 303}
 304
 305static int ipv6_nlattr_tuple_size(void)
 306{
 307        return nla_policy_len(ipv6_nla_policy, CTA_IP_MAX + 1);
 308}
 309#endif
 310
 311struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
 312        .l3proto                = PF_INET6,
 313        .name                   = "ipv6",
 314        .pkt_to_tuple           = ipv6_pkt_to_tuple,
 315        .invert_tuple           = ipv6_invert_tuple,
 316        .print_tuple            = ipv6_print_tuple,
 317        .get_l4proto            = ipv6_get_l4proto,
 318#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 319        .tuple_to_nlattr        = ipv6_tuple_to_nlattr,
 320        .nlattr_tuple_size      = ipv6_nlattr_tuple_size,
 321        .nlattr_to_tuple        = ipv6_nlattr_to_tuple,
 322        .nla_policy             = ipv6_nla_policy,
 323#endif
 324        .me                     = THIS_MODULE,
 325};
 326
 327MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6));
 328MODULE_LICENSE("GPL");
 329MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
 330
 331static struct nf_sockopt_ops so_getorigdst6 = {
 332        .pf             = NFPROTO_IPV6,
 333        .get_optmin     = IP6T_SO_ORIGINAL_DST,
 334        .get_optmax     = IP6T_SO_ORIGINAL_DST + 1,
 335        .get            = ipv6_getorigdst,
 336        .owner          = THIS_MODULE,
 337};
 338
 339static int ipv6_net_init(struct net *net)
 340{
 341        int ret = 0;
 342
 343        ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp6);
 344        if (ret < 0) {
 345                pr_err("nf_conntrack_tcp6: pernet registration failed\n");
 346                goto out;
 347        }
 348        ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp6);
 349        if (ret < 0) {
 350                pr_err("nf_conntrack_udp6: pernet registration failed\n");
 351                goto cleanup_tcp6;
 352        }
 353        ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmpv6);
 354        if (ret < 0) {
 355                pr_err("nf_conntrack_icmp6: pernet registration failed\n");
 356                goto cleanup_udp6;
 357        }
 358        ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv6);
 359        if (ret < 0) {
 360                pr_err("nf_conntrack_ipv6: pernet registration failed.\n");
 361                goto cleanup_icmpv6;
 362        }
 363        return 0;
 364 cleanup_icmpv6:
 365        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6);
 366 cleanup_udp6:
 367        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6);
 368 cleanup_tcp6:
 369        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6);
 370 out:
 371        return ret;
 372}
 373
 374static void ipv6_net_exit(struct net *net)
 375{
 376        nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv6);
 377        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6);
 378        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6);
 379        nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6);
 380}
 381
 382static struct pernet_operations ipv6_net_ops = {
 383        .init = ipv6_net_init,
 384        .exit = ipv6_net_exit,
 385};
 386
 387static int __init nf_conntrack_l3proto_ipv6_init(void)
 388{
 389        int ret = 0;
 390
 391        need_conntrack();
 392        nf_defrag_ipv6_enable();
 393
 394        ret = nf_register_sockopt(&so_getorigdst6);
 395        if (ret < 0) {
 396                pr_err("Unable to register netfilter socket option\n");
 397                return ret;
 398        }
 399
 400        ret = register_pernet_subsys(&ipv6_net_ops);
 401        if (ret < 0)
 402                goto cleanup_sockopt;
 403
 404        ret = nf_register_hooks(ipv6_conntrack_ops,
 405                                ARRAY_SIZE(ipv6_conntrack_ops));
 406        if (ret < 0) {
 407                pr_err("nf_conntrack_ipv6: can't register pre-routing defrag "
 408                       "hook.\n");
 409                goto cleanup_pernet;
 410        }
 411
 412        ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp6);
 413        if (ret < 0) {
 414                pr_err("nf_conntrack_ipv6: can't register tcp6 proto.\n");
 415                goto cleanup_hooks;
 416        }
 417
 418        ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp6);
 419        if (ret < 0) {
 420                pr_err("nf_conntrack_ipv6: can't register udp6 proto.\n");
 421                goto cleanup_tcp6;
 422        }
 423
 424        ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmpv6);
 425        if (ret < 0) {
 426                pr_err("nf_conntrack_ipv6: can't register icmpv6 proto.\n");
 427                goto cleanup_udp6;
 428        }
 429
 430        ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6);
 431        if (ret < 0) {
 432                pr_err("nf_conntrack_ipv6: can't register ipv6 proto.\n");
 433                goto cleanup_icmpv6;
 434        }
 435        return ret;
 436
 437 cleanup_icmpv6:
 438        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
 439 cleanup_udp6:
 440        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6);
 441 cleanup_tcp6:
 442        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
 443 cleanup_hooks:
 444        nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
 445 cleanup_pernet:
 446        unregister_pernet_subsys(&ipv6_net_ops);
 447 cleanup_sockopt:
 448        nf_unregister_sockopt(&so_getorigdst6);
 449        return ret;
 450}
 451
 452static void __exit nf_conntrack_l3proto_ipv6_fini(void)
 453{
 454        synchronize_net();
 455        nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
 456        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
 457        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6);
 458        nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
 459        nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
 460        unregister_pernet_subsys(&ipv6_net_ops);
 461        nf_unregister_sockopt(&so_getorigdst6);
 462}
 463
 464module_init(nf_conntrack_l3proto_ipv6_init);
 465module_exit(nf_conntrack_l3proto_ipv6_fini);
 466