1 /* $NetBSD: npf_alg_icmp.c,v 1.9 2012/02/20 00:18:19 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.9 2012/02/20 00:18:19 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 <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 TTL < 50. 60 */ 61 62 #define TR_BASE_PORT 33434 63 #define TR_PORT_RANGE 33484 64 #define TR_MAX_TTL 50 65 66 static npf_alg_t * alg_icmp __read_mostly; 67 68 static bool npfa_icmp_match(npf_cache_t *, nbuf_t *, void *); 69 static bool npfa_icmp_natin(npf_cache_t *, nbuf_t *, void *); 70 static bool npfa_icmp_session(npf_cache_t *, nbuf_t *, void *); 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 81 alg_icmp = npf_alg_register(npfa_icmp_match, NULL, 82 npfa_icmp_natin, npfa_icmp_session); 83 KASSERT(alg_icmp != NULL); 84 return 0; 85 } 86 87 static int 88 npf_alg_icmp_fini(void) 89 { 90 91 KASSERT(alg_icmp != NULL); 92 return npf_alg_unregister(alg_icmp); 93 } 94 95 static int 96 npf_alg_icmp_modcmd(modcmd_t cmd, void *arg) 97 { 98 99 switch (cmd) { 100 case MODULE_CMD_INIT: 101 return npf_alg_icmp_init(); 102 case MODULE_CMD_FINI: 103 return npf_alg_icmp_fini(); 104 default: 105 return ENOTTY; 106 } 107 return 0; 108 } 109 110 /* 111 * npfa_icmp_match: ALG matching inspector - determines ALG case and 112 * associates ALG with NAT entry. 113 */ 114 static bool 115 npfa_icmp_match(npf_cache_t *npc, nbuf_t *nbuf, void *ntptr) 116 { 117 const int proto = npf_cache_ipproto(npc); 118 struct ip *ip = &npc->npc_ip.v4; 119 in_port_t dport; 120 121 KASSERT(npf_iscached(npc, NPC_IP46)); 122 KASSERT(npf_iscached(npc, 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 void *n_ptr = nbuf_dataptr(nbuf); 251 const u_int hlen = npf_cache_hlen(npc); 252 253 if ((n_ptr = nbuf_advance(&nbuf, n_ptr, hlen)) == 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)); 301 KASSERT(npf_iscached(&enpc, NPC_LAYER4)); 302 npfa_srcdst_invert(&enpc); 303 304 /* 305 * Save ICMP and embedded IP with TCP/UDP header checksums, retrieve 306 * the original address and port, and calculate ICMP checksum for 307 * embedded packet changes, while data is not rewritten in the cache. 308 */ 309 const int proto = npf_cache_ipproto(&enpc); 310 const struct ip *eip = &enpc.npc_ip.v4; 311 const struct icmp * const ic = &npc->npc_l4.icmp; 312 uint16_t cksum = ic->icmp_cksum, ecksum = eip->ip_sum, l4cksum; 313 npf_nat_t *nt = ntptr; 314 npf_addr_t *addr; 315 in_port_t port; 316 317 npf_nat_getorig(nt, &addr, &port); 318 319 if (proto == IPPROTO_TCP) { 320 struct tcphdr *th = &enpc.npc_l4.tcp; 321 cksum = npf_fixup16_cksum(cksum, th->th_sport, port); 322 l4cksum = th->th_sum; 323 } else { 324 struct udphdr *uh = &enpc.npc_l4.udp; 325 cksum = npf_fixup16_cksum(cksum, uh->uh_sport, port); 326 l4cksum = uh->uh_sum; 327 } 328 cksum = npf_addr_cksum(cksum, enpc.npc_ipsz, enpc.npc_srcip, addr); 329 330 /* 331 * Save the original pointers to the main IP header and then advance 332 * to the embedded IP header after ICMP header. 333 */ 334 void *n_ptr = nbuf_dataptr(nbuf), *cnbuf = nbuf, *cnptr = n_ptr; 335 u_int offby = npf_cache_hlen(npc) + offsetof(struct icmp, icmp_ip); 336 337 if ((n_ptr = nbuf_advance(&nbuf, n_ptr, offby)) == NULL) { 338 return false; 339 } 340 341 /* 342 * Rewrite source IP address and port of the embedded IP header, 343 * which represents original packet - therefore passing PFIL_OUT. 344 * Note: checksums are first, since it uses values from the cache. 345 */ 346 if (!npf_rwrcksum(&enpc, nbuf, n_ptr, PFIL_OUT, addr, port)) { 347 return false; 348 } 349 if (!npf_rwrip(&enpc, nbuf, n_ptr, PFIL_OUT, addr)) { 350 return false; 351 } 352 if (!npf_rwrport(&enpc, nbuf, n_ptr, PFIL_OUT, port)) { 353 return false; 354 } 355 356 /* 357 * Finish calculation of the ICMP checksum. Update for embedded IP 358 * and TCP/UDP checksum changes. Finally, rewrite ICMP checksum. 359 */ 360 if (proto == IPPROTO_TCP) { 361 struct tcphdr *th = &enpc.npc_l4.tcp; 362 cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum); 363 } else if (l4cksum) { 364 struct udphdr *uh = &enpc.npc_l4.udp; 365 cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum); 366 } 367 cksum = npf_fixup16_cksum(cksum, ecksum, eip->ip_sum); 368 369 offby = npf_cache_hlen(npc) + offsetof(struct icmp, icmp_cksum); 370 if (nbuf_advstore(&cnbuf, &cnptr, offby, sizeof(uint16_t), &cksum)) { 371 return false; 372 } 373 return true; 374 } 375