linux/net/netfilter/nf_conntrack_proto_icmp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* (C) 1999-2001 Paul `Rusty' Russell
   3 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
   4 * (C) 2006-2010 Patrick McHardy <kaber@trash.net>
   5 */
   6
   7#include <linux/types.h>
   8#include <linux/timer.h>
   9#include <linux/netfilter.h>
  10#include <linux/in.h>
  11#include <linux/icmp.h>
  12#include <linux/seq_file.h>
  13#include <net/ip.h>
  14#include <net/checksum.h>
  15#include <linux/netfilter_ipv4.h>
  16#include <net/netfilter/nf_conntrack_tuple.h>
  17#include <net/netfilter/nf_conntrack_l4proto.h>
  18#include <net/netfilter/nf_conntrack_core.h>
  19#include <net/netfilter/nf_conntrack_timeout.h>
  20#include <net/netfilter/nf_conntrack_zones.h>
  21#include <net/netfilter/nf_log.h>
  22
  23static const unsigned int nf_ct_icmp_timeout = 30*HZ;
  24
  25bool icmp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
  26                       struct net *net, struct nf_conntrack_tuple *tuple)
  27{
  28        const struct icmphdr *hp;
  29        struct icmphdr _hdr;
  30
  31        hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
  32        if (hp == NULL)
  33                return false;
  34
  35        tuple->dst.u.icmp.type = hp->type;
  36        tuple->src.u.icmp.id = hp->un.echo.id;
  37        tuple->dst.u.icmp.code = hp->code;
  38
  39        return true;
  40}
  41
  42/* Add 1; spaces filled with 0. */
  43static const u_int8_t invmap[] = {
  44        [ICMP_ECHO] = ICMP_ECHOREPLY + 1,
  45        [ICMP_ECHOREPLY] = ICMP_ECHO + 1,
  46        [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
  47        [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
  48        [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
  49        [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
  50        [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
  51        [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1
  52};
  53
  54bool nf_conntrack_invert_icmp_tuple(struct nf_conntrack_tuple *tuple,
  55                                    const struct nf_conntrack_tuple *orig)
  56{
  57        if (orig->dst.u.icmp.type >= sizeof(invmap) ||
  58            !invmap[orig->dst.u.icmp.type])
  59                return false;
  60
  61        tuple->src.u.icmp.id = orig->src.u.icmp.id;
  62        tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1;
  63        tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
  64        return true;
  65}
  66
  67/* Returns verdict for packet, or -1 for invalid. */
  68int nf_conntrack_icmp_packet(struct nf_conn *ct,
  69                             struct sk_buff *skb,
  70                             enum ip_conntrack_info ctinfo,
  71                             const struct nf_hook_state *state)
  72{
  73        /* Do not immediately delete the connection after the first
  74           successful reply to avoid excessive conntrackd traffic
  75           and also to handle correctly ICMP echo reply duplicates. */
  76        unsigned int *timeout = nf_ct_timeout_lookup(ct);
  77        static const u_int8_t valid_new[] = {
  78                [ICMP_ECHO] = 1,
  79                [ICMP_TIMESTAMP] = 1,
  80                [ICMP_INFO_REQUEST] = 1,
  81                [ICMP_ADDRESS] = 1
  82        };
  83
  84        if (state->pf != NFPROTO_IPV4)
  85                return -NF_ACCEPT;
  86
  87        if (ct->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new) ||
  88            !valid_new[ct->tuplehash[0].tuple.dst.u.icmp.type]) {
  89                /* Can't create a new ICMP `conn' with this. */
  90                pr_debug("icmp: can't create new conn with type %u\n",
  91                         ct->tuplehash[0].tuple.dst.u.icmp.type);
  92                nf_ct_dump_tuple_ip(&ct->tuplehash[0].tuple);
  93                return -NF_ACCEPT;
  94        }
  95
  96        if (!timeout)
  97                timeout = &nf_icmp_pernet(nf_ct_net(ct))->timeout;
  98
  99        nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
 100        return NF_ACCEPT;
 101}
 102
 103/* Check inner header is related to any of the existing connections */
 104int nf_conntrack_inet_error(struct nf_conn *tmpl, struct sk_buff *skb,
 105                            unsigned int dataoff,
 106                            const struct nf_hook_state *state,
 107                            u8 l4proto, union nf_inet_addr *outer_daddr)
 108{
 109        struct nf_conntrack_tuple innertuple, origtuple;
 110        const struct nf_conntrack_tuple_hash *h;
 111        const struct nf_conntrack_zone *zone;
 112        enum ip_conntrack_info ctinfo;
 113        struct nf_conntrack_zone tmp;
 114        union nf_inet_addr *ct_daddr;
 115        enum ip_conntrack_dir dir;
 116        struct nf_conn *ct;
 117
 118        WARN_ON(skb_nfct(skb));
 119        zone = nf_ct_zone_tmpl(tmpl, skb, &tmp);
 120
 121        /* Are they talking about one of our connections? */
 122        if (!nf_ct_get_tuplepr(skb, dataoff,
 123                               state->pf, state->net, &origtuple))
 124                return -NF_ACCEPT;
 125
 126        /* Ordinarily, we'd expect the inverted tupleproto, but it's
 127           been preserved inside the ICMP. */
 128        if (!nf_ct_invert_tuple(&innertuple, &origtuple))
 129                return -NF_ACCEPT;
 130
 131        h = nf_conntrack_find_get(state->net, zone, &innertuple);
 132        if (!h)
 133                return -NF_ACCEPT;
 134
 135        /* Consider: A -> T (=This machine) -> B
 136         *   Conntrack entry will look like this:
 137         *      Original:  A->B
 138         *      Reply:     B->T (SNAT case) OR A
 139         *
 140         * When this function runs, we got packet that looks like this:
 141         * iphdr|icmphdr|inner_iphdr|l4header (tcp, udp, ..).
 142         *
 143         * Above nf_conntrack_find_get() makes lookup based on inner_hdr,
 144         * so we should expect that destination of the found connection
 145         * matches outer header destination address.
 146         *
 147         * In above example, we can consider these two cases:
 148         *  1. Error coming in reply direction from B or M (middle box) to
 149         *     T (SNAT case) or A.
 150         *     Inner saddr will be B, dst will be T or A.
 151         *     The found conntrack will be reply tuple (B->T/A).
 152         *  2. Error coming in original direction from A or M to B.
 153         *     Inner saddr will be A, inner daddr will be B.
 154         *     The found conntrack will be original tuple (A->B).
 155         *
 156         * In both cases, conntrack[dir].dst == inner.dst.
 157         *
 158         * A bogus packet could look like this:
 159         *   Inner: B->T
 160         *   Outer: B->X (other machine reachable by T).
 161         *
 162         * In this case, lookup yields connection A->B and will
 163         * set packet from B->X as *RELATED*, even though no connection
 164         * from X was ever seen.
 165         */
 166        ct = nf_ct_tuplehash_to_ctrack(h);
 167        dir = NF_CT_DIRECTION(h);
 168        ct_daddr = &ct->tuplehash[dir].tuple.dst.u3;
 169        if (!nf_inet_addr_cmp(outer_daddr, ct_daddr)) {
 170                if (state->pf == AF_INET) {
 171                        nf_l4proto_log_invalid(skb, state->net, state->pf,
 172                                               l4proto,
 173                                               "outer daddr %pI4 != inner %pI4",
 174                                               &outer_daddr->ip, &ct_daddr->ip);
 175                } else if (state->pf == AF_INET6) {
 176                        nf_l4proto_log_invalid(skb, state->net, state->pf,
 177                                               l4proto,
 178                                               "outer daddr %pI6 != inner %pI6",
 179                                               &outer_daddr->ip6, &ct_daddr->ip6);
 180                }
 181                nf_ct_put(ct);
 182                return -NF_ACCEPT;
 183        }
 184
 185        ctinfo = IP_CT_RELATED;
 186        if (dir == IP_CT_DIR_REPLY)
 187                ctinfo += IP_CT_IS_REPLY;
 188
 189        /* Update skb to refer to this connection */
 190        nf_ct_set(skb, ct, ctinfo);
 191        return NF_ACCEPT;
 192}
 193
 194static void icmp_error_log(const struct sk_buff *skb,
 195                           const struct nf_hook_state *state,
 196                           const char *msg)
 197{
 198        nf_l4proto_log_invalid(skb, state->net, state->pf,
 199                               IPPROTO_ICMP, "%s", msg);
 200}
 201
 202/* Small and modified version of icmp_rcv */
 203int nf_conntrack_icmpv4_error(struct nf_conn *tmpl,
 204                              struct sk_buff *skb, unsigned int dataoff,
 205                              const struct nf_hook_state *state)
 206{
 207        union nf_inet_addr outer_daddr;
 208        const struct icmphdr *icmph;
 209        struct icmphdr _ih;
 210
 211        /* Not enough header? */
 212        icmph = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih);
 213        if (icmph == NULL) {
 214                icmp_error_log(skb, state, "short packet");
 215                return -NF_ACCEPT;
 216        }
 217
 218        /* See nf_conntrack_proto_tcp.c */
 219        if (state->net->ct.sysctl_checksum &&
 220            state->hook == NF_INET_PRE_ROUTING &&
 221            nf_ip_checksum(skb, state->hook, dataoff, IPPROTO_ICMP)) {
 222                icmp_error_log(skb, state, "bad hw icmp checksum");
 223                return -NF_ACCEPT;
 224        }
 225
 226        /*
 227         *      18 is the highest 'known' ICMP type. Anything else is a mystery
 228         *
 229         *      RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently
 230         *                discarded.
 231         */
 232        if (icmph->type > NR_ICMP_TYPES) {
 233                icmp_error_log(skb, state, "invalid icmp type");
 234                return -NF_ACCEPT;
 235        }
 236
 237        /* Need to track icmp error message? */
 238        if (icmph->type != ICMP_DEST_UNREACH &&
 239            icmph->type != ICMP_SOURCE_QUENCH &&
 240            icmph->type != ICMP_TIME_EXCEEDED &&
 241            icmph->type != ICMP_PARAMETERPROB &&
 242            icmph->type != ICMP_REDIRECT)
 243                return NF_ACCEPT;
 244
 245        memset(&outer_daddr, 0, sizeof(outer_daddr));
 246        outer_daddr.ip = ip_hdr(skb)->daddr;
 247
 248        dataoff += sizeof(*icmph);
 249        return nf_conntrack_inet_error(tmpl, skb, dataoff, state,
 250                                       IPPROTO_ICMP, &outer_daddr);
 251}
 252
 253#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 254
 255#include <linux/netfilter/nfnetlink.h>
 256#include <linux/netfilter/nfnetlink_conntrack.h>
 257
 258static int icmp_tuple_to_nlattr(struct sk_buff *skb,
 259                                const struct nf_conntrack_tuple *t)
 260{
 261        if (nla_put_be16(skb, CTA_PROTO_ICMP_ID, t->src.u.icmp.id) ||
 262            nla_put_u8(skb, CTA_PROTO_ICMP_TYPE, t->dst.u.icmp.type) ||
 263            nla_put_u8(skb, CTA_PROTO_ICMP_CODE, t->dst.u.icmp.code))
 264                goto nla_put_failure;
 265        return 0;
 266
 267nla_put_failure:
 268        return -1;
 269}
 270
 271static const struct nla_policy icmp_nla_policy[CTA_PROTO_MAX+1] = {
 272        [CTA_PROTO_ICMP_TYPE]   = { .type = NLA_U8 },
 273        [CTA_PROTO_ICMP_CODE]   = { .type = NLA_U8 },
 274        [CTA_PROTO_ICMP_ID]     = { .type = NLA_U16 },
 275};
 276
 277static int icmp_nlattr_to_tuple(struct nlattr *tb[],
 278                                struct nf_conntrack_tuple *tuple)
 279{
 280        if (!tb[CTA_PROTO_ICMP_TYPE] ||
 281            !tb[CTA_PROTO_ICMP_CODE] ||
 282            !tb[CTA_PROTO_ICMP_ID])
 283                return -EINVAL;
 284
 285        tuple->dst.u.icmp.type = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]);
 286        tuple->dst.u.icmp.code = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]);
 287        tuple->src.u.icmp.id = nla_get_be16(tb[CTA_PROTO_ICMP_ID]);
 288
 289        if (tuple->dst.u.icmp.type >= sizeof(invmap) ||
 290            !invmap[tuple->dst.u.icmp.type])
 291                return -EINVAL;
 292
 293        return 0;
 294}
 295
 296static unsigned int icmp_nlattr_tuple_size(void)
 297{
 298        static unsigned int size __read_mostly;
 299
 300        if (!size)
 301                size = nla_policy_len(icmp_nla_policy, CTA_PROTO_MAX + 1);
 302
 303        return size;
 304}
 305#endif
 306
 307#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 308
 309#include <linux/netfilter/nfnetlink.h>
 310#include <linux/netfilter/nfnetlink_cttimeout.h>
 311
 312static int icmp_timeout_nlattr_to_obj(struct nlattr *tb[],
 313                                      struct net *net, void *data)
 314{
 315        unsigned int *timeout = data;
 316        struct nf_icmp_net *in = nf_icmp_pernet(net);
 317
 318        if (tb[CTA_TIMEOUT_ICMP_TIMEOUT]) {
 319                if (!timeout)
 320                        timeout = &in->timeout;
 321                *timeout =
 322                        ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMP_TIMEOUT])) * HZ;
 323        } else if (timeout) {
 324                /* Set default ICMP timeout. */
 325                *timeout = in->timeout;
 326        }
 327        return 0;
 328}
 329
 330static int
 331icmp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
 332{
 333        const unsigned int *timeout = data;
 334
 335        if (nla_put_be32(skb, CTA_TIMEOUT_ICMP_TIMEOUT, htonl(*timeout / HZ)))
 336                goto nla_put_failure;
 337        return 0;
 338
 339nla_put_failure:
 340        return -ENOSPC;
 341}
 342
 343static const struct nla_policy
 344icmp_timeout_nla_policy[CTA_TIMEOUT_ICMP_MAX+1] = {
 345        [CTA_TIMEOUT_ICMP_TIMEOUT]      = { .type = NLA_U32 },
 346};
 347#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 348
 349void nf_conntrack_icmp_init_net(struct net *net)
 350{
 351        struct nf_icmp_net *in = nf_icmp_pernet(net);
 352
 353        in->timeout = nf_ct_icmp_timeout;
 354}
 355
 356const struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp =
 357{
 358        .l4proto                = IPPROTO_ICMP,
 359#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 360        .tuple_to_nlattr        = icmp_tuple_to_nlattr,
 361        .nlattr_tuple_size      = icmp_nlattr_tuple_size,
 362        .nlattr_to_tuple        = icmp_nlattr_to_tuple,
 363        .nla_policy             = icmp_nla_policy,
 364#endif
 365#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 366        .ctnl_timeout           = {
 367                .nlattr_to_obj  = icmp_timeout_nlattr_to_obj,
 368                .obj_to_nlattr  = icmp_timeout_obj_to_nlattr,
 369                .nlattr_max     = CTA_TIMEOUT_ICMP_MAX,
 370                .obj_size       = sizeof(unsigned int),
 371                .nla_policy     = icmp_timeout_nla_policy,
 372        },
 373#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 374};
 375