Print this page
Current snapshot of OpenSolaris port.
Checkpoint
Checkpoint
Merge from parent.
Merge with WIDE update.
Pull from WIDE.
Pull from WIDE.
Checkpoint
Re-update.
blah
WIDE update
Update from WIDE.

@@ -61,14 +61,20 @@
 # include <netinet6/ipsec.h>
 #else
 # ifdef HAVE_NETIPSEC_IPSEC_H
 #  include <netipsec/ipsec.h>
 # else
+#  ifndef sun   /* XXX KEBE SAYS OpenSolaris */
 #  include <linux/ipsec.h>
 # endif
+# endif
 #endif
 
+#ifdef sun      /* XXX KEBE SAYS OpenSolaris */
+#define IPSEC_ULPROTO_ANY 0
+#endif
+
 #include "racoon.h"
 
 #include "var.h"
 /* #include "vmbuf.h" */
 /* #include "schedule.h" */

@@ -95,10 +101,15 @@
 #include "proposal.h"
 /* #include "sainfo.h" */
 /* #include "admin.h" */
 #include "strnames.h"
 
+#ifdef sun
+#include "ikev1_natt.h"
+#define IPSECDOI_PREFIX_HOST 32 /* Hack for port of NAT-OA from ipsec-tools. */
+#endif
+
 #include "ike_conf.h"
 
 /* quick mode */
 static rc_vchar_t *quick_ir1mx (struct ph2handle *, rc_vchar_t *, rc_vchar_t *);
 static int get_sainfo_r (struct ph2handle *);

