1 /* $NetBSD: npf_alg_icmp.c,v 1.6 2011/01/18 20:33:45 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.6 2011/01/18 20:33:45 rmind Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/kernel.h> 41 #include <sys/module.h> 42 #include <sys/pool.h> 43 44 #include <netinet/in_systm.h> 45 #include <netinet/in.h> 46 #include <netinet/ip.h> 47 #include <netinet/tcp.h> 48 #include <netinet/udp.h> 49 #include <netinet/ip_icmp.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 default: 106 return ENOTTY; 107 } 108 return 0; 109 } 110 111 /* 112 * npfa_icmp_match: ALG matching inspector - determines ALG case and 113 * associates ALG with NAT entry. 114 */ 115 static bool 116 npfa_icmp_match(npf_cache_t *npc, nbuf_t *nbuf, void *ntptr) 117 { 118 const int proto = npf_cache_ipproto(npc); 119 struct ip *ip = &npc->npc_ip.v4; 120 in_port_t dport; 121 122 KASSERT(npf_iscached(npc, NPC_IP46 | NPC_LAYER4)); 123 124 /* Check for low TTL. */ 125 if (ip->ip_ttl > TR_MAX_TTL) { 126 return false; 127 } 128 129 if (proto == IPPROTO_TCP) { 130 struct tcphdr *th = &npc->npc_l4.tcp; 131 dport = ntohs(th->th_dport); 132 } else if (proto == IPPROTO_UDP) { 133 struct udphdr *uh = &npc->npc_l4.udp; 134 dport = ntohs(uh->uh_dport); 135 } else { 136 return false; 137 } 138 139 /* Handle TCP/UDP traceroute - check for port range. */ 140 if (dport < TR_BASE_PORT || dport > TR_PORT_RANGE) { 141 return false; 142 } 143 144 /* Associate ALG with translation entry. */ 145 npf_nat_t *nt = ntptr; 146 npf_nat_setalg(nt, alg_icmp, 0); 147 return true; 148 } 149 150 /* 151 * npf_icmp_uniqid: retrieve unique identifiers - either ICMP query ID 152 * or TCP/UDP ports of the original packet, which is embedded. 153 */ 154 static bool 155 npf_icmp_uniqid(const int type, npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr) 156 { 157 struct icmp *ic; 158 u_int offby; 159 160 /* Per RFC 792. */ 161 switch (type) { 162 case ICMP_UNREACH: 163 case ICMP_SOURCEQUENCH: 164 case ICMP_REDIRECT: 165 case ICMP_TIMXCEED: 166 case ICMP_PARAMPROB: 167 /* Should contain original IP header. */ 168 offby = offsetof(struct icmp, icmp_ip); 169 if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL) { 170 return false; 171 } 172 /* Fetch into the cache. */ 173 if (!npf_fetch_ip(npc, nbuf, n_ptr)) { 174 return false; 175 } 176 switch (npf_cache_ipproto(npc)) { 177 case IPPROTO_TCP: 178 return npf_fetch_tcp(npc, nbuf, n_ptr); 179 case IPPROTO_UDP: 180 return npf_fetch_udp(npc, nbuf, n_ptr); 181 default: 182 return false; 183 } 184 return true; 185 186 case ICMP_ECHOREPLY: 187 case ICMP_ECHO: 188 case ICMP_TSTAMP: 189 case ICMP_TSTAMPREPLY: 190 case ICMP_IREQ: 191 case ICMP_IREQREPLY: 192 /* Should contain ICMP query ID. */ 193 ic = &npc->npc_l4.icmp; 194 offby = offsetof(struct icmp, icmp_id); 195 if (nbuf_advfetch(&nbuf, &n_ptr, offby, 196 sizeof(uint16_t), &ic->icmp_id)) { 197 return false; 198 } 199 npc->npc_info |= NPC_ICMP_ID; 200 return true; 201 default: 202 break; 203 } 204 /* No unique IDs. */ 205 return false; 206 } 207 208 static void 209 npfa_srcdst_invert(npf_cache_t *npc) 210 { 211 const int proto = npf_cache_ipproto(npc); 212 npf_addr_t *tmp_ip; 213 214 if (proto == IPPROTO_TCP) { 215 struct tcphdr *th = &npc->npc_l4.tcp; 216 in_port_t tmp_sport = th->th_sport; 217 th->th_sport = th->th_dport; 218 th->th_dport = tmp_sport; 219 220 } else if (proto == IPPROTO_UDP) { 221 struct udphdr *uh = &npc->npc_l4.udp; 222 in_port_t tmp_sport = uh->uh_sport; 223 uh->uh_sport = uh->uh_dport; 224 uh->uh_dport = tmp_sport; 225 } 226 tmp_ip = npc->npc_srcip; 227 npc->npc_srcip = npc->npc_dstip; 228 npc->npc_dstip = tmp_ip; 229 } 230 231 /* 232 * npfa_icmp_session: ALG session inspector, returns unique identifiers. 233 */ 234 static bool 235 npfa_icmp_session(npf_cache_t *npc, nbuf_t *nbuf, void *keyptr) 236 { 237 npf_cache_t *key = keyptr; 238 KASSERT(key->npc_info == 0); 239 240 /* IP + ICMP? Get unique identifiers from ICMP packet. */ 241 if (!npf_iscached(npc, NPC_IP4)) { 242 return false; 243 } 244 if (npf_cache_ipproto(npc) != IPPROTO_ICMP) { 245 return false; 246 } 247 KASSERT(npf_iscached(npc, NPC_ICMP)); 248 249 /* Advance to ICMP header. */ 250 struct ip *ip = &npc->npc_ip.v4; 251 void *n_ptr = nbuf_dataptr(nbuf); 252 253 if ((n_ptr = nbuf_advance(&nbuf, n_ptr, ip->ip_hl << 2)) == NULL) { 254 return false; 255 } 256 257 /* Fetch relevant data into the separate ("key") cache. */ 258 struct icmp *ic = &npc->npc_l4.icmp; 259 if (!npf_icmp_uniqid(ic->icmp_type, key, nbuf, n_ptr)) { 260 return false; 261 } 262 263 if (npf_iscached(key, NPC_ICMP_ID)) { 264 struct icmp *keyic = &key->npc_l4.icmp; 265 266 /* Copy ICMP ID to the cache and flag it. */ 267 npc->npc_info |= NPC_ICMP_ID; 268 ic->icmp_id = keyic->icmp_id; 269 270 /* Note: return False, since key is the original cache. */ 271 return false; 272 } 273 274 /* 275 * Embedded IP packet is the original of "forwards" stream. 276 * We should imitate the "backwards" stream for inspection. 277 */ 278 KASSERT(npf_iscached(key, NPC_IP46)); 279 KASSERT(npf_iscached(key, NPC_LAYER4)); 280 npfa_srcdst_invert(key); 281 key->npc_ipsz = npc->npc_ipsz; 282 283 return true; 284 } 285 286 /* 287 * npfa_icmp_natin: ALG inbound translation inspector, rewrite IP address 288 * in the IP header, which is embedded in ICMP packet. 289 */ 290 static bool 291 npfa_icmp_natin(npf_cache_t *npc, nbuf_t *nbuf, void *ntptr) 292 { 293 npf_cache_t enpc = { .npc_info = 0 }; 294 295 /* XXX: Duplicated work (done at session inspection). */ 296 if (!npfa_icmp_session(npc, nbuf, &enpc)) { 297 return false; 298 } 299 /* XXX: Restore inversion (inefficient). */ 300 KASSERT(npf_iscached(&enpc, NPC_IP46 | NPC_LAYER4)); 301 npfa_srcdst_invert(&enpc); 302 303 /* 304 * Save ICMP and embedded IP with TCP/UDP header checksums, retrieve 305 * the original address and port, and calculate ICMP checksum for 306 * embedded packet changes, while data is not rewritten in the cache. 307 */ 308 const int proto = npf_cache_ipproto(&enpc); 309 const struct ip * const ip = &npc->npc_ip.v4, *eip = &enpc.npc_ip.v4; 310 const struct icmp * const ic = &npc->npc_l4.icmp; 311 uint16_t cksum = ic->icmp_cksum, ecksum = eip->ip_sum, l4cksum; 312 npf_nat_t *nt = ntptr; 313 npf_addr_t *addr; 314 in_port_t port; 315 316 npf_nat_getorig(nt, &addr, &port); 317 318 if (proto == IPPROTO_TCP) { 319 struct tcphdr *th = &enpc.npc_l4.tcp; 320 cksum = npf_fixup16_cksum(cksum, th->th_sport, port); 321 l4cksum = th->th_sum; 322 } else { 323 struct udphdr *uh = &enpc.npc_l4.udp; 324 cksum = npf_fixup16_cksum(cksum, uh->uh_sport, port); 325 l4cksum = uh->uh_sum; 326 } 327 cksum = npf_addr_cksum(cksum, enpc.npc_ipsz, enpc.npc_srcip, addr); 328 329 /* 330 * Save the original pointers to the main IP header and then advance 331 * to the embedded IP header after ICMP header. 332 */ 333 void *n_ptr = nbuf_dataptr(nbuf), *cnbuf = nbuf, *cnptr = n_ptr; 334 u_int offby = (ip->ip_hl << 2) + offsetof(struct icmp, icmp_ip); 335 336 if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL) { 337 return false; 338 } 339 340 /* 341 * Rewrite source IP address and port of the embedded IP header, 342 * which represents original packet - therefore passing PFIL_OUT. 343 * Note: checksums are first, since it uses values from the cache. 344 */ 345 if (!npf_rwrcksum(&enpc, nbuf, n_ptr, PFIL_OUT, addr, port)) { 346 return false; 347 } 348 if (!npf_rwrip(&enpc, nbuf, n_ptr, PFIL_OUT, addr)) { 349 return false; 350 } 351 if (!npf_rwrport(&enpc, nbuf, n_ptr, PFIL_OUT, port)) { 352 return false; 353 } 354 355 /* 356 * Finish calculation of the ICMP checksum. Update for embedded IP 357 * and TCP/UDP checksum changes. Finally, rewrite ICMP checksum. 358 */ 359 if (proto == IPPROTO_TCP) { 360 struct tcphdr *th = &enpc.npc_l4.tcp; 361 cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum); 362 } else if (l4cksum) { 363 struct udphdr *uh = &enpc.npc_l4.udp; 364 cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum); 365 } 366 cksum = npf_fixup16_cksum(cksum, ecksum, eip->ip_sum); 367 368 offby = (ip->ip_hl << 2) + offsetof(struct icmp, icmp_cksum); 369 if (nbuf_advstore(&cnbuf, &cnptr, offby, sizeof(uint16_t), &cksum)) { 370 return false; 371 } 372 return true; 373 } 374