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