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