linux/net/netfilter/nf_conntrack_proto_icmpv6.c
<<
>>
Prefs
   1/*
   2 * Copyright (C)2003,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/timer.h>
  14#include <linux/module.h>
  15#include <linux/netfilter.h>
  16#include <linux/in6.h>
  17#include <linux/icmpv6.h>
  18#include <linux/ipv6.h>
  19#include <net/ipv6.h>
  20#include <net/ip6_checksum.h>
  21#include <linux/seq_file.h>
  22#include <linux/netfilter_ipv6.h>
  23#include <net/netfilter/nf_conntrack_tuple.h>
  24#include <net/netfilter/nf_conntrack_l4proto.h>
  25#include <net/netfilter/nf_conntrack_core.h>
  26#include <net/netfilter/nf_conntrack_timeout.h>
  27#include <net/netfilter/nf_conntrack_zones.h>
  28#include <net/netfilter/ipv6/nf_conntrack_icmpv6.h>
  29#include <net/netfilter/nf_log.h>
  30
  31static const unsigned int nf_ct_icmpv6_timeout = 30*HZ;
  32
  33static inline struct nf_icmp_net *icmpv6_pernet(struct net *net)
  34{
  35        return &net->ct.nf_ct_proto.icmpv6;
  36}
  37
  38static bool icmpv6_pkt_to_tuple(const struct sk_buff *skb,
  39                                unsigned int dataoff,
  40                                struct net *net,
  41                                struct nf_conntrack_tuple *tuple)
  42{
  43        const struct icmp6hdr *hp;
  44        struct icmp6hdr _hdr;
  45
  46        hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
  47        if (hp == NULL)
  48                return false;
  49        tuple->dst.u.icmp.type = hp->icmp6_type;
  50        tuple->src.u.icmp.id = hp->icmp6_identifier;
  51        tuple->dst.u.icmp.code = hp->icmp6_code;
  52
  53        return true;
  54}
  55
  56/* Add 1; spaces filled with 0. */
  57static const u_int8_t invmap[] = {
  58        [ICMPV6_ECHO_REQUEST - 128]     = ICMPV6_ECHO_REPLY + 1,
  59        [ICMPV6_ECHO_REPLY - 128]       = ICMPV6_ECHO_REQUEST + 1,
  60        [ICMPV6_NI_QUERY - 128]         = ICMPV6_NI_REPLY + 1,
  61        [ICMPV6_NI_REPLY - 128]         = ICMPV6_NI_QUERY + 1
  62};
  63
  64static const u_int8_t noct_valid_new[] = {
  65        [ICMPV6_MGM_QUERY - 130] = 1,
  66        [ICMPV6_MGM_REPORT - 130] = 1,
  67        [ICMPV6_MGM_REDUCTION - 130] = 1,
  68        [NDISC_ROUTER_SOLICITATION - 130] = 1,
  69        [NDISC_ROUTER_ADVERTISEMENT - 130] = 1,
  70        [NDISC_NEIGHBOUR_SOLICITATION - 130] = 1,
  71        [NDISC_NEIGHBOUR_ADVERTISEMENT - 130] = 1,
  72        [ICMPV6_MLD2_REPORT - 130] = 1
  73};
  74
  75static bool icmpv6_invert_tuple(struct nf_conntrack_tuple *tuple,
  76                                const struct nf_conntrack_tuple *orig)
  77{
  78        int type = orig->dst.u.icmp.type - 128;
  79        if (type < 0 || type >= sizeof(invmap) || !invmap[type])
  80                return false;
  81
  82        tuple->src.u.icmp.id   = orig->src.u.icmp.id;
  83        tuple->dst.u.icmp.type = invmap[type] - 1;
  84        tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
  85        return true;
  86}
  87
  88static unsigned int *icmpv6_get_timeouts(struct net *net)
  89{
  90        return &icmpv6_pernet(net)->timeout;
  91}
  92
  93/* Returns verdict for packet, or -1 for invalid. */
  94static int icmpv6_packet(struct nf_conn *ct,
  95                       const struct sk_buff *skb,
  96                       unsigned int dataoff,
  97                       enum ip_conntrack_info ctinfo)
  98{
  99        unsigned int *timeout = nf_ct_timeout_lookup(ct);
 100
 101        if (!timeout)
 102                timeout = icmpv6_get_timeouts(nf_ct_net(ct));
 103
 104        /* Do not immediately delete the connection after the first
 105           successful reply to avoid excessive conntrackd traffic
 106           and also to handle correctly ICMP echo reply duplicates. */
 107        nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
 108
 109        return NF_ACCEPT;
 110}
 111
 112/* Called when a new connection for this protocol found. */
 113static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb,
 114                       unsigned int dataoff)
 115{
 116        static const u_int8_t valid_new[] = {
 117                [ICMPV6_ECHO_REQUEST - 128] = 1,
 118                [ICMPV6_NI_QUERY - 128] = 1
 119        };
 120        int type = ct->tuplehash[0].tuple.dst.u.icmp.type - 128;
 121
 122        if (type < 0 || type >= sizeof(valid_new) || !valid_new[type]) {
 123                /* Can't create a new ICMPv6 `conn' with this. */
 124                pr_debug("icmpv6: can't create new conn with type %u\n",
 125                         type + 128);
 126                nf_ct_dump_tuple_ipv6(&ct->tuplehash[0].tuple);
 127                return false;
 128        }
 129        return true;
 130}
 131
 132static int
 133icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
 134                     struct sk_buff *skb,
 135                     unsigned int icmp6off)
 136{
 137        struct nf_conntrack_tuple intuple, origtuple;
 138        const struct nf_conntrack_tuple_hash *h;
 139        const struct nf_conntrack_l4proto *inproto;
 140        enum ip_conntrack_info ctinfo;
 141        struct nf_conntrack_zone tmp;
 142
 143        WARN_ON(skb_nfct(skb));
 144
 145        /* Are they talking about one of our connections? */
 146        if (!nf_ct_get_tuplepr(skb,
 147                               skb_network_offset(skb)
 148                                + sizeof(struct ipv6hdr)
 149                                + sizeof(struct icmp6hdr),
 150                               PF_INET6, net, &origtuple)) {
 151                pr_debug("icmpv6_error: Can't get tuple\n");
 152                return -NF_ACCEPT;
 153        }
 154
 155        /* rcu_read_lock()ed by nf_hook_thresh */
 156        inproto = __nf_ct_l4proto_find(PF_INET6, origtuple.dst.protonum);
 157
 158        /* Ordinarily, we'd expect the inverted tupleproto, but it's
 159           been preserved inside the ICMP. */
 160        if (!nf_ct_invert_tuple(&intuple, &origtuple, inproto)) {
 161                pr_debug("icmpv6_error: Can't invert tuple\n");
 162                return -NF_ACCEPT;
 163        }
 164
 165        ctinfo = IP_CT_RELATED;
 166
 167        h = nf_conntrack_find_get(net, nf_ct_zone_tmpl(tmpl, skb, &tmp),
 168                                  &intuple);
 169        if (!h) {
 170                pr_debug("icmpv6_error: no match\n");
 171                return -NF_ACCEPT;
 172        } else {
 173                if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
 174                        ctinfo += IP_CT_IS_REPLY;
 175        }
 176
 177        /* Update skb to refer to this connection */
 178        nf_ct_set(skb, nf_ct_tuplehash_to_ctrack(h), ctinfo);
 179        return NF_ACCEPT;
 180}
 181
 182static void icmpv6_error_log(const struct sk_buff *skb, struct net *net,
 183                             u8 pf, const char *msg)
 184{
 185        nf_l4proto_log_invalid(skb, net, pf, IPPROTO_ICMPV6, "%s", msg);
 186}
 187
 188static int
 189icmpv6_error(struct net *net, struct nf_conn *tmpl,
 190             struct sk_buff *skb, unsigned int dataoff,
 191             u8 pf, unsigned int hooknum)
 192{
 193        const struct icmp6hdr *icmp6h;
 194        struct icmp6hdr _ih;
 195        int type;
 196
 197        icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih);
 198        if (icmp6h == NULL) {
 199                icmpv6_error_log(skb, net, pf, "short packet");
 200                return -NF_ACCEPT;
 201        }
 202
 203        if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
 204            nf_ip6_checksum(skb, hooknum, dataoff, IPPROTO_ICMPV6)) {
 205                icmpv6_error_log(skb, net, pf, "ICMPv6 checksum failed");
 206                return -NF_ACCEPT;
 207        }
 208
 209        type = icmp6h->icmp6_type - 130;
 210        if (type >= 0 && type < sizeof(noct_valid_new) &&
 211            noct_valid_new[type]) {
 212                nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
 213                return NF_ACCEPT;
 214        }
 215
 216        /* is not error message ? */
 217        if (icmp6h->icmp6_type >= 128)
 218                return NF_ACCEPT;
 219
 220        return icmpv6_error_message(net, tmpl, skb, dataoff);
 221}
 222
 223#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 224
 225#include <linux/netfilter/nfnetlink.h>
 226#include <linux/netfilter/nfnetlink_conntrack.h>
 227static int icmpv6_tuple_to_nlattr(struct sk_buff *skb,
 228                                  const struct nf_conntrack_tuple *t)
 229{
 230        if (nla_put_be16(skb, CTA_PROTO_ICMPV6_ID, t->src.u.icmp.id) ||
 231            nla_put_u8(skb, CTA_PROTO_ICMPV6_TYPE, t->dst.u.icmp.type) ||
 232            nla_put_u8(skb, CTA_PROTO_ICMPV6_CODE, t->dst.u.icmp.code))
 233                goto nla_put_failure;
 234        return 0;
 235
 236nla_put_failure:
 237        return -1;
 238}
 239
 240static const struct nla_policy icmpv6_nla_policy[CTA_PROTO_MAX+1] = {
 241        [CTA_PROTO_ICMPV6_TYPE] = { .type = NLA_U8 },
 242        [CTA_PROTO_ICMPV6_CODE] = { .type = NLA_U8 },
 243        [CTA_PROTO_ICMPV6_ID]   = { .type = NLA_U16 },
 244};
 245
 246static int icmpv6_nlattr_to_tuple(struct nlattr *tb[],
 247                                struct nf_conntrack_tuple *tuple)
 248{
 249        if (!tb[CTA_PROTO_ICMPV6_TYPE] ||
 250            !tb[CTA_PROTO_ICMPV6_CODE] ||
 251            !tb[CTA_PROTO_ICMPV6_ID])
 252                return -EINVAL;
 253
 254        tuple->dst.u.icmp.type = nla_get_u8(tb[CTA_PROTO_ICMPV6_TYPE]);
 255        tuple->dst.u.icmp.code = nla_get_u8(tb[CTA_PROTO_ICMPV6_CODE]);
 256        tuple->src.u.icmp.id = nla_get_be16(tb[CTA_PROTO_ICMPV6_ID]);
 257
 258        if (tuple->dst.u.icmp.type < 128 ||
 259            tuple->dst.u.icmp.type - 128 >= sizeof(invmap) ||
 260            !invmap[tuple->dst.u.icmp.type - 128])
 261                return -EINVAL;
 262
 263        return 0;
 264}
 265
 266static unsigned int icmpv6_nlattr_tuple_size(void)
 267{
 268        static unsigned int size __read_mostly;
 269
 270        if (!size)
 271                size = nla_policy_len(icmpv6_nla_policy, CTA_PROTO_MAX + 1);
 272
 273        return size;
 274}
 275#endif
 276
 277#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 278
 279#include <linux/netfilter/nfnetlink.h>
 280#include <linux/netfilter/nfnetlink_cttimeout.h>
 281
 282static int icmpv6_timeout_nlattr_to_obj(struct nlattr *tb[],
 283                                        struct net *net, void *data)
 284{
 285        unsigned int *timeout = data;
 286        struct nf_icmp_net *in = icmpv6_pernet(net);
 287
 288        if (!timeout)
 289                timeout = icmpv6_get_timeouts(net);
 290        if (tb[CTA_TIMEOUT_ICMPV6_TIMEOUT]) {
 291                *timeout =
 292                    ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMPV6_TIMEOUT])) * HZ;
 293        } else {
 294                /* Set default ICMPv6 timeout. */
 295                *timeout = in->timeout;
 296        }
 297        return 0;
 298}
 299
 300static int
 301icmpv6_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
 302{
 303        const unsigned int *timeout = data;
 304
 305        if (nla_put_be32(skb, CTA_TIMEOUT_ICMPV6_TIMEOUT, htonl(*timeout / HZ)))
 306                goto nla_put_failure;
 307        return 0;
 308
 309nla_put_failure:
 310        return -ENOSPC;
 311}
 312
 313static const struct nla_policy
 314icmpv6_timeout_nla_policy[CTA_TIMEOUT_ICMPV6_MAX+1] = {
 315        [CTA_TIMEOUT_ICMPV6_TIMEOUT]    = { .type = NLA_U32 },
 316};
 317#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 318
 319#ifdef CONFIG_SYSCTL
 320static struct ctl_table icmpv6_sysctl_table[] = {
 321        {
 322                .procname       = "nf_conntrack_icmpv6_timeout",
 323                .maxlen         = sizeof(unsigned int),
 324                .mode           = 0644,
 325                .proc_handler   = proc_dointvec_jiffies,
 326        },
 327        { }
 328};
 329#endif /* CONFIG_SYSCTL */
 330
 331static int icmpv6_kmemdup_sysctl_table(struct nf_proto_net *pn,
 332                                       struct nf_icmp_net *in)
 333{
 334#ifdef CONFIG_SYSCTL
 335        pn->ctl_table = kmemdup(icmpv6_sysctl_table,
 336                                sizeof(icmpv6_sysctl_table),
 337                                GFP_KERNEL);
 338        if (!pn->ctl_table)
 339                return -ENOMEM;
 340
 341        pn->ctl_table[0].data = &in->timeout;
 342#endif
 343        return 0;
 344}
 345
 346static int icmpv6_init_net(struct net *net, u_int16_t proto)
 347{
 348        struct nf_icmp_net *in = icmpv6_pernet(net);
 349        struct nf_proto_net *pn = &in->pn;
 350
 351        in->timeout = nf_ct_icmpv6_timeout;
 352
 353        return icmpv6_kmemdup_sysctl_table(pn, in);
 354}
 355
 356static struct nf_proto_net *icmpv6_get_net_proto(struct net *net)
 357{
 358        return &net->ct.nf_ct_proto.icmpv6.pn;
 359}
 360
 361const struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 =
 362{
 363        .l3proto                = PF_INET6,
 364        .l4proto                = IPPROTO_ICMPV6,
 365        .pkt_to_tuple           = icmpv6_pkt_to_tuple,
 366        .invert_tuple           = icmpv6_invert_tuple,
 367        .packet                 = icmpv6_packet,
 368        .new                    = icmpv6_new,
 369        .error                  = icmpv6_error,
 370#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 371        .tuple_to_nlattr        = icmpv6_tuple_to_nlattr,
 372        .nlattr_tuple_size      = icmpv6_nlattr_tuple_size,
 373        .nlattr_to_tuple        = icmpv6_nlattr_to_tuple,
 374        .nla_policy             = icmpv6_nla_policy,
 375#endif
 376#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 377        .ctnl_timeout           = {
 378                .nlattr_to_obj  = icmpv6_timeout_nlattr_to_obj,
 379                .obj_to_nlattr  = icmpv6_timeout_obj_to_nlattr,
 380                .nlattr_max     = CTA_TIMEOUT_ICMP_MAX,
 381                .obj_size       = sizeof(unsigned int),
 382                .nla_policy     = icmpv6_timeout_nla_policy,
 383        },
 384#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 385        .init_net               = icmpv6_init_net,
 386        .get_net_proto          = icmpv6_get_net_proto,
 387};
 388