1 /* $NetBSD: npf_alg_icmp.c,v 1.13 2012/09/16 13:44:14 rmind 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 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.13 2012/09/16 13:44:14 rmind Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/module.h> 41 #include <sys/pool.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 52 #include "npf_impl.h" 53 54 MODULE(MODULE_CLASS_MISC, npf_alg_icmp, "npf"); 55 56 /* 57 * Traceroute criteria. 58 * 59 * IANA assigned base port: 33434. However, common practice is to increase 60 * the port, thus monitor [33434-33484] range. Additional filter is TTL < 50. 61 */ 62 63 #define TR_BASE_PORT 33434 64 #define TR_PORT_RANGE 33484 65 #define TR_MAX_TTL 50 66 67 static npf_alg_t * alg_icmp __read_mostly; 68 69 static bool npfa_icmp_match(npf_cache_t *, nbuf_t *, void *); 70 static bool npfa_icmp_natin(npf_cache_t *, nbuf_t *, void *); 71 static bool npfa_icmp_session(npf_cache_t *, nbuf_t *, void *); 72 73 /* 74 * npf_alg_icmp_{init,fini,modcmd}: ICMP ALG initialization, destruction 75 * and module interface. 76 */ 77 78 static int 79 npf_alg_icmp_init(void) 80 { 81 82 alg_icmp = npf_alg_register(npfa_icmp_match, NULL, 83 npfa_icmp_natin, npfa_icmp_session); 84 KASSERT(alg_icmp != NULL); 85 return 0; 86 } 87 88 static int 89 npf_alg_icmp_fini(void) 90 { 91 92 KASSERT(alg_icmp != NULL); 93 return npf_alg_unregister(alg_icmp); 94 } 95 96 static int 97 npf_alg_icmp_modcmd(modcmd_t cmd, void *arg) 98 { 99 100 switch (cmd) { 101 case MODULE_CMD_INIT: 102 return npf_alg_icmp_init(); 103 case MODULE_CMD_FINI: 104 return npf_alg_icmp_fini(); 105 case MODULE_CMD_AUTOUNLOAD: 106 return EBUSY; 107 default: 108 return ENOTTY; 109 } 110 return 0; 111 } 112 113 /* 114 * npfa_icmp_match: ALG matching inspector - determines ALG case and 115 * associates ALG with NAT entry. 116 */ 117 static bool 118 npfa_icmp_match(npf_cache_t *npc, nbuf_t *nbuf, void *ntptr) 119 { 120 const int proto = npf_cache_ipproto(npc); 121 struct ip *ip = &npc->npc_ip.v4; 122 in_port_t dport; 123 124 KASSERT(npf_iscached(npc, NPC_IP46)); 125 KASSERT(npf_iscached(npc, NPC_LAYER4)); 126 127 /* Check for low TTL. */ 128 if (ip->ip_ttl > TR_MAX_TTL) { 129 return false; 130 } 131 132 if (proto == IPPROTO_TCP) { 133 struct tcphdr *th = &npc->npc_l4.tcp; 134 dport = ntohs(th->th_dport); 135 } else if (proto == IPPROTO_UDP) { 136 struct udphdr *uh = &npc->npc_l4.udp; 137 dport = ntohs(uh->uh_dport); 138 } else { 139 return false; 140 } 141 142 /* Handle TCP/UDP traceroute - check for port range. */ 143 if (dport < TR_BASE_PORT || dport > TR_PORT_RANGE) { 144 return false; 145 } 146 147 /* Associate ALG with translation entry. */ 148 npf_nat_t *nt = ntptr; 149 npf_nat_setalg(nt, alg_icmp, 0); 150 return true; 151 } 152 153 /* 154 * npf_icmp{4,6}_uniqid: retrieve unique identifiers - either ICMP query ID 155 * or TCP/UDP ports of the original packet, which is embedded. 156 */ 157 158 static bool 159 npf_icmp4_uniqid(const int type, npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr) 160 { 161 struct icmp *ic; 162 u_int offby; 163 164 /* Per RFC 792. */ 165 switch (type) { 166 case ICMP_UNREACH: 167 case ICMP_SOURCEQUENCH: 168 case ICMP_REDIRECT: 169 case ICMP_TIMXCEED: 170 case ICMP_PARAMPROB: 171 /* Should contain original IP header. */ 172 offby = offsetof(struct icmp, icmp_ip); 173 if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL) { 174 return false; 175 } 176 /* Fetch into the cache. */ 177 if (!npf_fetch_ip(npc, nbuf, n_ptr)) { 178 return false; 179 } 180 switch (npf_cache_ipproto(npc)) { 181 case IPPROTO_TCP: 182 return npf_fetch_tcp(npc, nbuf, n_ptr); 183 case IPPROTO_UDP: 184 return npf_fetch_udp(npc, nbuf, n_ptr); 185 default: 186 return false; 187 } 188 return true; 189 190 case ICMP_ECHOREPLY: 191 case ICMP_ECHO: 192 case ICMP_TSTAMP: 193 case ICMP_TSTAMPREPLY: 194 case ICMP_IREQ: 195 case ICMP_IREQREPLY: 196 /* Should contain ICMP query ID. */ 197 ic = &npc->npc_l4.icmp; 198 offby = offsetof(struct icmp, icmp_id); 199 if (nbuf_advfetch(&nbuf, &n_ptr, offby, 200 sizeof(uint16_t), &ic->icmp_id)) { 201 return false; 202 } 203 npc->npc_info |= NPC_ICMP_ID; 204 return true; 205 default: 206 break; 207 } 208 /* No unique IDs. */ 209 return false; 210 } 211 212 static bool 213 npf_icmp6_uniqid(const int type, npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr) 214 { 215 struct icmp6_hdr *ic6; 216 u_int offby; 217 218 /* Per RFC 4443. */ 219 switch (type) { 220 case ICMP6_DST_UNREACH: 221 case ICMP6_PACKET_TOO_BIG: 222 case ICMP6_TIME_EXCEEDED: 223 case ICMP6_PARAM_PROB: 224 /* Should contain original IP header. */ 225 offby = sizeof(struct icmp6_hdr); 226 if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL) { 227 return false; 228 } 229 /* Fetch into the cache. */ 230 if (!npf_fetch_ip(npc, nbuf, n_ptr)) { 231 return false; 232 } 233 switch (npf_cache_ipproto(npc)) { 234 case IPPROTO_TCP: 235 return npf_fetch_tcp(npc, nbuf, n_ptr); 236 case IPPROTO_UDP: 237 return npf_fetch_udp(npc, nbuf, n_ptr); 238 default: 239 return false; 240 } 241 return true; 242 243 case ICMP6_ECHO_REQUEST: 244 case ICMP6_ECHO_REPLY: 245 /* Should contain ICMP query ID. */ 246 ic6 = &npc->npc_l4.icmp6; 247 offby = offsetof(struct icmp6_hdr, icmp6_id); 248 if (nbuf_advfetch(&nbuf, &n_ptr, offby, 249 sizeof(uint16_t), &ic6->icmp6_id)) { 250 return false; 251 } 252 npc->npc_info |= NPC_ICMP_ID; 253 return true; 254 default: 255 break; 256 } 257 /* No unique IDs. */ 258 return false; 259 } 260 261 static void 262 npfa_srcdst_invert(npf_cache_t *npc) 263 { 264 const int proto = npf_cache_ipproto(npc); 265 npf_addr_t *tmp_ip; 266 267 if (proto == IPPROTO_TCP) { 268 struct tcphdr *th = &npc->npc_l4.tcp; 269 in_port_t tmp_sport = th->th_sport; 270 th->th_sport = th->th_dport; 271 th->th_dport = tmp_sport; 272 273 } else if (proto == IPPROTO_UDP) { 274 struct udphdr *uh = &npc->npc_l4.udp; 275 in_port_t tmp_sport = uh->uh_sport; 276 uh->uh_sport = uh->uh_dport; 277 uh->uh_dport = tmp_sport; 278 } 279 tmp_ip = npc->npc_srcip; 280 npc->npc_srcip = npc->npc_dstip; 281 npc->npc_dstip = tmp_ip; 282 } 283 284 /* 285 * npfa_icmp_session: ALG session inspector, returns unique identifiers. 286 */ 287 static bool 288 npfa_icmp_session(npf_cache_t *npc, nbuf_t *nbuf, void *keyptr) 289 { 290 npf_cache_t *key = keyptr; 291 bool ret; 292 293 KASSERT(key->npc_info == 0); 294 295 /* IP + ICMP? Get unique identifiers from ICMP packet. */ 296 if (!npf_iscached(npc, NPC_IP4)) { 297 return false; 298 } 299 if (npf_cache_ipproto(npc) != IPPROTO_ICMP) { 300 return false; 301 } 302 KASSERT(npf_iscached(npc, NPC_ICMP)); 303 304 /* Advance to ICMP header. */ 305 void *n_ptr = nbuf_dataptr(nbuf); 306 const u_int hlen = npf_cache_hlen(npc); 307 308 if ((n_ptr = nbuf_advance(&nbuf, n_ptr, hlen)) == NULL) { 309 return false; 310 } 311 312 /* 313 * Fetch relevant data into the separate ("key") cache. 314 */ 315 struct icmp *ic = &npc->npc_l4.icmp; 316 317 if (npf_iscached(npc, NPC_IP4)) { 318 ret = npf_icmp4_uniqid(ic->icmp_type, key, nbuf, n_ptr); 319 } else if (npf_iscached(npc, NPC_IP6)) { 320 KASSERT(offsetof(struct icmp, icmp_id) == 321 offsetof(struct icmp6_hdr, icmp6_id)); 322 ret = npf_icmp6_uniqid(ic->icmp_type, key, nbuf, n_ptr); 323 } else { 324 ret = false; 325 } 326 327 if (!ret) { 328 return false; 329 } 330 331 if (npf_iscached(key, NPC_ICMP_ID)) { 332 struct icmp *keyic = &key->npc_l4.icmp; 333 334 /* Copy ICMP ID to the cache and flag it. */ 335 npc->npc_info |= NPC_ICMP_ID; 336 ic->icmp_id = keyic->icmp_id; 337 338 /* Note: return False, since key is the original cache. */ 339 return false; 340 } 341 342 /* 343 * Embedded IP packet is the original of "forwards" stream. 344 * We should imitate the "backwards" stream for inspection. 345 */ 346 KASSERT(npf_iscached(key, NPC_IP46)); 347 KASSERT(npf_iscached(key, NPC_LAYER4)); 348 npfa_srcdst_invert(key); 349 key->npc_alen = npc->npc_alen; 350 351 return true; 352 } 353 354 /* 355 * npfa_icmp_natin: ALG inbound translation inspector, rewrite IP address 356 * in the IP header, which is embedded in ICMP packet. 357 */ 358 static bool 359 npfa_icmp_natin(npf_cache_t *npc, nbuf_t *nbuf, void *ntptr) 360 { 361 npf_cache_t enpc = { .npc_info = 0 }; 362 363 /* XXX: Duplicated work (done at session inspection). */ 364 if (!npfa_icmp_session(npc, nbuf, &enpc)) { 365 return false; 366 } 367 /* XXX: Restore inversion (inefficient). */ 368 KASSERT(npf_iscached(&enpc, NPC_IP46)); 369 KASSERT(npf_iscached(&enpc, NPC_LAYER4)); 370 npfa_srcdst_invert(&enpc); 371 372 /* 373 * Save ICMP and embedded IP with TCP/UDP header checksums, retrieve 374 * the original address and port, and calculate ICMP checksum for 375 * embedded packet changes, while data is not rewritten in the cache. 376 */ 377 const int proto = npf_cache_ipproto(&enpc); 378 const struct ip *eip = &enpc.npc_ip.v4; 379 const struct icmp * const ic = &npc->npc_l4.icmp; 380 uint16_t cksum = ic->icmp_cksum, ecksum = eip->ip_sum, l4cksum; 381 npf_nat_t *nt = ntptr; 382 npf_addr_t *addr; 383 in_port_t port; 384 385 npf_nat_getorig(nt, &addr, &port); 386 387 if (proto == IPPROTO_TCP) { 388 struct tcphdr *th = &enpc.npc_l4.tcp; 389 cksum = npf_fixup16_cksum(cksum, th->th_sport, port); 390 l4cksum = th->th_sum; 391 } else { 392 struct udphdr *uh = &enpc.npc_l4.udp; 393 cksum = npf_fixup16_cksum(cksum, uh->uh_sport, port); 394 l4cksum = uh->uh_sum; 395 } 396 cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_srcip, addr); 397 398 /* 399 * Save the original pointers to the main IP header and then advance 400 * to the embedded IP header after ICMP header. 401 */ 402 void *n_ptr = nbuf_dataptr(nbuf), *cnbuf = nbuf, *cnptr = n_ptr; 403 u_int offby = npf_cache_hlen(npc) + offsetof(struct icmp, icmp_ip); 404 405 if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL) { 406 return false; 407 } 408 409 /* 410 * Rewrite source IP address and port of the embedded IP header, 411 * which represents original packet - therefore passing PFIL_OUT. 412 * Note: checksums are first, since it uses values from the cache. 413 */ 414 if (!npf_rwrcksum(&enpc, nbuf, n_ptr, PFIL_OUT, addr, port)) { 415 return false; 416 } 417 if (!npf_rwrip(&enpc, nbuf, n_ptr, PFIL_OUT, addr)) { 418 return false; 419 } 420 if (!npf_rwrport(&enpc, nbuf, n_ptr, PFIL_OUT, port)) { 421 return false; 422 } 423 424 /* 425 * Finish calculation of the ICMP checksum. Update for embedded IP 426 * and TCP/UDP checksum changes. Finally, rewrite ICMP checksum. 427 */ 428 if (proto == IPPROTO_TCP) { 429 struct tcphdr *th = &enpc.npc_l4.tcp; 430 cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum); 431 } else if (l4cksum) { 432 struct udphdr *uh = &enpc.npc_l4.udp; 433 cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum); 434 } 435 cksum = npf_fixup16_cksum(cksum, ecksum, eip->ip_sum); 436 437 offby = npf_cache_hlen(npc) + offsetof(struct icmp, icmp_cksum); 438 if (nbuf_advstore(&cnbuf, &cnptr, offby, sizeof(uint16_t), &cksum)) { 439 return false; 440 } 441 return true; 442 } 443