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