+ if (m->m_len < sizeof(*ip)) {
+ panic("ipsec46_encapsulate: assumption failed (first mbuf length)");
+ return EINVAL;
+ }
+
+ ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+ hlen = _IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+
+ if (m->m_len != hlen) {
+ panic("ipsec46_encapsulate: assumption failed (first mbuf length)");
+ return EINVAL;
+ }
+
+ /* generate header checksum */
+ ip->ip_sum = 0;
+#ifdef _IP_VHL
+ ip->ip_sum = in_cksum(m, hlen);
+#else
+ ip->ip_sum = in_cksum(m, hlen);
+#endif
+
+ plen = m->m_pkthdr.len; // save original IPv4 packet len, this will be ipv6 payload len
+
+ /*
+ * First move the IPv4 header to the second mbuf in the chain
+ */
+ if (M_LEADINGSPACE(m->m_next) < hlen) {
+ struct mbuf *n;
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (!n) {
+ m_freem(m);
+ return ENOBUFS;
+ }
+ n->m_len = hlen;
+ n->m_next = m->m_next;
+ m->m_next = n;
+ m->m_pkthdr.len += sizeof(struct ip6_hdr);
+ oip = mtod(n, struct ip *);
+ } else {
+ m->m_next->m_len += hlen;
+ m->m_next->m_data -= hlen;
+ m->m_pkthdr.len += sizeof(struct ip6_hdr);
+ oip = mtod(m->m_next, struct ip *);
+ }
+ ip = mtod(m, struct ip *);
+ ovbcopy((caddr_t)ip, (caddr_t)oip, hlen);
+
+ /*
+ * Grow the first mbuf to accomodate the new IPv6 header.
+ */
+ if (M_LEADINGSPACE(m) < sizeof(struct ip6_hdr) - hlen) {
+ struct mbuf *n;
+ MGETHDR(n, M_DONTWAIT, MT_HEADER);
+ if (!n) {
+ m_freem(m);
+ return ENOBUFS;
+ }
+ M_COPY_PKTHDR(n, m);
+ MH_ALIGN(n, sizeof(struct ip6_hdr));
+ n->m_len = sizeof(struct ip6_hdr);
+ n->m_next = m->m_next;
+ m->m_next = NULL;
+ m_freem(m);
+ state->m = n;
+ m = state->m;
+ } else {
+ m->m_len += (sizeof(struct ip6_hdr) - hlen);
+ m->m_data -= (sizeof(struct ip6_hdr) - hlen);
+ }
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6->ip6_vfc |= IPV6_VERSION;
+
+ /* construct new IPv6 header. see RFC 2401 5.1.2.2 */
+ /* ECN consideration. */
+ ip46_ecn_ingress(ip6_ipsec_ecn, &ip6->ip6_flow, &ip->ip_tos);
+ if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr))
+ ip6->ip6_plen = htons(plen);
+ else {
+ /* ip6->ip6_plen will be updated in ip6_output() */
+ }
+
+ ip6->ip6_nxt = IPPROTO_IPV4;
+ ip6->ip6_hlim = IPV6_DEFHLIM;
+
+ bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.src)->sin6_addr,
+ &ip6->ip6_src, sizeof(ip6->ip6_src));
+ bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.dst)->sin6_addr,
+ &ip6->ip6_dst, sizeof(ip6->ip6_dst));
+
+ return 0;
+}
+
+#endif /*INET6*/
+
+/*
+ * Check the variable replay window.
+ * ipsec_chkreplay() performs replay check before ICV verification.
+ * ipsec_updatereplay() updates replay bitmap. This must be called after
+ * ICV verification (it also performs replay check, which is usually done
+ * beforehand).
+ * 0 (zero) is returned if packet disallowed, 1 if packet permitted.
+ *
+ * based on RFC 2401.
+ */
+int
+ipsec_chkreplay(u_int32_t seq, struct secasvar *sav)
+{
+ const struct secreplay *replay;
+ u_int32_t diff;
+ int fr;
+ u_int32_t wsizeb; /* constant: bits of window size */
+ int frlast; /* constant: last frame */
+
+
+ /* sanity check */
+ if (sav == NULL)
+ panic("ipsec_chkreplay: NULL pointer was passed.\n");
+
+ lck_mtx_lock(sadb_mutex);
+ replay = sav->replay;
+
+ if (replay->wsize == 0) {
+ lck_mtx_unlock(sadb_mutex);
+ return 1; /* no need to check replay. */
+ }
+
+ /* constant */
+ frlast = replay->wsize - 1;
+ wsizeb = replay->wsize << 3;
+
+ /* sequence number of 0 is invalid */
+ if (seq == 0) {
+ lck_mtx_unlock(sadb_mutex);
+ return 0;
+ }
+
+ /* first time is always okay */
+ if (replay->count == 0) {
+ lck_mtx_unlock(sadb_mutex);
+ return 1;
+ }
+
+ if (seq > replay->lastseq) {
+ /* larger sequences are okay */
+ lck_mtx_unlock(sadb_mutex);
+ return 1;
+ } else {
+ /* seq is equal or less than lastseq. */
+ diff = replay->lastseq - seq;
+
+ /* over range to check, i.e. too old or wrapped */
+ if (diff >= wsizeb) {
+ lck_mtx_unlock(sadb_mutex);
+ return 0;
+ }
+
+ fr = frlast - diff / 8;
+
+ /* this packet already seen ? */
+ if ((replay->bitmap)[fr] & (1 << (diff % 8))) {
+ lck_mtx_unlock(sadb_mutex);
+ return 0;
+ }
+
+ /* out of order but good */
+ lck_mtx_unlock(sadb_mutex);
+ return 1;
+ }
+}
+
+/*
+ * check replay counter whether to update or not.
+ * OUT: 0: OK
+ * 1: NG
+ */
+int
+ipsec_updatereplay(u_int32_t seq, struct secasvar *sav)
+{
+ struct secreplay *replay;
+ u_int32_t diff;
+ int fr;
+ u_int32_t wsizeb; /* constant: bits of window size */
+ int frlast; /* constant: last frame */
+
+ /* sanity check */
+ if (sav == NULL)
+ panic("ipsec_chkreplay: NULL pointer was passed.\n");
+
+ lck_mtx_lock(sadb_mutex);
+ replay = sav->replay;
+
+ if (replay->wsize == 0)
+ goto ok; /* no need to check replay. */
+
+ /* constant */
+ frlast = replay->wsize - 1;
+ wsizeb = replay->wsize << 3;
+
+ /* sequence number of 0 is invalid */
+ if (seq == 0)
+ return 1;