@@ -185,11 +196,11 @@
         return error;
 }
 
 /*
  * send to responder
- *      HDR*, HASH(1), SA, Ni [, KE ] [, IDi2, IDr2 ]
+ *      HDR*, HASH(1), SA, Ni [, KE ] [, IDi2, IDr2 ] [, NAT-OAi, NAT-OAr ]
  */
 int
 quick_i1send(struct ph2handle *iph2, rc_vchar_t *msg /* must be null pointer */)
 {
         rc_vchar_t *body = NULL;

@@ -199,10 +210,15 @@
         int tlen;
         int error = ISAKMP_INTERNAL_ERROR;
         int pfsgroup, idci, idcr;
         int np;
         struct ipsecdoi_id_b *id, *id_p;
+#ifdef sun
+        int natoa = ISAKMP_NPTYPE_NONE;
+        rc_vchar_t *nat_oai = NULL;
+        rc_vchar_t *nat_oar = NULL;
+#endif
 
         /* validity check */
         if (msg != NULL) {
                 plog(PLOG_INTERR, PLOGLOC, NULL,
                         "msg has to be NULL in this function.\n");

@@ -273,19 +289,61 @@
          && ipsecdoi_transportmode(iph2->proposal)) {
                 idci = idcr = 0;
         } else
                 idci = idcr = 1;
 
+#ifdef sun
+        /*
+         * RFC3947 5.2. if we propose UDP-Encapsulated-Transport
+         * we should send NAT-OA
+         *
+         * XXX KEBE ASKS if we should send it for tunnel mode anyway, like
+         * we do with in.iked?
+         */
+        if (ipsecdoi_transportmode(iph2->proposal)
+            && (iph2->ph1->natt_flags & NAT_DETECTED)) {
+                natoa = iph2->ph1->natt_options->payload_nat_oa;
+
+                nat_oai = ipsecdoi_sockaddr2id(iph2->src,
+                    IPSECDOI_PREFIX_HOST, IPSEC_ULPROTO_ANY);
+                nat_oar = ipsecdoi_sockaddr2id(iph2->dst,
+                    IPSECDOI_PREFIX_HOST, IPSEC_ULPROTO_ANY);
+
+                if (nat_oai == NULL || nat_oar == NULL) {
+                        plog(PLOG_INTERR, PLOGLOC, NULL,
+                            "failed to generate NAT-OA payload.\n");
+                        goto end;
+                }
+
+                plog(PLOG_INFO, PLOGLOC, NULL, "Using NAT-OA.\n");
+                plog(PLOG_DEBUG, PLOGLOC, NULL, "NAT-OAi:\n");
+                plogdump(PLOG_DEBUG, PLOGLOC, 0, nat_oai->v, nat_oai->l);
+                plog(PLOG_DEBUG, PLOGLOC, NULL, "NAT-OAr:\n");
+                plogdump(PLOG_DEBUG, PLOGLOC, 0, nat_oar->v, nat_oar->l);
+        } else {
+                plog(PLOG_INFO, PLOGLOC, NULL, "Not using NAT-OA.\n");
+                plog(PLOG_INFO, PLOGLOC, NULL, "transportmode == %d, "
+                    "natt_flags == 0x%x\n",
+                    ipsecdoi_transportmode(iph2->proposal),
+                    iph2->ph1->natt_flags & NAT_DETECTED);
+                natoa = ISAKMP_NPTYPE_NONE;
+        }
+#endif
+
         /* create SA;NONCE payload, and KE if need, and IDii, IDir. */
         tlen = + sizeof(*gen) + iph2->sa->l
                 + sizeof(*gen) + iph2->nonce->l;
         if (pfsgroup)
                 tlen += (sizeof(*gen) + iph2->dhpub->l);
         if (idci)
                 tlen += sizeof(*gen) + iph2->id->l;
         if (idcr)
                 tlen += sizeof(*gen) + iph2->id_p->l;
+#ifdef sun
+        if (natoa != ISAKMP_NPTYPE_NONE)
+                tlen += 2 * sizeof(*gen) + nat_oai->l + nat_oar->l;
+#endif
 
         body = rc_vmalloc(tlen);
         if (body == NULL) {
                 plog(PLOG_INTERR, PLOGLOC, NULL,
                         "failed to get buffer to send.\n");

@@ -301,27 +359,35 @@
         if (pfsgroup)
                 np = ISAKMP_NPTYPE_KE;
         else if (idci || idcr)
                 np = ISAKMP_NPTYPE_ID;
         else
-                np = ISAKMP_NPTYPE_NONE;
+                np = natoa;
         p = set_isakmp_payload(p, iph2->nonce, np);
 
         /* add KE payload if need. */
-        np = (idci || idcr) ? ISAKMP_NPTYPE_ID : ISAKMP_NPTYPE_NONE;
+        np = (idci || idcr) ? ISAKMP_NPTYPE_ID : natoa;
         if (pfsgroup)
                 p = set_isakmp_payload(p, iph2->dhpub, np);
 
         /* IDci */
-        np = (idcr) ? ISAKMP_NPTYPE_ID : ISAKMP_NPTYPE_NONE;
+        np = (idcr) ? ISAKMP_NPTYPE_ID : natoa;
         if (idci)
                 p = set_isakmp_payload(p, iph2->id, np);
 
         /* IDcr */
         if (idcr)
-                p = set_isakmp_payload(p, iph2->id_p, ISAKMP_NPTYPE_NONE);
+                p = set_isakmp_payload(p, iph2->id_p, natoa);
 
+#ifdef sun
+        /* NAT-OA */
+        if (natoa != ISAKMP_NPTYPE_NONE) {
+                p = set_isakmp_payload(p, nat_oai, natoa);
+                p = set_isakmp_payload(p, nat_oar, ISAKMP_NPTYPE_NONE);
+        }
+#endif
+
         /* generate HASH(1) */
         hash = oakley_compute_hash1(iph2->ph1, iph2->msgid, body);
         if (hash == NULL)
                 goto end;
 

@@ -343,28 +409,33 @@
 end:
         if (body != NULL)
                 rc_vfree(body);
         if (hash != NULL)
                 rc_vfree(hash);
+        if (nat_oai != NULL)
+                rc_vfree(nat_oai);
+        if (nat_oar != NULL)
+                rc_vfree(nat_oar);
 
         return error;
 }
 
 /*
  * receive from responder
- *      HDR*, HASH(2), SA, Nr [, KE ] [, IDi2, IDr2 ]
+ *      HDR*, HASH(2), SA, Nr [, KE ] [, IDi2, IDr2 ] [, NAT-OAi, NAT-OAr ]
  */
 int
 quick_i2recv(struct ph2handle *iph2, rc_vchar_t *msg0)
 {
         rc_vchar_t *msg = NULL;
         rc_vchar_t *hbuf = NULL;        /* for hash computing. */
         rc_vchar_t *pbuf = NULL;        /* for payload parsing */
+        rc_vchar_t *idci = NULL;
+        rc_vchar_t *idcr = NULL;
         struct isakmp_parse_t *pa;
         struct isakmp *isakmp = (struct isakmp *)msg0->v;
         struct isakmp_pl_hash *hash = NULL;
-        int f_id;
         char *p;
         int tlen;
         int error = ISAKMP_INTERNAL_ERROR;
 
         /* validity check */

@@ -437,11 +508,10 @@
         /*
          * parse the payloads.
          * copy non-HASH payloads into hbuf, so that we can validate HASH.
          */
         iph2->sa_ret = NULL;
-        f_id = 0;       /* flag to use checking ID */
         tlen = 0;       /* count payload length except of HASH payload. */
         for (; pa->type; pa++) {
 
                 /* copy to buffer for HASH */
                 /* Don't modify the payload */

@@ -468,41 +538,64 @@
                         if (isakmp_p2ph(&iph2->dhpub_p, pa->ptr) < 0)
                                 goto end;
                         break;
 
                 case ISAKMP_NPTYPE_ID:
-                    {
-                        rc_vchar_t *vp;
-
-                        /* check ID value */
-                        if (f_id == 0) {
-                                /* for IDci */
-                                f_id = 1;
-                                vp = iph2->id;
+                        if (idci == NULL) {
+                                if (isakmp_p2ph(&idci, pa->ptr) < 0)
+                                        goto end;
+                        } else if (idcr == NULL) {
+                                if (isakmp_p2ph(&idcr, pa->ptr) < 0)
+                                        goto end;
                         } else {
-                                /* for IDcr */
-                                vp = iph2->id_p;
                         }
-
-                        if (memcmp(vp->v, (caddr_t)pa->ptr + sizeof(struct isakmp_gen), vp->l)) {
-
-                                plog(PLOG_PROTOERR, PLOGLOC, NULL,
-                                        "mismatched ID was returned.\n");
-                                error = ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED;
-                                goto end;
-                        }
-                    }
                         break;
 
                 case ISAKMP_NPTYPE_N:
                         isakmp_check_notify(pa->ptr, iph2->ph1);
                         break;
 
 #ifdef ENABLE_NATT
                 case ISAKMP_NPTYPE_NATOA_DRAFT:
                 case ISAKMP_NPTYPE_NATOA_RFC:
+#ifdef sun
+                        /* DON'T ignore original source/destination. */
+                {
+                        struct sockaddr_storage addr;
+                        struct sockaddr *daddr;
+                        uint8_t prefix;
+                        uint16_t ul_proto;
+                        rc_vchar_t *vp = NULL;
+
+                        if (isakmp_p2ph(&vp, pa->ptr) < 0)
+                                goto end;
+
+                        error = ipsecdoi_id2sockaddr(vp,
+                            (struct sockaddr *) &addr,
+                            &prefix, &ul_proto);
+
+                        rc_vfree(vp);
+
+                        if (error)
+                                goto end;
+
+                        daddr = rcs_sadup((struct sockaddr *) &addr);
+                        if (daddr == NULL)
+                                goto end;
+
+                        if (iph2->natoa_src == NULL)
+                                iph2->natoa_src = daddr;
+                        else if (iph2->natoa_dst == NULL)
+                                iph2->natoa_dst = daddr;
+                        else {
+                                racoon_free(daddr);
+                                goto end;
+                        }
+                }
+#else
                         /* Ignore original source/destination messages */
+#endif
                         break;
 #endif
 
                 default:
                         /* don't send information, see ident_r1recv() */

@@ -526,10 +619,102 @@
                            PLOG_PROTOERR, PLOGLOC,
                            "few isakmp message received.\n");
                 goto end;
         }
 
+       /* identity check */
+       if (idci != NULL) {
+               struct sockaddr_storage proposed_addr, got_addr;
+               uint8_t proposed_prefix, got_prefix;
+               uint16_t proposed_ulproto, got_ulproto;
+
+               error = ipsecdoi_id2sockaddr(iph2->id,
+                                       (struct sockaddr *) &proposed_addr,
+                                       &proposed_prefix, &proposed_ulproto);
+               if (error)
+                       goto end;
+               
+               error = ipsecdoi_id2sockaddr(idci,
+                                       (struct sockaddr *) &got_addr,
+                                       &got_prefix, &got_ulproto);
+               if (error)
+                       goto end;
+
+               if (proposed_prefix != got_prefix
+                || proposed_ulproto != got_ulproto) {
+                       plog(PLOG_DEBUG, PLOGLOC, NULL,
+                               "IDci prefix/ulproto does not match proposal.\n");
+                       error = ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED;
+                       goto end;
+               }
+
+               if (rcs_cmpsa((struct sockaddr *) &proposed_addr,
+                                  (struct sockaddr *) &got_addr) == 0) {
+                       plog(PLOG_DEBUG, PLOGLOC, NULL,
+                               "IDci matches proposal.\n");
+#ifdef ENABLE_NATT
+               } else if (iph2->natoa_src != NULL
+                       && rcs_cmpsa_wop(iph2->natoa_src,
+                                      (struct sockaddr *) &got_addr) == 0
+                       && extract_port((struct sockaddr *) &proposed_addr) ==
+                          extract_port((struct sockaddr *) &got_addr)) {
+                       plog(PLOG_DEBUG, PLOGLOC, NULL,
+                               "IDci matches NAT-OAi.\n");
+#endif
+               } else {
+                       plog(PLOG_INTERR, PLOGLOC, NULL,
+                               "mismatched IDci was returned.\n");
+                       error = ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED;
+                       goto end;
+               }
+       }
+       if (idcr != NULL) {
+               struct sockaddr_storage proposed_addr, got_addr;
+               uint8_t proposed_prefix, got_prefix;
+               uint16_t proposed_ulproto, got_ulproto;
+
+               error = ipsecdoi_id2sockaddr(iph2->id_p,
+                                       (struct sockaddr *) &proposed_addr,
+                                       &proposed_prefix, &proposed_ulproto);
+               if (error)
+                       goto end;
+
+               error = ipsecdoi_id2sockaddr(idcr,
+                                       (struct sockaddr *) &got_addr,
+                                       &got_prefix, &got_ulproto);
+               if (error)
+                       goto end;
+
+               if (proposed_prefix != got_prefix
+                || proposed_ulproto != got_ulproto) {
+                       plog(PLOG_DEBUG, PLOGLOC, NULL,
+                               "IDcr prefix/ulproto does not match proposal.\n");
+                       error = ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED;
+                       goto end;
+               }
+
+               if (rcs_cmpsa((struct sockaddr *) &proposed_addr,
+                                  (struct sockaddr *) &got_addr) == 0) {
+                       plog(PLOG_DEBUG, PLOGLOC, NULL,
+                               "IDcr matches proposal.\n");
+#ifdef ENABLE_NATT
+               } else if (iph2->natoa_dst != NULL
+                       && rcs_cmpsa_wop(iph2->natoa_dst,
+                                      (struct sockaddr *) &got_addr) == 0
+                       && extract_port((struct sockaddr *) &proposed_addr) ==
+                          extract_port((struct sockaddr *) &got_addr)) {
+                       plog(PLOG_DEBUG, PLOGLOC, NULL,
+                               "IDcr matches NAT-OAr.\n");
+#endif
+               } else {
+                       plog(PLOG_INTERR, PLOGLOC, NULL,
+                               "mismatched IDcr was returned.\n");
+                       error = ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED;
+                       goto end;
+               }
+       }
+
         /* Fixed buffer for calculating HASH */
         memcpy(hbuf->v, iph2->nonce->v, iph2->nonce->l);
         plog(PLOG_DEBUG, PLOGLOC, NULL,
                 "HASH allocated:hbuf->l=%d actual:tlen=%d\n",
                 hbuf->l, tlen + iph2->nonce->l);

@@ -579,22 +764,67 @@
                 rc_vfree(hbuf);
         if (pbuf)
                 rc_vfree(pbuf);
         if (msg)
                 rc_vfree(msg);
+        if (idci)
+                rc_vfree(idci);
+        if (idcr)
+                rc_vfree(idcr);
 
         if (error) {
                 VPTRINIT(iph2->sa_ret);
                 VPTRINIT(iph2->nonce_p);
                 VPTRINIT(iph2->dhpub_p);
                 VPTRINIT(iph2->id);
                 VPTRINIT(iph2->id_p);
+#ifdef ENABLE_NATT
+                if (iph2->natoa_src) {
+                        racoon_free(iph2->natoa_src);
+                        iph2->natoa_src = NULL;
         }
+                if (iph2->natoa_dst) {
+                        racoon_free(iph2->natoa_dst);
+                        iph2->natoa_dst = NULL;
+                }
+#endif
+        }
 
         return error;
 }
 
+static int
+fill_in_ipsec_sas(struct ph2handle *iph2)
+{
+        /*
+         * NOTE:  The OpenSolaris kernel can queue up packets on a larval SA
+         * such that when the SA is filled-in via SADB_UPDATE, these packets
+         * can be immediately processed.  If the reply packet is generated
+         * in-kernel (e.g. ICMP_ECHO processing), an additonal ACQUIRE can
+         * be sent if there is not a corresponding outbound SA waiting in
+         * the wings.  We therefore perform SADB_ADD first so that if an
+         * outbound SA is needed immediately during SADB_UPDATE processing,
+         * it is ready.
+         */
+
+        plog(PLOG_DEBUG, PLOGLOC, NULL, "call pk_sendadd\n");
+        if (pk_sendadd(iph2) < 0) {
+                plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey add failed.\n");
+                return (-1);
+        }
+        plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey add sent.\n");
+
+        plog(PLOG_DEBUG, PLOGLOC, NULL, "call pk_sendupdate\n");
+        if (pk_sendupdate(iph2) < 0) {
+                plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey update failed.\n");
+                return (-1);
+        }
+        plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey update sent.\n");
+
+        return (0);
+}
+
 /*
  * send to responder
  *      HDR*, HASH(3)
  */
 int

@@ -702,27 +932,13 @@
                 iph2->status = PHASE2ST_COMMIT;
                 error = 0;
                 goto end;
         }
 
