Home | History | Annotate | Line # | Download | only in npf
npf_inet.c revision 1.10.4.7
      1 /*	$NetBSD: npf_inet.c,v 1.10.4.7 2012/12/16 18:19:52 riz Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2009-2012 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  * Various procotol related helper routines.
     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).
     39  */
     40 
     41 #include <sys/cdefs.h>
     42 __KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.10.4.7 2012/12/16 18:19:52 riz Exp $");
     43 
     44 #include <sys/param.h>
     45 #include <sys/types.h>
     46 
     47 #include <net/pfil.h>
     48 #include <net/if.h>
     49 #include <net/ethertypes.h>
     50 #include <net/if_ether.h>
     51 
     52 #include <netinet/in_systm.h>
     53 #include <netinet/in.h>
     54 #include <netinet/ip.h>
     55 #include <netinet/ip6.h>
     56 #include <netinet/tcp.h>
     57 #include <netinet/udp.h>
     58 #include <netinet/ip_icmp.h>
     59 
     60 #include "npf_impl.h"
     61 
     62 /*
     63  * npf_fixup{16,32}_cksum: update IPv4 checksum.
     64  */
     65 
     66 uint16_t
     67 npf_fixup16_cksum(uint16_t cksum, uint16_t odatum, uint16_t ndatum)
     68 {
     69 	uint32_t sum;
     70 
     71 	/*
     72 	 * RFC 1624:
     73 	 *	HC' = ~(~HC + ~m + m')
     74 	 */
     75 	sum = ~ntohs(cksum) & 0xffff;
     76 	sum += (~ntohs(odatum) & 0xffff) + ntohs(ndatum);
     77 	sum = (sum >> 16) + (sum & 0xffff);
     78 	sum += (sum >> 16);
     79 
     80 	return htons(~sum & 0xffff);
     81 }
     82 
     83 uint16_t
     84 npf_fixup32_cksum(uint16_t cksum, uint32_t odatum, uint32_t ndatum)
     85 {
     86 
     87 	cksum = npf_fixup16_cksum(cksum, odatum & 0xffff, ndatum & 0xffff);
     88 	cksum = npf_fixup16_cksum(cksum, odatum >> 16, ndatum >> 16);
     89 	return cksum;
     90 }
     91 
     92 /*
     93  * npf_addr_cksum: calculate checksum of the address, either IPv4 or IPv6.
     94  */
     95 uint16_t
     96 npf_addr_cksum(uint16_t cksum, int sz, npf_addr_t *oaddr, npf_addr_t *naddr)
     97 {
     98 	uint32_t *oip32 = (uint32_t *)oaddr, *nip32 = (uint32_t *)naddr;
     99 
    100 	KASSERT(sz % sizeof(uint32_t) == 0);
    101 	do {
    102 		cksum = npf_fixup32_cksum(cksum, *oip32++, *nip32++);
    103 		sz -= sizeof(uint32_t);
    104 	} while (sz);
    105 
    106 	return cksum;
    107 }
    108 
    109 /*
    110  * npf_addr_sum: provide IP address as a summed (if needed) 32-bit integer.
    111  * Note: used for hash function.
    112  */
    113 uint32_t
    114 npf_addr_sum(const int sz, const npf_addr_t *a1, const npf_addr_t *a2)
    115 {
    116 	uint32_t mix = 0;
    117 	int i;
    118 
    119 	KASSERT(sz > 0 && a1 != NULL && a2 != NULL);
    120 
    121 	for (i = 0; i < (sz >> 2); i++) {
    122 		mix += a1->s6_addr32[i];
    123 		mix += a2->s6_addr32[i];
    124 	}
    125 	return mix;
    126 }
    127 
    128 /*
    129  * npf_addr_mask: apply the mask to a given address and store the result.
    130  */
    131 void
    132 npf_addr_mask(const npf_addr_t *addr, const npf_netmask_t mask,
    133     const int alen, npf_addr_t *out)
    134 {
    135 	const int nwords = alen >> 2;
    136 	uint_fast8_t length = mask;
    137 
    138 	/* Note: maximum length is 32 for IPv4 and 128 for IPv6. */
    139 	KASSERT(length <= NPF_MAX_NETMASK);
    140 
    141 	for (int i = 0; i < nwords; i++) {
    142 		uint32_t wordmask;
    143 
    144 		if (length >= 32) {
    145 			wordmask = htonl(0xffffffff);
    146 			length -= 32;
    147 		} else if (length) {
    148 			wordmask = htonl(0xffffffff << (32 - length));
    149 			length = 0;
    150 		} else {
    151 			wordmask = 0;
    152 		}
    153 		out->s6_addr32[i] = addr->s6_addr32[i] & wordmask;
    154 	}
    155 }
    156 
    157 /*
    158  * npf_addr_cmp: compare two addresses, either IPv4 or IPv6.
    159  *
    160  * => Return 0 if equal and negative/positive if less/greater accordingly.
    161  * => Ignore the mask, if NPF_NO_NETMASK is specified.
    162  */
    163 int
    164 npf_addr_cmp(const npf_addr_t *addr1, const npf_netmask_t mask1,
    165     const npf_addr_t *addr2, const npf_netmask_t mask2, const int alen)
    166 {
    167 	npf_addr_t realaddr1, realaddr2;
    168 
    169 	if (mask1 != NPF_NO_NETMASK) {
    170 		npf_addr_mask(addr1, mask1, alen, &realaddr1);
    171 		addr1 = &realaddr1;
    172 	}
    173 	if (mask2 != NPF_NO_NETMASK) {
    174 		npf_addr_mask(addr2, mask2, alen, &realaddr2);
    175 		addr2 = &realaddr2;
    176 	}
    177 	return memcmp(addr1, addr2, alen);
    178 }
    179 
    180 /*
    181  * npf_tcpsaw: helper to fetch SEQ, ACK, WIN and return TCP data length.
    182  *
    183  * => Returns all values in host byte-order.
    184  */
    185 int
    186 npf_tcpsaw(const npf_cache_t *npc, tcp_seq *seq, tcp_seq *ack, uint32_t *win)
    187 {
    188 	const struct tcphdr *th = &npc->npc_l4.tcp;
    189 	u_int thlen;
    190 
    191 	KASSERT(npf_iscached(npc, NPC_TCP));
    192 
    193 	*seq = ntohl(th->th_seq);
    194 	*ack = ntohl(th->th_ack);
    195 	*win = (uint32_t)ntohs(th->th_win);
    196 	thlen = th->th_off << 2;
    197 
    198 	if (npf_iscached(npc, NPC_IP4)) {
    199 		const struct ip *ip = &npc->npc_ip.v4;
    200 		return ntohs(ip->ip_len) - npf_cache_hlen(npc) - thlen;
    201 	} else if (npf_iscached(npc, NPC_IP6)) {
    202 		const struct ip6_hdr *ip6 = &npc->npc_ip.v6;
    203 		return ntohs(ip6->ip6_plen) - thlen;
    204 	}
    205 	return 0;
    206 }
    207 
    208 /*
    209  * npf_fetch_tcpopts: parse and return TCP options.
    210  */
    211 bool
    212 npf_fetch_tcpopts(const npf_cache_t *npc, nbuf_t *nbuf,
    213     uint16_t *mss, int *wscale)
    214 {
    215 	void *n_ptr = nbuf_dataptr(nbuf);
    216 	const struct tcphdr *th = &npc->npc_l4.tcp;
    217 	int topts_len, step;
    218 	uint16_t val16;
    219 	uint8_t val;
    220 
    221 	KASSERT(npf_iscached(npc, NPC_IP46));
    222 	KASSERT(npf_iscached(npc, NPC_TCP));
    223 
    224 	/* Determine if there are any TCP options, get their length. */
    225 	topts_len = (th->th_off << 2) - sizeof(struct tcphdr);
    226 	if (topts_len <= 0) {
    227 		/* No options. */
    228 		return false;
    229 	}
    230 	KASSERT(topts_len <= MAX_TCPOPTLEN);
    231 
    232 	/* First step: IP and TCP header up to options. */
    233 	step = npf_cache_hlen(npc) + sizeof(struct tcphdr);
    234 next:
    235 	if (nbuf_advfetch(&nbuf, &n_ptr, step, sizeof(val), &val)) {
    236 		return false;
    237 	}
    238 
    239 	switch (val) {
    240 	case TCPOPT_EOL:
    241 		/* Done. */
    242 		return true;
    243 	case TCPOPT_NOP:
    244 		topts_len--;
    245 		step = 1;
    246 		break;
    247 	case TCPOPT_MAXSEG:
    248 		/*
    249 		 * XXX: clean this mess.
    250 		 */
    251 		if (mss && *mss) {
    252 			val16 = *mss;
    253 			if (nbuf_advstore(&nbuf, &n_ptr, 2,
    254 			    sizeof(val16), &val16))
    255 				return false;
    256 		} else if (nbuf_advfetch(&nbuf, &n_ptr, 2,
    257 		    sizeof(val16), &val16)) {
    258 			return false;
    259 		}
    260 		if (mss) {
    261 			*mss = val16;
    262 		}
    263 		topts_len -= TCPOLEN_MAXSEG;
    264 		step = sizeof(val16);
    265 		break;
    266 	case TCPOPT_WINDOW:
    267 		/* TCP Window Scaling (RFC 1323). */
    268 		if (nbuf_advfetch(&nbuf, &n_ptr, 2, sizeof(val), &val)) {
    269 			return false;
    270 		}
    271 		*wscale = (val > TCP_MAX_WINSHIFT) ? TCP_MAX_WINSHIFT : val;
    272 		topts_len -= TCPOLEN_WINDOW;
    273 		step = sizeof(val);
    274 		break;
    275 	default:
    276 		if (nbuf_advfetch(&nbuf, &n_ptr, 1, sizeof(val), &val)) {
    277 			return false;
    278 		}
    279 		if (val < 2 || val > topts_len) {
    280 			return false;
    281 		}
    282 		topts_len -= val;
    283 		step = val - 1;
    284 	}
    285 
    286 	/* Any options left? */
    287 	if (__predict_true(topts_len > 0)) {
    288 		goto next;
    289 	}
    290 	return true;
    291 }
    292 
    293 /*
    294  * npf_fetch_ip: fetch, check and cache IP header.
    295  */
    296 bool
    297 npf_fetch_ip(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
    298 {
    299 	uint8_t ver;
    300 
    301 	if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(uint8_t), &ver)) {
    302 		return false;
    303 	}
    304 
    305 	switch (ver >> 4) {
    306 	case IPVERSION: {
    307 		struct ip *ip = &npc->npc_ip.v4;
    308 
    309 		/* Fetch IPv4 header. */
    310 		if (nbuf_fetch_datum(nbuf, n_ptr, sizeof(struct ip), ip)) {
    311 			return false;
    312 		}
    313 
    314 		/* Check header length and fragment offset. */
    315 		if ((u_int)(ip->ip_hl << 2) < sizeof(struct ip)) {
    316 			return false;
    317 		}
    318 		if (ip->ip_off & ~htons(IP_DF | IP_RF)) {
    319 			/* Note fragmentation. */
    320 			npc->npc_info |= NPC_IPFRAG;
    321 		}
    322 
    323 		/* Cache: layer 3 - IPv4. */
    324 		npc->npc_alen = sizeof(struct in_addr);
    325 		npc->npc_srcip = (npf_addr_t *)&ip->ip_src;
    326 		npc->npc_dstip = (npf_addr_t *)&ip->ip_dst;
    327 		npc->npc_info |= NPC_IP4;
    328 		npc->npc_hlen = ip->ip_hl << 2;
    329 		npc->npc_next_proto = npc->npc_ip.v4.ip_p;
    330 		break;
    331 	}
    332 
    333 	case (IPV6_VERSION >> 4): {
    334 		struct ip6_hdr *ip6 = &npc->npc_ip.v6;
    335 		size_t hlen = sizeof(struct ip6_hdr);
    336 		struct ip6_ext ip6e;
    337 
    338 		/* Fetch IPv6 header and set initial next-protocol value. */
    339 		if (nbuf_fetch_datum(nbuf, n_ptr, hlen, ip6)) {
    340 			return false;
    341 		}
    342 		npc->npc_next_proto = ip6->ip6_nxt;
    343 		npc->npc_hlen = hlen;
    344 
    345 		/*
    346 		 * Advance by the length of the current header and
    347 		 * prefetch the extension header.
    348 		 */
    349 		while (nbuf_advfetch(&nbuf, &n_ptr, hlen,
    350 		    sizeof(struct ip6_ext), &ip6e) == 0) {
    351 			/*
    352 			 * Determine whether we are going to continue.
    353 			 */
    354 			switch (npc->npc_next_proto) {
    355 			case IPPROTO_HOPOPTS:
    356 			case IPPROTO_DSTOPTS:
    357 			case IPPROTO_ROUTING:
    358 				hlen = (ip6e.ip6e_len + 1) << 3;
    359 				break;
    360 			case IPPROTO_FRAGMENT:
    361 				npc->npc_info |= NPC_IPFRAG;
    362 				hlen = sizeof(struct ip6_frag);
    363 				break;
    364 			case IPPROTO_AH:
    365 				hlen = (ip6e.ip6e_len + 2) << 2;
    366 				break;
    367 			default:
    368 				hlen = 0;
    369 				break;
    370 			}
    371 
    372 			if (!hlen) {
    373 				break;
    374 			}
    375 			npc->npc_next_proto = ip6e.ip6e_nxt;
    376 			npc->npc_hlen += hlen;
    377 		}
    378 
    379 		/* Cache: layer 3 - IPv6. */
    380 		npc->npc_alen = sizeof(struct in6_addr);
    381 		npc->npc_srcip = (npf_addr_t *)&ip6->ip6_src;
    382 		npc->npc_dstip = (npf_addr_t *)&ip6->ip6_dst;
    383 		npc->npc_info |= NPC_IP6;
    384 		break;
    385 	}
    386 	default:
    387 		return false;
    388 	}
    389 
    390 	return true;
    391 }
    392 
    393 /*
    394  * npf_fetch_tcp: fetch, check and cache TCP header.  If necessary,
    395  * fetch and cache layer 3 as well.
    396  */
    397 bool
    398 npf_fetch_tcp(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
    399 {
    400 	struct tcphdr *th;
    401 
    402 	/* Must have IP header processed for its length and protocol. */
    403 	if (!npf_iscached(npc, NPC_IP46) && !npf_fetch_ip(npc, nbuf, n_ptr)) {
    404 		return false;
    405 	}
    406 	if (npf_cache_ipproto(npc) != IPPROTO_TCP) {
    407 		return false;
    408 	}
    409 	th = &npc->npc_l4.tcp;
    410 
    411 	/* Fetch TCP header. */
    412 	if (nbuf_advfetch(&nbuf, &n_ptr, npf_cache_hlen(npc),
    413 	    sizeof(struct tcphdr), th)) {
    414 		return false;
    415 	}
    416 
    417 	/* Cache: layer 4 - TCP. */
    418 	npc->npc_info |= (NPC_LAYER4 | NPC_TCP);
    419 	return true;
    420 }
    421 
    422 /*
    423  * npf_fetch_udp: fetch, check and cache UDP header.  If necessary,
    424  * fetch and cache layer 3 as well.
    425  */
    426 bool
    427 npf_fetch_udp(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
    428 {
    429 	struct udphdr *uh;
    430 	u_int hlen;
    431 
    432 	/* Must have IP header processed for its length and protocol. */
    433 	if (!npf_iscached(npc, NPC_IP46) && !npf_fetch_ip(npc, nbuf, n_ptr)) {
    434 		return false;
    435 	}
    436 	if (npf_cache_ipproto(npc) != IPPROTO_UDP) {
    437 		return false;
    438 	}
    439 	uh = &npc->npc_l4.udp;
    440 	hlen = npf_cache_hlen(npc);
    441 
    442 	/* Fetch UDP header. */
    443 	if (nbuf_advfetch(&nbuf, &n_ptr, hlen, sizeof(struct udphdr), uh)) {
    444 		return false;
    445 	}
    446 
    447 	/* Cache: layer 4 - UDP. */
    448 	npc->npc_info |= (NPC_LAYER4 | NPC_UDP);
    449 	return true;
    450 }
    451 
    452 /*
    453  * npf_fetch_icmp: fetch ICMP code, type and possible query ID.
    454  */
    455 bool
    456 npf_fetch_icmp(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr)
    457 {
    458 	struct icmp *ic;
    459 	u_int hlen, iclen;
    460 
    461 	/* Must have IP header processed for its length and protocol. */
    462 	if (!npf_iscached(npc, NPC_IP46) && !npf_fetch_ip(npc, nbuf, n_ptr)) {
    463 		return false;
    464 	}
    465 	if (npf_cache_ipproto(npc) != IPPROTO_ICMP &&
    466 	    npf_cache_ipproto(npc) != IPPROTO_ICMPV6) {
    467 		return false;
    468 	}
    469 	ic = &npc->npc_l4.icmp;
    470 	hlen = npf_cache_hlen(npc);
    471 
    472 	/* Fetch basic ICMP header, up to the "data" point. */
    473 	CTASSERT(offsetof(struct icmp, icmp_void) ==
    474 	         offsetof(struct icmp6_hdr, icmp6_data32));
    475 
    476 	iclen = offsetof(struct icmp, icmp_void);
    477 	if (nbuf_advfetch(&nbuf, &n_ptr, hlen, iclen, ic)) {
    478 		return false;
    479 	}
    480 
    481 	/* Cache: layer 4 - ICMP. */
    482 	npc->npc_info |= (NPC_LAYER4 | NPC_ICMP);
    483 	return true;
    484 }
    485 
    486 /*
    487  * npf_cache_all: general routine to cache all relevant IP (v4 or v6)
    488  * and TCP, UDP or ICMP headers.
    489  */
    490 int
    491 npf_cache_all(npf_cache_t *npc, nbuf_t *nbuf)
    492 {
    493 	void *n_ptr = nbuf_dataptr(nbuf);
    494 
    495 	if (!npf_iscached(npc, NPC_IP46) && !npf_fetch_ip(npc, nbuf, n_ptr)) {
    496 		return npc->npc_info;
    497 	}
    498 	if (npf_iscached(npc, NPC_IPFRAG)) {
    499 		return npc->npc_info;
    500 	}
    501 	switch (npf_cache_ipproto(npc)) {
    502 	case IPPROTO_TCP:
    503 		(void)npf_fetch_tcp(npc, nbuf, n_ptr);
    504 		break;
    505 	case IPPROTO_UDP:
    506 		(void)npf_fetch_udp(npc, nbuf, n_ptr);
    507 		break;
    508 	case IPPROTO_ICMP:
    509 	case IPPROTO_ICMPV6:
    510 		(void)npf_fetch_icmp(npc, nbuf, n_ptr);
    511 		break;
    512 	}
    513 	return npc->npc_info;
    514 }
    515 
    516 /*
    517  * npf_rwrip: rewrite required IP address, update the cache.
    518  */
    519 bool
    520 npf_rwrip(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr, const int di,
    521     npf_addr_t *addr)
    522 {
    523 	npf_addr_t *oaddr;
    524 	u_int offby;
    525 
    526 	KASSERT(npf_iscached(npc, NPC_IP46));
    527 
    528 	if (di == PFIL_OUT) {
    529 		/* Rewrite source address, if outgoing. */
    530 		offby = offsetof(struct ip, ip_src);
    531 		oaddr = npc->npc_srcip;
    532 	} else {
    533 		/* Rewrite destination, if incoming. */
    534 		offby = offsetof(struct ip, ip_dst);
    535 		oaddr = npc->npc_dstip;
    536 	}
    537 
    538 	/* Advance to the address and rewrite it. */
    539 	if (nbuf_advstore(&nbuf, &n_ptr, offby, npc->npc_alen, addr))
    540 		return false;
    541 
    542 	/* Cache: IP address. */
    543 	memcpy(oaddr, addr, npc->npc_alen);
    544 	return true;
    545 }
    546 
    547 /*
    548  * npf_rwrport: rewrite required TCP/UDP port, update the cache.
    549  */
    550 bool
    551 npf_rwrport(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr, const int di,
    552     in_port_t port)
    553 {
    554 	const int proto = npf_cache_ipproto(npc);
    555 	u_int offby = npf_cache_hlen(npc);
    556 	in_port_t *oport;
    557 
    558 	KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
    559 	KASSERT(proto == IPPROTO_TCP || proto == IPPROTO_UDP);
    560 
    561 	/* Offset to the port and pointer in the cache. */
    562 	if (proto == IPPROTO_TCP) {
    563 		struct tcphdr *th = &npc->npc_l4.tcp;
    564 		if (di == PFIL_OUT) {
    565 			CTASSERT(offsetof(struct tcphdr, th_sport) == 0);
    566 			oport = &th->th_sport;
    567 		} else {
    568 			offby += offsetof(struct tcphdr, th_dport);
    569 			oport = &th->th_dport;
    570 		}
    571 	} else {
    572 		struct udphdr *uh = &npc->npc_l4.udp;
    573 		if (di == PFIL_OUT) {
    574 			CTASSERT(offsetof(struct udphdr, uh_sport) == 0);
    575 			oport = &uh->uh_sport;
    576 		} else {
    577 			offby += offsetof(struct udphdr, uh_dport);
    578 			oport = &uh->uh_dport;
    579 		}
    580 	}
    581 
    582 	/* Advance and rewrite the port. */
    583 	if (nbuf_advstore(&nbuf, &n_ptr, offby, sizeof(in_port_t), &port))
    584 		return false;
    585 
    586 	/* Cache: TCP/UDP port. */
    587 	*oport = port;
    588 	return true;
    589 }
    590 
    591 /*
    592  * npf_rwrcksum: rewrite IPv4 and/or TCP/UDP checksum, update the cache.
    593  */
    594 bool
    595 npf_rwrcksum(npf_cache_t *npc, nbuf_t *nbuf, void *n_ptr, const int di,
    596     npf_addr_t *addr, in_port_t port)
    597 {
    598 	const int proto = npf_cache_ipproto(npc);
    599 	npf_addr_t *oaddr;
    600 	uint16_t *ocksum;
    601 	in_port_t oport;
    602 	u_int offby;
    603 
    604 	/* XXX: NetBSD - process delayed checksums. */
    605 	if (di == PFIL_OUT && proto != IPPROTO_ICMP) {
    606 		nbuf_cksum_barrier(nbuf);
    607 		npc->npc_info &= ~(NPC_LAYER4 | NPC_TCP | NPC_UDP);
    608 		if (!npf_cache_all(npc, nbuf)) {
    609 			return false;
    610 		}
    611 	}
    612 
    613 	oaddr = (di == PFIL_OUT) ? npc->npc_srcip : npc->npc_dstip;
    614 
    615 	if (npf_iscached(npc, NPC_IP4)) {
    616 		struct ip *ip = &npc->npc_ip.v4;
    617 		uint16_t ipsum;
    618 
    619 		/* Recalculate IPv4 checksum, advance to it and rewrite. */
    620 		ipsum = npf_addr_cksum(ip->ip_sum, npc->npc_alen, oaddr, addr);
    621 		offby = offsetof(struct ip, ip_sum);
    622 		if (nbuf_advstore(&nbuf, &n_ptr, offby, sizeof(ipsum), &ipsum))
    623 			return false;
    624 		ip->ip_sum = ipsum;
    625 	} else {
    626 		/* No checksum for IPv6. */
    627 		KASSERT(npf_iscached(npc, NPC_IP6));
    628 		offby = 0;
    629 	}
    630 
    631 	/* Nothing else to do for ICMP. */
    632 	if (proto == IPPROTO_ICMP) {
    633 		return true;
    634 	}
    635 	KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
    636 	offby = npf_cache_hlen(npc) - offby;
    637 
    638 	/*
    639 	 * Calculate TCP/UDP checksum:
    640 	 * - Skip if UDP and the current checksum is zero.
    641 	 * - Fixup the IP address change.
    642 	 * - Fixup the port change, if required (non-zero).
    643 	 */
    644 	if (proto == IPPROTO_TCP) {
    645 		struct tcphdr *th = &npc->npc_l4.tcp;
    646 
    647 		ocksum = &th->th_sum;
    648 		offby += offsetof(struct tcphdr, th_sum);
    649 		oport = (di == PFIL_OUT) ? th->th_sport : th->th_dport;
    650 	} else {
    651 		struct udphdr *uh = &npc->npc_l4.udp;
    652 
    653 		KASSERT(proto == IPPROTO_UDP);
    654 		ocksum = &uh->uh_sum;
    655 		if (*ocksum == 0) {
    656 			/* No need to update. */
    657 			return true;
    658 		}
    659 		offby += offsetof(struct udphdr, uh_sum);
    660 		oport = (di == PFIL_OUT) ? uh->uh_sport : uh->uh_dport;
    661 	}
    662 
    663 	uint16_t cksum = *ocksum;
    664 	cksum = npf_addr_cksum(cksum, npc->npc_alen, oaddr, addr);
    665 	if (port) {
    666 		cksum = npf_fixup16_cksum(cksum, oport, port);
    667 	}
    668 
    669 	/* Advance to TCP/UDP checksum and rewrite it. */
    670 	if (nbuf_advstore(&nbuf, &n_ptr, offby, sizeof(cksum), &cksum)) {
    671 		return false;
    672 	}
    673 	*ocksum = cksum;
    674 	return true;
    675 }
    676 
    677 #if defined(DDB) || defined(_NPF_TESTING)
    678 
    679 void
    680 npf_addr_dump(const npf_addr_t *addr)
    681 {
    682 	printf("IP[%x:%x:%x:%x]\n",
    683 	    addr->s6_addr32[0], addr->s6_addr32[1],
    684 	    addr->s6_addr32[2], addr->s6_addr32[3]);
    685 }
    686 
    687 #endif
    688