1 /*-
2 * Copyright (c) 2010 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This material is based upon work partially supported by The
6 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * NPF ALG for ICMP and traceroute translations.
32 */
33
34 #ifdef _KERNEL
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.33 2020/05/30 14:16:56 rmind Exp $");
37
38 #include <sys/param.h>
39 #include <sys/module.h>
40
41 #include <netinet/in_systm.h>
42 #include <netinet/in.h>
43 #include <netinet/ip.h>
44 #include <netinet/tcp.h>
45 #include <netinet/udp.h>
46 #include <netinet/ip_icmp.h>
47 #include <netinet/icmp6.h>
48 #include <net/pfil.h>
49 #endif
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
npfa_icmp_match(npf_cache_t * npc,npf_nat_t * nt,int di)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 * => Sets hasqid=true if the packet has a Query Id. In this case neither
123 * the nbuf nor npc is touched.
124 */
125
126 static bool
npfa_icmp4_inspect(const int type,npf_cache_t * npc,bool * hasqid)127 npfa_icmp4_inspect(const int type, npf_cache_t *npc, bool *hasqid)
128 {
129 nbuf_t *nbuf = npc->npc_nbuf;
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 /* Should contain original IP header. */
139 if (!nbuf_advance(nbuf, offsetof(struct icmp, icmp_ip), 0)) {
140 return false;
141 }
142 return (npf_cache_all(npc) & NPC_LAYER4) != 0;
143
144 case ICMP_ECHOREPLY:
145 case ICMP_ECHO:
146 case ICMP_TSTAMP:
147 case ICMP_TSTAMPREPLY:
148 case ICMP_IREQ:
149 case ICMP_IREQREPLY:
150 /* Contains ICMP query ID. */
151 *hasqid = true;
152 return true;
153 default:
154 break;
155 }
156 return false;
157 }
158
159 static bool
npfa_icmp6_inspect(const int type,npf_cache_t * npc,bool * hasqid)160 npfa_icmp6_inspect(const int type, npf_cache_t *npc, bool *hasqid)
161 {
162 nbuf_t *nbuf = npc->npc_nbuf;
163
164 /* Per RFC 4443. */
165 switch (type) {
166 case ICMP6_DST_UNREACH:
167 case ICMP6_PACKET_TOO_BIG:
168 case ICMP6_TIME_EXCEEDED:
169 case ICMP6_PARAM_PROB:
170 /* Should contain original IP header. */
171 if (!nbuf_advance(nbuf, sizeof(struct icmp6_hdr), 0)) {
172 return false;
173 }
174 return (npf_cache_all(npc) & NPC_LAYER4) != 0;
175
176 case ICMP6_ECHO_REQUEST:
177 case ICMP6_ECHO_REPLY:
178 /* Contains ICMP query ID. */
179 *hasqid = true;
180 return true;
181 default:
182 break;
183 }
184 return false;
185 }
186
187 /*
188 * npfa_icmp_inspect: ALG ICMP inspector.
189 *
190 * => Returns false if there is a problem with the format.
191 */
192 static bool
npfa_icmp_inspect(npf_cache_t * npc,npf_cache_t * enpc)193 npfa_icmp_inspect(npf_cache_t *npc, npf_cache_t *enpc)
194 {
195 nbuf_t *nbuf = npc->npc_nbuf;
196 bool ret, hasqid = false;
197
198 KASSERT(npf_iscached(npc, NPC_IP46));
199 KASSERT(npf_iscached(npc, NPC_ICMP));
200
201 /* Advance to ICMP header. */
202 nbuf_reset(nbuf);
203 if (!nbuf_advance(nbuf, npc->npc_hlen, 0)) {
204 return false;
205 }
206 memset(enpc, 0, sizeof(npf_cache_t));
207 enpc->npc_ctx = npc->npc_ctx;
208 enpc->npc_nbuf = nbuf;
209
210 /*
211 * Inspect the ICMP packet. The relevant data might be in the
212 * embedded packet. Fill the "enpc" cache, if so.
213 */
214 if (npf_iscached(npc, NPC_IP4) &&
215 npc->npc_proto == IPPROTO_ICMP) {
216 const struct icmp *ic = npc->npc_l4.icmp;
217 ret = npfa_icmp4_inspect(ic->icmp_type, enpc, &hasqid);
218 } else if (npf_iscached(npc, NPC_IP6) &&
219 npc->npc_proto == IPPROTO_ICMPV6) {
220 const struct icmp6_hdr *ic6 = npc->npc_l4.icmp6;
221 ret = npfa_icmp6_inspect(ic6->icmp6_type, enpc, &hasqid);
222 } else {
223 ret = false;
224 }
225 if (!ret) {
226 return false;
227 }
228
229 /* ICMP ID is the original packet, just indicate it. */
230 if (hasqid) {
231 npc->npc_info |= NPC_ICMP_ID;
232 }
233
234 return true;
235 }
236
237 static npf_conn_t *
npfa_icmp_conn(npf_cache_t * npc,int di)238 npfa_icmp_conn(npf_cache_t *npc, int di)
239 {
240 npf_conn_t *conn = NULL;
241 npf_cache_t enpc;
242 bool hasqid = false;
243
244 /* Inspect ICMP packet for an embedded packet. */
245 if (!npf_iscached(npc, NPC_ICMP))
246 return NULL;
247 if (!npfa_icmp_inspect(npc, &enpc))
248 goto out;
249
250 /*
251 * If the ICMP packet had a Query Id, leave now. The packet didn't get
252 * modified, so no need to recache npc.
253 */
254 if (npf_iscached(npc, NPC_ICMP_ID)) {
255 KASSERT(!nbuf_flag_p(npc->npc_nbuf, NBUF_DATAREF_RESET));
256 return NULL;
257 }
258
259 /*
260 * Invert the identifiers of the embedded packet.
261 * If it is ICMP, then ensure ICMP ID.
262 */
263 union l4 {
264 struct tcphdr th;
265 struct udphdr uh;
266 } l4;
267 npf_flow_t flow;
268 bool ret;
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, &hasqid);
287 if (!ret || !hasqid)
288 goto out;
289 enpc.npc_info |= NPC_ICMP_ID;
290 break;
291 }
292 case IPPROTO_ICMPV6: {
293 const struct icmp6_hdr *ic6 = enpc.npc_l4.icmp6;
294 ret = npfa_icmp6_inspect(ic6->icmp6_type, &enpc, &hasqid);
295 if (!ret || !hasqid)
296 goto out;
297 enpc.npc_info |= NPC_ICMP_ID;
298 break;
299 }
300 default:
301 goto out;
302 }
303
304 /* Lookup a connection using the embedded packet. */
305 conn = npf_conn_lookup(&enpc, di, &flow);
306 out:
307 /*
308 * Recache npc. The nbuf may have been updated as a result of
309 * caching enpc.
310 */
311 npf_recache(npc);
312 return conn;
313 }
314
315 /*
316 * npfa_icmp_nat: ALG translator - rewrites IP address in the IP header
317 * which is embedded in ICMP packet. Note: backwards stream only.
318 */
319 static bool
npfa_icmp_nat(npf_cache_t * npc,npf_nat_t * nt,npf_flow_t flow)320 npfa_icmp_nat(npf_cache_t *npc, npf_nat_t *nt, npf_flow_t flow)
321 {
322 const unsigned which = NPF_SRC;
323 npf_cache_t enpc;
324 struct icmp *ic;
325 uint16_t cksum;
326
327 if (flow == NPF_FLOW_FORW || !npf_iscached(npc, NPC_ICMP))
328 return false;
329
330 /*
331 * ICMP: fetch the current checksum we are going to fixup.
332 */
333 ic = npc->npc_l4.icmp;
334 cksum = ic->icmp_cksum;
335
336 if (!npfa_icmp_inspect(npc, &enpc))
337 goto err;
338
339 /*
340 * If the ICMP packet had a Query Id, leave now. The packet didn't get
341 * modified, so no need to recache npc.
342 */
343 if (npf_iscached(npc, NPC_ICMP_ID)) {
344 KASSERT(!nbuf_flag_p(npc->npc_nbuf, NBUF_DATAREF_RESET));
345 return false;
346 }
347
348 KASSERT(npf_iscached(&enpc, NPC_IP46));
349 KASSERT(npf_iscached(&enpc, NPC_LAYER4));
350
351 CTASSERT(offsetof(struct icmp, icmp_cksum) ==
352 offsetof(struct icmp6_hdr, icmp6_cksum));
353
354 /*
355 * Fetch the IP and port in the _embedded_ packet. Also, fetch
356 * the IPv4 and TCP/UDP checksums before they are rewritten.
357 */
358 const int proto = enpc.npc_proto;
359 uint16_t ipcksum = 0, l4cksum = 0;
360 in_port_t old_port = 0;
361
362 if (npf_iscached(&enpc, NPC_IP4)) {
363 const struct ip *eip = enpc.npc_ip.v4;
364 ipcksum = eip->ip_sum;
365 }
366 switch (proto) {
367 case IPPROTO_TCP: {
368 const struct tcphdr *th = enpc.npc_l4.tcp;
369 old_port = th->th_sport;
370 l4cksum = th->th_sum;
371 break;
372 }
373 case IPPROTO_UDP: {
374 const struct udphdr *uh = enpc.npc_l4.udp;
375 old_port = uh->uh_sport;
376 l4cksum = uh->uh_sum;
377 break;
378 }
379 case IPPROTO_ICMP:
380 case IPPROTO_ICMPV6:
381 break;
382 default:
383 goto err;
384 }
385
386 /*
387 * Get the original IP address and port.
388 * Calculate the part of the ICMP checksum fixup.
389 */
390 npf_addr_t *addr;
391 in_port_t port;
392
393 npf_nat_getorig(nt, &addr, &port);
394
395 cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_ips[which], addr);
396 if (port) {
397 cksum = npf_fixup16_cksum(cksum, old_port, port);
398 }
399
400 /*
401 * Translate the embedded packet. The following changes will
402 * be performed by npf_napt_rwr():
403 *
404 * 1) Rewrite the IP address and, if not ICMP, port.
405 * 2) Rewrite the TCP/UDP checksum (if not ICMP).
406 * 3) Rewrite the IPv4 checksum for (1) and (2).
407 *
408 * XXX: Assumes NPF_NATOUT (source address/port). Currently,
409 * npfa_icmp_match() matches only for the PFIL_OUT traffic.
410 */
411 if (npf_napt_rwr(&enpc, which, addr, port)) {
412 goto err;
413 }
414
415 /*
416 * Finally, finish the ICMP checksum fixup: include the checksum
417 * changes in the embedded packet.
418 */
419 if (npf_iscached(&enpc, NPC_IP4)) {
420 const struct ip *eip = enpc.npc_ip.v4;
421 cksum = npf_fixup16_cksum(cksum, ipcksum, eip->ip_sum);
422 }
423 switch (proto) {
424 case IPPROTO_TCP: {
425 const struct tcphdr *th = enpc.npc_l4.tcp;
426 cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum);
427 break;
428 }
429 case IPPROTO_UDP:
430 if (l4cksum) {
431 const struct udphdr *uh = enpc.npc_l4.udp;
432 cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum);
433 }
434 break;
435 }
436 npf_recache(npc);
437 KASSERT(npf_iscached(npc, NPC_ICMP));
438 ic = npc->npc_l4.icmp;
439 ic->icmp_cksum = cksum;
440 return true;
441
442 err:
443 /*
444 * Recache npc. The nbuf may have been updated as a result of
445 * caching enpc.
446 */
447 npf_recache(npc);
448 return false;
449 }
450
451 /*
452 * npf_alg_icmp_{init,fini,modcmd}: ICMP ALG initialization, destruction
453 * and module interface.
454 */
455
456 __dso_public int
npf_alg_icmp_init(npf_t * npf)457 npf_alg_icmp_init(npf_t *npf)
458 {
459 static const npfa_funcs_t icmp = {
460 .match = npfa_icmp_match,
461 .translate = npfa_icmp_nat,
462 .inspect = npfa_icmp_conn,
463 .destroy = NULL,
464 };
465 alg_icmp = npf_alg_register(npf, "icmp", &icmp);
466 return alg_icmp ? 0 : ENOMEM;
467 }
468
469 __dso_public int
npf_alg_icmp_fini(npf_t * npf)470 npf_alg_icmp_fini(npf_t *npf)
471 {
472 KASSERT(alg_icmp != NULL);
473 return npf_alg_unregister(npf, alg_icmp);
474 }
475
476 #ifdef _KERNEL
477 static int
npf_alg_icmp_modcmd(modcmd_t cmd,void * arg)478 npf_alg_icmp_modcmd(modcmd_t cmd, void *arg)
479 {
480 npf_t *npf = npf_getkernctx();
481
482 switch (cmd) {
483 case MODULE_CMD_INIT:
484 return npf_alg_icmp_init(npf);
485 case MODULE_CMD_FINI:
486 return npf_alg_icmp_fini(npf);
487 case MODULE_CMD_AUTOUNLOAD:
488 return EBUSY;
489 default:
490 return ENOTTY;
491 }
492 return 0;
493 }
494 #endif
495