npf_alg_icmp.c revision 1.8.4.8 1 1.8.4.8 martin /* $NetBSD: npf_alg_icmp.c,v 1.8.4.8 2018/05/17 13:45:15 martin Exp $ */
2 1.1 rmind
3 1.1 rmind /*-
4 1.1 rmind * Copyright (c) 2010 The NetBSD Foundation, Inc.
5 1.1 rmind * All rights reserved.
6 1.1 rmind *
7 1.1 rmind * This material is based upon work partially supported by The
8 1.1 rmind * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
9 1.1 rmind *
10 1.1 rmind * Redistribution and use in source and binary forms, with or without
11 1.1 rmind * modification, are permitted provided that the following conditions
12 1.1 rmind * are met:
13 1.1 rmind * 1. Redistributions of source code must retain the above copyright
14 1.1 rmind * notice, this list of conditions and the following disclaimer.
15 1.1 rmind * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 rmind * notice, this list of conditions and the following disclaimer in the
17 1.1 rmind * documentation and/or other materials provided with the distribution.
18 1.1 rmind *
19 1.1 rmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 rmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 rmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 rmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 rmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 rmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 rmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 rmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 rmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 rmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 rmind * POSSIBILITY OF SUCH DAMAGE.
30 1.1 rmind */
31 1.1 rmind
32 1.1 rmind /*
33 1.1 rmind * NPF ALG for ICMP and traceroute translations.
34 1.1 rmind */
35 1.1 rmind
36 1.1 rmind #include <sys/cdefs.h>
37 1.8.4.8 martin __KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.8.4.8 2018/05/17 13:45:15 martin Exp $");
38 1.1 rmind
39 1.1 rmind #include <sys/param.h>
40 1.1 rmind #include <sys/module.h>
41 1.1 rmind #include <sys/pool.h>
42 1.1 rmind
43 1.1 rmind #include <netinet/in_systm.h>
44 1.1 rmind #include <netinet/in.h>
45 1.1 rmind #include <netinet/ip.h>
46 1.1 rmind #include <netinet/tcp.h>
47 1.1 rmind #include <netinet/udp.h>
48 1.1 rmind #include <netinet/ip_icmp.h>
49 1.8.4.3 jdc #include <netinet/icmp6.h>
50 1.1 rmind #include <net/pfil.h>
51 1.1 rmind
52 1.1 rmind #include "npf_impl.h"
53 1.1 rmind
54 1.1 rmind MODULE(MODULE_CLASS_MISC, npf_alg_icmp, "npf");
55 1.1 rmind
56 1.1 rmind /*
57 1.1 rmind * Traceroute criteria.
58 1.1 rmind *
59 1.1 rmind * IANA assigned base port: 33434. However, common practice is to increase
60 1.8.4.6 riz * the port, thus monitor [33434-33484] range. Additional filter is low TTL.
61 1.1 rmind */
62 1.1 rmind
63 1.1 rmind #define TR_BASE_PORT 33434
64 1.1 rmind #define TR_PORT_RANGE 33484
65 1.8.4.6 riz #define TR_MAX_TTL 48
66 1.1 rmind
67 1.6 rmind static npf_alg_t * alg_icmp __read_mostly;
68 1.1 rmind
69 1.8.4.6 riz static bool npfa_icmp_match(npf_cache_t *, nbuf_t *, npf_nat_t *, int);
70 1.8.4.6 riz static bool npfa_icmp_nat(npf_cache_t *, nbuf_t *, npf_nat_t *, int);
71 1.8.4.6 riz static npf_session_t *npfa_icmp_session(npf_cache_t *, nbuf_t *, int);
72 1.1 rmind
73 1.1 rmind /*
74 1.1 rmind * npf_alg_icmp_{init,fini,modcmd}: ICMP ALG initialization, destruction
75 1.1 rmind * and module interface.
76 1.1 rmind */
77 1.1 rmind
78 1.1 rmind static int
79 1.1 rmind npf_alg_icmp_init(void)
80 1.1 rmind {
81 1.1 rmind
82 1.8.4.6 riz alg_icmp = npf_alg_register(npfa_icmp_match,
83 1.8.4.6 riz npfa_icmp_nat, npfa_icmp_session);
84 1.1 rmind KASSERT(alg_icmp != NULL);
85 1.1 rmind return 0;
86 1.1 rmind }
87 1.1 rmind
88 1.1 rmind static int
89 1.1 rmind npf_alg_icmp_fini(void)
90 1.1 rmind {
91 1.1 rmind
92 1.1 rmind KASSERT(alg_icmp != NULL);
93 1.1 rmind return npf_alg_unregister(alg_icmp);
94 1.1 rmind }
95 1.1 rmind
96 1.1 rmind static int
97 1.1 rmind npf_alg_icmp_modcmd(modcmd_t cmd, void *arg)
98 1.1 rmind {
99 1.1 rmind
100 1.1 rmind switch (cmd) {
101 1.1 rmind case MODULE_CMD_INIT:
102 1.1 rmind return npf_alg_icmp_init();
103 1.1 rmind case MODULE_CMD_FINI:
104 1.1 rmind return npf_alg_icmp_fini();
105 1.8.4.2 riz case MODULE_CMD_AUTOUNLOAD:
106 1.8.4.2 riz return EBUSY;
107 1.1 rmind default:
108 1.1 rmind return ENOTTY;
109 1.1 rmind }
110 1.1 rmind return 0;
111 1.1 rmind }
112 1.1 rmind
113 1.1 rmind /*
114 1.4 rmind * npfa_icmp_match: ALG matching inspector - determines ALG case and
115 1.4 rmind * associates ALG with NAT entry.
116 1.1 rmind */
117 1.1 rmind static bool
118 1.8.4.6 riz npfa_icmp_match(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, int di)
119 1.1 rmind {
120 1.8.4.7 riz const int proto = npc->npc_proto;
121 1.8.4.6 riz const struct ip *ip = npc->npc_ip.v4;
122 1.4 rmind in_port_t dport;
123 1.4 rmind
124 1.7 zoltan KASSERT(npf_iscached(npc, NPC_IP46));
125 1.7 zoltan KASSERT(npf_iscached(npc, NPC_LAYER4));
126 1.4 rmind
127 1.6 rmind /* Check for low TTL. */
128 1.6 rmind if (ip->ip_ttl > TR_MAX_TTL) {
129 1.6 rmind return false;
130 1.6 rmind }
131 1.6 rmind
132 1.8.4.6 riz switch (proto) {
133 1.8.4.6 riz case IPPROTO_TCP: {
134 1.8.4.6 riz const struct tcphdr *th = npc->npc_l4.tcp;
135 1.4 rmind dport = ntohs(th->th_dport);
136 1.8.4.6 riz break;
137 1.8.4.6 riz }
138 1.8.4.6 riz case IPPROTO_UDP: {
139 1.8.4.6 riz const struct udphdr *uh = npc->npc_l4.udp;
140 1.4 rmind dport = ntohs(uh->uh_dport);
141 1.8.4.6 riz break;
142 1.8.4.6 riz }
143 1.8.4.6 riz case IPPROTO_ICMP:
144 1.8.4.6 riz case IPPROTO_ICMPV6:
145 1.8.4.6 riz /* Just to pass the test below. */
146 1.8.4.6 riz dport = TR_BASE_PORT;
147 1.8.4.6 riz break;
148 1.8.4.6 riz default:
149 1.4 rmind return false;
150 1.4 rmind }
151 1.1 rmind
152 1.1 rmind /* Handle TCP/UDP traceroute - check for port range. */
153 1.1 rmind if (dport < TR_BASE_PORT || dport > TR_PORT_RANGE) {
154 1.1 rmind return false;
155 1.1 rmind }
156 1.1 rmind
157 1.1 rmind /* Associate ALG with translation entry. */
158 1.1 rmind npf_nat_setalg(nt, alg_icmp, 0);
159 1.1 rmind return true;
160 1.1 rmind }
161 1.1 rmind
162 1.1 rmind /*
163 1.8.4.6 riz * npfa_icmp{4,6}_inspect: retrieve unique identifiers - either ICMP query
164 1.8.4.6 riz * ID or TCP/UDP ports of the original packet, which is embedded.
165 1.8.4.8 martin *
166 1.8.4.8 martin * => Sets hasqid=true if the packet has a Query Id. In this case neither
167 1.8.4.8 martin * the nbuf nor npc is touched.
168 1.1 rmind */
169 1.8.4.5 riz
170 1.5 rmind static bool
171 1.8.4.8 martin npfa_icmp4_inspect(const int type, npf_cache_t *npc, nbuf_t *nbuf, bool *hasqid)
172 1.1 rmind {
173 1.8.4.3 jdc
174 1.8.4.5 riz /* Per RFC 792. */
175 1.8.4.5 riz switch (type) {
176 1.8.4.5 riz case ICMP_UNREACH:
177 1.8.4.5 riz case ICMP_SOURCEQUENCH:
178 1.8.4.5 riz case ICMP_REDIRECT:
179 1.8.4.5 riz case ICMP_TIMXCEED:
180 1.8.4.5 riz case ICMP_PARAMPROB:
181 1.8.4.6 riz if (npc == NULL) {
182 1.8.4.5 riz return false;
183 1.8.4.5 riz }
184 1.8.4.6 riz /* Should contain original IP header. */
185 1.8.4.6 riz if (!nbuf_advance(nbuf, offsetof(struct icmp, icmp_ip), 0)) {
186 1.8.4.5 riz return false;
187 1.1 rmind }
188 1.8.4.6 riz return (npf_cache_all(npc, nbuf) & NPC_LAYER4) != 0;
189 1.8.4.5 riz
190 1.8.4.5 riz case ICMP_ECHOREPLY:
191 1.8.4.5 riz case ICMP_ECHO:
192 1.8.4.5 riz case ICMP_TSTAMP:
193 1.8.4.5 riz case ICMP_TSTAMPREPLY:
194 1.8.4.5 riz case ICMP_IREQ:
195 1.8.4.5 riz case ICMP_IREQREPLY:
196 1.8.4.8 martin /* Contains ICMP query ID. */
197 1.8.4.8 martin *hasqid = true;
198 1.8.4.5 riz return true;
199 1.8.4.5 riz default:
200 1.8.4.5 riz break;
201 1.8.4.3 jdc }
202 1.8.4.5 riz return false;
203 1.8.4.5 riz }
204 1.8.4.5 riz
205 1.8.4.5 riz static bool
206 1.8.4.8 martin npfa_icmp6_inspect(const int type, npf_cache_t *npc, nbuf_t *nbuf, bool *hasqid)
207 1.8.4.5 riz {
208 1.8.4.5 riz
209 1.8.4.5 riz /* Per RFC 4443. */
210 1.8.4.5 riz switch (type) {
211 1.8.4.5 riz case ICMP6_DST_UNREACH:
212 1.8.4.5 riz case ICMP6_PACKET_TOO_BIG:
213 1.8.4.5 riz case ICMP6_TIME_EXCEEDED:
214 1.8.4.5 riz case ICMP6_PARAM_PROB:
215 1.8.4.6 riz if (npc == NULL) {
216 1.8.4.5 riz return false;
217 1.8.4.5 riz }
218 1.8.4.6 riz /* Should contain original IP header. */
219 1.8.4.6 riz if (!nbuf_advance(nbuf, sizeof(struct icmp6_hdr), 0)) {
220 1.8.4.5 riz return false;
221 1.1 rmind }
222 1.8.4.6 riz return (npf_cache_all(npc, nbuf) & NPC_LAYER4) != 0;
223 1.8.4.5 riz
224 1.8.4.5 riz case ICMP6_ECHO_REQUEST:
225 1.8.4.5 riz case ICMP6_ECHO_REPLY:
226 1.8.4.8 martin /* Contains ICMP query ID. */
227 1.8.4.8 martin *hasqid = true;
228 1.8.4.5 riz return true;
229 1.8.4.5 riz default:
230 1.8.4.5 riz break;
231 1.1 rmind }
232 1.1 rmind return false;
233 1.1 rmind }
234 1.1 rmind
235 1.1 rmind /*
236 1.8.4.6 riz * npfa_icmp_session: ALG ICMP inspector.
237 1.8.4.6 riz *
238 1.8.4.8 martin * => Returns false if there is a problem with the format.
239 1.1 rmind */
240 1.1 rmind static bool
241 1.8.4.6 riz npfa_icmp_inspect(npf_cache_t *npc, nbuf_t *nbuf, npf_cache_t *enpc)
242 1.1 rmind {
243 1.8.4.8 martin bool ret, hasqid = false;
244 1.8.4.5 riz
245 1.8.4.6 riz KASSERT(npf_iscached(npc, NPC_IP46));
246 1.4 rmind KASSERT(npf_iscached(npc, NPC_ICMP));
247 1.1 rmind
248 1.1 rmind /* Advance to ICMP header. */
249 1.8.4.6 riz nbuf_reset(nbuf);
250 1.8.4.7 riz if (!nbuf_advance(nbuf, npc->npc_hlen, 0)) {
251 1.1 rmind return false;
252 1.1 rmind }
253 1.8.4.6 riz enpc->npc_info = 0;
254 1.1 rmind
255 1.8.4.5 riz /*
256 1.8.4.6 riz * Inspect the ICMP packet. The relevant data might be in the
257 1.8.4.6 riz * embedded packet. Fill the "enpc" cache, if so.
258 1.8.4.5 riz */
259 1.8.4.5 riz if (npf_iscached(npc, NPC_IP4)) {
260 1.8.4.6 riz const struct icmp *ic = npc->npc_l4.icmp;
261 1.8.4.8 martin ret = npfa_icmp4_inspect(ic->icmp_type, enpc, nbuf, &hasqid);
262 1.8.4.5 riz } else if (npf_iscached(npc, NPC_IP6)) {
263 1.8.4.6 riz const struct icmp6_hdr *ic6 = npc->npc_l4.icmp6;
264 1.8.4.8 martin ret = npfa_icmp6_inspect(ic6->icmp6_type, enpc, nbuf, &hasqid);
265 1.8.4.5 riz } else {
266 1.8.4.5 riz ret = false;
267 1.8.4.5 riz }
268 1.8.4.5 riz if (!ret) {
269 1.1 rmind return false;
270 1.1 rmind }
271 1.1 rmind
272 1.8.4.6 riz /* ICMP ID is the original packet, just indicate it. */
273 1.8.4.8 martin if (hasqid) {
274 1.4 rmind npc->npc_info |= NPC_ICMP_ID;
275 1.1 rmind }
276 1.4 rmind
277 1.8.4.6 riz return true;
278 1.8.4.6 riz }
279 1.8.4.6 riz
280 1.8.4.6 riz static npf_session_t *
281 1.8.4.6 riz npfa_icmp_session(npf_cache_t *npc, nbuf_t *nbuf, int di)
282 1.8.4.6 riz {
283 1.8.4.8 martin npf_session_t *sess = NULL;
284 1.8.4.6 riz npf_cache_t enpc;
285 1.8.4.8 martin bool hasqid = false;
286 1.8.4.6 riz
287 1.8.4.6 riz /* Inspect ICMP packet for an embedded packet. */
288 1.8.4.6 riz if (!npf_iscached(npc, NPC_ICMP))
289 1.8.4.6 riz return NULL;
290 1.8.4.6 riz if (!npfa_icmp_inspect(npc, nbuf, &enpc))
291 1.8.4.8 martin goto out;
292 1.8.4.8 martin
293 1.8.4.8 martin /*
294 1.8.4.8 martin * If the ICMP packet had a Query Id, leave now. The packet didn't get
295 1.8.4.8 martin * modified, so no need to recache npc.
296 1.8.4.8 martin */
297 1.8.4.8 martin if (npf_iscached(npc, NPC_ICMP_ID)) {
298 1.8.4.8 martin KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
299 1.8.4.6 riz return NULL;
300 1.8.4.8 martin }
301 1.8.4.6 riz
302 1.4 rmind /*
303 1.8.4.6 riz * Invert the identifiers of the embedded packet.
304 1.8.4.6 riz * If it is ICMP, then ensure ICMP ID.
305 1.4 rmind */
306 1.8.4.6 riz union l4 {
307 1.8.4.6 riz struct tcphdr th;
308 1.8.4.6 riz struct udphdr uh;
309 1.8.4.6 riz } l4;
310 1.8.4.6 riz bool ret, forw;
311 1.8.4.6 riz
312 1.8.4.6 riz #define SWAP(type, x, y) { type tmp = x; x = y; y = tmp; }
313 1.8.4.6 riz SWAP(npf_addr_t *, enpc.npc_srcip, enpc.npc_dstip);
314 1.8.4.6 riz
315 1.8.4.7 riz switch (enpc.npc_proto) {
316 1.8.4.6 riz case IPPROTO_TCP:
317 1.8.4.6 riz l4.th.th_sport = enpc.npc_l4.tcp->th_dport;
318 1.8.4.6 riz l4.th.th_dport = enpc.npc_l4.tcp->th_sport;
319 1.8.4.6 riz enpc.npc_l4.tcp = &l4.th;
320 1.8.4.6 riz break;
321 1.8.4.6 riz case IPPROTO_UDP:
322 1.8.4.6 riz l4.uh.uh_sport = enpc.npc_l4.udp->uh_dport;
323 1.8.4.6 riz l4.uh.uh_dport = enpc.npc_l4.udp->uh_sport;
324 1.8.4.6 riz enpc.npc_l4.udp = &l4.uh;
325 1.8.4.6 riz break;
326 1.8.4.6 riz case IPPROTO_ICMP: {
327 1.8.4.6 riz const struct icmp *ic = enpc.npc_l4.icmp;
328 1.8.4.8 martin ret = npfa_icmp4_inspect(ic->icmp_type, &enpc, nbuf, &hasqid);
329 1.8.4.8 martin if (!ret || !hasqid)
330 1.8.4.8 martin goto out;
331 1.8.4.8 martin enpc.npc_info |= NPC_ICMP_ID;
332 1.8.4.6 riz break;
333 1.8.4.6 riz }
334 1.8.4.6 riz case IPPROTO_ICMPV6: {
335 1.8.4.6 riz const struct icmp6_hdr *ic6 = enpc.npc_l4.icmp6;
336 1.8.4.8 martin ret = npfa_icmp6_inspect(ic6->icmp6_type, &enpc, nbuf, &hasqid);
337 1.8.4.8 martin if (!ret || !hasqid)
338 1.8.4.8 martin goto out;
339 1.8.4.8 martin enpc.npc_info |= NPC_ICMP_ID;
340 1.8.4.6 riz break;
341 1.8.4.6 riz }
342 1.8.4.6 riz default:
343 1.8.4.6 riz return false;
344 1.8.4.6 riz }
345 1.4 rmind
346 1.8.4.6 riz /* Lookup for a session using embedded packet. */
347 1.8.4.8 martin sess = npf_session_lookup(&enpc, nbuf, di, &forw);
348 1.8.4.8 martin
349 1.8.4.8 martin out:
350 1.8.4.8 martin /*
351 1.8.4.8 martin * Recache npc. The nbuf may have been updated as a result of
352 1.8.4.8 martin * caching enpc.
353 1.8.4.8 martin */
354 1.8.4.8 martin npf_recache(npc, nbuf);
355 1.8.4.8 martin return sess;
356 1.1 rmind }
357 1.1 rmind
358 1.1 rmind /*
359 1.8.4.6 riz * npfa_icmp_nat: ALG inbound translation inspector, rewrite IP address
360 1.1 rmind * in the IP header, which is embedded in ICMP packet.
361 1.1 rmind */
362 1.1 rmind static bool
363 1.8.4.6 riz npfa_icmp_nat(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, int di)
364 1.1 rmind {
365 1.8.4.6 riz npf_cache_t enpc;
366 1.1 rmind
367 1.8.4.6 riz if (di != PFIL_IN || !npf_iscached(npc, NPC_ICMP))
368 1.1 rmind return false;
369 1.8.4.6 riz if (!npfa_icmp_inspect(npc, nbuf, &enpc))
370 1.8.4.8 martin goto err;
371 1.8.4.8 martin
372 1.8.4.8 martin /*
373 1.8.4.8 martin * If the ICMP packet had a Query Id, leave now. The packet didn't get
374 1.8.4.8 martin * modified, so no need to recache npc.
375 1.8.4.8 martin */
376 1.8.4.8 martin if (npf_iscached(npc, NPC_ICMP_ID)) {
377 1.8.4.8 martin KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
378 1.8.4.6 riz return false;
379 1.8.4.8 martin }
380 1.8.4.6 riz
381 1.7 zoltan KASSERT(npf_iscached(&enpc, NPC_IP46));
382 1.7 zoltan KASSERT(npf_iscached(&enpc, NPC_LAYER4));
383 1.8.4.6 riz
384 1.8.4.6 riz struct icmp *ic = npc->npc_l4.icmp;
385 1.8.4.6 riz uint16_t cksum = ic->icmp_cksum;
386 1.8.4.6 riz
387 1.8.4.6 riz CTASSERT(offsetof(struct icmp, icmp_cksum) ==
388 1.8.4.6 riz offsetof(struct icmp6_hdr, icmp6_cksum));
389 1.1 rmind
390 1.6 rmind /*
391 1.8.4.6 riz * Retrieve the original address and port, then calculate ICMP
392 1.8.4.6 riz * checksum for these changes in the embedded packet. While data
393 1.8.4.6 riz * is not rewritten in the cache, save IP and TCP/UDP checksums.
394 1.6 rmind */
395 1.8.4.7 riz const int proto = enpc.npc_proto;
396 1.8.4.6 riz uint16_t ipcksum = 0, l4cksum = 0;
397 1.6 rmind npf_addr_t *addr;
398 1.6 rmind in_port_t port;
399 1.6 rmind
400 1.6 rmind npf_nat_getorig(nt, &addr, &port);
401 1.4 rmind
402 1.8.4.6 riz if (npf_iscached(&enpc, NPC_IP4)) {
403 1.8.4.6 riz const struct ip *eip = enpc.npc_ip.v4;
404 1.8.4.6 riz ipcksum = eip->ip_sum;
405 1.8.4.6 riz }
406 1.8.4.6 riz cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_srcip, addr);
407 1.8.4.6 riz
408 1.8.4.6 riz switch (proto) {
409 1.8.4.6 riz case IPPROTO_TCP: {
410 1.8.4.6 riz const struct tcphdr *th = enpc.npc_l4.tcp;
411 1.6 rmind cksum = npf_fixup16_cksum(cksum, th->th_sport, port);
412 1.4 rmind l4cksum = th->th_sum;
413 1.8.4.6 riz break;
414 1.8.4.6 riz }
415 1.8.4.6 riz case IPPROTO_UDP: {
416 1.8.4.6 riz const struct udphdr *uh = enpc.npc_l4.udp;
417 1.6 rmind cksum = npf_fixup16_cksum(cksum, uh->uh_sport, port);
418 1.4 rmind l4cksum = uh->uh_sum;
419 1.8.4.6 riz break;
420 1.1 rmind }
421 1.8.4.6 riz case IPPROTO_ICMP:
422 1.8.4.6 riz case IPPROTO_ICMPV6:
423 1.8.4.6 riz break;
424 1.8.4.6 riz default:
425 1.8.4.8 martin goto err;
426 1.1 rmind }
427 1.1 rmind
428 1.4 rmind /*
429 1.8.4.6 riz * Rewrite the source IP address and port of the embedded IP header,
430 1.8.4.6 riz * which represents the original packet, therefore passing PFIL_OUT.
431 1.8.4.6 riz * This updates the checksums in the embedded packet.
432 1.4 rmind */
433 1.8.4.6 riz if (npf_nat_translate(&enpc, nbuf, nt, false, PFIL_OUT)) {
434 1.8.4.8 martin goto err;
435 1.1 rmind }
436 1.1 rmind
437 1.1 rmind /*
438 1.8.4.6 riz * Finish calculation of the ICMP checksum: include the checksum
439 1.8.4.6 riz * change in the embedded packet.
440 1.1 rmind */
441 1.8.4.6 riz if (npf_iscached(&enpc, NPC_IP4)) {
442 1.8.4.6 riz const struct ip *eip = enpc.npc_ip.v4;
443 1.8.4.6 riz cksum = npf_fixup16_cksum(cksum, ipcksum, eip->ip_sum);
444 1.8.4.6 riz }
445 1.8.4.6 riz switch (proto) {
446 1.8.4.6 riz case IPPROTO_TCP: {
447 1.8.4.6 riz const struct tcphdr *th = enpc.npc_l4.tcp;
448 1.4 rmind cksum = npf_fixup16_cksum(cksum, l4cksum, th->th_sum);
449 1.8.4.6 riz break;
450 1.1 rmind }
451 1.8.4.6 riz case IPPROTO_UDP:
452 1.8.4.6 riz if (l4cksum) {
453 1.8.4.6 riz const struct udphdr *uh = enpc.npc_l4.udp;
454 1.8.4.6 riz cksum = npf_fixup16_cksum(cksum, l4cksum, uh->uh_sum);
455 1.8.4.6 riz }
456 1.8.4.6 riz break;
457 1.6 rmind }
458 1.8.4.8 martin npf_recache(npc, nbuf);
459 1.8.4.8 martin KASSERT(npf_iscached(npc, NPC_ICMP));
460 1.8.4.8 martin ic = npc->npc_l4.icmp;
461 1.8.4.6 riz ic->icmp_cksum = cksum;
462 1.6 rmind return true;
463 1.8.4.8 martin
464 1.8.4.8 martin err:
465 1.8.4.8 martin /*
466 1.8.4.8 martin * Recache npc. The nbuf may have been updated as a result of
467 1.8.4.8 martin * caching enpc.
468 1.8.4.8 martin */
469 1.8.4.8 martin npf_recache(npc, nbuf);
470 1.8.4.8 martin return false;
471 1.1 rmind }
472