1 /* $NetBSD: npf_alg_icmp.c,v 1.30 2018/03/23 08:34:57 maxv Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This material is based upon work partially supported by The 8 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * NPF ALG for ICMP and traceroute translations. 34 */ 35 36 #ifdef _KERNEL 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.30 2018/03/23 08:34:57 maxv Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/module.h> 42 43 #include <netinet/in_systm.h> 44 #include <netinet/in.h> 45 #include <netinet/ip.h> 46 #include <netinet/tcp.h> 47 #include <netinet/udp.h> 48 #include <netinet/ip_icmp.h> 49 #include <netinet/icmp6.h> 50 #include <net/pfil.h> 51 #endif 52 53 #include "npf_impl.h" 54 #include "npf_conn.h" 55 56 MODULE(MODULE_CLASS_MISC, npf_alg_icmp, "npf"); 57 58 /* 59 * Traceroute criteria. 60 * 61 * IANA assigned base port: 33434. However, common practice is to increase 62 * the port, thus monitor [33434-33484] range. Additional filter is low TTL. 63 */ 64 65 #define TR_BASE_PORT 33434 66 #define TR_PORT_RANGE 33484 67 #define TR_MAX_TTL 48 68 69 static npf_alg_t * alg_icmp __read_mostly; 70 71 /* 72 * npfa_icmp_match: matching inspector determines ALG case and associates 73 * our ALG with the NAT entry. 74 */ 75 static bool 76 npfa_icmp_match(npf_cache_t *npc, npf_nat_t *nt, int di) 77 { 78 const int proto = npc->npc_proto; 79 const struct ip *ip = npc->npc_ip.v4; 80 in_port_t dport; 81 82 KASSERT(npf_iscached(npc, NPC_IP46)); 83 KASSERT(npf_iscached(npc, NPC_LAYER4)); 84 85 /* Check for low TTL. Also, we support outbound NAT only. */ 86 if (ip->ip_ttl > TR_MAX_TTL || di != PFIL_OUT) { 87 return false; 88 } 89 90 switch (proto) { 91 case IPPROTO_TCP: { 92 const struct tcphdr *th = npc->npc_l4.tcp; 93 dport = ntohs(th->th_dport); 94 break; 95 } 96 case IPPROTO_UDP: { 97 const struct udphdr *uh = npc->npc_l4.udp; 98 dport = ntohs(uh->uh_dport); 99 break; 100 } 101 case IPPROTO_ICMP: 102 case IPPROTO_ICMPV6: 103 /* Just to pass the test below. */ 104 dport = TR_BASE_PORT; 105 break; 106 default: 107 return false; 108 } 109 110 /* Handle TCP/UDP traceroute - check for port range. */ 111 if (dport < TR_BASE_PORT || dport > TR_PORT_RANGE) { 112 return false; 113 } 114 115 /* Associate ALG with translation entry. */ 116 npf_nat_setalg(nt, alg_icmp, 0); 117 return true; 118 } 119 120 /* 121 * npfa_icmp{4,6}_inspect: retrieve unique identifiers - either ICMP query 122 * ID or TCP/UDP ports of the original packet, which is embedded. 123 * 124 * => Sets hasqid=true if the packet has a Query Id. In this case neither 125 * the nbuf nor npc is touched. 126 */ 127 128 static bool 129 npfa_icmp4_inspect(const int type, npf_cache_t *npc, bool *hasqid) 130 { 131 nbuf_t *nbuf = npc->npc_nbuf; 132 133 /* Per RFC 792. */ 134 switch (type) { 135 case ICMP_UNREACH: 136 case ICMP_SOURCEQUENCH: 137 case ICMP_REDIRECT: 138 case ICMP_TIMXCEED: 139 case ICMP_PARAMPROB: 140 /* Should contain original IP header. */ 141 if (!nbuf_advance(nbuf, offsetof(struct icmp, icmp_ip), 0)) { 142 return false; 143 } 144 return (npf_cache_all(npc) & NPC_LAYER4) != 0; 145 146 case ICMP_ECHOREPLY: 147 case ICMP_ECHO: 148 case ICMP_TSTAMP: 149 case ICMP_TSTAMPREPLY: 150 case ICMP_IREQ: 151 case ICMP_IREQREPLY: 152 /* Contains ICMP query ID. */ 153 *hasqid = true; 154 return true; 155 default: 156 break; 157 } 158 return false; 159 } 160 161 static bool 162 npfa_icmp6_inspect(const int type, npf_cache_t *npc, bool *hasqid) 163 { 164 nbuf_t *nbuf = npc->npc_nbuf; 165 166 /* Per RFC 4443. */ 167 switch (type) { 168 case ICMP6_DST_UNREACH: 169 case ICMP6_PACKET_TOO_BIG: 170 case ICMP6_TIME_EXCEEDED: 171 case ICMP6_PARAM_PROB: 172 /* Should contain original IP header. */ 173 if (!nbuf_advance(nbuf, sizeof(struct icmp6_hdr), 0)) { 174 return false; 175 } 176 return (npf_cache_all(npc) & NPC_LAYER4) != 0; 177 178 case ICMP6_ECHO_REQUEST: 179 case ICMP6_ECHO_REPLY: 180 /* Contains ICMP query ID. */ 181 *hasqid = true; 182 return true; 183 default: 184 break; 185 } 186 return false; 187 } 188 189 /* 190 * npfa_icmp_inspect: ALG ICMP inspector. 191 * 192 * => Returns false if there is a problem with the format. 193 */ 194 static bool 195 npfa_icmp_inspect(npf_cache_t *npc, npf_cache_t *enpc) 196 { 197 nbuf_t *nbuf = npc->npc_nbuf; 198 bool ret, hasqid = false; 199 200 KASSERT(npf_iscached(npc, NPC_IP46)); 201 KASSERT(npf_iscached(npc, NPC_ICMP)); 202 203 /* Advance to ICMP header. */ 204 nbuf_reset(nbuf); 205 if (!nbuf_advance(nbuf, npc->npc_hlen, 0)) { 206 return false; 207 } 208 enpc->npc_ctx = npc->npc_ctx; 209 enpc->npc_nbuf = nbuf; 210 enpc->npc_info = 0; 211 212 /* 213 * Inspect the ICMP packet. The relevant data might be in the 214 * embedded packet. Fill the "enpc" cache, if so. 215 */ 216 if (npf_iscached(npc, NPC_IP4) && 217 npc->npc_proto == IPPROTO_ICMP) { 218 const struct icmp *ic = npc->npc_l4.icmp; 219 ret = npfa_icmp4_inspect(ic->icmp_type, enpc, &hasqid); 220 } else if (npf_iscached(npc, NPC_IP6) && 221 npc->npc_proto == IPPROTO_ICMPV6) { 222 const struct icmp6_hdr *ic6 = npc->npc_l4.icmp6; 223 ret = npfa_icmp6_inspect(ic6->icmp6_type, enpc, &hasqid); 224 } else { 225 ret = false; 226 } 227 if (!ret) { 228 return false; 229 } 230 231 /* ICMP ID is the original packet, just indicate it. */ 232 if (hasqid) { 233 npc->npc_info |= NPC_ICMP_ID; 234 } 235 236 return true; 237 } 238 239 static npf_conn_t * 240 npfa_icmp_conn(npf_cache_t *npc, int di) 241 { 242 npf_conn_t *conn = NULL; 243 npf_cache_t enpc; 244 bool hasqid = false; 245 246 /* Inspect ICMP packet for an embedded packet. */ 247 if (!npf_iscached(npc, NPC_ICMP)) 248 return NULL; 249 if (!npfa_icmp_inspect(npc, &enpc)) 250 goto out; 251 252 /* 253 * If the ICMP packet had a Query Id, leave now. The packet didn't get 254 * modified, so no need to recache npc. 255 */ 256 if (npf_iscached(npc, NPC_ICMP_ID)) { 257 KASSERT(!nbuf_flag_p(npc->npc_nbuf, NBUF_DATAREF_RESET)); 258 return NULL; 259 } 260 261 /* 262 * Invert the identifiers of the embedded packet. 263 * If it is ICMP, then ensure ICMP ID. 264 */ 265 union l4 { 266 struct tcphdr th; 267 struct udphdr uh; 268 } l4; 269 bool ret, forw; 270 271 #define SWAP(type, x, y) { type tmp = x; x = y; y = tmp; } 272 SWAP(npf_addr_t *, enpc.npc_ips[NPF_SRC], enpc.npc_ips[NPF_DST]); 273 274 switch (enpc.npc_proto) { 275 case IPPROTO_TCP: 276 l4.th.th_sport = enpc.npc_l4.tcp->th_dport; 277 l4.th.th_dport = enpc.npc_l4.tcp->th_sport; 278 enpc.npc_l4.tcp = &l4.th; 279 break; 280 case IPPROTO_UDP: 281 l4.uh.uh_sport = enpc.npc_l4.udp->uh_dport; 282 l4.uh.uh_dport = enpc.npc_l4.udp->uh_sport; 283 enpc.npc_l4.udp = &l4.uh; 284 break; 285 case IPPROTO_ICMP: { 286 const struct icmp *ic = enpc.npc_l4.icmp; 287 ret = npfa_icmp4_inspect(ic->icmp_type, &enpc, &hasqid); 288 if (!ret || !hasqid) 289 goto out; 290 enpc.npc_info |= NPC_ICMP_ID; 291 break; 292 } 293 case IPPROTO_ICMPV6: { 294 const struct icmp6_hdr *ic6 = enpc.npc_l4.icmp6; 295 ret = npfa_icmp6_inspect(ic6->icmp6_type, &enpc, &hasqid); 296 if (!ret || !hasqid) 297 goto out; 298 enpc.npc_info |= NPC_ICMP_ID; 299 break; 300 } 301 default: 302 goto out; 303 } 304 305 /* Lookup a connection using the embedded packet. */ 306 conn = npf_conn_lookup(&enpc, di, &forw); 307 308 out: 309 /* 310 * Recache npc. The nbuf may have been updated as a result of 311 * caching enpc. 312 */ 313 npf_recache(npc); 314 return conn; 315 } 316 317 /* 318 * npfa_icmp_nat: ALG translator - rewrites IP address in the IP header 319 * which is embedded in ICMP packet. Note: backwards stream only. 320 */ 321 static bool 322 npfa_icmp_nat(npf_cache_t *npc, npf_nat_t *nt, bool forw) 323 { 324 const u_int which = NPF_SRC; 325 npf_cache_t enpc; 326 struct icmp *ic; 327 uint16_t cksum; 328 329 if (forw || !npf_iscached(npc, NPC_ICMP)) 330 return false; 331 332 /* 333 * ICMP: fetch the current checksum we are going to fixup. 334 */ 335 ic = npc->npc_l4.icmp; 336 cksum = ic->icmp_cksum; 337 338 if (!npfa_icmp_inspect(npc, &enpc)) 339 goto err; 340 341 /* 342 * If the ICMP packet had a Query Id, leave now. The packet didn't get 343 * modified, so no need to recache npc. 344 */ 345 if (npf_iscached(npc, NPC_ICMP_ID)) { 346 KASSERT(!nbuf_flag_p(npc->npc_nbuf, NBUF_DATAREF_RESET)); 347 return false; 348 } 349 350 KASSERT(npf_iscached(&enpc, NPC_IP46)); 351 KASSERT(npf_iscached(&enpc, NPC_LAYER4)); 352 353 CTASSERT(offsetof(struct icmp, icmp_cksum) == 354 offsetof(struct icmp6_hdr, icmp6_cksum)); 355 356 /* 357 * Fetch the IP and port in the _embedded_ packet. Also, fetch 358 * the IPv4 and TCP/UDP checksums before they are rewritten. 359 */ 360 const int proto = enpc.npc_proto; 361 uint16_t ipcksum = 0, l4cksum = 0; 362 in_port_t old_port = 0; 363 364 if (npf_iscached(&enpc, NPC_IP4)) { 365 const struct ip *eip = enpc.npc_ip.v4; 366 ipcksum = eip->ip_sum; 367 } 368 switch (proto) { 369 case IPPROTO_TCP: { 370 const struct tcphdr *th = enpc.npc_l4.tcp; 371 old_port = th->th_sport; 372 l4cksum = th->th_sum; 373 break; 374 } 375 case IPPROTO_UDP: { 376 const struct udphdr *uh = enpc.npc_l4.udp; 377 old_port = uh->uh_sport; 378 l4cksum = uh->uh_sum; 379 break; 380 } 381 case IPPROTO_ICMP: 382 case IPPROTO_ICMPV6: 383 break; 384 default: 385 goto err; 386 } 387 388 /* 389 * Get the original IP address and port. 390 * Calculate the part of the ICMP checksum fixup. 391 */ 392 npf_addr_t *addr; 393 in_port_t port; 394 395 npf_nat_getorig(nt, &addr, &port); 396 397 cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_ips[which], addr); 398 if (port) { 399 cksum = npf_fixup16_cksum(cksum, old_port, port); 400 } 401 402 /* 403 * Translate the embedded packet. The following changes will 404 * be performed by npf_napt_rwr(): 405 * 406 * 1) Rewrite the IP address and, if not ICMP, port. 407 * 2) Rewrite the TCP/UDP checksum (if not ICMP). 408 * 3) Rewrite the IPv4 checksum for (1) and (2). 409 * 410 * XXX: Assumes NPF_NATOUT (source address/port). Currently, 411 * npfa_icmp_match() matches only for the PFIL_OUT traffic. 412 */ 413 if (npf_napt_rwr(&enpc, which, addr, port)) { 414 goto err; 415 } 416 417 /* 418 * Finally, finish the ICMP checksum fixup: include the checksum 419 * changes in the embedded packet. 420 */ 421 if (npf_iscached(&enpc, NPC_IP4)) { 422 const struct ip *eip = enpc.npc_ip.v4; 423 cksum = npf_fixup16_cksum(cksum, ipcksum, eip->ip_sum); 424 } 425 switch (proto) { 426 case IPPROTO_TCP: { 427 const struct tcphdr *th = enpc.npc_l4.tcp; 428 cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum); 429 break; 430 } 431 case IPPROTO_UDP: 432 if (l4cksum) { 433 const struct udphdr *uh = enpc.npc_l4.udp; 434 cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum); 435 } 436 break; 437 } 438 npf_recache(npc); 439 KASSERT(npf_iscached(npc, NPC_ICMP)); 440 ic = npc->npc_l4.icmp; 441 ic->icmp_cksum = cksum; 442 return true; 443 444 err: 445 /* 446 * Recache npc. The nbuf may have been updated as a result of 447 * caching enpc. 448 */ 449 npf_recache(npc); 450 return false; 451 } 452 453 /* 454 * npf_alg_icmp_{init,fini,modcmd}: ICMP ALG initialization, destruction 455 * and module interface. 456 */ 457 458 static int 459 npf_alg_icmp_init(void) 460 { 461 static const npfa_funcs_t icmp = { 462 .match = npfa_icmp_match, 463 .translate = npfa_icmp_nat, 464 .inspect = npfa_icmp_conn, 465 }; 466 alg_icmp = npf_alg_register(npf_getkernctx(), "icmp", &icmp); 467 return alg_icmp ? 0 : ENOMEM; 468 } 469 470 static int 471 npf_alg_icmp_fini(void) 472 { 473 KASSERT(alg_icmp != NULL); 474 return npf_alg_unregister(npf_getkernctx(), alg_icmp); 475 } 476 477 static int 478 npf_alg_icmp_modcmd(modcmd_t cmd, void *arg) 479 { 480 switch (cmd) { 481 case MODULE_CMD_INIT: 482 return npf_alg_icmp_init(); 483 case MODULE_CMD_FINI: 484 return npf_alg_icmp_fini(); 485 case MODULE_CMD_AUTOUNLOAD: 486 return EBUSY; 487 default: 488 return ENOTTY; 489 } 490 return 0; 491 } 492