-        /* Do UPDATE for initiator */
-        plog(PLOG_DEBUG, PLOGLOC, NULL, "call pk_sendupdate\n");
-        if (pk_sendupdate(iph2) < 0) {
-                plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey update failed.\n");
-                goto end;
-        }
-        plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey update sent.\n");
+        plog(PLOG_DEBUG, PLOGLOC, NULL, "call fill_in_ipsec_sas");
+        error = fill_in_ipsec_sas(iph2);
 
-        /* Do ADD for responder */
-        if (pk_sendadd(iph2) < 0) {
-                plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey add failed.\n");
-                goto end;
-        }
-        plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey add sent.\n");
-
-        error = 0;
-
 end:
         if (buf != NULL)
                 rc_vfree(buf);
         if (msg != NULL)
                 rc_vfree(msg);

@@ -849,27 +1065,13 @@
                 error = 0;
                 goto end;
         }
 #endif
 
-        /* Do UPDATE for initiator */
-        plog(PLOG_DEBUG, PLOGLOC, NULL, "call pk_sendupdate\n");
-        if (pk_sendupdate(iph2) < 0) {
-                plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey update failed.\n");
-                goto end;
-        }
-        plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey update sent.\n");
+        plog(PLOG_DEBUG, PLOGLOC, NULL, "call fill_in_ipsec_sas");
+        error = fill_in_ipsec_sas(iph2);
 
