1 /* $NetBSD: npf_alg_icmp.c,v 1.16 2013/03/20 00:29:47 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 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.16 2013/03/20 00:29:47 christos 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 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 static bool npfa_icmp_match(npf_cache_t *, nbuf_t *, npf_nat_t *, int); 70 static bool npfa_icmp_nat(npf_cache_t *, nbuf_t *, npf_nat_t *, int); 71 static npf_session_t *npfa_icmp_session(npf_cache_t *, nbuf_t *, int); 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("icmp", npfa_icmp_match, 83 npfa_icmp_nat, 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, npf_nat_t *nt, int di) 119 { 120 const int proto = npc->npc_proto; 121 const 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 switch (proto) { 133 case IPPROTO_TCP: { 134 const struct tcphdr *th = npc->npc_l4.tcp; 135 dport = ntohs(th->th_dport); 136 break; 137 } 138 case IPPROTO_UDP: { 139 const struct udphdr *uh = npc->npc_l4.udp; 140 dport = ntohs(uh->uh_dport); 141 break; 142 } 143 case IPPROTO_ICMP: 144 case IPPROTO_ICMPV6: 145 /* Just to pass the test below. */ 146 dport = TR_BASE_PORT; 147 break; 148 default: 149 return false; 150 } 151 152 /* Handle TCP/UDP traceroute - check for port range. */ 153 if (dport < TR_BASE_PORT || dport > TR_PORT_RANGE) { 154 return false; 155 } 156 157 /* Associate ALG with translation entry. */ 158 npf_nat_setalg(nt, alg_icmp, 0); 159 return true; 160 } 161 162 /* 163 * npfa_icmp{4,6}_inspect: retrieve unique identifiers - either ICMP query 164 * ID or TCP/UDP ports of the original packet, which is embedded. 165 */ 166 167 static bool 168 npfa_icmp4_inspect(const int type, npf_cache_t *npc, nbuf_t *nbuf) 169 { 170 u_int offby; 171 172 /* Per RFC 792. */ 173 switch (type) { 174 case ICMP_UNREACH: 175 case ICMP_SOURCEQUENCH: 176 case ICMP_REDIRECT: 177 case ICMP_TIMXCEED: 178 case ICMP_PARAMPROB: 179 if (npc == NULL) { 180 return false; 181 } 182 /* Should contain original IP header. */ 183 if (!nbuf_advance(nbuf, offsetof(struct icmp, icmp_ip), 0)) { 184 return false; 185 } 186 return (npf_cache_all(npc, nbuf) & NPC_LAYER4) != 0; 187 188 case ICMP_ECHOREPLY: 189 case ICMP_ECHO: 190 case ICMP_TSTAMP: 191 case ICMP_TSTAMPREPLY: 192 case ICMP_IREQ: 193 case ICMP_IREQREPLY: 194 /* Should contain ICMP query ID - ensure. */ 195 offby = offsetof(struct icmp, icmp_id); 196 if (!nbuf_advance(nbuf, offby, sizeof(uint16_t))) { 197 return false; 198 } 199 npc->npc_info |= NPC_ICMP_ID; 200 return true; 201 default: 202 break; 203 } 204 return false; 205 } 206 207 static bool 208 npfa_icmp6_inspect(const int type, npf_cache_t *npc, nbuf_t *nbuf) 209 { 210 u_int offby; 211 212 /* Per RFC 4443. */ 213 switch (type) { 214 case ICMP6_DST_UNREACH: 215 case ICMP6_PACKET_TOO_BIG: 216 case ICMP6_TIME_EXCEEDED: 217 case ICMP6_PARAM_PROB: 218 if (npc == NULL) { 219 return false; 220 } 221 /* Should contain original IP header. */ 222 if (!nbuf_advance(nbuf, sizeof(struct icmp6_hdr), 0)) { 223 return false; 224 } 225 return (npf_cache_all(npc, nbuf) & NPC_LAYER4) != 0; 226 227 case ICMP6_ECHO_REQUEST: 228 case ICMP6_ECHO_REPLY: 229 /* Should contain ICMP query ID - ensure. */ 230 offby = offsetof(struct icmp6_hdr, icmp6_id); 231 if (!nbuf_advance(nbuf, offby, sizeof(uint16_t))) { 232 return false; 233 } 234 npc->npc_info |= NPC_ICMP_ID; 235 return true; 236 default: 237 break; 238 } 239 return false; 240 } 241 242 /* 243 * npfa_icmp_session: ALG ICMP inspector. 244 * 245 * => Returns true if "enpc" is filled. 246 */ 247 static bool 248 npfa_icmp_inspect(npf_cache_t *npc, nbuf_t *nbuf, npf_cache_t *enpc) 249 { 250 bool ret; 251 252 KASSERT(npf_iscached(npc, NPC_IP46)); 253 KASSERT(npf_iscached(npc, NPC_ICMP)); 254 255 /* Advance to ICMP header. */ 256 nbuf_reset(nbuf); 257 if (!nbuf_advance(nbuf, npc->npc_hlen, 0)) { 258 return false; 259 } 260 enpc->npc_info = 0; 261 262 /* 263 * Inspect the ICMP packet. The relevant data might be in the 264 * embedded packet. Fill the "enpc" cache, if so. 265 */ 266 if (npf_iscached(npc, NPC_IP4)) { 267 const struct icmp *ic = npc->npc_l4.icmp; 268 ret = npfa_icmp4_inspect(ic->icmp_type, enpc, nbuf); 269 } else if (npf_iscached(npc, NPC_IP6)) { 270 const struct icmp6_hdr *ic6 = npc->npc_l4.icmp6; 271 ret = npfa_icmp6_inspect(ic6->icmp6_type, enpc, nbuf); 272 } else { 273 ret = false; 274 } 275 if (!ret) { 276 return false; 277 } 278 279 /* ICMP ID is the original packet, just indicate it. */ 280 if (npf_iscached(enpc, NPC_ICMP_ID)) { 281 npc->npc_info |= NPC_ICMP_ID; 282 return false; 283 } 284 285 /* Indicate that embedded packet is in the cache. */ 286 return true; 287 } 288 289 static npf_session_t * 290 npfa_icmp_session(npf_cache_t *npc, nbuf_t *nbuf, int di) 291 { 292 npf_cache_t enpc; 293 294 /* Inspect ICMP packet for an embedded packet. */ 295 if (!npf_iscached(npc, NPC_ICMP)) 296 return NULL; 297 if (!npfa_icmp_inspect(npc, nbuf, &enpc)) 298 return NULL; 299 300 /* 301 * Invert the identifiers of the embedded packet. 302 * If it is ICMP, then ensure ICMP ID. 303 */ 304 union l4 { 305 struct tcphdr th; 306 struct udphdr uh; 307 } l4; 308 bool ret, forw; 309 310 #define SWAP(type, x, y) { type tmp = x; x = y; y = tmp; } 311 SWAP(npf_addr_t *, enpc.npc_srcip, enpc.npc_dstip); 312 313 switch (enpc.npc_proto) { 314 case IPPROTO_TCP: 315 l4.th.th_sport = enpc.npc_l4.tcp->th_dport; 316 l4.th.th_dport = enpc.npc_l4.tcp->th_sport; 317 enpc.npc_l4.tcp = &l4.th; 318 break; 319 case IPPROTO_UDP: 320 l4.uh.uh_sport = enpc.npc_l4.udp->uh_dport; 321 l4.uh.uh_dport = enpc.npc_l4.udp->uh_sport; 322 enpc.npc_l4.udp = &l4.uh; 323 break; 324 case IPPROTO_ICMP: { 325 const struct icmp *ic = enpc.npc_l4.icmp; 326 ret = npfa_icmp4_inspect(ic->icmp_type, &enpc, nbuf); 327 if (!ret || !npf_iscached(&enpc, NPC_ICMP_ID)) 328 return false; 329 break; 330 } 331 case IPPROTO_ICMPV6: { 332 const struct icmp6_hdr *ic6 = enpc.npc_l4.icmp6; 333 ret = npfa_icmp6_inspect(ic6->icmp6_type, &enpc, nbuf); 334 if (!ret || !npf_iscached(&enpc, NPC_ICMP_ID)) 335 return false; 336 break; 337 } 338 default: 339 return false; 340 } 341 342 /* Lookup for a session using embedded packet. */ 343 return npf_session_lookup(&enpc, nbuf, di, &forw); 344 } 345 346 /* 347 * npfa_icmp_nat: ALG inbound translation inspector, rewrite IP address 348 * in the IP header, which is embedded in ICMP packet. 349 */ 350 static bool 351 npfa_icmp_nat(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, int di) 352 { 353 npf_cache_t enpc; 354 355 if (di != PFIL_IN || !npf_iscached(npc, NPC_ICMP)) 356 return false; 357 if (!npfa_icmp_inspect(npc, nbuf, &enpc)) 358 return false; 359 360 KASSERT(npf_iscached(&enpc, NPC_IP46)); 361 KASSERT(npf_iscached(&enpc, NPC_LAYER4)); 362 363 struct icmp *ic = npc->npc_l4.icmp; 364 uint16_t cksum = ic->icmp_cksum; 365 366 CTASSERT(offsetof(struct icmp, icmp_cksum) == 367 offsetof(struct icmp6_hdr, icmp6_cksum)); 368 369 /* 370 * Retrieve the original address and port, then calculate ICMP 371 * checksum for these changes in the embedded packet. While data 372 * is not rewritten in the cache, save IP and TCP/UDP checksums. 373 */ 374 const int proto = enpc.npc_proto; 375 uint16_t ipcksum = 0, l4cksum = 0; 376 npf_addr_t *addr; 377 in_port_t port; 378 379 npf_nat_getorig(nt, &addr, &port); 380 381 if (npf_iscached(&enpc, NPC_IP4)) { 382 const struct ip *eip = enpc.npc_ip.v4; 383 ipcksum = eip->ip_sum; 384 } 385 cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_srcip, addr); 386 387 switch (proto) { 388 case IPPROTO_TCP: { 389 const struct tcphdr *th = enpc.npc_l4.tcp; 390 cksum = npf_fixup16_cksum(cksum, th->th_sport, port); 391 l4cksum = th->th_sum; 392 break; 393 } 394 case IPPROTO_UDP: { 395 const struct udphdr *uh = enpc.npc_l4.udp; 396 cksum = npf_fixup16_cksum(cksum, uh->uh_sport, port); 397 l4cksum = uh->uh_sum; 398 break; 399 } 400 case IPPROTO_ICMP: 401 case IPPROTO_ICMPV6: 402 break; 403 default: 404 return false; 405 } 406 407 /* 408 * Rewrite the source IP address and port of the embedded IP header, 409 * which represents the original packet, therefore passing PFIL_OUT. 410 * This updates the checksums in the embedded packet. 411 */ 412 if (npf_nat_translate(&enpc, nbuf, nt, false, PFIL_OUT)) { 413 return false; 414 } 415 416 /* 417 * Finish calculation of the ICMP checksum: include the checksum 418 * change in the embedded packet. 419 */ 420 if (npf_iscached(&enpc, NPC_IP4)) { 421 const struct ip *eip = enpc.npc_ip.v4; 422 cksum = npf_fixup16_cksum(cksum, ipcksum, eip->ip_sum); 423 } 424 switch (proto) { 425 case IPPROTO_TCP: { 426 const struct tcphdr *th = enpc.npc_l4.tcp; 427 cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum); 428 break; 429 } 430 case IPPROTO_UDP: 431 if (l4cksum) { 432 const struct udphdr *uh = enpc.npc_l4.udp; 433 cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum); 434 } 435 break; 436 } 437 ic->icmp_cksum = cksum; 438 return true; 439 } 440