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