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