-        /* Do ADD for responder */
-        if (pk_sendadd(iph2) < 0) {
-                plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey add failed.\n");
-                goto end;
-        }
-        plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey add sent.\n");
-
-        error = 0;
-
 end:
         if (msg != NULL)
                 rc_vfree(msg);
         if (pbuf != NULL)
                 rc_vfree(pbuf);

@@ -879,11 +1081,11 @@
         return error;
 }
 
 /*
  * receive from initiator
- *      HDR*, HASH(1), SA, Ni [, KE ] [, IDi2, IDr2 ]
+ *      HDR*, HASH(1), SA, Ni [, KE ] [, IDi2, IDr2 ] [, NAT-OAi, NAT-OAr ]
  */
 int
 quick_r1recv(struct ph2handle *iph2, rc_vchar_t *msg0)
 {
         rc_vchar_t *msg = NULL;

@@ -1049,11 +1251,46 @@
                         break;
 
 #ifdef ENABLE_NATT
                 case ISAKMP_NPTYPE_NATOA_DRAFT:
                 case ISAKMP_NPTYPE_NATOA_RFC:
+#ifdef sun
+                {
+                        struct sockaddr_storage addr;
+                        struct sockaddr *daddr;
+                        uint8_t prefix;
+                        uint16_t ul_proto;
+                        rc_vchar_t *vp = NULL;
+
+                        if (isakmp_p2ph(&vp, pa->ptr) < 0)
+                                goto end;
+
+                        error = ipsecdoi_id2sockaddr(vp,
+                            (struct sockaddr *) &addr,
+                            &prefix, &ul_proto);
+
+                        rc_vfree(vp);
+
+                        if (error)
+                                goto end;
+
+                        daddr = rcs_sadup((struct sockaddr *) &addr);
+                        if (daddr == NULL)
+                                goto end;
+
+                        if (iph2->natoa_dst == NULL)
+                                iph2->natoa_dst = daddr;
+                        else if (iph2->natoa_src == NULL)
+                                iph2->natoa_src = daddr;
+                        else {
+                                racoon_free(daddr);
+                                goto end;
+                        }
+                }
+#else
                         /* Ignore original source/destination messages */
+#endif
                         break;
 #endif
 
                 default:
                         isakmp_log(0, 0, iph2->ph1->remote, 0,

@@ -1117,10 +1354,11 @@
                 error = ISAKMP_NTYPE_INVALID_HASH_INFORMATION;
                 goto end;
         }
     }
 
