Annotation of src/sys/net/npf/npf_inet.c, Revision 1.37.12.6
1.37.12.6! pgoyette 1: /* $NetBSD: npf_inet.c,v 1.51 2018/08/31 14:16:06 maxv Exp $ */
1.1 rmind 2:
3: /*-
1.29 rmind 4: * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
1.1 rmind 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: /*
1.22 rmind 33: * Various protocol related helper routines.
1.12 rmind 34: *
35: * This layer manipulates npf_cache_t structure i.e. caches requested headers
36: * and stores which information was cached in the information bit field.
37: * It is also responsibility of this layer to update or invalidate the cache
38: * on rewrites (e.g. by translation routines).
1.1 rmind 39: */
40:
1.36 christos 41: #ifdef _KERNEL
1.1 rmind 42: #include <sys/cdefs.h>
1.37.12.6! pgoyette 43: __KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.51 2018/08/31 14:16:06 maxv Exp $");
1.1 rmind 44:
45: #include <sys/param.h>
1.11 rmind 46: #include <sys/types.h>
1.1 rmind 47:
1.4 rmind 48: #include <net/pfil.h>
49: #include <net/if.h>
50: #include <net/ethertypes.h>
51: #include <net/if_ether.h>
52:
1.1 rmind 53: #include <netinet/in_systm.h>
54: #include <netinet/in.h>
1.33 mlelstv 55: #include <netinet6/in6_var.h>
1.1 rmind 56: #include <netinet/ip.h>
1.4 rmind 57: #include <netinet/ip6.h>
1.1 rmind 58: #include <netinet/tcp.h>
59: #include <netinet/udp.h>
60: #include <netinet/ip_icmp.h>
1.36 christos 61: #endif
1.1 rmind 62:
63: #include "npf_impl.h"
64:
65: /*
1.27 rmind 66: * npf_fixup{16,32}_cksum: incremental update of the Internet checksum.
1.1 rmind 67: */
68:
69: uint16_t
70: npf_fixup16_cksum(uint16_t cksum, uint16_t odatum, uint16_t ndatum)
71: {
72: uint32_t sum;
73:
74: /*
75: * RFC 1624:
76: * HC' = ~(~HC + ~m + m')
1.27 rmind 77: *
78: * Note: 1's complement sum is endian-independent (RFC 1071, page 2).
1.1 rmind 79: */
1.27 rmind 80: sum = ~cksum & 0xffff;
81: sum += (~odatum & 0xffff) + ndatum;
1.1 rmind 82: sum = (sum >> 16) + (sum & 0xffff);
83: sum += (sum >> 16);
84:
1.27 rmind 85: return ~sum & 0xffff;
1.1 rmind 86: }
87:
88: uint16_t
89: npf_fixup32_cksum(uint16_t cksum, uint32_t odatum, uint32_t ndatum)
90: {
1.27 rmind 91: uint32_t sum;
92:
93: /*
94: * Checksum 32-bit datum as as two 16-bit. Note, the first
95: * 32->16 bit reduction is not necessary.
96: */
97: sum = ~cksum & 0xffff;
98: sum += (~odatum & 0xffff) + (ndatum & 0xffff);
1.1 rmind 99:
1.27 rmind 100: sum += (~odatum >> 16) + (ndatum >> 16);
101: sum = (sum >> 16) + (sum & 0xffff);
102: sum += (sum >> 16);
103: return ~sum & 0xffff;
1.1 rmind 104: }
105:
106: /*
1.4 rmind 107: * npf_addr_cksum: calculate checksum of the address, either IPv4 or IPv6.
108: */
109: uint16_t
1.19 rmind 110: npf_addr_cksum(uint16_t cksum, int sz, const npf_addr_t *oaddr,
111: const npf_addr_t *naddr)
1.4 rmind 112: {
1.19 rmind 113: const uint32_t *oip32 = (const uint32_t *)oaddr;
114: const uint32_t *nip32 = (const uint32_t *)naddr;
1.4 rmind 115:
116: KASSERT(sz % sizeof(uint32_t) == 0);
117: do {
118: cksum = npf_fixup32_cksum(cksum, *oip32++, *nip32++);
119: sz -= sizeof(uint32_t);
120: } while (sz);
121:
122: return cksum;
123: }
124:
125: /*
1.26 rmind 126: * npf_addr_sum: provide IP addresses as a XORed 32-bit integer.
1.4 rmind 127: * Note: used for hash function.
1.1 rmind 128: */
1.4 rmind 129: uint32_t
1.26 rmind 130: npf_addr_mix(const int sz, const npf_addr_t *a1, const npf_addr_t *a2)
1.1 rmind 131: {
1.4 rmind 132: uint32_t mix = 0;
1.1 rmind 133:
1.5 rmind 134: KASSERT(sz > 0 && a1 != NULL && a2 != NULL);
135:
1.26 rmind 136: for (int i = 0; i < (sz >> 2); i++) {
1.36 christos 137: mix ^= a1->word32[i];
138: mix ^= a2->word32[i];
1.4 rmind 139: }
140: return mix;
141: }
1.1 rmind 142:
1.13 rmind 143: /*
144: * npf_addr_mask: apply the mask to a given address and store the result.
145: */
146: void
147: npf_addr_mask(const npf_addr_t *addr, const npf_netmask_t mask,
148: const int alen, npf_addr_t *out)
1.12 rmind 149: {
1.13 rmind 150: const int nwords = alen >> 2;
1.12 rmind 151: uint_fast8_t length = mask;
152:
153: /* Note: maximum length is 32 for IPv4 and 128 for IPv6. */
154: KASSERT(length <= NPF_MAX_NETMASK);
155:
1.13 rmind 156: for (int i = 0; i < nwords; i++) {
157: uint32_t wordmask;
158:
1.12 rmind 159: if (length >= 32) {
1.13 rmind 160: wordmask = htonl(0xffffffff);
1.12 rmind 161: length -= 32;
1.13 rmind 162: } else if (length) {
163: wordmask = htonl(0xffffffff << (32 - length));
164: length = 0;
1.12 rmind 165: } else {
1.13 rmind 166: wordmask = 0;
1.12 rmind 167: }
1.36 christos 168: out->word32[i] = addr->word32[i] & wordmask;
1.12 rmind 169: }
170: }
171:
172: /*
173: * npf_addr_cmp: compare two addresses, either IPv4 or IPv6.
174: *
1.13 rmind 175: * => Return 0 if equal and negative/positive if less/greater accordingly.
1.12 rmind 176: * => Ignore the mask, if NPF_NO_NETMASK is specified.
177: */
178: int
179: npf_addr_cmp(const npf_addr_t *addr1, const npf_netmask_t mask1,
1.13 rmind 180: const npf_addr_t *addr2, const npf_netmask_t mask2, const int alen)
1.12 rmind 181: {
1.13 rmind 182: npf_addr_t realaddr1, realaddr2;
1.12 rmind 183:
184: if (mask1 != NPF_NO_NETMASK) {
1.13 rmind 185: npf_addr_mask(addr1, mask1, alen, &realaddr1);
186: addr1 = &realaddr1;
1.12 rmind 187: }
188: if (mask2 != NPF_NO_NETMASK) {
1.13 rmind 189: npf_addr_mask(addr2, mask2, alen, &realaddr2);
190: addr2 = &realaddr2;
1.12 rmind 191: }
1.13 rmind 192: return memcmp(addr1, addr2, alen);
1.12 rmind 193: }
194:
1.4 rmind 195: /*
196: * npf_tcpsaw: helper to fetch SEQ, ACK, WIN and return TCP data length.
1.12 rmind 197: *
198: * => Returns all values in host byte-order.
1.4 rmind 199: */
200: int
1.12 rmind 201: npf_tcpsaw(const npf_cache_t *npc, tcp_seq *seq, tcp_seq *ack, uint32_t *win)
1.4 rmind 202: {
1.19 rmind 203: const struct tcphdr *th = npc->npc_l4.tcp;
1.8 rmind 204: u_int thlen;
1.1 rmind 205:
1.7 zoltan 206: KASSERT(npf_iscached(npc, NPC_TCP));
1.1 rmind 207:
1.4 rmind 208: *seq = ntohl(th->th_seq);
209: *ack = ntohl(th->th_ack);
210: *win = (uint32_t)ntohs(th->th_win);
1.8 rmind 211: thlen = th->th_off << 2;
1.1 rmind 212:
1.7 zoltan 213: if (npf_iscached(npc, NPC_IP4)) {
1.19 rmind 214: const struct ip *ip = npc->npc_ip.v4;
1.21 rmind 215: return ntohs(ip->ip_len) - npc->npc_hlen - thlen;
1.12 rmind 216: } else if (npf_iscached(npc, NPC_IP6)) {
1.19 rmind 217: const struct ip6_hdr *ip6 = npc->npc_ip.v6;
1.37.12.2 pgoyette 218: return ntohs(ip6->ip6_plen) -
219: (npc->npc_hlen - sizeof(*ip6)) - thlen;
1.7 zoltan 220: }
221: return 0;
1.1 rmind 222: }
223:
224: /*
1.4 rmind 225: * npf_fetch_tcpopts: parse and return TCP options.
1.1 rmind 226: */
227: bool
1.32 rmind 228: npf_fetch_tcpopts(npf_cache_t *npc, uint16_t *mss, int *wscale)
1.1 rmind 229: {
1.32 rmind 230: nbuf_t *nbuf = npc->npc_nbuf;
1.19 rmind 231: const struct tcphdr *th = npc->npc_l4.tcp;
1.37.12.5 pgoyette 232: int cnt, optlen = 0;
233: uint8_t *cp, opt;
1.4 rmind 234: uint8_t val;
1.19 rmind 235: bool ok;
1.4 rmind 236:
1.7 zoltan 237: KASSERT(npf_iscached(npc, NPC_IP46));
238: KASSERT(npf_iscached(npc, NPC_TCP));
1.10 rmind 239:
1.4 rmind 240: /* Determine if there are any TCP options, get their length. */
1.37.12.5 pgoyette 241: cnt = (th->th_off << 2) - sizeof(struct tcphdr);
242: if (cnt <= 0) {
1.4 rmind 243: /* No options. */
1.1 rmind 244: return false;
1.4 rmind 245: }
1.37.12.5 pgoyette 246: KASSERT(cnt <= MAX_TCPOPTLEN);
1.1 rmind 247:
1.37.12.5 pgoyette 248: /* Fetch all the options at once. */
1.19 rmind 249: nbuf_reset(nbuf);
1.37.12.5 pgoyette 250: const int step = npc->npc_hlen + sizeof(struct tcphdr);
251: if ((cp = nbuf_advance(nbuf, step, cnt)) == NULL) {
1.19 rmind 252: ok = false;
253: goto done;
1.4 rmind 254: }
1.12 rmind 255:
1.37.12.5 pgoyette 256: /* Scan the options. */
257: for (; cnt > 0; cnt -= optlen, cp += optlen) {
258: opt = cp[0];
259: if (opt == TCPOPT_EOL)
260: break;
261: if (opt == TCPOPT_NOP)
262: optlen = 1;
263: else {
264: if (cnt < 2)
265: break;
266: optlen = cp[1];
267: if (optlen < 2 || optlen > cnt)
268: break;
269: }
270:
271: switch (opt) {
272: case TCPOPT_MAXSEG:
273: if (optlen != TCPOLEN_MAXSEG)
274: continue;
275: if (mss) {
1.37.12.6! pgoyette 276: memcpy(mss, cp + 2, sizeof(uint16_t));
1.19 rmind 277: }
1.37.12.5 pgoyette 278: break;
279: case TCPOPT_WINDOW:
280: if (optlen != TCPOLEN_WINDOW)
281: continue;
282: val = *(cp + 2);
283: *wscale = (val > TCP_MAX_WINSHIFT) ? TCP_MAX_WINSHIFT : val;
284: break;
285: default:
286: break;
1.4 rmind 287: }
288: }
1.37.12.5 pgoyette 289:
1.19 rmind 290: ok = true;
291: done:
292: if (nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)) {
1.32 rmind 293: npf_recache(npc);
1.19 rmind 294: }
295: return ok;
1.1 rmind 296: }
297:
1.37.12.6! pgoyette 298: /*
! 299: * npf_set_mss: set the MSS.
! 300: */
! 301: bool
! 302: npf_set_mss(npf_cache_t *npc, uint16_t mss, uint16_t *old, uint16_t *new,
! 303: bool *mid)
! 304: {
! 305: nbuf_t *nbuf = npc->npc_nbuf;
! 306: const struct tcphdr *th = npc->npc_l4.tcp;
! 307: int cnt, optlen = 0;
! 308: uint8_t *cp, *base, opt;
! 309: bool ok;
! 310:
! 311: KASSERT(npf_iscached(npc, NPC_IP46));
! 312: KASSERT(npf_iscached(npc, NPC_TCP));
! 313:
! 314: /* Determine if there are any TCP options, get their length. */
! 315: cnt = (th->th_off << 2) - sizeof(struct tcphdr);
! 316: if (cnt <= 0) {
! 317: /* No options. */
! 318: return false;
! 319: }
! 320: KASSERT(cnt <= MAX_TCPOPTLEN);
! 321:
! 322: /* Fetch all the options at once. */
! 323: nbuf_reset(nbuf);
! 324: const int step = npc->npc_hlen + sizeof(struct tcphdr);
! 325: if ((base = nbuf_advance(nbuf, step, cnt)) == NULL) {
! 326: ok = false;
! 327: goto done;
! 328: }
! 329:
! 330: /* Scan the options. */
! 331: for (cp = base; cnt > 0; cnt -= optlen, cp += optlen) {
! 332: opt = cp[0];
! 333: if (opt == TCPOPT_EOL)
! 334: break;
! 335: if (opt == TCPOPT_NOP)
! 336: optlen = 1;
! 337: else {
! 338: if (cnt < 2)
! 339: break;
! 340: optlen = cp[1];
! 341: if (optlen < 2 || optlen > cnt)
! 342: break;
! 343: }
! 344:
! 345: switch (opt) {
! 346: case TCPOPT_MAXSEG:
! 347: if (optlen != TCPOLEN_MAXSEG)
! 348: continue;
! 349: if (((cp + 2) - base) % sizeof(uint16_t) != 0) {
! 350: *mid = true;
! 351: memcpy(&old[0], cp + 1, sizeof(uint16_t));
! 352: memcpy(&old[1], cp + 3, sizeof(uint16_t));
! 353: memcpy(cp + 2, &mss, sizeof(uint16_t));
! 354: memcpy(&new[0], cp + 1, sizeof(uint16_t));
! 355: memcpy(&new[1], cp + 3, sizeof(uint16_t));
! 356: } else {
! 357: *mid = false;
! 358: memcpy(cp + 2, &mss, sizeof(uint16_t));
! 359: }
! 360: break;
! 361: default:
! 362: break;
! 363: }
! 364: }
! 365:
! 366: ok = true;
! 367: done:
! 368: if (nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)) {
! 369: npf_recache(npc);
! 370: }
! 371: return ok;
! 372: }
! 373:
1.19 rmind 374: static int
375: npf_cache_ip(npf_cache_t *npc, nbuf_t *nbuf)
1.1 rmind 376: {
1.19 rmind 377: const void *nptr = nbuf_dataptr(nbuf);
378: const uint8_t ver = *(const uint8_t *)nptr;
379: int flags = 0;
1.12 rmind 380:
1.37.12.2 pgoyette 381: /*
382: * We intentionally don't read the L4 payload after IPPROTO_AH.
383: */
384:
1.4 rmind 385: switch (ver >> 4) {
1.12 rmind 386: case IPVERSION: {
1.19 rmind 387: struct ip *ip;
1.12 rmind 388:
1.19 rmind 389: ip = nbuf_ensure_contig(nbuf, sizeof(struct ip));
390: if (ip == NULL) {
1.37.12.1 pgoyette 391: return NPC_FMTERR;
1.4 rmind 392: }
1.12 rmind 393:
1.37.12.3 pgoyette 394: /* Retrieve the complete header. */
1.10 rmind 395: if ((u_int)(ip->ip_hl << 2) < sizeof(struct ip)) {
1.37.12.1 pgoyette 396: return NPC_FMTERR;
1.4 rmind 397: }
1.37.12.3 pgoyette 398: ip = nbuf_ensure_contig(nbuf, (u_int)(ip->ip_hl << 2));
399: if (ip == NULL) {
400: return NPC_FMTERR;
401: }
402:
1.4 rmind 403: if (ip->ip_off & ~htons(IP_DF | IP_RF)) {
404: /* Note fragmentation. */
1.19 rmind 405: flags |= NPC_IPFRAG;
1.4 rmind 406: }
1.12 rmind 407:
1.4 rmind 408: /* Cache: layer 3 - IPv4. */
1.14 rmind 409: npc->npc_alen = sizeof(struct in_addr);
1.28 rmind 410: npc->npc_ips[NPF_SRC] = (npf_addr_t *)&ip->ip_src;
411: npc->npc_ips[NPF_DST] = (npf_addr_t *)&ip->ip_dst;
1.7 zoltan 412: npc->npc_hlen = ip->ip_hl << 2;
1.19 rmind 413: npc->npc_proto = ip->ip_p;
414:
415: npc->npc_ip.v4 = ip;
416: flags |= NPC_IP4;
1.4 rmind 417: break;
1.12 rmind 418: }
1.4 rmind 419:
1.12 rmind 420: case (IPV6_VERSION >> 4): {
1.19 rmind 421: struct ip6_hdr *ip6;
422: struct ip6_ext *ip6e;
1.37 christos 423: struct ip6_frag *ip6f;
1.19 rmind 424: size_t off, hlen;
1.37.12.1 pgoyette 425: int frag_present;
1.19 rmind 426:
427: ip6 = nbuf_ensure_contig(nbuf, sizeof(struct ip6_hdr));
428: if (ip6 == NULL) {
1.37.12.1 pgoyette 429: return NPC_FMTERR;
1.7 zoltan 430: }
1.19 rmind 431:
1.37.12.2 pgoyette 432: /*
433: * XXX: We don't handle IPv6 Jumbograms.
434: */
435:
1.19 rmind 436: /* Set initial next-protocol value. */
437: hlen = sizeof(struct ip6_hdr);
438: npc->npc_proto = ip6->ip6_nxt;
1.13 rmind 439: npc->npc_hlen = hlen;
1.7 zoltan 440:
1.37.12.1 pgoyette 441: frag_present = 0;
442:
1.12 rmind 443: /*
1.19 rmind 444: * Advance by the length of the current header.
1.12 rmind 445: */
1.19 rmind 446: off = nbuf_offset(nbuf);
1.37.12.1 pgoyette 447: while ((ip6e = nbuf_advance(nbuf, hlen, sizeof(*ip6e))) != NULL) {
1.13 rmind 448: /*
449: * Determine whether we are going to continue.
450: */
1.19 rmind 451: switch (npc->npc_proto) {
1.13 rmind 452: case IPPROTO_HOPOPTS:
1.7 zoltan 453: case IPPROTO_DSTOPTS:
454: case IPPROTO_ROUTING:
1.19 rmind 455: hlen = (ip6e->ip6e_len + 1) << 3;
1.7 zoltan 456: break;
457: case IPPROTO_FRAGMENT:
1.37.12.1 pgoyette 458: if (frag_present++)
459: return NPC_FMTERR;
1.37 christos 460: ip6f = nbuf_ensure_contig(nbuf, sizeof(*ip6f));
461: if (ip6f == NULL)
1.37.12.1 pgoyette 462: return NPC_FMTERR;
463:
464: /* RFC6946: Skip dummy fragments. */
465: if (!ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK) &&
466: !(ip6f->ip6f_offlg & IP6F_MORE_FRAG)) {
467: hlen = sizeof(struct ip6_frag);
468: break;
469: }
470:
471: hlen = 0;
472: flags |= NPC_IPFRAG;
1.37 christos 473:
1.7 zoltan 474: break;
475: default:
1.13 rmind 476: hlen = 0;
477: break;
478: }
479:
480: if (!hlen) {
1.7 zoltan 481: break;
482: }
1.19 rmind 483: npc->npc_proto = ip6e->ip6e_nxt;
1.13 rmind 484: npc->npc_hlen += hlen;
485: }
1.7 zoltan 486:
1.37.12.3 pgoyette 487: if (ip6e == NULL) {
488: return NPC_FMTERR;
489: }
490:
1.23 rmind 491: /*
492: * Re-fetch the header pointers (nbufs might have been
493: * reallocated). Restore the original offset (if any).
494: */
1.19 rmind 495: nbuf_reset(nbuf);
1.23 rmind 496: ip6 = nbuf_dataptr(nbuf);
1.19 rmind 497: if (off) {
498: nbuf_advance(nbuf, off, 0);
499: }
500:
1.12 rmind 501: /* Cache: layer 3 - IPv6. */
1.14 rmind 502: npc->npc_alen = sizeof(struct in6_addr);
1.28 rmind 503: npc->npc_ips[NPF_SRC] = (npf_addr_t *)&ip6->ip6_src;
1.37.12.2 pgoyette 504: npc->npc_ips[NPF_DST] = (npf_addr_t *)&ip6->ip6_dst;
1.19 rmind 505:
506: npc->npc_ip.v6 = ip6;
507: flags |= NPC_IP6;
1.7 zoltan 508: break;
1.12 rmind 509: }
1.4 rmind 510: default:
1.19 rmind 511: break;
1.4 rmind 512: }
1.19 rmind 513: return flags;
1.1 rmind 514: }
515:
516: /*
1.4 rmind 517: * npf_cache_all: general routine to cache all relevant IP (v4 or v6)
1.12 rmind 518: * and TCP, UDP or ICMP headers.
1.19 rmind 519: *
520: * => nbuf offset shall be set accordingly.
1.1 rmind 521: */
1.10 rmind 522: int
1.32 rmind 523: npf_cache_all(npf_cache_t *npc)
1.1 rmind 524: {
1.32 rmind 525: nbuf_t *nbuf = npc->npc_nbuf;
1.19 rmind 526: int flags, l4flags;
527: u_int hlen;
528:
529: /*
530: * This routine is a main point where the references are cached,
531: * therefore clear the flag as we reset.
532: */
533: again:
534: nbuf_unset_flag(nbuf, NBUF_DATAREF_RESET);
1.1 rmind 535:
1.19 rmind 536: /*
537: * First, cache the L3 header (IPv4 or IPv6). If IP packet is
538: * fragmented, then we cannot look into L4.
539: */
540: flags = npf_cache_ip(npc, nbuf);
1.37.12.1 pgoyette 541: if ((flags & NPC_IP46) == 0 || (flags & NPC_IPFRAG) != 0 ||
542: (flags & NPC_FMTERR) != 0) {
1.37.12.3 pgoyette 543: goto out;
1.1 rmind 544: }
1.19 rmind 545: hlen = npc->npc_hlen;
546:
1.37.12.3 pgoyette 547: /*
548: * Note: we guarantee that the potential "Query Id" field of the
549: * ICMPv4/ICMPv6 packets is in the nbuf. This field is used in the
550: * ICMP ALG.
551: */
1.19 rmind 552: switch (npc->npc_proto) {
1.1 rmind 553: case IPPROTO_TCP:
1.19 rmind 554: /* Cache: layer 4 - TCP. */
555: npc->npc_l4.tcp = nbuf_advance(nbuf, hlen,
556: sizeof(struct tcphdr));
557: l4flags = NPC_LAYER4 | NPC_TCP;
1.10 rmind 558: break;
1.1 rmind 559: case IPPROTO_UDP:
1.19 rmind 560: /* Cache: layer 4 - UDP. */
561: npc->npc_l4.udp = nbuf_advance(nbuf, hlen,
562: sizeof(struct udphdr));
563: l4flags = NPC_LAYER4 | NPC_UDP;
1.10 rmind 564: break;
1.1 rmind 565: case IPPROTO_ICMP:
1.19 rmind 566: /* Cache: layer 4 - ICMPv4. */
567: npc->npc_l4.icmp = nbuf_advance(nbuf, hlen,
1.37.12.3 pgoyette 568: ICMP_MINLEN);
1.19 rmind 569: l4flags = NPC_LAYER4 | NPC_ICMP;
570: break;
1.15 spz 571: case IPPROTO_ICMPV6:
1.19 rmind 572: /* Cache: layer 4 - ICMPv6. */
573: npc->npc_l4.icmp6 = nbuf_advance(nbuf, hlen,
1.37.12.3 pgoyette 574: sizeof(struct icmp6_hdr));
1.19 rmind 575: l4flags = NPC_LAYER4 | NPC_ICMP;
576: break;
577: default:
578: l4flags = 0;
1.10 rmind 579: break;
1.1 rmind 580: }
1.19 rmind 581:
1.37.12.3 pgoyette 582: /* Error out if nbuf_advance failed. */
583: if (l4flags && npc->npc_l4.hdr == NULL) {
584: goto err;
585: }
586:
1.19 rmind 587: if (nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)) {
588: goto again;
589: }
590:
1.37.12.3 pgoyette 591: flags |= l4flags;
592: npc->npc_info |= flags;
593: return flags;
594:
595: err:
596: flags = NPC_FMTERR;
597: out:
598: nbuf_unset_flag(nbuf, NBUF_DATAREF_RESET);
1.19 rmind 599: npc->npc_info |= flags;
600: return flags;
601: }
602:
603: void
1.32 rmind 604: npf_recache(npf_cache_t *npc)
1.19 rmind 605: {
1.32 rmind 606: nbuf_t *nbuf = npc->npc_nbuf;
1.24 martin 607: const int mflags __diagused = npc->npc_info & (NPC_IP46 | NPC_LAYER4);
1.25 mrg 608: int flags __diagused;
1.19 rmind 609:
610: nbuf_reset(nbuf);
611: npc->npc_info = 0;
1.32 rmind 612: flags = npf_cache_all(npc);
613:
1.19 rmind 614: KASSERT((flags & mflags) == mflags);
615: KASSERT(nbuf_flag_p(nbuf, NBUF_DATAREF_RESET) == 0);
1.1 rmind 616: }
617:
618: /*
1.19 rmind 619: * npf_rwrip: rewrite required IP address.
1.4 rmind 620: */
621: bool
1.28 rmind 622: npf_rwrip(const npf_cache_t *npc, u_int which, const npf_addr_t *addr)
1.4 rmind 623: {
624: KASSERT(npf_iscached(npc, NPC_IP46));
1.28 rmind 625: KASSERT(which == NPF_SRC || which == NPF_DST);
1.4 rmind 626:
1.28 rmind 627: memcpy(npc->npc_ips[which], addr, npc->npc_alen);
1.4 rmind 628: return true;
629: }
630:
631: /*
1.19 rmind 632: * npf_rwrport: rewrite required TCP/UDP port.
1.1 rmind 633: */
634: bool
1.28 rmind 635: npf_rwrport(const npf_cache_t *npc, u_int which, const in_port_t port)
1.1 rmind 636: {
1.21 rmind 637: const int proto = npc->npc_proto;
1.4 rmind 638: in_port_t *oport;
1.1 rmind 639:
1.4 rmind 640: KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
1.1 rmind 641: KASSERT(proto == IPPROTO_TCP || proto == IPPROTO_UDP);
1.28 rmind 642: KASSERT(which == NPF_SRC || which == NPF_DST);
1.1 rmind 643:
1.19 rmind 644: /* Get the offset and store the port in it. */
1.4 rmind 645: if (proto == IPPROTO_TCP) {
1.19 rmind 646: struct tcphdr *th = npc->npc_l4.tcp;
1.28 rmind 647: oport = (which == NPF_SRC) ? &th->th_sport : &th->th_dport;
1.1 rmind 648: } else {
1.19 rmind 649: struct udphdr *uh = npc->npc_l4.udp;
1.28 rmind 650: oport = (which == NPF_SRC) ? &uh->uh_sport : &uh->uh_dport;
1.1 rmind 651: }
1.19 rmind 652: memcpy(oport, &port, sizeof(in_port_t));
1.1 rmind 653: return true;
654: }
655:
656: /*
1.19 rmind 657: * npf_rwrcksum: rewrite IPv4 and/or TCP/UDP checksum.
1.1 rmind 658: */
659: bool
1.28 rmind 660: npf_rwrcksum(const npf_cache_t *npc, u_int which,
1.19 rmind 661: const npf_addr_t *addr, const in_port_t port)
1.1 rmind 662: {
1.28 rmind 663: const npf_addr_t *oaddr = npc->npc_ips[which];
1.21 rmind 664: const int proto = npc->npc_proto;
1.19 rmind 665: const int alen = npc->npc_alen;
1.18 rmind 666: uint16_t *ocksum;
667: in_port_t oport;
668:
1.19 rmind 669: KASSERT(npf_iscached(npc, NPC_LAYER4));
1.28 rmind 670: KASSERT(which == NPF_SRC || which == NPF_DST);
1.18 rmind 671:
1.4 rmind 672: if (npf_iscached(npc, NPC_IP4)) {
1.19 rmind 673: struct ip *ip = npc->npc_ip.v4;
674: uint16_t ipsum = ip->ip_sum;
1.4 rmind 675:
1.19 rmind 676: /* Recalculate IPv4 checksum and rewrite. */
677: ip->ip_sum = npf_addr_cksum(ipsum, alen, oaddr, addr);
1.4 rmind 678: } else {
679: /* No checksum for IPv6. */
680: KASSERT(npf_iscached(npc, NPC_IP6));
681: }
682:
1.18 rmind 683: /* Nothing else to do for ICMP. */
1.30 rmind 684: if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) {
1.4 rmind 685: return true;
686: }
1.7 zoltan 687: KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
1.4 rmind 688:
1.18 rmind 689: /*
690: * Calculate TCP/UDP checksum:
691: * - Skip if UDP and the current checksum is zero.
692: * - Fixup the IP address change.
693: * - Fixup the port change, if required (non-zero).
694: */
1.4 rmind 695: if (proto == IPPROTO_TCP) {
1.19 rmind 696: struct tcphdr *th = npc->npc_l4.tcp;
1.4 rmind 697:
1.18 rmind 698: ocksum = &th->th_sum;
1.28 rmind 699: oport = (which == NPF_SRC) ? th->th_sport : th->th_dport;
1.4 rmind 700: } else {
1.19 rmind 701: struct udphdr *uh = npc->npc_l4.udp;
1.4 rmind 702:
703: KASSERT(proto == IPPROTO_UDP);
1.18 rmind 704: ocksum = &uh->uh_sum;
705: if (*ocksum == 0) {
1.4 rmind 706: /* No need to update. */
707: return true;
708: }
1.28 rmind 709: oport = (which == NPF_SRC) ? uh->uh_sport : uh->uh_dport;
1.18 rmind 710: }
711:
1.19 rmind 712: uint16_t cksum = npf_addr_cksum(*ocksum, alen, oaddr, addr);
1.18 rmind 713: if (port) {
714: cksum = npf_fixup16_cksum(cksum, oport, port);
1.4 rmind 715: }
1.1 rmind 716:
1.19 rmind 717: /* Rewrite TCP/UDP checksum. */
718: memcpy(ocksum, &cksum, sizeof(uint16_t));
1.4 rmind 719: return true;
720: }
721:
1.29 rmind 722: /*
1.30 rmind 723: * npf_napt_rwr: perform address and/or port translation.
724: */
725: int
726: npf_napt_rwr(const npf_cache_t *npc, u_int which,
727: const npf_addr_t *addr, const in_addr_t port)
728: {
729: const unsigned proto = npc->npc_proto;
730:
731: /*
732: * Rewrite IP and/or TCP/UDP checksums first, since we need the
733: * current (old) address/port for the calculations. Then perform
734: * the address translation i.e. rewrite source or destination.
735: */
736: if (!npf_rwrcksum(npc, which, addr, port)) {
737: return EINVAL;
738: }
739: if (!npf_rwrip(npc, which, addr)) {
740: return EINVAL;
741: }
742: if (port == 0) {
743: /* Done. */
744: return 0;
745: }
746:
747: switch (proto) {
748: case IPPROTO_TCP:
749: case IPPROTO_UDP:
750: /* Rewrite source/destination port. */
751: if (!npf_rwrport(npc, which, port)) {
752: return EINVAL;
753: }
754: break;
755: case IPPROTO_ICMP:
756: case IPPROTO_ICMPV6:
757: KASSERT(npf_iscached(npc, NPC_ICMP));
758: /* Nothing. */
759: break;
760: default:
761: return ENOTSUP;
762: }
763: return 0;
764: }
765:
766: /*
1.29 rmind 767: * IPv6-to-IPv6 Network Prefix Translation (NPTv6), as per RFC 6296.
768: */
769:
770: int
771: npf_npt66_rwr(const npf_cache_t *npc, u_int which, const npf_addr_t *pref,
772: npf_netmask_t len, uint16_t adj)
773: {
774: npf_addr_t *addr = npc->npc_ips[which];
775: unsigned remnant, word, preflen = len >> 4;
776: uint32_t sum;
777:
778: KASSERT(which == NPF_SRC || which == NPF_DST);
779:
780: if (!npf_iscached(npc, NPC_IP6)) {
781: return EINVAL;
782: }
783: if (len <= 48) {
784: /*
785: * The word to adjust. Cannot translate the 0xffff
786: * subnet if /48 or shorter.
787: */
788: word = 3;
1.36 christos 789: if (addr->word16[word] == 0xffff) {
1.29 rmind 790: return EINVAL;
791: }
792: } else {
793: /*
794: * Also, all 0s or 1s in the host part are disallowed for
795: * longer than /48 prefixes.
796: */
1.36 christos 797: if ((addr->word32[2] == 0 && addr->word32[3] == 0) ||
798: (addr->word32[2] == ~0U && addr->word32[3] == ~0U))
1.29 rmind 799: return EINVAL;
800:
801: /* Determine the 16-bit word to adjust. */
802: for (word = 4; word < 8; word++)
1.36 christos 803: if (addr->word16[word] != 0xffff)
1.29 rmind 804: break;
805: }
806:
807: /* Rewrite the prefix. */
808: for (unsigned i = 0; i < preflen; i++) {
1.36 christos 809: addr->word16[i] = pref->word16[i];
1.29 rmind 810: }
811:
812: /*
813: * If prefix length is within a 16-bit word (not dividable by 16),
814: * then prepare a mask, determine the word and adjust it.
815: */
816: if ((remnant = len - (preflen << 4)) != 0) {
817: const uint16_t wordmask = (1U << remnant) - 1;
818: const unsigned i = preflen;
819:
1.36 christos 820: addr->word16[i] = (pref->word16[i] & wordmask) |
821: (addr->word16[i] & ~wordmask);
1.29 rmind 822: }
823:
824: /*
825: * Performing 1's complement sum/difference.
826: */
1.36 christos 827: sum = addr->word16[word] + adj;
1.29 rmind 828: while (sum >> 16) {
829: sum = (sum >> 16) + (sum & 0xffff);
830: }
831: if (sum == 0xffff) {
832: /* RFC 1071. */
833: sum = 0x0000;
834: }
1.36 christos 835: addr->word16[word] = sum;
1.29 rmind 836: return 0;
837: }
838:
1.13 rmind 839: #if defined(DDB) || defined(_NPF_TESTING)
840:
1.31 rmind 841: const char *
842: npf_addr_dump(const npf_addr_t *addr, int alen)
1.13 rmind 843: {
1.31 rmind 844: if (alen == sizeof(struct in_addr)) {
845: struct in_addr ip;
846: memcpy(&ip, addr, alen);
847: return inet_ntoa(ip);
848: }
1.36 christos 849: return "[IPv6]";
1.13 rmind 850: }
851:
852: #endif
CVSweb <webmaster@jp.NetBSD.org>