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
  31#include "nf_internals.h"
  32
  33static const unsigned int nf_ct_icmpv6_timeout = 30*HZ;
  34
  35bool icmpv6_pkt_to_tuple(const struct sk_buff *skb,
  36                         unsigned int dataoff,
  37                         struct net *net,
  38                         struct nf_conntrack_tuple *tuple)
  39{
  40        const struct icmp6hdr *hp;
  41        struct icmp6hdr _hdr;
  42
  43        hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
  44        if (hp == NULL)
  45                return false;
  46        tuple->dst.u.icmp.type = hp->icmp6_type;
  47        tuple->src.u.icmp.id = hp->icmp6_identifier;
  48        tuple->dst.u.icmp.code = hp->icmp6_code;
  49
  50        return true;
  51}
  52
  53/* Add 1; spaces filled with 0. */
  54static const u_int8_t invmap[] = {
  55        [ICMPV6_ECHO_REQUEST - 128]     = ICMPV6_ECHO_REPLY + 1,
  56        [ICMPV6_ECHO_REPLY - 128]       = ICMPV6_ECHO_REQUEST + 1,
  57        [ICMPV6_NI_QUERY - 128]         = ICMPV6_NI_REPLY + 1,
  58        [ICMPV6_NI_REPLY - 128]         = ICMPV6_NI_QUERY + 1
  59};
  60
  61static const u_int8_t noct_valid_new[] = {
  62        [ICMPV6_MGM_QUERY - 130] = 1,
  63        [ICMPV6_MGM_REPORT - 130] = 1,
  64        [ICMPV6_MGM_REDUCTION - 130] = 1,
  65        [NDISC_ROUTER_SOLICITATION - 130] = 1,
  66        [NDISC_ROUTER_ADVERTISEMENT - 130] = 1,
  67        [NDISC_NEIGHBOUR_SOLICITATION - 130] = 1,
  68        [NDISC_NEIGHBOUR_ADVERTISEMENT - 130] = 1,
  69        [ICMPV6_MLD2_REPORT - 130] = 1
  70};
  71
  72bool nf_conntrack_invert_icmpv6_tuple(struct nf_conntrack_tuple *tuple,
  73                                      const struct nf_conntrack_tuple *orig)
  74{
  75        int type = orig->dst.u.icmp.type - 128;
  76        if (type < 0 || type >= sizeof(invmap) || !invmap[type])
  77                return false;
  78
  79        tuple->src.u.icmp.id   = orig->src.u.icmp.id;
  80        tuple->dst.u.icmp.type = invmap[type] - 1;
  81        tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
  82        return true;
  83}
  84
  85static unsigned int *icmpv6_get_timeouts(struct net *net)
  86{
  87        return &nf_icmpv6_pernet(net)->timeout;
  88}
  89
  90/* Returns verdict for packet, or -1 for invalid. */
  91int nf_conntrack_icmpv6_packet(struct nf_conn *ct,
  92                               struct sk_buff *skb,
  93                               enum ip_conntrack_info ctinfo,
  94                               const struct nf_hook_state *state)
  95{
  96        unsigned int *timeout = nf_ct_timeout_lookup(ct);
  97        static const u8 valid_new[] = {
  98                [ICMPV6_ECHO_REQUEST - 128] = 1,
  99                [ICMPV6_NI_QUERY - 128] = 1
 100        };
 101
 102        if (state->pf != NFPROTO_IPV6)
 103                return -NF_ACCEPT;
 104
 105        if (!nf_ct_is_confirmed(ct)) {
 106                int type = ct->tuplehash[0].tuple.dst.u.icmp.type - 128;
 107
 108                if (type < 0 || type >= sizeof(valid_new) || !valid_new[type]) {
 109                        /* Can't create a new ICMPv6 `conn' with this. */
 110                        pr_debug("icmpv6: can't create new conn with type %u\n",
 111                                 type + 128);
 112                        nf_ct_dump_tuple_ipv6(&ct->tuplehash[0].tuple);
 113                        return -NF_ACCEPT;
 114                }
 115        }
 116
 117        if (!timeout)
 118                timeout = icmpv6_get_timeouts(nf_ct_net(ct));
 119
 120        /* Do not immediately delete the connection after the first
 121           successful reply to avoid excessive conntrackd traffic
 122           and also to handle correctly ICMP echo reply duplicates. */
 123        nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
 124
 125        return NF_ACCEPT;
 126}
 127
 128
 129static void icmpv6_error_log(const struct sk_buff *skb,
 130                             const struct nf_hook_state *state,
 131                             const char *msg)
 132{
 133        nf_l4proto_log_invalid(skb, state->net, state->pf,
 134                               IPPROTO_ICMPV6, "%s", msg);
 135}
 136
 137int nf_conntrack_icmpv6_error(struct nf_conn *tmpl,
 138                              struct sk_buff *skb,
 139                              unsigned int dataoff,
 140                              const struct nf_hook_state *state)
 141{
 142        union nf_inet_addr outer_daddr;
 143        const struct icmp6hdr *icmp6h;
 144        struct icmp6hdr _ih;
 145        int type;
 146
 147        icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih);
 148        if (icmp6h == NULL) {
 149                icmpv6_error_log(skb, state, "short packet");
 150                return -NF_ACCEPT;
 151        }
 152
 153        if (state->hook == NF_INET_PRE_ROUTING &&
 154            state->net->ct.sysctl_checksum &&
 155            nf_ip6_checksum(skb, state->hook, dataoff, IPPROTO_ICMPV6)) {
 156                icmpv6_error_log(skb, state, "ICMPv6 checksum failed");
 157                return -NF_ACCEPT;
 158        }
 159
 160        type = icmp6h->icmp6_type - 130;
 161        if (type >= 0 && type < sizeof(noct_valid_new) &&
 162            noct_valid_new[type]) {
 163                nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
 164                return NF_ACCEPT;
 165        }
 166
 167        /* is not error message ? */
 168        if (icmp6h->icmp6_type >= 128)
 169                return NF_ACCEPT;
 170
 171        memcpy(&outer_daddr.ip6, &ipv6_hdr(skb)->daddr,
 172               sizeof(outer_daddr.ip6));
 173        dataoff += sizeof(*icmp6h);
 174        return nf_conntrack_inet_error(tmpl, skb, dataoff, state,
 175                                       IPPROTO_ICMPV6, &outer_daddr);
 176}
 177
 178#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 179
 180#include <linux/netfilter/nfnetlink.h>
 181#include <linux/netfilter/nfnetlink_conntrack.h>
 182static int icmpv6_tuple_to_nlattr(struct sk_buff *skb,
 183                                  const struct nf_conntrack_tuple *t)
 184{
 185        if (nla_put_be16(skb, CTA_PROTO_ICMPV6_ID, t->src.u.icmp.id) ||
 186            nla_put_u8(skb, CTA_PROTO_ICMPV6_TYPE, t->dst.u.icmp.type) ||
 187            nla_put_u8(skb, CTA_PROTO_ICMPV6_CODE, t->dst.u.icmp.code))
 188                goto nla_put_failure;
 189        return 0;
 190
 191nla_put_failure:
 192        return -1;
 193}
 194
 195static const struct nla_policy icmpv6_nla_policy[CTA_PROTO_MAX+1] = {
 196        [CTA_PROTO_ICMPV6_TYPE] = { .type = NLA_U8 },
 197        [CTA_PROTO_ICMPV6_CODE] = { .type = NLA_U8 },
 198        [CTA_PROTO_ICMPV6_ID]   = { .type = NLA_U16 },
 199};
 200
 201static int icmpv6_nlattr_to_tuple(struct nlattr *tb[],
 202                                struct nf_conntrack_tuple *tuple,
 203                                u_int32_t flags)
 204{
 205        if (flags & CTA_FILTER_FLAG(CTA_PROTO_ICMPV6_TYPE)) {
 206                if (!tb[CTA_PROTO_ICMPV6_TYPE])
 207                        return -EINVAL;
 208
 209                tuple->dst.u.icmp.type = nla_get_u8(tb[CTA_PROTO_ICMPV6_TYPE]);
 210                if (tuple->dst.u.icmp.type < 128 ||
 211                    tuple->dst.u.icmp.type - 128 >= sizeof(invmap) ||
 212                    !invmap[tuple->dst.u.icmp.type - 128])
 213                        return -EINVAL;
 214        }
 215
 216        if (flags & CTA_FILTER_FLAG(CTA_PROTO_ICMPV6_CODE)) {
 217                if (!tb[CTA_PROTO_ICMPV6_CODE])
 218                        return -EINVAL;
 219
 220                tuple->dst.u.icmp.code = nla_get_u8(tb[CTA_PROTO_ICMPV6_CODE]);
 221        }
 222
 223        if (flags & CTA_FILTER_FLAG(CTA_PROTO_ICMPV6_ID)) {
 224                if (!tb[CTA_PROTO_ICMPV6_ID])
 225                        return -EINVAL;
 226
 227                tuple->src.u.icmp.id = nla_get_be16(tb[CTA_PROTO_ICMPV6_ID]);
 228        }
 229
 230        return 0;
 231}
 232
 233static unsigned int icmpv6_nlattr_tuple_size(void)
 234{
 235        static unsigned int size __read_mostly;
 236
 237        if (!size)
 238                size = nla_policy_len(icmpv6_nla_policy, CTA_PROTO_MAX + 1);
 239
 240        return size;
 241}
 242#endif
 243
 244#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 245
 246#include <linux/netfilter/nfnetlink.h>
 247#include <linux/netfilter/nfnetlink_cttimeout.h>
 248
 249static int icmpv6_timeout_nlattr_to_obj(struct nlattr *tb[],
 250                                        struct net *net, void *data)
 251{
 252        unsigned int *timeout = data;
 253        struct nf_icmp_net *in = nf_icmpv6_pernet(net);
 254
 255        if (!timeout)
 256                timeout = icmpv6_get_timeouts(net);
 257        if (tb[CTA_TIMEOUT_ICMPV6_TIMEOUT]) {
 258                *timeout =
 259                    ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMPV6_TIMEOUT])) * HZ;
 260        } else {
 261                /* Set default ICMPv6 timeout. */
 262                *timeout = in->timeout;
 263        }
 264        return 0;
 265}
 266
 267static int
 268icmpv6_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
 269{
 270        const unsigned int *timeout = data;
 271
 272        if (nla_put_be32(skb, CTA_TIMEOUT_ICMPV6_TIMEOUT, htonl(*timeout / HZ)))
 273                goto nla_put_failure;
 274        return 0;
 275
 276nla_put_failure:
 277        return -ENOSPC;
 278}
 279
 280static const struct nla_policy
 281icmpv6_timeout_nla_policy[CTA_TIMEOUT_ICMPV6_MAX+1] = {
 282        [CTA_TIMEOUT_ICMPV6_TIMEOUT]    = { .type = NLA_U32 },
 283};
 284#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 285
 286#ifdef CONFIG_SYSCTL
 287static struct ctl_table icmpv6_sysctl_table[] = {
 288        {
 289                .procname       = "nf_conntrack_icmpv6_timeout",
 290                .maxlen         = sizeof(unsigned int),
 291                .mode           = 0644,
 292                .proc_handler   = proc_dointvec_jiffies,
 293        },
 294        { }
 295};
 296#endif /* CONFIG_SYSCTL */
 297
 298static int icmpv6_kmemdup_sysctl_table(struct nf_proto_net *pn,
 299                                       struct nf_icmp_net *in)
 300{
 301#ifdef CONFIG_SYSCTL
 302        pn->ctl_table = kmemdup(icmpv6_sysctl_table,
 303                                sizeof(icmpv6_sysctl_table),
 304                                GFP_KERNEL);
 305        if (!pn->ctl_table)
 306                return -ENOMEM;
 307
 308        pn->ctl_table[0].data = &in->timeout;
 309#endif
 310        return 0;
 311}
 312
 313static int icmpv6_init_net(struct net *net)
 314{
 315        struct nf_icmp_net *in = nf_icmpv6_pernet(net);
 316        struct nf_proto_net *pn = &in->pn;
 317
 318        in->timeout = nf_ct_icmpv6_timeout;
 319
 320        return icmpv6_kmemdup_sysctl_table(pn, in);
 321}
 322
 323static struct nf_proto_net *icmpv6_get_net_proto(struct net *net)
 324{
 325        return &net->ct.nf_ct_proto.icmpv6.pn;
 326}
 327
 328const struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 =
 329{
 330        .l4proto                = IPPROTO_ICMPV6,
 331#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 332        .tuple_to_nlattr        = icmpv6_tuple_to_nlattr,
 333        .nlattr_tuple_size      = icmpv6_nlattr_tuple_size,
 334        .nlattr_to_tuple        = icmpv6_nlattr_to_tuple,
 335        .nla_policy             = icmpv6_nla_policy,
 336#endif
 337#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 338        .ctnl_timeout           = {
 339                .nlattr_to_obj  = icmpv6_timeout_nlattr_to_obj,
 340                .obj_to_nlattr  = icmpv6_timeout_obj_to_nlattr,
 341                .nlattr_max     = CTA_TIMEOUT_ICMP_MAX,
 342                .obj_size       = sizeof(unsigned int),
 343                .nla_policy     = icmpv6_timeout_nla_policy,
 344        },
 345#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 346        .init_net               = icmpv6_init_net,
 347        .get_net_proto          = icmpv6_get_net_proto,
 348};
 349