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