Home | History | Annotate | Line # | Download | only in npf
npf_alg_icmp.c revision 1.12.2.3
      1 /*	$NetBSD: npf_alg_icmp.c,v 1.12.2.3 2013/06/23 06:20:25 tls 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.12.2.3 2013/06/23 06:20:25 tls 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: ALG matching inspector - determines ALG case and
    110  * associates ALG with 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. */
    123 	if (ip->ip_ttl > TR_MAX_TTL) {
    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_srcip, enpc.npc_dstip);
    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 inbound translation inspector, rewrite IP address
    343  * in the IP header, which is embedded in ICMP packet.
    344  */
    345 static bool
    346 npfa_icmp_nat(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, int di)
    347 {
    348 	npf_cache_t enpc;
    349 
    350 	if (di != PFIL_IN || !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 	const int proto = enpc.npc_proto;
    370 	uint16_t ipcksum = 0, l4cksum = 0;
    371 	npf_addr_t *addr;
    372 	in_port_t port;
    373 
    374 	npf_nat_getorig(nt, &addr, &port);
    375 
    376 	if (npf_iscached(&enpc, NPC_IP4)) {
    377 		const struct ip *eip = enpc.npc_ip.v4;
    378 		ipcksum = eip->ip_sum;
    379 	}
    380 	cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_srcip, addr);
    381 
    382 	switch (proto) {
    383 	case IPPROTO_TCP: {
    384 		const struct tcphdr *th = enpc.npc_l4.tcp;
    385 		cksum = npf_fixup16_cksum(cksum, th->th_sport, port);
    386 		l4cksum = th->th_sum;
    387 		break;
    388 	}
    389 	case IPPROTO_UDP: {
    390 		const struct udphdr *uh = enpc.npc_l4.udp;
    391 		cksum = npf_fixup16_cksum(cksum, uh->uh_sport, port);
    392 		l4cksum = uh->uh_sum;
    393 		break;
    394 	}
    395 	case IPPROTO_ICMP:
    396 	case IPPROTO_ICMPV6:
    397 		break;
    398 	default:
    399 		return false;
    400 	}
    401 
    402 	/*
    403 	 * Rewrite the source IP address and port of the embedded IP header,
    404 	 * which represents the original packet, therefore passing PFIL_OUT.
    405 	 * This updates the checksums in the embedded packet.
    406 	 */
    407 	if (npf_nat_translate(&enpc, nbuf, nt, false, PFIL_OUT)) {
    408 		return false;
    409 	}
    410 
    411 	/*
    412 	 * Finish calculation of the ICMP checksum: include the checksum
    413 	 * change in the embedded packet.
    414 	 */
    415 	if (npf_iscached(&enpc, NPC_IP4)) {
    416 		const struct ip *eip = enpc.npc_ip.v4;
    417 		cksum = npf_fixup16_cksum(cksum, ipcksum, eip->ip_sum);
    418 	}
    419 	switch (proto) {
    420 	case IPPROTO_TCP: {
    421 		const struct tcphdr *th = enpc.npc_l4.tcp;
    422 		cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum);
    423 		break;
    424 	}
    425 	case IPPROTO_UDP:
    426 		if (l4cksum) {
    427 			const struct udphdr *uh = enpc.npc_l4.udp;
    428 			cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum);
    429 		}
    430 		break;
    431 	}
    432 	ic->icmp_cksum = cksum;
    433 	return true;
    434 }
    435