saddr, &(*state)->lan.addr,
daddr, &(*state)->lan.addr,
pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif,    struct mbuf *m, int off, void *h, struct pf_pdesc *pd){	struct pf_addr	*saddr = pd->src, *daddr = pd->dst;	u_int16_t	 icmpid, *icmpsum;	u_int8_t	 icmptype;	int		 state_icmp = 0;	switch (pd->proto) {#ifdef INET	case IPPROTO_ICMP:		icmptype = pd->hdr.icmp->icmp_type;		icmpid = pd->hdr.icmp->icmp_id;		icmpsum = &pd->hdr.icmp->icmp_cksum;		if (icmptype == ICMP_UNREACH ||		    icmptype == ICMP_SOURCEQUENCH ||		    icmptype == ICMP_REDIRECT ||		    icmptype == ICMP_TIMXCEED ||		    icmptype == ICMP_PARAMPROB)			state_icmp++;		break;#endif /* INET */#ifdef INET6	case IPPROTO_ICMPV6:		icmptype = pd->hdr.icmp6->icmp6_type;		icmpid = pd->hdr.icmp6->icmp6_id;		icmpsum = &pd->hdr.icmp6->icmp6_cksum;		if (icmptype == ICMP6_DST_UNREACH ||		    icmptype == ICMP6_PACKET_TOO_BIG ||		    icmptype == ICMP6_TIME_EXCEEDED ||		    icmptype == ICMP6_PARAM_PROB)			state_icmp++;		break;#endif /* INET6 */	}	if (!state_icmp) {		/*		 * ICMP query/reply message not related to a TCP/UDP packet.		 * Search for an ICMP state.		 */		struct pf_state		key;		key.af = pd->af;		key.proto = pd->proto;		if (direction == PF_IN)	{			PF_ACPY(&key.ext.addr, pd->src, key.af);			PF_ACPY(&key.gwy.addr, pd->dst, key.af);			key.ext.port = icmpid;			key.gwy.port = icmpid;		} else {			PF_ACPY(&key.lan.addr, pd->src, key.af);			PF_ACPY(&key.ext.addr, pd->dst, key.af);			key.lan.port = icmpid;			key.ext.port = icmpid;		}		STATE_LOOKUP();		(*state)->expire = time.tv_sec;		(*state)->timeout = PFTM_ICMP_ERROR_REPLY;		/* translate source/destination address, if necessary */		if (PF_ANEQ(&(*state)->lan.addr, &(*state)->gwy.addr, pd->af)) {			if (direction == PF_OUT) {				switch (pd->af) {#ifdef INET				case AF_INET:					pf_change_a(&saddr->v4.s_addr,					    pd->ip_sum,					    (*state)->gwy.addr.v4.s_addr, 0);					break;#endif /* INET */#ifdef INET6				case AF_INET6:					pf_change_a6(saddr,					    &pd->hdr.icmp6->icmp6_cksum,					    &(*state)->gwy.addr, 0);					m_copyback(m, off,					    sizeof(struct icmp6_hdr),					    pd->hdr.icmp6);					break;#endif /* INET6 */				}			} else {				switch (pd->af) {#ifdef INET				case AF_INET:					pf_change_a(&daddr->v4.s_addr,					    pd->ip_sum,					    (*state)->lan.addr.v4.s_addr, 0);					break;#endif /* INET */#ifdef INET6				case AF_INET6:					pf_change_a6(daddr,					    &pd->hdr.icmp6->icmp6_cksum,					    &(*state)->lan.addr, 0);					m_copyback(m, off,					    sizeof(struct icmp6_hdr),					    pd->hdr.icmp6);					break;#endif /* INET6 */				}			}		}		return (PF_PASS);	} else {		/*		 * ICMP error message in response to a TCP/UDP packet.		 * Extract the inner TCP/UDP header and search for that state.		 */		struct pf_pdesc	pd2;#ifdef INET		struct ip	h2;#endif /* INET */#ifdef INET6		struct ip6_hdr	h2_6;		int		terminal = 0;#endif /* INET6 */		int		ipoff2;		int		off2;		pd2.af = pd->af;		switch (pd->af) {#ifdef INET		case AF_INET:			/* offset of h2 in mbuf chain */			ipoff2 = off + ICMP_MINLEN;			if (!pf_pull_hdr(m, ipoff2, &h2, sizeof(h2),			    NULL, NULL, pd2.af)) {				DPFPRINTF(PF_DEBUG_MISC,				    ("pf: ICMP error message too short "				    "(ip)\n"));				return (PF_DROP);			}			/*			 * ICMP error messages don't refer to non-first			 * fragments			 */			if (h2.ip_off & htons(IP_OFFMASK))				return (PF_DROP);			/* offset of protocol header that follows h2 */			off2 = ipoff2 + (h2.ip_hl << 2);			pd2.proto = h2.ip_p;			pd2.src = (struct pf_addr *)&h2.ip_src;			pd2.dst = (struct pf_addr *)&h2.ip_dst;			pd2.ip_sum = &h2.ip_sum;			break;#endif /* INET */#ifdef INET6		case AF_INET6:			ipoff2 = off + sizeof(struct icmp6_hdr);			if (!pf_pull_hdr(m, ipoff2, &h2_6, sizeof(h2_6),			    NULL, NULL, pd2.af)) {				DPFPRINTF(PF_DEBUG_MISC,				    ("pf: ICMP error message too short "				    "(ip6)\n"));				return (PF_DROP);			}			pd2.proto = h2_6.ip6_nxt;			pd2.src = (struct pf_addr *)&h2_6.ip6_src;			pd2.dst = (struct pf_addr *)&h2_6.ip6_dst;			pd2.ip_sum = NULL;			off2 = ipoff2 + sizeof(h2_6);			do {				switch (pd2.proto) {				case IPPROTO_FRAGMENT:					/*					 * ICMPv6 error messages for					 * non-first fragments					 */					return (PF_DROP);				case IPPROTO_AH:				case IPPROTO_HOPOPTS:				case IPPROTO_ROUTING:				case IPPROTO_DSTOPTS: {					/* get next header and header length */					struct ip6_ext opt6;					if (!pf_pull_hdr(m, off2, &opt6,					    sizeof(opt6), NULL, NULL, pd2.af)) {						DPFPRINTF(PF_DEBUG_MISC,						    ("pf: ICMPv6 short opt\n"));						return (PF_DROP);					}					if (pd2.proto == IPPROTO_AH)						off2 += (opt6.ip6e_len + 2) * 4;					else						off2 += (opt6.ip6e_len + 1) * 8;					pd2.proto = opt6.ip6e_nxt;					/* goto the next header */					break;				}				default:					terminal++;					break;				}			} while (!terminal);			break;#endif /* INET6 */		}		switch (pd2.proto) {		case IPPROTO_TCP: {			struct tcphdr		 th;			u_int32_t		 seq;			struct pf_state		 key;			struct pf_state_peer	*src, *dst;			u_int8_t		 dws;			/*			 * Only the first 8 bytes of the TCP header can be			 * expected. Don't access any TCP header fields after			 * th_seq, an ackskew test is not possible.			 */			if (!pf_pull_hdr(m, off2, &th, 8, NULL, NULL, pd2.af)) {				DPFPRINTF(PF_DEBUG_MISC,				    ("pf: ICMP error message too short "				    "(tcp)\n"));				return (PF_DROP);			}			key.af = pd2.af;			key.proto = IPPROTO_TCP;			if (direction == PF_IN)	{				PF_ACPY(&key.ext.addr, pd2.dst, key.af);				PF_ACPY(&key.gwy.addr, pd2.src, key.af);				key.ext.port = th.th_dport;				key.gwy.port = th.th_sport;			} else {				PF_ACPY(&key.lan.addr, pd2.dst, key.af);				PF_ACPY(&key.ext.addr, pd2.src, key.af);				key.lan.port = th.th_dport;				key.ext.port = th.th_sport;			}			STATE_LOOKUP();			if (direction == (*state)->direction) {				src = &(*state)->dst;				dst = &(*state)->src;			} else {				src = &(*state)->src;				dst = &(*state)->dst;			}			if (src->wscale && dst->wscale &&			    !(th.th_flags & TH_SYN))				dws = dst->wscale & PF_WSCALE_MASK;			else				dws = 0;			/* Demodulate sequence number */			seq = ntohl(th.th_seq) - src->seqdiff;			if (src->seqdiff)				pf_change_a(&th.th_seq, &th.th_sum,				    htonl(seq), 0);			if (!SEQ_GEQ(src->seqhi, seq) ||			    !SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws))) {				if (pf_status.debug >= PF_DEBUG_MISC) {					printf("pf: BAD ICMP %d:%d ",					    icmptype, pd->hdr.icmp->icmp_code);					pf_print_host(pd->src, 0, pd->af);					printf(" -> ");					pf_print_host(pd->dst, 0, pd->af);					printf(" state: ");					pf_print_state(*state);					printf(" seq=%u\n", seq);				}				return (PF_DROP);			}			if (STATE_TRANSLATE(*state)) {				if (direction == PF_IN) {					pf_change_icmp(pd2.src, &th.th_sport,					    saddr, &(*state)->lan.addr,					    (*state)->lan.port, NULL,					    pd2.ip_sum, icmpsum,					    pd->ip_sum, 0, pd2.af);				} else {					pf_change_icmp(pd2.dst, &th.th_dport,					    saddr, &(*state)->gwy.addr,					    (*state)->gwy.port, NULL,					    pd2.ip_sum, icmpsum,					    pd->ip_sum, 0, pd2.af);				}				switch (pd2.af) {#ifdef INET				case AF_INET:					m_copyback(m, off, ICMP_MINLEN,					    pd->hdr.icmp);					m_copyback(m, ipoff2, sizeof(h2),					    &h2);					break;#endif /* INET */#ifdef INET6				case AF_INET6:					m_copyback(m, off,					    sizeof(struct icmp6_hdr),					    pd->hdr.icmp6);					m_copyback(m, ipoff2, sizeof(h2_6),					    &h2_6);					break;#endif /* INET6 */				}				m_copyback(m, off2, 8, &th);			} else if (src->seqdiff) {				m_copyback(m, off2, 8, &th);			}			return (PF_PASS);			break;		}		case IPPROTO_UDP: {			struct udphdr		uh;			struct pf_state		key;			if (!pf_pull_hdr(m, off2, &uh, sizeof(uh),			    NULL, NULL, pd2.af)) {				DPFPRINTF(PF_DEBUG_MISC,				    ("pf: ICMP error message too short "				    "(udp)\n"));				return (PF_DROP);			}			key.af = pd2.af;			key.proto = IPPROTO_UDP;			if (direction == PF_IN)	{				PF_ACPY(&key.ext.addr, pd2.dst, key.af);				PF_ACPY(&key.gwy.addr, pd2.src, key.af);				key.ext.port = uh.uh_dport;				key.gwy.port = uh.uh_sport;			} else {				PF_ACPY(&key.lan.addr, pd2.dst, key.af);				PF_ACPY(&key.ext.addr, pd2.src, key.af);				key.lan.port = uh.uh_dport;				key.ext.port = uh.uh_sport;			}			STATE_LOOKUP();			if (STATE_TRANSLATE(*state)) {				if (direction == PF_IN) {					pf_change_icmp(pd2.src, &uh.uh_sport,					    daddr, &(*state)->lan.addr,					    (*state)->lan.port, &uh.uh_sum,					    pd2.ip_sum, icmpsum,					    pd->ip_sum, 1, pd2.af);				} else {					pf_change_icmp(pd2.dst, &uh.uh_dport,					    saddr, &(*state)->gwy.addr,					    (*state)->gwy.port, &uh.uh_sum,					    pd2.ip_sum, icmpsum,					    pd->ip_sum, 1, pd2.af);				}				switch (pd2.af) {#ifdef INET				case AF_INET:					m_copyback(m, off, ICMP_MINLEN,					    pd->hdr.icmp);					m_copyback(m, ipoff2, sizeof(h2), &h2);					break;#endif /* INET */#ifdef INET6				case AF_INET6:					m_copyback(m, off,					    sizeof(struct icmp6_hdr),					    pd->hdr.icmp6);					m_copyback(m, ipoff2, sizeof(h2_6),					    &h2_6);					break;#endif /* INET6 */				}				m_copyback(m, off2, sizeof(uh), &uh);			}			return (PF_PASS);			break;		}#ifdef INET		case IPPROTO_ICMP: {			struct icmp		iih;			struct pf_state		key;			if (!pf_pull_hdr(m, off2, &iih, ICMP_MINLEN,			    NULL, NULL, pd2.af)) {				DPFPRINTF(PF_DEBUG_MISC,				    ("pf: ICMP error message too short i"				    "(icmp)\n"));				return (PF_DROP);			}			key.af = pd2.af;			key.proto = IPPROTO_ICMP;			if (direction == PF_IN)	{				PF_ACPY(&key.ext.addr, pd2.dst, key.af);				PF_ACPY(&key.gwy.addr, pd2.src, key.af);				key.ext.port = iih.icmp_id;				key.gwy.port = iih.icmp_id;			} else {				PF_ACPY(&key.lan.addr, pd2.dst, key.af);				PF_ACPY(&key.ext.addr, pd2.src, key.af);				key.lan.port = iih.icmp_id;				key.ext.port = iih.icmp_id;			}			STATE_LOOKUP();			if (STATE_TRANSLATE(*state)) {				if (direction == PF_IN) {					pf_change_icmp(pd2.src, &iih.icmp_id,					    daddr, &(*state)->lan.addr,					    (*state)->lan.port, NULL,					    pd2.ip_sum, icmpsum,					    pd->ip_sum, 0, AF_INET);				} else {					pf_change_icmp(pd2.dst, &iih.icmp_id,					    saddr, &(*state)->gwy.addr,					    (*state)->gwy.port, NULL,					    pd2.ip_sum, icmpsum,					    pd->ip_sum, 0, AF_INET);				}				m_copyback(m, off, ICMP_MINLEN, pd->hdr.icmp);				m_copyback(m, ipoff2, sizeof(h2), &h2);				m_copyback(m, off2, ICMP_MINLEN, &iih);			}			return (PF_PASS);			break;		}#endif /* INET */#ifdef INET6		case IPPROTO_ICMPV6: {			struct icmp6_hdr	iih;			struct pf_state		key;			if (!pf_pull_hdr(m, off2, &iih,			    sizeof(struct icmp6_hdr), NULL, NULL, pd2.af)) {				DPFPRINTF(PF_DEBUG_MISC,				    ("pf: ICMP error message too short "				    "(icmp6)\n"));				return (PF_DROP);			}			key.af = pd2.af;			key.proto = IPPROTO_ICMPV6;			if (direction == PF_IN)	{				PF_ACPY(&key.ext.addr, pd2.dst, key.af);				PF_ACPY(&key.gwy.addr, pd2.src, key.af);				key.ext.port = iih.icmp6_id;				key.gwy.port = iih.icmp6_id;			} else {				PF_ACPY(&key.lan.addr, pd2.dst, key.af);				PF_ACPY(&key.ext.addr, pd2.src, key.af);				key.lan.port = iih.icmp6_id;				key.ext.port = iih.icmp6_id;			}			STATE_LOOKUP();			if (STATE_TRANSLATE(*state)) {				if (direction == PF_IN) {					pf_change_icmp(pd2.src, &iih.icmp6_id,					    daddr, &(*state)->lan.addr,					    (*state)->lan.port, NULL,					    pd2.ip_sum, icmpsum,					    pd->ip_sum, 0, AF_INET6);				} else {					pf_change_icmp(pd2.dst, &iih.icmp6_id,					    saddr, &(*state)->gwy.addr,					    (*state)->gwy.port, NULL,					    pd2.ip_sum, icmpsum,					    pd->ip_sum, 0, AF_INET6);				}				m_copyback(m, off, sizeof(struct icmp6_hdr),				    pd->hdr.icmp6);				m_copyback(m, ipoff2, sizeof(h2_6), &h2_6);				m_copyback(m, off2, sizeof(struct icmp6_hdr),				    &iih);			}			return (PF_PASS);			break;		}#endif /* INET6 */		default: {			struct pf_state		key;			key.af = pd2.af;			key.proto = pd2.proto;			if (direction == PF_IN)	{				PF_ACPY(&key.ext.addr, pd2.dst, key.af);				PF_ACPY(&key.gwy.addr, pd2.src, key.af);				key.ext.port = 0;				key.gwy.port = 0;			} else {				PF_ACPY(&key.lan.addr, pd2.dst, key.af);				PF_ACPY(&key.ext.addr, pd2.src, key.af);				key.lan.port = 0;				key.ext.port = 0;			}			STATE_LOOKUP();			if (STATE_TRANSLATE(*state)) {				if (direction == PF_IN) {					pf_change_icmp(pd2.src, NULL,					    daddr, &(*state)->lan.addr,					    0, NULL,					    pd2.ip_sum, icmpsum,					    pd->ip_sum, 0, pd2.af);				} else {					pf_change_icmp(pd2.dst, NULL,					    saddr, &(*state)->gwy.addr,					    0, NULL,					    pd2.ip_sum, icmpsum,					    pd->ip_sum, 0, pd2.af);				}				switch (pd2.af) {#ifdef INET				case AF_INET:					m_copyback(m, off, ICMP_MINLEN,					    pd->hdr.icmp);					m_copyback(m, ipoff2, sizeof(h2), &h2);					break;#endif /* INET */#ifdef INET6				case AF_INET6:					m_copyback(m, off,					    sizeof(struct icmp6_hdr),					    pd->hdr.icmp6);					m_copyback(m, ipoff2, sizeof(h2_6),					    &h2_6);					break;#endif /* INET6 */				}			}			return (PF_PASS);			break;		}		}	}}