npf_alg_icmp.c revision 1.23.12.1 1 /* $NetBSD: npf_alg_icmp.c,v 1.23.12.1 2018/05/14 16:17:19 martin 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.12.1 2018/05/14 16:17:19 martin 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 * => 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
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 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 /* Contains ICMP query ID. */
154 *hasqid = true;
155 return true;
156 default:
157 break;
158 }
159 return false;
160 }
161
162 static bool
163 npfa_icmp6_inspect(const int type, npf_cache_t *npc, bool *hasqid)
164 {
165 nbuf_t *nbuf = npc->npc_nbuf;
166
167 /* Per RFC 4443. */
168 switch (type) {
169 case ICMP6_DST_UNREACH:
170 case ICMP6_PACKET_TOO_BIG:
171 case ICMP6_TIME_EXCEEDED:
172 case ICMP6_PARAM_PROB:
173 if (npc == NULL) {
174 return false;
175 }
176 /* Should contain original IP header. */
177 if (!nbuf_advance(nbuf, sizeof(struct icmp6_hdr), 0)) {
178 return false;
179 }
180 return (npf_cache_all(npc) & NPC_LAYER4) != 0;
181
182 case ICMP6_ECHO_REQUEST:
183 case ICMP6_ECHO_REPLY:
184 /* Contains ICMP query ID. */
185 *hasqid = true;
186 return true;
187 default:
188 break;
189 }
190 return false;
191 }
192
193 /*
194 * npfa_icmp_inspect: ALG ICMP inspector.
195 *
196 * => Returns false if there is a problem with the format.
197 */
198 static bool
199 npfa_icmp_inspect(npf_cache_t *npc, npf_cache_t *enpc)
200 {
201 nbuf_t *nbuf = npc->npc_nbuf;
202 bool ret, hasqid = false;
203
204 KASSERT(npf_iscached(npc, NPC_IP46));
205 KASSERT(npf_iscached(npc, NPC_ICMP));
206
207 /* Advance to ICMP header. */
208 nbuf_reset(nbuf);
209 if (!nbuf_advance(nbuf, npc->npc_hlen, 0)) {
210 return false;
211 }
212 enpc->npc_nbuf = nbuf;
213 enpc->npc_info = 0;
214
215 /*
216 * Inspect the ICMP packet. The relevant data might be in the
217 * embedded packet. Fill the "enpc" cache, if so.
218 */
219 if (npf_iscached(npc, NPC_IP4)) {
220 const struct icmp *ic = npc->npc_l4.icmp;
221 ret = npfa_icmp4_inspect(ic->icmp_type, enpc, &hasqid);
222 } else if (npf_iscached(npc, NPC_IP6)) {
223 const struct icmp6_hdr *ic6 = npc->npc_l4.icmp6;
224 ret = npfa_icmp6_inspect(ic6->icmp6_type, enpc, &hasqid);
225 } else {
226 ret = false;
227 }
228 if (!ret) {
229 return false;
230 }
231
232 /* ICMP ID is the original packet, just indicate it. */
233 if (hasqid) {
234 npc->npc_info |= NPC_ICMP_ID;
235 }
236
237 return true;
238 }
239
240 static npf_conn_t *
241 npfa_icmp_conn(npf_cache_t *npc, int di)
242 {
243 npf_conn_t *conn = NULL;
244 npf_cache_t enpc;
245 bool hasqid = false;
246
247 /* Inspect ICMP packet for an embedded packet. */
248 if (!npf_iscached(npc, NPC_ICMP))
249 return NULL;
250 if (!npfa_icmp_inspect(npc, &enpc))
251 goto out;
252
253 /*
254 * If the ICMP packet had a Query Id, leave now. The packet didn't get
255 * modified, so no need to recache npc.
256 */
257 if (npf_iscached(npc, NPC_ICMP_ID)) {
258 KASSERT(!nbuf_flag_p(npc->npc_nbuf, NBUF_DATAREF_RESET));
259 return NULL;
260 }
261
262 /*
263 * Invert the identifiers of the embedded packet.
264 * If it is ICMP, then ensure ICMP ID.
265 */
266 union l4 {
267 struct tcphdr th;
268 struct udphdr uh;
269 } l4;
270 bool ret, forw;
271
272 #define SWAP(type, x, y) { type tmp = x; x = y; y = tmp; }
273 SWAP(npf_addr_t *, enpc.npc_ips[NPF_SRC], enpc.npc_ips[NPF_DST]);
274
275 switch (enpc.npc_proto) {
276 case IPPROTO_TCP:
277 l4.th.th_sport = enpc.npc_l4.tcp->th_dport;
278 l4.th.th_dport = enpc.npc_l4.tcp->th_sport;
279 enpc.npc_l4.tcp = &l4.th;
280 break;
281 case IPPROTO_UDP:
282 l4.uh.uh_sport = enpc.npc_l4.udp->uh_dport;
283 l4.uh.uh_dport = enpc.npc_l4.udp->uh_sport;
284 enpc.npc_l4.udp = &l4.uh;
285 break;
286 case IPPROTO_ICMP: {
287 const struct icmp *ic = enpc.npc_l4.icmp;
288 ret = npfa_icmp4_inspect(ic->icmp_type, &enpc, &hasqid);
289 if (!ret || !hasqid)
290 goto out;
291 enpc.npc_info |= NPC_ICMP_ID;
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, &hasqid);
297 if (!ret || !hasqid)
298 goto out;
299 enpc.npc_info |= NPC_ICMP_ID;
300 break;
301 }
302 default:
303 goto out;
304 }
305
306 /* Lookup a connection using the embedded packet. */
307 conn = npf_conn_lookup(&enpc, di, &forw);
308
309 out:
310 /*
311 * Recache npc. The nbuf may have been updated as a result of
312 * caching enpc.
313 */
314 npf_recache(npc);
315 return conn;
316 }
317
318 /*
319 * npfa_icmp_nat: ALG translator - rewrites IP address in the IP header
320 * which is embedded in ICMP packet. Note: backwards stream only.
321 */
322 static bool
323 npfa_icmp_nat(npf_cache_t *npc, npf_nat_t *nt, bool forw)
324 {
325 const u_int which = NPF_SRC;
326 npf_cache_t enpc;
327 struct icmp *ic;
328 uint16_t cksum;
329
330 if (forw || !npf_iscached(npc, NPC_ICMP))
331 return false;
332
333 /*
334 * ICMP: fetch the current checksum we are going to fixup.
335 */
336 ic = npc->npc_l4.icmp;
337 cksum = ic->icmp_cksum;
338
339 if (!npfa_icmp_inspect(npc, &enpc))
340 goto err;
341
342 /*
343 * If the ICMP packet had a Query Id, leave now. The packet didn't get
344 * modified, so no need to recache npc.
345 */
346 if (npf_iscached(npc, NPC_ICMP_ID)) {
347 KASSERT(!nbuf_flag_p(npc->npc_nbuf, NBUF_DATAREF_RESET));
348 return false;
349 }
350
351 KASSERT(npf_iscached(&enpc, NPC_IP46));
352 KASSERT(npf_iscached(&enpc, NPC_LAYER4));
353
354 CTASSERT(offsetof(struct icmp, icmp_cksum) ==
355 offsetof(struct icmp6_hdr, icmp6_cksum));
356
357 /*
358 * Fetch the IP and port in the _embedded_ packet. Also, fetch
359 * the IPv4 and TCP/UDP checksums before they are rewritten.
360 * Calculate the part of the ICMP checksum fixup.
361 */
362 const int proto = enpc.npc_proto;
363 uint16_t ipcksum = 0, l4cksum = 0;
364 npf_addr_t *addr;
365 in_port_t port;
366
367 npf_nat_getorig(nt, &addr, &port);
368
369 if (npf_iscached(&enpc, NPC_IP4)) {
370 const struct ip *eip = enpc.npc_ip.v4;
371 ipcksum = eip->ip_sum;
372 }
373 cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_ips[which], addr);
374
375 switch (proto) {
376 case IPPROTO_TCP: {
377 const struct tcphdr *th = enpc.npc_l4.tcp;
378 cksum = npf_fixup16_cksum(cksum, th->th_sport, port);
379 l4cksum = th->th_sum;
380 break;
381 }
382 case IPPROTO_UDP: {
383 const struct udphdr *uh = enpc.npc_l4.udp;
384 cksum = npf_fixup16_cksum(cksum, uh->uh_sport, port);
385 l4cksum = uh->uh_sum;
386 break;
387 }
388 case IPPROTO_ICMP:
389 case IPPROTO_ICMPV6:
390 break;
391 default:
392 goto err;
393 }
394
395 /*
396 * Translate the embedded packet. The following changes will
397 * be performed by npf_napt_rwr():
398 *
399 * 1) Rewrite the IP address and, if not ICMP, port.
400 * 2) Rewrite the TCP/UDP checksum (if not ICMP).
401 * 3) Rewrite the IPv4 checksum for (1) and (2).
402 *
403 * XXX: Assumes NPF_NATOUT (source address/port). Currently,
404 * npfa_icmp_match() matches only for the PFIL_OUT traffic.
405 */
406 if (npf_napt_rwr(&enpc, which, addr, port)) {
407 goto err;
408 }
409
410 /*
411 * Finally, finish the ICMP checksum fixup: include the checksum
412 * changes in the embedded packet.
413 */
414 if (npf_iscached(&enpc, NPC_IP4)) {
415 const struct ip *eip = enpc.npc_ip.v4;
416 cksum = npf_fixup16_cksum(cksum, ipcksum, eip->ip_sum);
417 }
418 switch (proto) {
419 case IPPROTO_TCP: {
420 const struct tcphdr *th = enpc.npc_l4.tcp;
421 cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum);
422 break;
423 }
424 case IPPROTO_UDP:
425 if (l4cksum) {
426 const struct udphdr *uh = enpc.npc_l4.udp;
427 cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum);
428 }
429 break;
430 }
431 npf_recache(npc);
432 KASSERT(npf_iscached(npc, NPC_ICMP));
433 ic = npc->npc_l4.icmp;
434 ic->icmp_cksum = cksum;
435 return true;
436
437 err:
438 /*
439 * Recache npc. The nbuf may have been updated as a result of
440 * caching enpc.
441 */
442 npf_recache(npc);
443 return false;
444 }
445
446 /*
447 * npf_alg_icmp_{init,fini,modcmd}: ICMP ALG initialization, destruction
448 * and module interface.
449 */
450
451 static int
452 npf_alg_icmp_init(void)
453 {
454 static const npfa_funcs_t icmp = {
455 .match = npfa_icmp_match,
456 .translate = npfa_icmp_nat,
457 .inspect = npfa_icmp_conn,
458 };
459 alg_icmp = npf_alg_register("icmp", &icmp);
460 return alg_icmp ? 0 : ENOMEM;
461 }
462
463 static int
464 npf_alg_icmp_fini(void)
465 {
466 KASSERT(alg_icmp != NULL);
467 return npf_alg_unregister(alg_icmp);
468 }
469
470 static int
471 npf_alg_icmp_modcmd(modcmd_t cmd, void *arg)
472 {
473 switch (cmd) {
474 case MODULE_CMD_INIT:
475 return npf_alg_icmp_init();
476 case MODULE_CMD_FINI:
477 return npf_alg_icmp_fini();
478 case MODULE_CMD_AUTOUNLOAD:
479 return EBUSY;
480 default:
481 return ENOTTY;
482 }
483 return 0;
484 }
485