root/net/netfilter/nf_conntrack_proto_icmp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. icmp_pkt_to_tuple
  2. nf_conntrack_invert_icmp_tuple
  3. nf_conntrack_icmp_packet
  4. nf_conntrack_inet_error
  5. icmp_error_log
  6. nf_conntrack_icmpv4_error
  7. icmp_tuple_to_nlattr
  8. icmp_nlattr_to_tuple
  9. icmp_nlattr_tuple_size
  10. icmp_timeout_nlattr_to_obj
  11. icmp_timeout_obj_to_nlattr
  12. nf_conntrack_icmp_init_net

   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 
  23 static const unsigned int nf_ct_icmp_timeout = 30*HZ;
  24 
  25 bool 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. */
  43 static 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 
  54 bool 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. */
  68 int 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 */
 104 int 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 
 194 static 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 */
 203 int 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 
 258 static 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 
 267 nla_put_failure:
 268         return -1;
 269 }
 270 
 271 static 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 
 277 static 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 
 296 static 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 
 312 static 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 
 330 static int
 331 icmp_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 
 339 nla_put_failure:
 340         return -ENOSPC;
 341 }
 342 
 343 static const struct nla_policy
 344 icmp_timeout_nla_policy[CTA_TIMEOUT_ICMP_MAX+1] = {
 345         [CTA_TIMEOUT_ICMP_TIMEOUT]      = { .type = NLA_U32 },
 346 };
 347 #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 348 
 349 void 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 
 356 const 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 };

/* [<][>][^][v][top][bottom][index][help] */