Home | History | Annotate | Line # | Download | only in npf
npf_alg_icmp.c revision 1.8.4.7.2.1
      1  1.8.4.7.2.1  martin /*	$NetBSD: npf_alg_icmp.c,v 1.8.4.7.2.1 2018/05/17 13:47:24 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.7.2.1  martin __KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.8.4.7.2.1 2018/05/17 13:47:24 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.7.2.1  martin  *
    166  1.8.4.7.2.1  martin  * => Sets hasqid=true if the packet has a Query Id. In this case neither
    167  1.8.4.7.2.1  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.7.2.1  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.7.2.1  martin 		/* Contains ICMP query ID. */
    197  1.8.4.7.2.1  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.7.2.1  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.7.2.1  martin 		/* Contains ICMP query ID. */
    227  1.8.4.7.2.1  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.7.2.1  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.7.2.1  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.7.2.1  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.7.2.1  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.7.2.1  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.7.2.1  martin 	npf_session_t *sess = NULL;
    284      1.8.4.6     riz 	npf_cache_t enpc;
    285  1.8.4.7.2.1  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.7.2.1  martin 		goto out;
    292  1.8.4.7.2.1  martin 
    293  1.8.4.7.2.1  martin 	/*
    294  1.8.4.7.2.1  martin 	 * If the ICMP packet had a Query Id, leave now. The packet didn't get
    295  1.8.4.7.2.1  martin 	 * modified, so no need to recache npc.
    296  1.8.4.7.2.1  martin 	 */
    297  1.8.4.7.2.1  martin 	if (npf_iscached(npc, NPC_ICMP_ID)) {
    298  1.8.4.7.2.1  martin 		KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
    299      1.8.4.6     riz 		return NULL;
    300  1.8.4.7.2.1  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.7.2.1  martin 		ret = npfa_icmp4_inspect(ic->icmp_type, &enpc, nbuf, &hasqid);
    329  1.8.4.7.2.1  martin 		if (!ret || !hasqid)
    330  1.8.4.7.2.1  martin 			goto out;
    331  1.8.4.7.2.1  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.7.2.1  martin 		ret = npfa_icmp6_inspect(ic6->icmp6_type, &enpc, nbuf, &hasqid);
    337  1.8.4.7.2.1  martin 		if (!ret || !hasqid)
    338  1.8.4.7.2.1  martin 			goto out;
    339  1.8.4.7.2.1  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.7.2.1  martin 	sess = npf_session_lookup(&enpc, nbuf, di, &forw);
    348  1.8.4.7.2.1  martin 
    349  1.8.4.7.2.1  martin out:
    350  1.8.4.7.2.1  martin 	/*
    351  1.8.4.7.2.1  martin 	 * Recache npc. The nbuf may have been updated as a result of
    352  1.8.4.7.2.1  martin 	 * caching enpc.
    353  1.8.4.7.2.1  martin 	 */
    354  1.8.4.7.2.1  martin 	npf_recache(npc, nbuf);
    355  1.8.4.7.2.1  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.7.2.1  martin 		goto err;
    371  1.8.4.7.2.1  martin 
    372  1.8.4.7.2.1  martin 	/*
    373  1.8.4.7.2.1  martin 	 * If the ICMP packet had a Query Id, leave now. The packet didn't get
    374  1.8.4.7.2.1  martin 	 * modified, so no need to recache npc.
    375  1.8.4.7.2.1  martin 	 */
    376  1.8.4.7.2.1  martin 	if (npf_iscached(npc, NPC_ICMP_ID)) {
    377  1.8.4.7.2.1  martin 		KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
    378      1.8.4.6     riz 		return false;
    379  1.8.4.7.2.1  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.7.2.1  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.7.2.1  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.7.2.1  martin 	npf_recache(npc, nbuf);
    459  1.8.4.7.2.1  martin 	KASSERT(npf_iscached(npc, NPC_ICMP));
    460  1.8.4.7.2.1  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.7.2.1  martin 
    464  1.8.4.7.2.1  martin err:
    465  1.8.4.7.2.1  martin 	/*
    466  1.8.4.7.2.1  martin 	 * Recache npc. The nbuf may have been updated as a result of
    467  1.8.4.7.2.1  martin 	 * caching enpc.
    468  1.8.4.7.2.1  martin 	 */
    469  1.8.4.7.2.1  martin 	npf_recache(npc, nbuf);
    470  1.8.4.7.2.1  martin 	return false;
    471          1.1   rmind }
    472