for (p = LIST_FIRST(&ph2tree); p; p = next) {
next = LIST_NEXT(p, chain);
-
+ if (p->is_dying || p->status == PHASE2ST_EXPIRED) {
+ continue;
+ }
if (p->status == PHASE2ST_ESTABLISHED){
if (ignore_established_handles) {
plog(LLV_DEBUG2, LOCATION, NULL,
for (iph2 = LIST_FIRST(&ph2tree); iph2 != NULL; iph2 = next) {
next = LIST_NEXT(iph2, chain);
+ if (iph2->is_dying || iph2->status == PHASE2ST_EXPIRED) {
+ continue;
+ }
if (iph2->proposal == NULL && iph2->approval == NULL)
continue;
if (cmpsaddrwop(src, iph2->src) != 0 ||
// do ph2's first... we need the ph1s for notifications
LIST_FOREACH(iph2, &ph2tree, chain) {
if (spid == iph2->spid) {
+ if (iph2->is_dying || iph2->status == PHASE2ST_EXPIRED) {
+ continue;
+ }
if (iph2->status == PHASE2ST_ESTABLISHED) {
isakmp_info_send_d2(iph2);
}
- isakmp_ph2expire(iph2); // iph2 will go down 1 second later.
ike_session_stopped_by_controller(iph2->parent_session,
ike_session_stopped_by_flush);
+ isakmp_ph2expire(iph2); // iph2 will go down 1 second later.
}
}
if (spid == iph2->spid) {
if (del_boundph1 && iph2->parent_session) {
for (iph1 = LIST_FIRST(&iph2->parent_session->ikev1_state.ph1tree); iph1; iph1 = LIST_NEXT(iph1, ph1ofsession_chain)) {
+ if (iph1->is_dying || iph1->status == PHASE1ST_EXPIRED) {
+ continue;
+ }
if (iph1->status == PHASE1ST_ESTABLISHED) {
isakmp_info_send_d1(iph1);
}
* TODO: currently, most recently established SA wins. Need to revisit to see if
* alternative selections is better (e.g. largest p->index stays).
*/
- if (p != new_iph1) {
+ if (p != new_iph1 && !p->is_dying) {
SCHED_KILL(p->sce);
SCHED_KILL(p->sce_rekey);
p->is_dying = 1;
SCHED_KILL(iph2->sce);
+ plog(LLV_ERROR, LOCATION, NULL,
+ "about to cleanup ph2: status %d, seq %d dying %d\n",
+ iph2->status, iph2->seq, iph2->is_dying);
+
/* send delete information */
if (iph2->status == PHASE2ST_ESTABLISHED) {
isakmp_info_send_d2(iph2);
* TODO: currently, most recently established SA wins. Need to revisit to see if
* alternative selections is better.
*/
- if (p != new_iph2 && p->spid == new_iph2->spid) {
+ if (p != new_iph2 && p->spid == new_iph2->spid && !p->is_dying) {
SCHED_KILL(p->sce);
p->is_dying = 1;
for (p = LIST_FIRST(&iph1->parent_session->ikev1_state.ph2tree); p; p = next) {
// take next pointer now, since delete change the underlying ph2tree list
next = LIST_NEXT(p, ph2ofsession_chain);
+ if (p->is_dying) {
+ continue;
+ }
SCHED_KILL(p->sce);
p->is_dying = 1;
return 0;
}
+static int
+ike_session_is_id_portany (vchar_t *ext_id)
+{
+ struct id {
+ u_int8_t type; /* ID Type */
+ u_int8_t proto_id; /* Protocol ID */
+ u_int16_t port; /* Port */
+ u_int32_t addr; /* IPv4 address */
+ u_int32_t mask;
+ } *id_ptr;
+
+ /* ignore addr */
+ id_ptr = (struct id *)ext_id->v;
+ if (id_ptr->type == IPSECDOI_ID_IPV4_ADDR &&
+ id_ptr->port == 0) {
+ return 1;
+ }
+ plog(LLV_DEBUG2, LOCATION, NULL, "not portany_ids in %s: type %d, port %x.\n",
+ __FUNCTION__, id_ptr->type, id_ptr->port);
+ return 0;
+}
+
+static void
+ike_session_set_id_portany (vchar_t *ext_id)
+{
+ struct id {
+ u_int8_t type; /* ID Type */
+ u_int8_t proto_id; /* Protocol ID */
+ u_int16_t port; /* Port */
+ u_int32_t addr; /* IPv4 address */
+ u_int32_t mask;
+ } *id_ptr;
+
+ /* ignore addr */
+ id_ptr = (struct id *)ext_id->v;
+ if (id_ptr->type == IPSECDOI_ID_IPV4_ADDR) {
+ id_ptr->port = 0;
+ return;
+ }
+}
+
static int
ike_session_cmp_ph2_ids_ipany (vchar_t *ext_id,
vchar_t *ext_id_p)
return 0;
}
-static int
+/*
+ * ipsec rekeys for l2tp-over-ipsec fail particularly when client is behind nat because the client's configs and policies don't
+ * match the server's view of the client's address and port.
+ * servers behave differently when using this address-port info to generate ids during phase2 rekeys, so try to match the incoming id to
+ * a variety of info saved in the older phase2.
+ */
+int
ike_session_cmp_ph2_ids (struct ph2handle *iph2,
struct ph2handle *older_ph2)
{
+ vchar_t *portany_id = NULL;
+ vchar_t *portany_id_p = NULL;
+
if (iph2->id && older_ph2->id &&
iph2->id->l == older_ph2->id->l &&
memcmp(iph2->id->v, older_ph2->id->v, iph2->id->l) == 0 &&
memcmp(iph2->id_p->v, older_ph2->ext_nat_id_p->v, iph2->id_p->l) == 0) {
return 0;
}
+ if (iph2->id && older_ph2->ext_nat_id &&
+ iph2->id->l == older_ph2->ext_nat_id->l &&
+ memcmp(iph2->id->v, older_ph2->ext_nat_id->v, iph2->id->l) == 0 &&
+ iph2->id_p && older_ph2->id_p &&
+ iph2->id_p->l == older_ph2->id_p->l &&
+ memcmp(iph2->id_p->v, older_ph2->id_p->v, iph2->id_p->l) == 0) {
+ return 0;
+ }
+ if (iph2->id && older_ph2->id &&
+ iph2->id->l == older_ph2->id->l &&
+ memcmp(iph2->id->v, older_ph2->id->v, iph2->id->l) == 0 &&
+ iph2->id_p && older_ph2->ext_nat_id_p &&
+ iph2->id_p->l == older_ph2->ext_nat_id_p->l &&
+ memcmp(iph2->id_p->v, older_ph2->ext_nat_id_p->v, iph2->id_p->l) == 0) {
+ return 0;
+ }
+
+ /* check if the external id has a wildcard port and compare ids accordingly */
+ if ((older_ph2->ext_nat_id && ike_session_is_id_portany(older_ph2->ext_nat_id)) ||
+ (older_ph2->ext_nat_id_p && ike_session_is_id_portany(older_ph2->ext_nat_id_p))) {
+ // try ignoring ports in iph2->id and iph2->id
+ if (iph2->id && (portany_id = vdup(iph2->id))) {
+ ike_session_set_id_portany(portany_id);
+ }
+ if (iph2->id_p && (portany_id_p = vdup(iph2->id_p))) {
+ ike_session_set_id_portany(portany_id_p);
+ }
+ if (portany_id && older_ph2->ext_nat_id &&
+ portany_id->l == older_ph2->ext_nat_id->l &&
+ memcmp(portany_id->v, older_ph2->ext_nat_id->v, portany_id->l) == 0 &&
+ portany_id_p && older_ph2->ext_nat_id_p &&
+ portany_id_p->l == older_ph2->ext_nat_id_p->l &&
+ memcmp(portany_id_p->v, older_ph2->ext_nat_id_p->v, portany_id_p->l) == 0) {
+ if (portany_id) {
+ vfree(portany_id);
+ }
+ if (portany_id_p) {
+ vfree(portany_id_p);
+ }
+ return 0;
+ }
+ if (iph2->id && older_ph2->ext_nat_id &&
+ iph2->id->l == older_ph2->ext_nat_id->l &&
+ memcmp(portany_id->v, older_ph2->ext_nat_id->v, portany_id->l) == 0 &&
+ iph2->id_p && older_ph2->id_p &&
+ iph2->id_p->l == older_ph2->id_p->l &&
+ memcmp(iph2->id_p->v, older_ph2->id_p->v, iph2->id_p->l) == 0) {
+ if (portany_id) {
+ vfree(portany_id);
+ }
+ if (portany_id_p) {
+ vfree(portany_id_p);
+ }
+ return 0;
+ }
+ if (iph2->id && older_ph2->id &&
+ iph2->id->l == older_ph2->id->l &&
+ memcmp(iph2->id->v, older_ph2->id->v, iph2->id->l) == 0 &&
+ iph2->id_p && older_ph2->ext_nat_id_p &&
+ iph2->id_p->l == older_ph2->ext_nat_id_p->l &&
+ memcmp(portany_id_p->v, older_ph2->ext_nat_id_p->v, portany_id_p->l) == 0) {
+ if (portany_id) {
+ vfree(portany_id);
+ }
+ if (portany_id_p) {
+ vfree(portany_id_p);
+ }
+ return 0;
+ }
+ if (portany_id) {
+ vfree(portany_id);
+ }
+ if (portany_id_p) {
+ vfree(portany_id_p);
+ }
+ }
return -1;
}
ike_session_cmp_ph2_ids(iph2, p) == 0) {
plog(LLV_DEBUG2, LOCATION, NULL, "candidate ph2 matched in %s.\n", __FUNCTION__);
iph2->sainfo = p->sainfo;
+ if (p->ext_nat_id) {
+ if (iph2->ext_nat_id) {
+ vfree(iph2->ext_nat_id);
+ }
+ iph2->ext_nat_id = vdup(p->ext_nat_id);
+ }
+ if (p->ext_nat_id_p) {
+ if (iph2->ext_nat_id_p) {
+ vfree(iph2->ext_nat_id_p);
+ }
+ iph2->ext_nat_id_p = vdup(p->ext_nat_id_p);
+ }
return 0;
}
}
return -1;
}
+int
+ike_session_get_proposal_r (struct ph2handle *iph2)
+{
+ if (iph2->parent_session &&
+ iph2->parent_session->is_client &&
+ iph2->id && iph2->id_p) {
+ struct ph2handle *p;
+ int ipany_ids = ike_session_cmp_ph2_ids_ipany(iph2->id, iph2->id_p);
+ plog(LLV_DEBUG2, LOCATION, NULL, "ipany_ids %d in %s.\n", ipany_ids, __FUNCTION__);
+
+ for (p = LIST_FIRST(&iph2->parent_session->ikev1_state.ph2tree); p; p = LIST_NEXT(p, ph2ofsession_chain)) {
+ if (iph2 != p && !p->is_dying && p->status >= PHASE2ST_ESTABLISHED &&
+ p->approval) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "candidate ph2 found in %s.\n", __FUNCTION__);
+ if (ipany_ids ||
+ ike_session_cmp_ph2_ids(iph2, p) == 0) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "candidate ph2 matched in %s.\n", __FUNCTION__);
+ iph2->proposal = dupsaprop(p->approval, 1);
+ return 0;
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+void
+ike_session_update_natt_version (struct ph1handle *iph1)
+{
+ if (iph1->parent_session) {
+ if (iph1->natt_options) {
+ iph1->parent_session->natt_version = iph1->natt_options->version;
+ } else {
+ iph1->parent_session->natt_version = 0;
+ }
+ }
+}
+
+int
+ike_session_get_natt_version (struct ph1handle *iph1)
+{
+ if (iph1->parent_session) {
+ return(iph1->parent_session->natt_version);
+ }
+ return 0;
+}
+
int
ike_session_drop_rekey (ike_session_t *session)
{
int is_client:1;
time_t last_time_data_sc_detected;
u_int32_t natt_flags;
+ u_int32_t natt_version;
char *term_reason;
struct timeval start_timestamp;
extern void ike_session_start_xauth_timer __P((struct ph1handle *));
extern void ike_session_stop_xauth_timer __P((struct ph1handle *));
extern int ike_session_get_sainfo_r __P((struct ph2handle *));
+extern int ike_session_get_proposal_r __P((struct ph2handle *));
+extern void ike_session_update_natt_version __P((struct ph1handle *));
+extern int ike_session_get_natt_version __P((struct ph1handle *));
extern int ike_session_drop_rekey __P((ike_session_t *));
extern void ike_session_ph2_retransmits __P((struct ph2handle *));
{
char *src, *dst;
+ if (iph2->status == PHASE2ST_EXPIRED) {
+ return;
+ }
+
SCHED_KILL(iph2->sce);
src = racoon_strdup(saddrwop2str(iph2->src));
if (iph1->natt_flags & NAT_DETECTED)
natt_float_ports (iph1);
+ ike_session_update_natt_version(iph1);
}
#endif
}
#ifdef ENABLE_NATT
- if (NATT_AVAILABLE(iph1))
+ if (NATT_AVAILABLE(iph1)) {
plog(LLV_INFO, LOCATION, iph1->remote,
"Selected NAT-T version: %s\n",
vid_string_by_id(iph1->natt_options->version));
+ ike_session_update_natt_version(iph1);
+ }
#endif
/* check SA payload and set approval SA for use */
}
#ifdef ENABLE_NATT
- if (NATT_AVAILABLE(iph1))
+ if (NATT_AVAILABLE(iph1)) {
plog(LLV_INFO, LOCATION, iph1->remote,
"Selected NAT-T version: %s\n",
vid_string_by_id(iph1->natt_options->version));
+ ike_session_update_natt_version(iph1);
+ }
#endif
/* check SA payload and set approval SA for use */
}
#ifdef ENABLE_NATT
- if (NATT_AVAILABLE(iph1))
+ if (NATT_AVAILABLE(iph1)) {
plog(LLV_INFO, LOCATION, iph1->remote,
"Selected NAT-T version: %s\n",
vid_string_by_id(iph1->natt_options->version));
+ ike_session_update_natt_version(iph1);
+ }
#endif
/* check SA payload and set approval SA for use */
}
#ifdef ENABLE_NATT
- if (NATT_AVAILABLE(iph1))
+ if (NATT_AVAILABLE(iph1)) {
plog(LLV_INFO, LOCATION, iph1->remote,
"Selected NAT-T version: %s\n",
vid_string_by_id(iph1->natt_options->version));
+ ike_session_update_natt_version(iph1);
+ }
#endif
/* check SA payload and set approval SA for use */
}
#ifdef ENABLE_NATT
- if (NATT_AVAILABLE(iph1))
+ if (NATT_AVAILABLE(iph1)) {
plog(LLV_INFO, LOCATION, iph1->remote,
"Selected NAT-T version: %s\n",
vid_string_by_id(iph1->natt_options->version));
+ ike_session_update_natt_version(iph1);
+ }
#endif
/* check SA payload and set approval SA for use */
for (i = 0; i < n; i++) {
iph1 = getph1byindex(&spi[i]);
- if (!iph1)
+ if (!iph1 || iph1->is_dying || iph1->status == PHASE1ST_EXPIRED)
continue;
plog(LLV_INFO, LOCATION, NULL,
/* quick mode */
static vchar_t *quick_ir1mx __P((struct ph2handle *, vchar_t *, vchar_t *));
static int get_sainfo_r __P((struct ph2handle *));
-static int get_proposal_r __P((struct ph2handle *, int));
+static int get_proposal_r __P((struct ph2handle *));
+static int get_proposal_r_remote __P((struct ph2handle *, int));
/* \f%%%
* Quick Mode
goto end;
}
plog(LLV_DEBUG, LOCATION, NULL, "external nat address saved.\n");
+ plogdump(LLV_DEBUG, iph2->ext_nat_id->v, iph2->ext_nat_id->l);
} else if (f_id && (iph2->ph1->natt_flags & NAT_DETECTED_PEER)) {
if (iph2->ext_nat_id_p)
vfree(iph2->ext_nat_id_p);
}
memcpy(iph2->ext_nat_id_p->v, &(idp_ptr->b), iph2->ext_nat_id_p->l);
plog(LLV_DEBUG, LOCATION, NULL, "peer's external nat address saved.\n");
+ plogdump(LLV_DEBUG, iph2->ext_nat_id_p->v, iph2->ext_nat_id_p->l);
}
} else {
plog(LLV_ERROR, LOCATION, NULL, "mismatched ID was returned.\n");
}
/* check the existence of ID payload and create responder's proposal */
- error = get_proposal_r(iph2, 0);
- if (error != -2 && error != 0 &&
- (((iph2->ph1->natt_flags & NAT_DETECTED_ME) && lcconf->ext_nat_id != NULL) ||
- (iph2->parent_session && iph2->parent_session->is_client)))
- error = get_proposal_r(iph2, 1);
-
+ error = get_proposal_r(iph2);
switch (error) {
case -2:
/* generate a policy template from peer's proposal */
}
#endif
+ plog(LLV_DEBUG, LOCATION, NULL, "Approved SA\n");
+ printsaprop0(LLV_DEBUG, iph2->approval);
body = vmalloc(tlen);
if (body == NULL) {
if (iph2->id_p != NULL) {
/* IDci */
p = set_isakmp_payload(p, iph2->id_p, ISAKMP_NPTYPE_ID);
+ plog(LLV_DEBUG, LOCATION, NULL, "sending IDci2:\n");
+ plogdump(LLV_DEBUG, iph2->id_p->v, iph2->id_p->l);
/* IDcr */
np_p = &((struct isakmp_gen *)p)->np; /* XXX */
p = set_isakmp_payload(p, iph2->id, (natoa_type ? natoa_type : ISAKMP_NPTYPE_NONE));
+ plog(LLV_DEBUG, LOCATION, NULL, "sending IDcr2:\n");
+ plogdump(LLV_DEBUG, iph2->id->v, iph2->id->l);
}
/* add a RESPONDER-LIFETIME notify payload if needed */
return error;
}
+static int
+get_proposal_r(iph2)
+ struct ph2handle *iph2;
+{
+ int error = get_proposal_r_remote(iph2, 0);
+ if (error != -2 && error != 0 &&
+ (((iph2->ph1->natt_flags & NAT_DETECTED_ME) && lcconf->ext_nat_id != NULL) ||
+ (iph2->parent_session && iph2->parent_session->is_client))) {
+ if (iph2->parent_session && iph2->parent_session->is_client)
+ error = ike_session_get_proposal_r(iph2);
+ if (error != -2 && error != 0)
+ error = get_proposal_r_remote(iph2, 1);
+ }
+ return error;
+}
+
/*
* Copy both IP addresses in ID payloads into [src,dst]_id if both ID types
* are IP address and same address family.
* NOTE: This function is only for responder.
*/
static int
-get_proposal_r(iph2, use_remote_addr)
+get_proposal_r_remote(iph2, use_remote_addr)
struct ph2handle *iph2;
int use_remote_addr;
{
void
natt_handle_vendorid (struct ph1handle *iph1, int vid_numeric)
{
+ int version;
+
if (! iph1->natt_options)
iph1->natt_options = racoon_calloc (1, sizeof (*iph1->natt_options));
"Allocating memory for natt_options failed!\n");
return;
}
-
+
+ // stick to the version we already selected on a previous phase1
+ version = ike_session_get_natt_version(iph1);
+ if (version) {
+ vid_numeric = version;
+ }
+
if (iph1->natt_options->version < vid_numeric)
if (natt_fill_options (iph1->natt_options, vid_numeric) == 0)
iph1->natt_flags |= NAT_ANNOUNCED;
return -1;
}
+ if (iph2->is_dying) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "status mismatch phase2 dying (db:%d msg:%d)\n",
+ iph2->status, PHASE2ST_GETSPISENT);
+ return -1;
+ }
+
if (iph2->status != PHASE2ST_GETSPISENT) {
plog(LLV_ERROR, LOCATION, NULL,
"status mismatch (db:%d msg:%d)\n",
return -1;
}
+ if (iph2->is_dying) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "status mismatch phase2 dying (db:%d msg:%d)\n",
+ iph2->status, PHASE2ST_ADDSA);
+ return -1;
+ }
+
if (iph2->status != PHASE2ST_ADDSA) {
plog(LLV_ERROR, LOCATION, NULL,
"status mismatch (db:%d msg:%d)\n",
sa_mode));
return 0;
}
- if (iph2->status != PHASE2ST_ESTABLISHED) {
+ if (iph2->is_dying || iph2->status != PHASE2ST_ESTABLISHED) {
/*
* If the status is not equal to PHASE2ST_ESTABLISHED,
* racoon ignores this expire message. There are two reason.
*/
plog(LLV_WARNING, LOCATION, NULL,
"the expire message is received "
- "but the handler has not been established.\n");
+ "but the handler is dying or has not been established.\n");
return 0;
}
return;
}
+#ifdef ENABLE_NATT
+static void
+saprop_udp_encap (struct saproto *pr)
+{
+ switch (pr->encmode) {
+ case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC:
+ case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT:
+ pr->encmode = IPSECDOI_ATTR_ENC_MODE_TUNNEL;
+ pr->udp_encap = 1;
+ break;
+ case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC:
+ case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT:
+ pr->encmode = IPSECDOI_ATTR_ENC_MODE_TRNS;
+ pr->udp_encap = 1;
+ break;
+ }
+}
+
+static void
+saprop_adjust_encmode (struct saproto *pr2, struct saproto *pr1)
+{
+ int prev;
+
+ if (natt_udp_encap(pr2->encmode)) {
+ prev = pr2->encmode;
+ saprop_udp_encap(pr2);
+ plog(LLV_INFO, LOCATION, NULL, "Adjusting my encmode %s(%d)->%s(%d)\n",
+ s_ipsecdoi_encmode(prev),
+ prev,
+ s_ipsecdoi_encmode(pr2->encmode),
+ pr2->encmode);
+ }
+ if (natt_udp_encap(pr1->encmode)) {
+ prev = pr1->encmode;
+ saprop_udp_encap(pr1);
+ plog(LLV_INFO, LOCATION, NULL, "Adjusting peer's encmode %s(%d)->%s(%d)\n",
+ s_ipsecdoi_encmode(prev),
+ prev,
+ s_ipsecdoi_encmode(pr1->encmode),
+ pr1->encmode);
+ }
+}
+#endif // ENABLE_NATT
+
/*
* take a single match between saprop. allocate a new proposal and return it
* for future use (like picking single proposal from a bundle).
goto err;
}
-#ifdef ENABLE_NATT
- if ((ph1->natt_flags & NAT_DETECTED) &&
- natt_udp_encap (pr2->encmode))
- {
- plog(LLV_INFO, LOCATION, NULL, "Adjusting my encmode %s->%s\n",
- s_ipsecdoi_encmode(pr2->encmode),
- s_ipsecdoi_encmode(pr2->encmode - ph1->natt_options->mode_udp_diff));
- pr2->encmode -= ph1->natt_options->mode_udp_diff;
- pr2->udp_encap = 1;
- }
-
- if ((ph1->natt_flags & NAT_DETECTED) &&
- natt_udp_encap (pr1->encmode))
- {
- plog(LLV_INFO, LOCATION, NULL, "Adjusting peer's encmode %s(%d)->%s(%d)\n",
- s_ipsecdoi_encmode(pr1->encmode),
- pr1->encmode,
- s_ipsecdoi_encmode(pr1->encmode - ph1->natt_options->mode_udp_diff),
- pr1->encmode - ph1->natt_options->mode_udp_diff);
- pr1->encmode -= ph1->natt_options->mode_udp_diff;
- pr1->udp_encap = 1;
+#ifdef ENABLE_NATT
+ if (ph1->natt_flags & NAT_DETECTED) {
+ saprop_adjust_encmode(pr2, pr1);
}
#endif
return 1;
return 0;
}
+
+void
+dupsatrns(newpr, head)
+ struct saproto *newpr;
+ struct satrns *head;
+{
+ struct satrns *p, *newtr;
+
+ for (p = head; p != NULL; p = p->next) {
+ newtr = newsatrns();
+ if (newtr) {
+ newtr->trns_no = p->trns_no;
+ newtr->trns_id = p->trns_id;
+ newtr->encklen = p->encklen;
+ newtr->authtype = p->authtype;
+ inssatrns(newpr, newtr);
+ } else {
+ break;
+ }
+
+ }
+
+ return;
+}
+
+void
+dupsaproto(newpp, head, ignore_spis)
+ struct saprop *newpp;
+ struct saproto *head;
+ int ignore_spis;
+{
+ struct saproto *p, *newpr;
+
+ for (p = head; p != NULL; p = p->next) {
+ newpr = newsaproto();
+ if (newpr) {
+ newpr->proto_id = p->proto_id;
+ newpr->spisize = p->spisize;
+ newpr->encmode = p->encmode;
+ newpr->udp_encap = p->udp_encap;
+ if (!ignore_spis) {
+ newpr->spi = p->spi;
+ newpr->spi_p = p->spi_p;
+ newpr->reqid_in = p->reqid_in;
+ newpr->reqid_out = p->reqid_out;
+ }
+ dupsatrns(newpr, p->head);
+ inssaproto(newpp, newpr);
+ } else {
+ break;
+ }
+
+ }
+
+ return;
+}
+
+struct saprop *
+dupsaprop(head, ignore_spis)
+ struct saprop *head;
+ int ignore_spis;
+{
+ struct saprop *p, *newpp;
+
+ for (p = head, newpp = NULL; p != NULL; p = p->next) {
+ struct saprop *tmp = newsaprop();
+ if (tmp) {
+ tmp->prop_no = p->prop_no;
+ tmp->lifetime = p->lifetime;
+ tmp->lifebyte = p->lifebyte;
+ tmp->pfs_group = p->pfs_group;
+ tmp->claim = p->claim;
+ dupsaproto(tmp, p->head, ignore_spis);
+ inssaprop(&newpp, tmp);
+ } else {
+ break;
+ }
+ }
+
+ return newpp;
+}
struct secpolicy *, struct secpolicy *));
extern int set_proposal_from_proposal __P((struct ph2handle *));
extern int tunnel_mode_prop __P((struct saprop *));
+extern struct saprop *dupsaprop __P((struct saprop *, int));
#endif /* _PROPOSAL_H */