+#ifndef sun
         /* get sainfo */
         error = get_sainfo_r(iph2);
         if (error) {
                 plog(PLOG_PROTOERR, PLOGLOC, NULL,
                         "failed to get sainfo.\n");

@@ -1165,20 +1403,21 @@
                 plog(PLOG_PROTOERR, PLOGLOC, NULL,
                         "PFS is specified, but peer doesn't sends KE.\n");
                 error = ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN;
                 goto end;
         }
+        /* change status of isakmp status entry */
+        iph2->status = PHASE2ST_STATUS2;
 
+#endif /* sun/OpenSolaris */
+
         /*
          * save the packet from the initiator in order to resend the
          * responder's first packet against this packet.
          */
         iph2->msg1 = rc_vdup(msg0);
 
-        /* change status of isakmp status entry */
-        iph2->status = PHASE2ST_STATUS2;
-
         error = 0;
 
 end:
         if (hbuf)
                 rc_vfree(hbuf);

@@ -1191,11 +1430,21 @@
                 VPTRINIT(iph2->sa);
                 VPTRINIT(iph2->nonce_p);
                 VPTRINIT(iph2->dhpub_p);
                 VPTRINIT(iph2->id);
                 VPTRINIT(iph2->id_p);
+#ifdef ENABLE_NATT
+                if (iph2->natoa_src) {
+                        racoon_free(iph2->natoa_src);
+                        iph2->natoa_src = NULL;
         }
