xref: /netbsd-src/sys/net/npf/npf_alg_icmp.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: npf_alg_icmp.c,v 1.23 2014/07/20 00:37:41 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.23 2014/07/20 00:37:41 rmind 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 #include "npf_conn.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 /*
70  * npfa_icmp_match: matching inspector determines ALG case and associates
71  * our ALG with the NAT entry.
72  */
73 static bool
74 npfa_icmp_match(npf_cache_t *npc, npf_nat_t *nt, int di)
75 {
76 	const int proto = npc->npc_proto;
77 	const struct ip *ip = npc->npc_ip.v4;
78 	in_port_t dport;
79 
80 	KASSERT(npf_iscached(npc, NPC_IP46));
81 	KASSERT(npf_iscached(npc, NPC_LAYER4));
82 
83 	/* Check for low TTL.  Also, we support outbound NAT only. */
84 	if (ip->ip_ttl > TR_MAX_TTL || di != PFIL_OUT) {
85 		return false;
86 	}
87 
88 	switch (proto) {
89 	case IPPROTO_TCP: {
90 		const struct tcphdr *th = npc->npc_l4.tcp;
91 		dport = ntohs(th->th_dport);
92 		break;
93 	}
94 	case IPPROTO_UDP: {
95 		const struct udphdr *uh = npc->npc_l4.udp;
96 		dport = ntohs(uh->uh_dport);
97 		break;
98 	}
99 	case IPPROTO_ICMP:
100 	case IPPROTO_ICMPV6:
101 		/* Just to pass the test below. */
102 		dport = TR_BASE_PORT;
103 		break;
104 	default:
105 		return false;
106 	}
107 
108 	/* Handle TCP/UDP traceroute - check for port range. */
109 	if (dport < TR_BASE_PORT || dport > TR_PORT_RANGE) {
110 		return false;
111 	}
112 
113 	/* Associate ALG with translation entry. */
114 	npf_nat_setalg(nt, alg_icmp, 0);
115 	return true;
116 }
117 
118 /*
119  * npfa_icmp{4,6}_inspect: retrieve unique identifiers - either ICMP query
120  * ID or TCP/UDP ports of the original packet, which is embedded.
121  */
122 
123 static bool
124 npfa_icmp4_inspect(const int type, npf_cache_t *npc)
125 {
126 	nbuf_t *nbuf = npc->npc_nbuf;
127 	u_int offby;
128 
129 	/* Per RFC 792. */
130 	switch (type) {
131 	case ICMP_UNREACH:
132 	case ICMP_SOURCEQUENCH:
133 	case ICMP_REDIRECT:
134 	case ICMP_TIMXCEED:
135 	case ICMP_PARAMPROB:
136 		if (npc == NULL) {
137 			return false;
138 		}
139 		/* Should contain original IP header. */
140 		if (!nbuf_advance(nbuf, offsetof(struct icmp, icmp_ip), 0)) {
141 			return false;
142 		}
143 		return (npf_cache_all(npc) & NPC_LAYER4) != 0;
144 
145 	case ICMP_ECHOREPLY:
146 	case ICMP_ECHO:
147 	case ICMP_TSTAMP:
148 	case ICMP_TSTAMPREPLY:
149 	case ICMP_IREQ:
150 	case ICMP_IREQREPLY:
151 		/* Should contain ICMP query ID - ensure. */
152 		offby = offsetof(struct icmp, icmp_id);
153 		if (!nbuf_advance(nbuf, offby, sizeof(uint16_t))) {
154 			return false;
155 		}
156 		npc->npc_info |= NPC_ICMP_ID;
157 		return true;
158 	default:
159 		break;
160 	}
161 	return false;
162 }
163 
164 static bool
165 npfa_icmp6_inspect(const int type, npf_cache_t *npc)
166 {
167 	nbuf_t *nbuf = npc->npc_nbuf;
168 	u_int offby;
169 
170 	/* Per RFC 4443. */
171 	switch (type) {
172 	case ICMP6_DST_UNREACH:
173 	case ICMP6_PACKET_TOO_BIG:
174 	case ICMP6_TIME_EXCEEDED:
175 	case ICMP6_PARAM_PROB:
176 		if (npc == NULL) {
177 			return false;
178 		}
179 		/* Should contain original IP header. */
180 		if (!nbuf_advance(nbuf, sizeof(struct icmp6_hdr), 0)) {
181 			return false;
182 		}
183 		return (npf_cache_all(npc) & NPC_LAYER4) != 0;
184 
185 	case ICMP6_ECHO_REQUEST:
186 	case ICMP6_ECHO_REPLY:
187 		/* Should contain ICMP query ID - ensure. */
188 		offby = offsetof(struct icmp6_hdr, icmp6_id);
189 		if (!nbuf_advance(nbuf, offby, sizeof(uint16_t))) {
190 			return false;
191 		}
192 		npc->npc_info |= NPC_ICMP_ID;
193 		return true;
194 	default:
195 		break;
196 	}
197 	return false;
198 }
199 
200 /*
201  * npfa_icmp_inspect: ALG ICMP inspector.
202  *
203  * => Returns true if "enpc" is filled.
204  */
205 static bool
206 npfa_icmp_inspect(npf_cache_t *npc, npf_cache_t *enpc)
207 {
208 	nbuf_t *nbuf = npc->npc_nbuf;
209 	bool ret;
210 
211 	KASSERT(npf_iscached(npc, NPC_IP46));
212 	KASSERT(npf_iscached(npc, NPC_ICMP));
213 
214 	/* Advance to ICMP header. */
215 	nbuf_reset(nbuf);
216 	if (!nbuf_advance(nbuf, npc->npc_hlen, 0)) {
217 		return false;
218 	}
219 	enpc->npc_nbuf = nbuf;
220 	enpc->npc_info = 0;
221 
222 	/*
223 	 * Inspect the ICMP packet.  The relevant data might be in the
224 	 * embedded packet.  Fill the "enpc" cache, if so.
225 	 */
226 	if (npf_iscached(npc, NPC_IP4)) {
227 		const struct icmp *ic = npc->npc_l4.icmp;
228 		ret = npfa_icmp4_inspect(ic->icmp_type, enpc);
229 	} else if (npf_iscached(npc, NPC_IP6)) {
230 		const struct icmp6_hdr *ic6 = npc->npc_l4.icmp6;
231 		ret = npfa_icmp6_inspect(ic6->icmp6_type, enpc);
232 	} else {
233 		ret = false;
234 	}
235 	if (!ret) {
236 		return false;
237 	}
238 
239 	/* ICMP ID is the original packet, just indicate it. */
240 	if (npf_iscached(enpc, NPC_ICMP_ID)) {
241 		npc->npc_info |= NPC_ICMP_ID;
242 		return false;
243 	}
244 
245 	/* Indicate that embedded packet is in the cache. */
246 	return true;
247 }
248 
249 static npf_conn_t *
250 npfa_icmp_conn(npf_cache_t *npc, int di)
251 {
252 	npf_cache_t enpc;
253 
254 	/* Inspect ICMP packet for an embedded packet. */
255 	if (!npf_iscached(npc, NPC_ICMP))
256 		return NULL;
257 	if (!npfa_icmp_inspect(npc, &enpc))
258 		return NULL;
259 
260 	/*
261 	 * Invert the identifiers of the embedded packet.
262 	 * If it is ICMP, then ensure ICMP ID.
263 	 */
264 	union l4 {
265 		struct tcphdr th;
266 		struct udphdr uh;
267 	} l4;
268 	bool ret, forw;
269 
270 	#define	SWAP(type, x, y) { type tmp = x; x = y; y = tmp; }
271 	SWAP(npf_addr_t *, enpc.npc_ips[NPF_SRC], enpc.npc_ips[NPF_DST]);
272 
273 	switch (enpc.npc_proto) {
274 	case IPPROTO_TCP:
275 		l4.th.th_sport = enpc.npc_l4.tcp->th_dport;
276 		l4.th.th_dport = enpc.npc_l4.tcp->th_sport;
277 		enpc.npc_l4.tcp = &l4.th;
278 		break;
279 	case IPPROTO_UDP:
280 		l4.uh.uh_sport = enpc.npc_l4.udp->uh_dport;
281 		l4.uh.uh_dport = enpc.npc_l4.udp->uh_sport;
282 		enpc.npc_l4.udp = &l4.uh;
283 		break;
284 	case IPPROTO_ICMP: {
285 		const struct icmp *ic = enpc.npc_l4.icmp;
286 		ret = npfa_icmp4_inspect(ic->icmp_type, &enpc);
287 		if (!ret || !npf_iscached(&enpc, NPC_ICMP_ID))
288 			return false;
289 		break;
290 	}
291 	case IPPROTO_ICMPV6: {
292 		const struct icmp6_hdr *ic6 = enpc.npc_l4.icmp6;
293 		ret = npfa_icmp6_inspect(ic6->icmp6_type, &enpc);
294 		if (!ret || !npf_iscached(&enpc, NPC_ICMP_ID))
295 			return false;
296 		break;
297 	}
298 	default:
299 		return false;
300 	}
301 
302 	/* Lookup a connection using the embedded packet. */
303 	return npf_conn_lookup(&enpc, di, &forw);
304 }
305 
306 /*
307  * npfa_icmp_nat: ALG translator - rewrites IP address in the IP header
308  * which is embedded in ICMP packet.  Note: backwards stream only.
309  */
310 static bool
311 npfa_icmp_nat(npf_cache_t *npc, npf_nat_t *nt, bool forw)
312 {
313 	const u_int which = NPF_SRC;
314 	npf_cache_t enpc;
315 
316 	if (forw || !npf_iscached(npc, NPC_ICMP))
317 		return false;
318 	if (!npfa_icmp_inspect(npc, &enpc))
319 		return false;
320 
321 	KASSERT(npf_iscached(&enpc, NPC_IP46));
322 	KASSERT(npf_iscached(&enpc, NPC_LAYER4));
323 
324 	/*
325 	 * ICMP: fetch the current checksum we are going to fixup.
326 	 */
327 	struct icmp *ic = npc->npc_l4.icmp;
328 	uint16_t cksum = ic->icmp_cksum;
329 
330 	CTASSERT(offsetof(struct icmp, icmp_cksum) ==
331 	    offsetof(struct icmp6_hdr, icmp6_cksum));
332 
333 	/*
334 	 * Fetch the IP and port in the _embedded_ packet.  Also, fetch
335 	 * the IPv4 and TCP/UDP checksums before they are rewritten.
336 	 * Calculate the part of the ICMP checksum fixup.
337 	 */
338 	const int proto = enpc.npc_proto;
339 	uint16_t ipcksum = 0, l4cksum = 0;
340 	npf_addr_t *addr;
341 	in_port_t port;
342 
343 	npf_nat_getorig(nt, &addr, &port);
344 
345 	if (npf_iscached(&enpc, NPC_IP4)) {
346 		const struct ip *eip = enpc.npc_ip.v4;
347 		ipcksum = eip->ip_sum;
348 	}
349 	cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_ips[which], addr);
350 
351 	switch (proto) {
352 	case IPPROTO_TCP: {
353 		const struct tcphdr *th = enpc.npc_l4.tcp;
354 		cksum = npf_fixup16_cksum(cksum, th->th_sport, port);
355 		l4cksum = th->th_sum;
356 		break;
357 	}
358 	case IPPROTO_UDP: {
359 		const struct udphdr *uh = enpc.npc_l4.udp;
360 		cksum = npf_fixup16_cksum(cksum, uh->uh_sport, port);
361 		l4cksum = uh->uh_sum;
362 		break;
363 	}
364 	case IPPROTO_ICMP:
365 	case IPPROTO_ICMPV6:
366 		break;
367 	default:
368 		return false;
369 	}
370 
371 	/*
372 	 * Translate the embedded packet.  The following changes will
373 	 * be performed by npf_napt_rwr():
374 	 *
375 	 *	1) Rewrite the IP address and, if not ICMP, port.
376 	 *	2) Rewrite the TCP/UDP checksum (if not ICMP).
377 	 *	3) Rewrite the IPv4 checksum for (1) and (2).
378 	 *
379 	 * XXX: Assumes NPF_NATOUT (source address/port).  Currently,
380 	 * npfa_icmp_match() matches only for the PFIL_OUT traffic.
381 	 */
382 	if (npf_napt_rwr(&enpc, which, addr, port)) {
383 		return false;
384 	}
385 
386 	/*
387 	 * Finally, finish the ICMP checksum fixup: include the checksum
388 	 * changes in the embedded packet.
389 	 */
390 	if (npf_iscached(&enpc, NPC_IP4)) {
391 		const struct ip *eip = enpc.npc_ip.v4;
392 		cksum = npf_fixup16_cksum(cksum, ipcksum, eip->ip_sum);
393 	}
394 	switch (proto) {
395 	case IPPROTO_TCP: {
396 		const struct tcphdr *th = enpc.npc_l4.tcp;
397 		cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum);
398 		break;
399 	}
400 	case IPPROTO_UDP:
401 		if (l4cksum) {
402 			const struct udphdr *uh = enpc.npc_l4.udp;
403 			cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum);
404 		}
405 		break;
406 	}
407 	ic->icmp_cksum = cksum;
408 	return true;
409 }
410 
411 /*
412  * npf_alg_icmp_{init,fini,modcmd}: ICMP ALG initialization, destruction
413  * and module interface.
414  */
415 
416 static int
417 npf_alg_icmp_init(void)
418 {
419 	static const npfa_funcs_t icmp = {
420 		.match		= npfa_icmp_match,
421 		.translate	= npfa_icmp_nat,
422 		.inspect	= npfa_icmp_conn,
423 	};
424 	alg_icmp = npf_alg_register("icmp", &icmp);
425 	return alg_icmp ? 0 : ENOMEM;
426 }
427 
428 static int
429 npf_alg_icmp_fini(void)
430 {
431 	KASSERT(alg_icmp != NULL);
432 	return npf_alg_unregister(alg_icmp);
433 }
434 
435 static int
436 npf_alg_icmp_modcmd(modcmd_t cmd, void *arg)
437 {
438 	switch (cmd) {
439 	case MODULE_CMD_INIT:
440 		return npf_alg_icmp_init();
441 	case MODULE_CMD_FINI:
442 		return npf_alg_icmp_fini();
443 	case MODULE_CMD_AUTOUNLOAD:
444 		return EBUSY;
445 	default:
446 		return ENOTTY;
447 	}
448 	return 0;
449 }
450