+
+static int
+ipsec64_encapsulate(m, sav)
+ struct mbuf *m;
+ struct secasvar *sav;
+{
+ struct ip6_hdr *ip6, *ip6i;
+ struct ip *ip;
+ size_t plen;
+ u_int8_t hlim;
+
+ /* tunneling over IPv4 */
+ if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
+ != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
+ || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET) {
+ m_freem(m);
+ return EINVAL;
+ }
+#if 0
+ /* XXX if the dst is myself, perform nothing. */
+ if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) {
+ m_freem(m);
+ return EINVAL;
+ }
+#endif
+
+ plen = m->m_pkthdr.len;
+ ip6 = mtod(m, struct ip6_hdr *);
+ hlim = ip6->ip6_hlim;
+ /*
+ * grow the mbuf to accomodate the new IPv4 header.
+ */
+ if (m->m_len != sizeof(struct ip6_hdr))
+ panic("ipsec6_encapsulate: assumption failed (first mbuf length)");
+ if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) {
+ struct mbuf *n;
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (!n) {
+ m_freem(m);
+ return ENOBUFS;
+ }
+ n->m_len = sizeof(struct ip6_hdr);
+ n->m_next = m->m_next;
+ m->m_next = n;
+ m->m_pkthdr.len += sizeof(struct ip);
+ ip6i = mtod(n, struct ip6_hdr *);
+ } else {
+ m->m_next->m_len += sizeof(struct ip6_hdr);
+ m->m_next->m_data -= sizeof(struct ip6_hdr);
+ m->m_pkthdr.len += sizeof(struct ip);
+ ip6i = mtod(m->m_next, struct ip6_hdr *);
+ }
+ /* construct new IPv4 header. see RFC 2401 5.1.2.1 */
+ /* ECN consideration. */
+ /* XXX To be fixed later if needed */
+ // ip_ecn_ingress(ip4_ipsec_ecn, &ip->ip_tos, &oip->ip_tos);
+
+ bcopy(ip6, ip6i, sizeof(struct ip6_hdr));
+ ip = mtod(m, struct ip *);
+ m->m_len = sizeof(struct ip);
+ /*
+ * Fill in some of the IPv4 fields - we don't need all of them
+ * because the rest will be filled in by ip_output
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_id = 0;
+ ip->ip_sum = 0;
+ ip->ip_tos = 0;
+ ip->ip_off = 0;
+ ip->ip_ttl = hlim;
+ ip->ip_p = IPPROTO_IPV6;
+ if (plen + sizeof(struct ip) < IP_MAXPACKET)
+ ip->ip_len = htons(plen + sizeof(struct ip));
+ else {
+ ip->ip_len = htons(plen);
+ ipseclog((LOG_ERR, "IPv4 ipsec: size exceeds limit: "
+ "leave ip_len as is (invalid packet)\n"));
+ }
+ bcopy(&((struct sockaddr_in *)&sav->sah->saidx.src)->sin_addr,
+ &ip->ip_src, sizeof(ip->ip_src));
+ bcopy(&((struct sockaddr_in *)&sav->sah->saidx.dst)->sin_addr,
+ &ip->ip_dst, sizeof(ip->ip_dst));
+
+ return 0;
+}