+                if (iph2->natoa_dst) {
+                        racoon_free(iph2->natoa_dst);
+                        iph2->natoa_dst = NULL;
+                }
+#endif
+        }
 
         return error;
 }
 
 /*

@@ -1230,11 +1479,11 @@
         return error;
 }
 
 /*
  * send to initiator
- *      HDR*, HASH(2), SA, Nr [, KE ] [, IDi2, IDr2 ]
+ *      HDR*, HASH(2), SA, Nr [, KE ] [, IDi2, IDr2 ] [, NAT-OAi, NAT-OAr ]
  */
 int
 quick_r2send(struct ph2handle *iph2, rc_vchar_t *msg)
 {
         rc_vchar_t *body = NULL;

@@ -1241,12 +1490,17 @@
         rc_vchar_t *hash = NULL;
         struct isakmp_gen *gen;
         char *p;
         int tlen;
         int error = ISAKMP_INTERNAL_ERROR;
+        int natoa = ISAKMP_NPTYPE_NONE;
         int pfsgroup;
         uint8_t *np_p = NULL;
+#ifdef ENABLE_NATT
+        rc_vchar_t *nat_oai = NULL;
+        rc_vchar_t *nat_oar = NULL;
+#endif
 
         /* validity check */
         if (msg != NULL) {
                 plog(PLOG_INTERR, PLOGLOC, NULL,
                         "msg has to be NULL in this function.\n");

@@ -1283,19 +1537,51 @@
                                 &iph2->dhpub, &iph2->dhpriv) < 0) {
                         goto end;
                 }
         }
 
