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