1 /* $NetBSD: npf_alg_icmp.c,v 1.24 2016/12/26 23:05:06 christos 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.24 2016/12/26 23:05:06 christos 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 125 static bool 126 npfa_icmp4_inspect(const int type, npf_cache_t *npc) 127 { 128 nbuf_t *nbuf = npc->npc_nbuf; 129 u_int offby; 130 131 /* Per RFC 792. */ 132 switch (type) { 133 case ICMP_UNREACH: 134 case ICMP_SOURCEQUENCH: 135 case ICMP_REDIRECT: 136 case ICMP_TIMXCEED: 137 case ICMP_PARAMPROB: 138 if (npc == NULL) { 139 return false; 140 } 141 /* Should contain original IP header. */ 142 if (!nbuf_advance(nbuf, offsetof(struct icmp, icmp_ip), 0)) { 143 return false; 144 } 145 return (npf_cache_all(npc) & NPC_LAYER4) != 0; 146 147 case ICMP_ECHOREPLY: 148 case ICMP_ECHO: 149 case ICMP_TSTAMP: 150 case ICMP_TSTAMPREPLY: 151 case ICMP_IREQ: 152 case ICMP_IREQREPLY: 153 /* Should contain ICMP query ID - ensure. */ 154 offby = offsetof(struct icmp, icmp_id); 155 if (!nbuf_advance(nbuf, offby, sizeof(uint16_t))) { 156 return false; 157 } 158 npc->npc_info |= NPC_ICMP_ID; 159 return true; 160 default: 161 break; 162 } 163 return false; 164 } 165 166 static bool 167 npfa_icmp6_inspect(const int type, npf_cache_t *npc) 168 { 169 nbuf_t *nbuf = npc->npc_nbuf; 170 u_int offby; 171 172 /* Per RFC 4443. */ 173 switch (type) { 174 case ICMP6_DST_UNREACH: 175 case ICMP6_PACKET_TOO_BIG: 176 case ICMP6_TIME_EXCEEDED: 177 case ICMP6_PARAM_PROB: 178 if (npc == NULL) { 179 return false; 180 } 181 /* Should contain original IP header. */ 182 if (!nbuf_advance(nbuf, sizeof(struct icmp6_hdr), 0)) { 183 return false; 184 } 185 return (npf_cache_all(npc) & NPC_LAYER4) != 0; 186 187 case ICMP6_ECHO_REQUEST: 188 case ICMP6_ECHO_REPLY: 189 /* Should contain ICMP query ID - ensure. */ 190 offby = offsetof(struct icmp6_hdr, icmp6_id); 191 if (!nbuf_advance(nbuf, offby, sizeof(uint16_t))) { 192 return false; 193 } 194 npc->npc_info |= NPC_ICMP_ID; 195 return true; 196 default: 197 break; 198 } 199 return false; 200 } 201 202 /* 203 * npfa_icmp_inspect: ALG ICMP inspector. 204 * 205 * => Returns true if "enpc" is filled. 206 */ 207 static bool 208 npfa_icmp_inspect(npf_cache_t *npc, npf_cache_t *enpc) 209 { 210 nbuf_t *nbuf = npc->npc_nbuf; 211 bool ret; 212 213 KASSERT(npf_iscached(npc, NPC_IP46)); 214 KASSERT(npf_iscached(npc, NPC_ICMP)); 215 216 /* Advance to ICMP header. */ 217 nbuf_reset(nbuf); 218 if (!nbuf_advance(nbuf, npc->npc_hlen, 0)) { 219 return false; 220 } 221 enpc->npc_ctx = npc->npc_ctx; 222 enpc->npc_nbuf = nbuf; 223 enpc->npc_info = 0; 224 225 /* 226 * Inspect the ICMP packet. The relevant data might be in the 227 * embedded packet. Fill the "enpc" cache, if so. 228 */ 229 if (npf_iscached(npc, NPC_IP4)) { 230 const struct icmp *ic = npc->npc_l4.icmp; 231 ret = npfa_icmp4_inspect(ic->icmp_type, enpc); 232 } else if (npf_iscached(npc, NPC_IP6)) { 233 const struct icmp6_hdr *ic6 = npc->npc_l4.icmp6; 234 ret = npfa_icmp6_inspect(ic6->icmp6_type, enpc); 235 } else { 236 ret = false; 237 } 238 if (!ret) { 239 return false; 240 } 241 242 /* ICMP ID is the original packet, just indicate it. */ 243 if (npf_iscached(enpc, NPC_ICMP_ID)) { 244 npc->npc_info |= NPC_ICMP_ID; 245 return false; 246 } 247 248 /* Indicate that embedded packet is in the cache. */ 249 return true; 250 } 251 252 static npf_conn_t * 253 npfa_icmp_conn(npf_cache_t *npc, int di) 254 { 255 npf_cache_t enpc; 256 257 /* Inspect ICMP packet for an embedded packet. */ 258 if (!npf_iscached(npc, NPC_ICMP)) 259 return NULL; 260 if (!npfa_icmp_inspect(npc, &enpc)) 261 return NULL; 262 263 /* 264 * Invert the identifiers of the embedded packet. 265 * If it is ICMP, then ensure ICMP ID. 266 */ 267 union l4 { 268 struct tcphdr th; 269 struct udphdr uh; 270 } l4; 271 bool ret, forw; 272 273 #define SWAP(type, x, y) { type tmp = x; x = y; y = tmp; } 274 SWAP(npf_addr_t *, enpc.npc_ips[NPF_SRC], enpc.npc_ips[NPF_DST]); 275 276 switch (enpc.npc_proto) { 277 case IPPROTO_TCP: 278 l4.th.th_sport = enpc.npc_l4.tcp->th_dport; 279 l4.th.th_dport = enpc.npc_l4.tcp->th_sport; 280 enpc.npc_l4.tcp = &l4.th; 281 break; 282 case IPPROTO_UDP: 283 l4.uh.uh_sport = enpc.npc_l4.udp->uh_dport; 284 l4.uh.uh_dport = enpc.npc_l4.udp->uh_sport; 285 enpc.npc_l4.udp = &l4.uh; 286 break; 287 case IPPROTO_ICMP: { 288 const struct icmp *ic = enpc.npc_l4.icmp; 289 ret = npfa_icmp4_inspect(ic->icmp_type, &enpc); 290 if (!ret || !npf_iscached(&enpc, NPC_ICMP_ID)) 291 return false; 292 break; 293 } 294 case IPPROTO_ICMPV6: { 295 const struct icmp6_hdr *ic6 = enpc.npc_l4.icmp6; 296 ret = npfa_icmp6_inspect(ic6->icmp6_type, &enpc); 297 if (!ret || !npf_iscached(&enpc, NPC_ICMP_ID)) 298 return false; 299 break; 300 } 301 default: 302 return false; 303 } 304 305 /* Lookup a connection using the embedded packet. */ 306 return npf_conn_lookup(&enpc, di, &forw); 307 } 308 309 /* 310 * npfa_icmp_nat: ALG translator - rewrites IP address in the IP header 311 * which is embedded in ICMP packet. Note: backwards stream only. 312 */ 313 static bool 314 npfa_icmp_nat(npf_cache_t *npc, npf_nat_t *nt, bool forw) 315 { 316 const u_int which = NPF_SRC; 317 npf_cache_t enpc; 318 319 if (forw || !npf_iscached(npc, NPC_ICMP)) 320 return false; 321 if (!npfa_icmp_inspect(npc, &enpc)) 322 return false; 323 324 KASSERT(npf_iscached(&enpc, NPC_IP46)); 325 KASSERT(npf_iscached(&enpc, NPC_LAYER4)); 326 327 /* 328 * ICMP: fetch the current checksum we are going to fixup. 329 */ 330 struct icmp *ic = npc->npc_l4.icmp; 331 uint16_t cksum = ic->icmp_cksum; 332 333 CTASSERT(offsetof(struct icmp, icmp_cksum) == 334 offsetof(struct icmp6_hdr, icmp6_cksum)); 335 336 /* 337 * Fetch the IP and port in the _embedded_ packet. Also, fetch 338 * the IPv4 and TCP/UDP checksums before they are rewritten. 339 * Calculate the part of the ICMP checksum fixup. 340 */ 341 const int proto = enpc.npc_proto; 342 uint16_t ipcksum = 0, l4cksum = 0; 343 npf_addr_t *addr; 344 in_port_t port; 345 346 npf_nat_getorig(nt, &addr, &port); 347 348 if (npf_iscached(&enpc, NPC_IP4)) { 349 const struct ip *eip = enpc.npc_ip.v4; 350 ipcksum = eip->ip_sum; 351 } 352 cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_ips[which], addr); 353 354 switch (proto) { 355 case IPPROTO_TCP: { 356 const struct tcphdr *th = enpc.npc_l4.tcp; 357 cksum = npf_fixup16_cksum(cksum, th->th_sport, port); 358 l4cksum = th->th_sum; 359 break; 360 } 361 case IPPROTO_UDP: { 362 const struct udphdr *uh = enpc.npc_l4.udp; 363 cksum = npf_fixup16_cksum(cksum, uh->uh_sport, port); 364 l4cksum = uh->uh_sum; 365 break; 366 } 367 case IPPROTO_ICMP: 368 case IPPROTO_ICMPV6: 369 break; 370 default: 371 return false; 372 } 373 374 /* 375 * Translate the embedded packet. The following changes will 376 * be performed by npf_napt_rwr(): 377 * 378 * 1) Rewrite the IP address and, if not ICMP, port. 379 * 2) Rewrite the TCP/UDP checksum (if not ICMP). 380 * 3) Rewrite the IPv4 checksum for (1) and (2). 381 * 382 * XXX: Assumes NPF_NATOUT (source address/port). Currently, 383 * npfa_icmp_match() matches only for the PFIL_OUT traffic. 384 */ 385 if (npf_napt_rwr(&enpc, which, addr, port)) { 386 return false; 387 } 388 389 /* 390 * Finally, finish the ICMP checksum fixup: include the checksum 391 * changes in the embedded packet. 392 */ 393 if (npf_iscached(&enpc, NPC_IP4)) { 394 const struct ip *eip = enpc.npc_ip.v4; 395 cksum = npf_fixup16_cksum(cksum, ipcksum, eip->ip_sum); 396 } 397 switch (proto) { 398 case IPPROTO_TCP: { 399 const struct tcphdr *th = enpc.npc_l4.tcp; 400 cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum); 401 break; 402 } 403 case IPPROTO_UDP: 404 if (l4cksum) { 405 const struct udphdr *uh = enpc.npc_l4.udp; 406 cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum); 407 } 408 break; 409 } 410 ic->icmp_cksum = cksum; 411 return true; 412 } 413 414 /* 415 * npf_alg_icmp_{init,fini,modcmd}: ICMP ALG initialization, destruction 416 * and module interface. 417 */ 418 419 static int 420 npf_alg_icmp_init(void) 421 { 422 static const npfa_funcs_t icmp = { 423 .match = npfa_icmp_match, 424 .translate = npfa_icmp_nat, 425 .inspect = npfa_icmp_conn, 426 }; 427 alg_icmp = npf_alg_register(npf_getkernctx(), "icmp", &icmp); 428 return alg_icmp ? 0 : ENOMEM; 429 } 430 431 static int 432 npf_alg_icmp_fini(void) 433 { 434 KASSERT(alg_icmp != NULL); 435 return npf_alg_unregister(npf_getkernctx(), alg_icmp); 436 } 437 438 static int 439 npf_alg_icmp_modcmd(modcmd_t cmd, void *arg) 440 { 441 switch (cmd) { 442 case MODULE_CMD_INIT: 443 return npf_alg_icmp_init(); 444 case MODULE_CMD_FINI: 445 return npf_alg_icmp_fini(); 446 case MODULE_CMD_AUTOUNLOAD: 447 return EBUSY; 448 default: 449 return ENOTTY; 450 } 451 return 0; 452 } 453