+#ifdef ENABLE_NATT
+        /*
+         * RFC3947 5.2. if we chose UDP-Encapsulated-Transport
+         * we should send NAT-OA
+         */
+        if (ipsecdoi_transportmode(iph2->proposal)
+            && (iph2->ph1->natt_flags & NAT_DETECTED)) {
+                natoa = iph2->ph1->natt_options->payload_nat_oa;
+                
+                nat_oai = ipsecdoi_sockaddr2id(iph2->dst,
+                    IPSECDOI_PREFIX_HOST, IPSEC_ULPROTO_ANY);
+                nat_oar = ipsecdoi_sockaddr2id(iph2->src,
+                    IPSECDOI_PREFIX_HOST, IPSEC_ULPROTO_ANY);
+                
+                if (nat_oai == NULL || nat_oar == NULL) {
+                        plog(PLOG_INTERR, PLOGLOC, NULL,
+                            "failed to generate NAT-OA payload.\n");
+                        goto end;
+                }
+
+                plog(PLOG_DEBUG, PLOGLOC, NULL, "NAT-OAi:\n");
+                plogdump(PLOG_DEBUG, PLOGLOC, 0, nat_oai->v, nat_oai->l);
+                plog(PLOG_DEBUG, PLOGLOC, 0, NULL, "NAT-OAr:\n");
+                plogdump(PLOG_DEBUG, PLOGLOC, 0, nat_oar->v, nat_oar->l);
+        }
+#endif
+
         /* create SA;NONCE payload, and KE and ID if need */
         tlen = sizeof(*gen) + iph2->sa_ret->l
                 + sizeof(*gen) + iph2->nonce->l;
         if (iph2->dhpub_p != NULL && pfsgroup != 0)
                 tlen += (sizeof(*gen) + iph2->dhpub->l);
         if (iph2->id_p != NULL)
                 tlen += (sizeof(*gen) + iph2->id_p->l
                         + sizeof(*gen) + iph2->id->l);
 
