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 <net/netfilter/nf_conntrack.h>
  25#include <net/netfilter/nf_conntrack_helper.h>
  26#include <net/netfilter/nf_conntrack_l4proto.h>
  27#include <net/netfilter/nf_conntrack_l3proto.h>
  28#include <net/netfilter/nf_conntrack_core.h>
  29#include <net/netfilter/nf_conntrack_zones.h>
  30#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
  31#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
  32#include <net/netfilter/nf_log.h>
  33
  34static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
  35                              struct nf_conntrack_tuple *tuple)
  36{
  37        const u_int32_t *ap;
  38        u_int32_t _addrs[8];
  39
  40        ap = skb_header_pointer(skb, nhoff + offsetof(struct ipv6hdr, saddr),
  41                                sizeof(_addrs), _addrs);
  42        if (ap == NULL)
  43                return false;
  44
  45        memcpy(tuple->src.u3.ip6, ap, sizeof(tuple->src.u3.ip6));
  46        memcpy(tuple->dst.u3.ip6, ap + 4, sizeof(tuple->dst.u3.ip6));
  47
  48        return true;
  49}
  50
  51static bool ipv6_invert_tuple(struct nf_conntrack_tuple *tuple,
  52                              const struct nf_conntrack_tuple *orig)
  53{
  54        memcpy(tuple->src.u3.ip6, orig->dst.u3.ip6, sizeof(tuple->src.u3.ip6));
  55        memcpy(tuple->dst.u3.ip6, orig->src.u3.ip6, sizeof(tuple->dst.u3.ip6));
  56
  57        return true;
  58}
  59
  60static int ipv6_print_tuple(struct seq_file *s,
  61                            const struct nf_conntrack_tuple *tuple)
  62{
  63        return seq_printf(s, "src=%pI6 dst=%pI6 ",
  64                          tuple->src.u3.ip6, tuple->dst.u3.ip6);
  65}
  66
  67/*
  68 * Based on ipv6_skip_exthdr() in net/ipv6/exthdr.c
  69 *
  70 * This function parses (probably truncated) exthdr set "hdr"
  71 * of length "len". "nexthdrp" initially points to some place,
  72 * where type of the first header can be found.
  73 *
  74 * It skips all well-known exthdrs, and returns pointer to the start
  75 * of unparsable area i.e. the first header with unknown type.
  76 * if success, *nexthdr is updated by type/protocol of this header.
  77 *
  78 * NOTES: - it may return pointer pointing beyond end of packet,
  79 *          if the last recognized header is truncated in the middle.
  80 *        - if packet is truncated, so that all parsed headers are skipped,
  81 *          it returns -1.
  82 *        - if packet is fragmented, return pointer of the fragment header.
  83 *        - ESP is unparsable for now and considered like
  84 *          normal payload protocol.
  85 *        - Note also special handling of AUTH header. Thanks to IPsec wizards.
  86 */
  87
  88static int nf_ct_ipv6_skip_exthdr(const struct sk_buff *skb, int start,
  89                                  u8 *nexthdrp, int len)
  90{
  91        u8 nexthdr = *nexthdrp;
  92
  93        while (ipv6_ext_hdr(nexthdr)) {
  94                struct ipv6_opt_hdr hdr;
  95                int hdrlen;
  96
  97                if (len < (int)sizeof(struct ipv6_opt_hdr))
  98                        return -1;
  99                if (nexthdr == NEXTHDR_NONE)
 100                        break;
 101                if (nexthdr == NEXTHDR_FRAGMENT)
 102                        break;
 103                if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
 104                        BUG();
 105                if (nexthdr == NEXTHDR_AUTH)
 106                        hdrlen = (hdr.hdrlen+2)<<2;
 107                else
 108                        hdrlen = ipv6_optlen(&hdr);
 109
 110                nexthdr = hdr.nexthdr;
 111                len -= hdrlen;
 112                start += hdrlen;
 113        }
 114
 115        *nexthdrp = nexthdr;
 116        return start;
 117}
 118
 119static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
 120                            unsigned int *dataoff, u_int8_t *protonum)
 121{
 122        unsigned int extoff = nhoff + sizeof(struct ipv6hdr);
 123        unsigned char pnum;
 124        int protoff;
 125
 126        if (skb_copy_bits(skb, nhoff + offsetof(struct ipv6hdr, nexthdr),
 127                          &pnum, sizeof(pnum)) != 0) {
 128                pr_debug("ip6_conntrack_core: can't get nexthdr\n");
 129                return -NF_ACCEPT;
 130        }
 131        protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum, skb->len - extoff);
 132        /*
 133         * (protoff == skb->len) mean that the packet doesn't have no data
 134         * except of IPv6 & ext headers. but it's tracked anyway. - YK
 135         */
 136        if ((protoff < 0) || (protoff > skb->len)) {
 137                pr_debug("ip6_conntrack_core: can't find proto in pkt\n");
 138                return -NF_ACCEPT;
 139        }
 140
 141        *dataoff = protoff;
 142        *protonum = pnum;
 143        return NF_ACCEPT;
 144}
 145
 146static unsigned int ipv6_confirm(unsigned int hooknum,
 147                                 struct sk_buff *skb,
 148                                 const struct net_device *in,
 149                                 const struct net_device *out,
 150                                 int (*okfn)(struct sk_buff *))
 151{
 152        struct nf_conn *ct;
 153        const struct nf_conn_help *help;
 154        const struct nf_conntrack_helper *helper;
 155        enum ip_conntrack_info ctinfo;
 156        unsigned int ret, protoff;
 157        unsigned int extoff = (u8 *)(ipv6_hdr(skb) + 1) - skb->data;
 158        unsigned char pnum = ipv6_hdr(skb)->nexthdr;
 159
 160
 161        /* This is where we call the helper: as the packet goes out. */
 162        ct = nf_ct_get(skb, &ctinfo);
 163        if (!ct || ctinfo == IP_CT_RELATED_REPLY)
 164                goto out;
 165
 166        help = nfct_help(ct);
 167        if (!help)
 168                goto out;
 169        /* rcu_read_lock()ed by nf_hook_slow */
 170        helper = rcu_dereference(help->helper);
 171        if (!helper)
 172                goto out;
 173
 174        protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum,
 175                                         skb->len - extoff);
 176        if (protoff > skb->len || pnum == NEXTHDR_FRAGMENT) {
 177                pr_debug("proto header not found\n");
 178                return NF_ACCEPT;
 179        }
 180
 181        ret = helper->help(skb, protoff, ct, ctinfo);
 182        if (ret != NF_ACCEPT) {
 183                nf_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL,
 184                              "nf_ct_%s: dropping packet", helper->name);
 185                return ret;
 186        }
 187out:
 188        /* We've seen it coming out the other side: confirm it */
 189        return nf_conntrack_confirm(skb);
 190}
 191
 192static unsigned int __ipv6_conntrack_in(struct net *net,
 193                                        unsigned int hooknum,
 194                                        struct sk_buff *skb,
 195                                        int (*okfn)(struct sk_buff *))
 196{
 197        struct sk_buff *reasm = skb->nfct_reasm;
 198
 199        /* This packet is fragmented and has reassembled packet. */
 200        if (reasm) {
 201                /* Reassembled packet isn't parsed yet ? */
 202                if (!reasm->nfct) {
 203                        unsigned int ret;
 204
 205                        ret = nf_conntrack_in(net, PF_INET6, hooknum, reasm);
 206                        if (ret != NF_ACCEPT)
 207                                return ret;
 208                }
 209                nf_conntrack_get(reasm->nfct);
 210                skb->nfct = reasm->nfct;
 211                skb->nfctinfo = reasm->nfctinfo;
 212                return NF_ACCEPT;
 213        }
 214
 215        return nf_conntrack_in(net, PF_INET6, hooknum, skb);
 216}
 217
 218static unsigned int ipv6_conntrack_in(unsigned int hooknum,
 219                                      struct sk_buff *skb,
 220                                      const struct net_device *in,
 221                                      const struct net_device *out,
 222                                      int (*okfn)(struct sk_buff *))
 223{
 224        return __ipv6_conntrack_in(dev_net(in), hooknum, skb, okfn);
 225}
 226
 227static unsigned int ipv6_conntrack_local(unsigned int hooknum,
 228                                         struct sk_buff *skb,
 229                                         const struct net_device *in,
 230                                         const struct net_device *out,
 231                                         int (*okfn)(struct sk_buff *))
 232{
 233        /* root is playing with raw sockets. */
 234        if (skb->len < sizeof(struct ipv6hdr)) {
 235                if (net_ratelimit())
 236                        pr_notice("ipv6_conntrack_local: packet too short\n");
 237                return NF_ACCEPT;
 238        }
 239        return __ipv6_conntrack_in(dev_net(out), hooknum, skb, okfn);
 240}
 241
 242static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
 243        {
 244                .hook           = ipv6_conntrack_in,
 245                .owner          = THIS_MODULE,
 246                .pf             = NFPROTO_IPV6,
 247                .hooknum        = NF_INET_PRE_ROUTING,
 248                .priority       = NF_IP6_PRI_CONNTRACK,
 249        },
 250        {
 251                .hook           = ipv6_conntrack_local,
 252                .owner          = THIS_MODULE,
 253                .pf             = NFPROTO_IPV6,
 254                .hooknum        = NF_INET_LOCAL_OUT,
 255                .priority       = NF_IP6_PRI_CONNTRACK,
 256        },
 257        {
 258                .hook           = ipv6_confirm,
 259                .owner          = THIS_MODULE,
 260                .pf             = NFPROTO_IPV6,
 261                .hooknum        = NF_INET_POST_ROUTING,
 262                .priority       = NF_IP6_PRI_LAST,
 263        },
 264        {
 265                .hook           = ipv6_confirm,
 266                .owner          = THIS_MODULE,
 267                .pf             = NFPROTO_IPV6,
 268                .hooknum        = NF_INET_LOCAL_IN,
 269                .priority       = NF_IP6_PRI_LAST-1,
 270        },
 271};
 272
 273#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
 274
 275#include <linux/netfilter/nfnetlink.h>
 276#include <linux/netfilter/nfnetlink_conntrack.h>
 277
 278static int ipv6_tuple_to_nlattr(struct sk_buff *skb,
 279                                const struct nf_conntrack_tuple *tuple)
 280{
 281        NLA_PUT(skb, CTA_IP_V6_SRC, sizeof(u_int32_t) * 4,
 282                &tuple->src.u3.ip6);
 283        NLA_PUT(skb, CTA_IP_V6_DST, sizeof(u_int32_t) * 4,
 284                &tuple->dst.u3.ip6);
 285        return 0;
 286
 287nla_put_failure:
 288        return -1;
 289}
 290
 291static const struct nla_policy ipv6_nla_policy[CTA_IP_MAX+1] = {
 292        [CTA_IP_V6_SRC] = { .len = sizeof(u_int32_t)*4 },
 293        [CTA_IP_V6_DST] = { .len = sizeof(u_int32_t)*4 },
 294};
 295
 296static int ipv6_nlattr_to_tuple(struct nlattr *tb[],
 297                                struct nf_conntrack_tuple *t)
 298{
 299        if (!tb[CTA_IP_V6_SRC] || !tb[CTA_IP_V6_DST])
 300                return -EINVAL;
 301
 302        memcpy(&t->src.u3.ip6, nla_data(tb[CTA_IP_V6_SRC]),
 303               sizeof(u_int32_t) * 4);
 304        memcpy(&t->dst.u3.ip6, nla_data(tb[CTA_IP_V6_DST]),
 305               sizeof(u_int32_t) * 4);
 306
 307        return 0;
 308}
 309
 310static int ipv6_nlattr_tuple_size(void)
 311{
 312        return nla_policy_len(ipv6_nla_policy, CTA_IP_MAX + 1);
 313}
 314#endif
 315
 316struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
 317        .l3proto                = PF_INET6,
 318        .name                   = "ipv6",
 319        .pkt_to_tuple           = ipv6_pkt_to_tuple,
 320        .invert_tuple           = ipv6_invert_tuple,
 321        .print_tuple            = ipv6_print_tuple,
 322        .get_l4proto            = ipv6_get_l4proto,
 323#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
 324        .tuple_to_nlattr        = ipv6_tuple_to_nlattr,
 325        .nlattr_tuple_size      = ipv6_nlattr_tuple_size,
 326        .nlattr_to_tuple        = ipv6_nlattr_to_tuple,
 327        .nla_policy             = ipv6_nla_policy,
 328#endif
 329        .me                     = THIS_MODULE,
 330};
 331
 332MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6));
 333MODULE_LICENSE("GPL");
 334MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
 335
 336static int __init nf_conntrack_l3proto_ipv6_init(void)
 337{
 338        int ret = 0;
 339
 340        need_conntrack();
 341        nf_defrag_ipv6_enable();
 342
 343        ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp6);
 344        if (ret < 0) {
 345                pr_err("nf_conntrack_ipv6: can't register tcp.\n");
 346                return ret;
 347        }
 348
 349        ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp6);
 350        if (ret < 0) {
 351                pr_err("nf_conntrack_ipv6: can't register udp.\n");
 352                goto cleanup_tcp;
 353        }
 354
 355        ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_icmpv6);
 356        if (ret < 0) {
 357                pr_err("nf_conntrack_ipv6: can't register icmpv6.\n");
 358                goto cleanup_udp;
 359        }
 360
 361        ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6);
 362        if (ret < 0) {
 363                pr_err("nf_conntrack_ipv6: can't register ipv6\n");
 364                goto cleanup_icmpv6;
 365        }
 366
 367        ret = nf_register_hooks(ipv6_conntrack_ops,
 368                                ARRAY_SIZE(ipv6_conntrack_ops));
 369        if (ret < 0) {
 370                pr_err("nf_conntrack_ipv6: can't register pre-routing defrag "
 371                       "hook.\n");
 372                goto cleanup_ipv6;
 373        }
 374        return ret;
 375
 376 cleanup_ipv6:
 377        nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
 378 cleanup_icmpv6:
 379        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
 380 cleanup_udp:
 381        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6);
 382 cleanup_tcp:
 383        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
 384        return ret;
 385}
 386
 387static void __exit nf_conntrack_l3proto_ipv6_fini(void)
 388{
 389        synchronize_net();
 390        nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
 391        nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
 392        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
 393        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6);
 394        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
 395}
 396
 397module_init(nf_conntrack_l3proto_ipv6_init);
 398module_exit(nf_conntrack_l3proto_ipv6_fini);
 399