+#ifdef ENABLE_NATT
+       if (natoa != ISAKMP_NPTYPE_NONE)
+               tlen += 2 * sizeof(*gen) + nat_oai->l + nat_oar->l;
+#endif
+
         body = rc_vmalloc(tlen);
         if (body == NULL) { 
                 plog(PLOG_INTERR, PLOGLOC, NULL,
                         "failed to get buffer to send.\n");
                 goto end;

@@ -1310,29 +1596,36 @@
         p = set_isakmp_payload(p, iph2->nonce,
                 (iph2->dhpub_p != NULL && pfsgroup != 0)
                                 ? ISAKMP_NPTYPE_KE
                                 : (iph2->id_p != NULL
                                         ? ISAKMP_NPTYPE_ID
-                                        : ISAKMP_NPTYPE_NONE));
+                                        : natoa));
 
         /* add KE payload if need. */
         if (iph2->dhpub_p != NULL && pfsgroup != 0) {
                 np_p = &((struct isakmp_gen *)p)->np;   /* XXX */
                 p = set_isakmp_payload(p, iph2->dhpub,
                         (iph2->id_p == NULL)
-                                ? ISAKMP_NPTYPE_NONE
+                                ? natoa
                                 : ISAKMP_NPTYPE_ID);
         }
 
         /* add ID payloads received. */
         if (iph2->id_p != NULL) {
                 /* IDci */
                 p = set_isakmp_payload(p, iph2->id_p, ISAKMP_NPTYPE_ID);
                 /* IDcr */
                 np_p = &((struct isakmp_gen *)p)->np;   /* XXX */
-                p = set_isakmp_payload(p, iph2->id, ISAKMP_NPTYPE_NONE);
+                p = set_isakmp_payload(p, iph2->id, natoa);
         }
+#ifdef ENABLE_NATT
+        /* NAT-OA */
+        if (natoa != ISAKMP_NPTYPE_NONE) {
+                p = set_isakmp_payload(p, nat_oai, natoa);
+                p = set_isakmp_payload(p, nat_oar, ISAKMP_NPTYPE_NONE);
+        }
+#endif
 
         /* add a RESPONDER-LIFETIME notify payload if needed */
     {
         rc_vchar_t *data = NULL;
         struct saprop *pp = iph2->approval;

@@ -1423,10 +1716,16 @@
 end:
         if (body != NULL)
                 rc_vfree(body);
         if (hash != NULL)
                 rc_vfree(hash);
+#ifdef ENABLE_NATT
+        if (nat_oai != NULL)
+                rc_vfree(nat_oai);
+        if (nat_oar != NULL)
+                rc_vfree(nat_oar);
+#endif
 
         return error;
 }
 
 /*

@@ -1692,27 +1991,13 @@
                              "generate policy failed.\n");
                         goto end;
                 }
         }
 
-        /* Do UPDATE as responder */
-        plog(PLOG_DEBUG, PLOGLOC, NULL, "call pk_sendupdate\n");
-        if (pk_sendupdate(iph2) < 0) {
-                plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey update failed.\n");
-                goto end;
-        }
-        plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey update sent.\n");
+        plog(PLOG_DEBUG, PLOGLOC, NULL, "call fill_in_ipsec_sas");
+        error = fill_in_ipsec_sas(iph2);
 
-        /* Do ADD for responder */
-        if (pk_sendadd(iph2) < 0) {
-                plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey add failed.\n");
-                goto end;
-        }
-        plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey add sent.\n");
-
-        error = 0;
-
 end:
         if (msg != NULL)
                 rc_vfree(msg);
 
         return error;

@@ -2124,11 +2409,11 @@
                 plog(LLV_INFO, LOCATION, NULL,
                          "Update the generated policy : %s\n",
                          spidx2str(&spidx));
                 iph2->spidx_gen = racoon_malloc(sizeof(spidx));
                 if (!iph2->spidx_gen) {
-                        plog(LLV_ERROR, LOCATION, NULL,
+                        plog(PLOG_INTERR, PLOGLOC, NULL,
                                  "buffer allocation failed.\n");
                         return ISAKMP_INTERNAL_ERROR;
                 }
                 memcpy(iph2->spidx_gen, &spidx, sizeof(spidx));
         }

@@ -2174,11 +2459,11 @@
                         "failed to create saprop.\n");
                 return ISAKMP_INTERNAL_ERROR;
         }
 
         return 0;
-#endif
+#endif /* 0 */
 }
 
 #ifdef INET6
 static uint32_t
 setscopeid(struct sockaddr *sp_addr0, struct sockaddr *sa_addr0)