summaryrefslogtreecommitdiff
path: root/programs/pluto/modecfg.c
diff options
context:
space:
mode:
Diffstat (limited to 'programs/pluto/modecfg.c')
-rw-r--r--programs/pluto/modecfg.c798
1 files changed, 798 insertions, 0 deletions
diff --git a/programs/pluto/modecfg.c b/programs/pluto/modecfg.c
new file mode 100644
index 000000000..1c22845a5
--- /dev/null
+++ b/programs/pluto/modecfg.c
@@ -0,0 +1,798 @@
+/* Mode config related functions
+ * Copyright (C) 2001-2002 Colubris Networks
+ * Copyright (C) 2003 Sean Mathews - Nu Tech Software Solutions, inc.
+ * Copyright (C) 2003-2004 Xelerance Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * RCSID $Id: modecfg.c,v 1.6 2006/04/24 20:44:57 as Exp $
+ *
+ * This code originally written by Colubris Networks, Inc.
+ * Extraction of patch and porting to 1.99 codebases by Xelerance Corporation
+ * Porting to 2.x by Sean Mathews
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeswan.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "state.h"
+#include "demux.h"
+#include "timer.h"
+#include "ipsec_doi.h"
+#include "log.h"
+#include "md5.h"
+#include "sha1.h"
+#include "crypto.h"
+#include "modecfg.h"
+#include "whack.h"
+
+/*
+ * Addresses assigned (usually via MODE_CONFIG) to the Initiator
+ */
+struct internal_addr
+{
+ ip_address ipaddr;
+ ip_address dns[2];
+ ip_address wins[2];
+};
+
+/*
+ * Get inside IP address for a connection
+ */
+static void
+get_internal_addresses(struct connection *c, struct internal_addr *ia)
+{
+ zero(ia);
+
+ if (isanyaddr(&c->spd.that.host_srcip))
+ {
+ /* not defined in connection - fetch it from LDAP */
+ }
+ else
+ {
+ ia->ipaddr = c->spd.that.host_srcip;
+ }
+}
+
+/*
+ * Compute HASH of Mode Config.
+ */
+static size_t
+mode_cfg_hash(u_char *dest, const u_char *start, const u_char *roof
+ , const struct state *st)
+{
+ struct hmac_ctx ctx;
+
+ hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid_a);
+ hmac_update(&ctx, (const u_char *) &st->st_msgid, sizeof(st->st_msgid));
+ hmac_update(&ctx, start, roof-start);
+ hmac_final(dest, &ctx);
+
+ DBG(DBG_CRYPT,
+ DBG_log("MODE CFG: HASH computed:");
+ DBG_dump("", dest, ctx.hmac_digest_size)
+ )
+ return ctx.hmac_digest_size;
+}
+
+
+/* Mode Config Reply
+ * Generates a reply stream containing Mode Config information (eg: IP, DNS, WINS)
+ */
+stf_status modecfg_resp(struct state *st
+ , u_int resp
+ , pb_stream *rbody
+ , u_int16_t replytype
+ , bool hackthat
+ , u_int16_t ap_id)
+{
+ u_char *r_hash_start,*r_hashval;
+
+ /* START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_ATTR); */
+
+ {
+ pb_stream hash_pbs;
+ int np = ISAKMP_NEXT_ATTR;
+
+ if (!out_generic(np, &isakmp_hash_desc, rbody, &hash_pbs))
+ return STF_INTERNAL_ERROR;
+ r_hashval = hash_pbs.cur; /* remember where to plant value */
+ if (!out_zero(st->st_oakley.hasher->hash_digest_size, &hash_pbs, "HASH"))
+ return STF_INTERNAL_ERROR;
+ close_output_pbs(&hash_pbs);
+ r_hash_start = (rbody)->cur; /* hash from after HASH payload */
+ }
+
+ /* ATTR out */
+ {
+ struct isakmp_mode_attr attrh;
+ struct isakmp_attribute attr;
+ pb_stream strattr,attrval;
+ int attr_type;
+ struct internal_addr ia;
+ int dns_idx, wins_idx;
+ bool dont_advance;
+
+ attrh.isama_np = ISAKMP_NEXT_NONE;
+ attrh.isama_type = replytype;
+
+ attrh.isama_identifier = ap_id;
+ if (!out_struct(&attrh, &isakmp_attr_desc, rbody, &strattr))
+ return STF_INTERNAL_ERROR;
+
+ get_internal_addresses(st->st_connection, &ia);
+
+ if (!isanyaddr(&ia.dns[0])) /* We got DNS addresses, answer with those */
+ resp |= LELEM(INTERNAL_IP4_DNS);
+ else
+ resp &= ~LELEM(INTERNAL_IP4_DNS);
+
+ if (!isanyaddr(&ia.wins[0])) /* We got WINS addresses, answer with those */
+ resp |= LELEM(INTERNAL_IP4_NBNS);
+ else
+ resp &= ~LELEM(INTERNAL_IP4_NBNS);
+
+ if (hackthat)
+ {
+ if (memcmp(&st->st_connection->spd.that.client.addr
+ ,&ia.ipaddr
+ ,sizeof(ia.ipaddr)) != 0)
+ {
+ /* Make the Internal IP address and Netmask
+ * as that client address
+ */
+ st->st_connection->spd.that.client.addr = ia.ipaddr;
+ st->st_connection->spd.that.client.maskbits = 32;
+ st->st_connection->spd.that.has_client = TRUE;
+ }
+ }
+
+ attr_type = 0;
+ dns_idx = 0;
+ wins_idx = 0;
+
+ while (resp != 0)
+ {
+ dont_advance = FALSE;
+ if (resp & 1)
+ {
+ const u_char *byte_ptr;
+ u_int len;
+
+ /* ISAKMP attr out */
+ attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TLV;
+ out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, &attrval);
+
+ switch (attr_type)
+ {
+ case INTERNAL_IP4_ADDRESS:
+ {
+ char srcip[ADDRTOT_BUF];
+
+ addrtot(&ia.ipaddr, 0, srcip, sizeof(srcip));
+ plog("assigning virtual IP source address %s", srcip);
+ len = addrbytesptr(&ia.ipaddr, &byte_ptr);
+ out_raw(byte_ptr,len,&attrval,"IP4_addr");
+ }
+ break;
+ case INTERNAL_IP4_NETMASK:
+ {
+ u_int mask;
+#if 0
+ char mask[4],bits[8]={0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe};
+ int t,m=st->st_connection->that.host_addr.maskbit;
+ for (t=0; t<4; t++)
+ {
+ if (m < 8)
+ mask[t] = bits[m];
+ else
+ mask[t] = 0xff;
+ m -= 8;
+ }
+#endif
+ if (st->st_connection->spd.this.client.maskbits == 0)
+ mask = 0;
+ else
+ mask = 0xffffffff * 1;
+ out_raw(&mask,4,&attrval,"IP4_mask");
+ }
+ break;
+ case INTERNAL_IP4_SUBNET:
+ {
+ char mask[4];
+ char bits[8] = {0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe};
+ int t;
+ int m = st->st_connection->spd.this.client.maskbits;
+
+ for (t = 0; t < 4; t++)
+ {
+ if (m < 8)
+ mask[t] = bits[m];
+ else
+ mask[t] = 0xff;
+ m -= 8;
+ if (m < 0)
+ m = 0;
+ }
+ len = addrbytesptr(&st->st_connection->spd.this.client.addr, &byte_ptr);
+ out_raw(byte_ptr,len,&attrval,"IP4_subnet");
+ out_raw(mask,sizeof(mask),&attrval,"IP4_submsk");
+ }
+ break;
+ case INTERNAL_IP4_DNS:
+ len = addrbytesptr(&ia.dns[dns_idx++], &byte_ptr);
+ out_raw(byte_ptr,len,&attrval,"IP4_dns");
+ if (dns_idx < 2 && !isanyaddr(&ia.dns[dns_idx]))
+ {
+ dont_advance = TRUE;
+ }
+ break;
+ case INTERNAL_IP4_NBNS:
+ len = addrbytesptr(&ia.wins[wins_idx++], &byte_ptr);
+ out_raw(byte_ptr,len,&attrval,"IP4_wins");
+ if (wins_idx < 2 && !isanyaddr(&ia.wins[wins_idx]))
+ {
+ dont_advance = TRUE;
+ }
+ break;
+ default:
+ plog("attempt to send unsupported mode cfg attribute %s."
+ , enum_show(&modecfg_attr_names, attr_type));
+ break;
+ }
+ close_output_pbs(&attrval);
+
+ }
+ if (!dont_advance)
+ {
+ attr_type++;
+ resp >>= 1;
+ }
+ }
+ close_message(&strattr);
+ }
+
+ mode_cfg_hash(r_hashval,r_hash_start,rbody->cur,st);
+ close_message(rbody);
+ encrypt_message(rbody, st);
+ return STF_OK;
+}
+
+/* Set MODE_CONFIG data to client.
+ * Pack IP Addresses, DNS, etc... and ship
+ */
+stf_status modecfg_send_set(struct state *st)
+{
+ pb_stream reply,rbody;
+ char buf[256];
+
+ /* set up reply */
+ init_pbs(&reply, buf, sizeof(buf), "ModecfgR1");
+
+ st->st_state = STATE_MODE_CFG_R1;
+ /* HDR out */
+ {
+ struct isakmp_hdr hdr;
+
+ zero(&hdr); /* default to 0 */
+ hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;
+ hdr.isa_np = ISAKMP_NEXT_HASH;
+ hdr.isa_xchg = ISAKMP_XCHG_MODE_CFG;
+ hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION;
+ memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE);
+ memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE);
+ hdr.isa_msgid = st->st_msgid;
+
+ if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody))
+ {
+ return STF_INTERNAL_ERROR;
+ }
+ }
+
+#define MODECFG_SET_ITEM ( LELEM(INTERNAL_IP4_ADDRESS) | LELEM(INTERNAL_IP4_SUBNET) | LELEM(INTERNAL_IP4_NBNS) | LELEM(INTERNAL_IP4_DNS) )
+
+ modecfg_resp(st, MODECFG_SET_ITEM
+ , &rbody
+ , ISAKMP_CFG_SET
+ , TRUE
+ , 0/* XXX ID */);
+#undef MODECFG_SET_ITEM
+
+ clonetochunk(st->st_tpacket, reply.start
+ , pbs_offset(&reply), "ModeCfg set");
+
+ /* Transmit */
+ send_packet(st, "ModeCfg set");
+
+ /* RETRANSMIT if Main, SA_REPLACE if Aggressive */
+ if (st->st_event->ev_type != EVENT_RETRANSMIT
+ && st->st_event->ev_type != EVENT_NULL)
+ {
+ delete_event(st);
+ event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st);
+ }
+
+ return STF_OK;
+}
+
+/* Set MODE_CONFIG data to client.
+ * Pack IP Addresses, DNS, etc... and ship
+ */
+stf_status
+modecfg_start_set(struct state *st)
+{
+ if (st->st_msgid == 0)
+ {
+ /* pick a new message id */
+ st->st_msgid = generate_msgid(st);
+ }
+ st->st_modecfg.vars_set = TRUE;
+
+ return modecfg_send_set(st);
+}
+
+/*
+ * Send modecfg IP address request (IP4 address)
+ */
+stf_status
+modecfg_send_request(struct state *st)
+{
+ pb_stream reply;
+ pb_stream rbody;
+ char buf[256];
+ u_char *r_hash_start,*r_hashval;
+
+ /* set up reply */
+ init_pbs(&reply, buf, sizeof(buf), "modecfg_buf");
+
+ plog("sending ModeCfg request");
+
+ /* this is the beginning of a new exchange */
+ st->st_msgid = generate_msgid(st);
+ st->st_state = STATE_MODE_CFG_I1;
+
+ /* HDR out */
+ {
+ struct isakmp_hdr hdr;
+
+ zero(&hdr); /* default to 0 */
+ hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;
+ hdr.isa_np = ISAKMP_NEXT_HASH;
+ hdr.isa_xchg = ISAKMP_XCHG_MODE_CFG;
+ hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION;
+ memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE);
+ memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE);
+ hdr.isa_msgid = st->st_msgid;
+
+ if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody))
+ {
+ return STF_INTERNAL_ERROR;
+ }
+ }
+
+ START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_ATTR);
+
+ /* ATTR out */
+ {
+ struct isakmp_mode_attr attrh;
+ struct isakmp_attribute attr;
+ pb_stream strattr;
+
+ attrh.isama_np = ISAKMP_NEXT_NONE;
+ attrh.isama_type = ISAKMP_CFG_REQUEST;
+ attrh.isama_identifier = 0;
+ if (!out_struct(&attrh, &isakmp_attr_desc, &rbody, &strattr))
+ return STF_INTERNAL_ERROR;
+ /* ISAKMP attr out (ipv4) */
+ attr.isaat_af_type = INTERNAL_IP4_ADDRESS;
+ attr.isaat_lv = 0;
+ out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, NULL);
+
+ /* ISAKMP attr out (netmask) */
+ attr.isaat_af_type = INTERNAL_IP4_NETMASK;
+ attr.isaat_lv = 0;
+ out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, NULL);
+
+ close_message(&strattr);
+ }
+
+ mode_cfg_hash(r_hashval,r_hash_start,rbody.cur,st);
+
+ close_message(&rbody);
+ close_output_pbs(&reply);
+
+ init_phase2_iv(st, &st->st_msgid);
+ encrypt_message(&rbody, st);
+
+ clonetochunk(st->st_tpacket, reply.start, pbs_offset(&reply)
+ , "modecfg: req");
+
+ /* Transmit */
+ send_packet(st, "modecfg: req");
+
+ /* RETRANSMIT if Main, SA_REPLACE if Aggressive */
+ if (st->st_event->ev_type != EVENT_RETRANSMIT)
+ {
+ delete_event(st);
+ event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0 * 3, st);
+ }
+ st->st_modecfg.started = TRUE;
+
+ return STF_OK;
+}
+
+/*
+ * parse a modecfg attribute payload
+ */
+static stf_status
+modecfg_parse_attributes(pb_stream *attrs, u_int *set)
+{
+ struct isakmp_attribute attr;
+ pb_stream strattr;
+
+ while (pbs_left(attrs) > sizeof(struct isakmp_attribute))
+ {
+ if (!in_struct(&attr, &isakmp_modecfg_attribute_desc, attrs, &strattr))
+ {
+ int len = (attr.isaat_af_type & 0x8000)? 4 : attr.isaat_lv;
+
+ if (len < 4)
+ {
+ plog("Attribute was too short: %d", len);
+ return STF_FAIL;
+ }
+
+ attrs->cur += len;
+ }
+
+ switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK )
+ {
+ case INTERNAL_IP4_ADDRESS:
+ case INTERNAL_IP4_NETMASK:
+ case INTERNAL_IP4_DNS:
+ case INTERNAL_IP4_SUBNET:
+ case INTERNAL_IP4_NBNS:
+ *set |= LELEM(attr.isaat_af_type);
+ break;
+ default:
+ plog("unsupported mode cfg attribute %s received."
+ , enum_show(&modecfg_attr_names
+ , attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ));
+ break;
+ }
+ }
+ return STF_OK;
+}
+
+/* STATE_MODE_CFG_R0:
+ * HDR*, HASH, ATTR(REQ=IP) --> HDR*, HASH, ATTR(REPLY=IP)
+ *
+ * This state occurs both in the responder and in the initiator.
+ *
+ * In the responding server, it occurs when the client *asks* for an IP
+ * address or other information.
+ *
+ * Otherwise, it occurs in the initiator when the server sends a challenge
+ * a set, or has a reply to our request.
+ */
+stf_status
+modecfg_inR0(struct msg_digest *md)
+{
+ struct state *const st = md->st;
+ struct payload_digest *p;
+ stf_status stat;
+
+ plog("received ModeCfg request");
+
+ st->st_msgid = md->hdr.isa_msgid;
+ CHECK_QUICK_HASH(md, mode_cfg_hash(hash_val
+ ,hash_pbs->roof
+ , md->message_pbs.roof, st)
+ , "MODECFG-HASH", "MODE R0");
+
+ /* process the MODECFG payloads therein */
+ for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next)
+ {
+ u_int set_modecfg_attrs = LEMPTY;
+
+ switch (p->payload.attribute.isama_type)
+ {
+ default:
+ plog("Expecting ISAKMP_CFG_REQUEST, got %s instead (ignored)."
+ , enum_name(&attr_msg_type_names
+ , p->payload.attribute.isama_type));
+
+ stat = modecfg_parse_attributes(&p->pbs, &set_modecfg_attrs);
+ if (stat != STF_OK)
+ return stat;
+ break;
+
+ case ISAKMP_CFG_REQUEST:
+ stat = modecfg_parse_attributes(&p->pbs, &set_modecfg_attrs);
+ if (stat != STF_OK)
+ return stat;
+
+ stat = modecfg_resp(st, set_modecfg_attrs
+ ,&md->rbody
+ ,ISAKMP_CFG_REPLY
+ ,TRUE
+ ,p->payload.attribute.isama_identifier);
+
+ if (stat != STF_OK)
+ {
+ /* notification payload - not exactly the right choice, but okay */
+ md->note = CERTIFICATE_UNAVAILABLE;
+ return stat;
+ }
+
+ /* they asked us, we responded, msgid is done */
+ st->st_msgid = 0;
+ }
+ }
+ return STF_OK;
+}
+
+/* STATE_MODE_CFG_R2:
+ * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK)
+ *
+ * used in server push mode, on the client (initiator).
+ */
+static stf_status
+modecfg_inI2(struct msg_digest *md)
+{
+ struct state *const st = md->st;
+ pb_stream *attrs = &md->chain[ISAKMP_NEXT_ATTR]->pbs;
+ int resp = LEMPTY;
+ stf_status stat;
+ struct payload_digest *p;
+ u_int16_t isama_id = 0;
+
+ st->st_msgid = md->hdr.isa_msgid;
+ CHECK_QUICK_HASH(md
+ , mode_cfg_hash(hash_val
+ ,hash_pbs->roof
+ , md->message_pbs.roof
+ , st)
+ , "MODECFG-HASH", "MODE R1");
+
+ for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next)
+ {
+ struct isakmp_attribute attr;
+ pb_stream strattr;
+
+ isama_id = p->payload.attribute.isama_identifier;
+
+ if (p->payload.attribute.isama_type != ISAKMP_CFG_SET)
+ {
+ plog("Expecting MODE_CFG_SET, got %x instead."
+ ,md->chain[ISAKMP_NEXT_ATTR]->payload.attribute.isama_type);
+ return STF_IGNORE;
+ }
+
+ /* CHECK that SET has been received. */
+
+ while (pbs_left(attrs) > sizeof(struct isakmp_attribute))
+ {
+ if (!in_struct(&attr, &isakmp_modecfg_attribute_desc
+ , attrs, &strattr))
+ {
+ int len;
+
+ /* Skip unknown */
+ if (attr.isaat_af_type & 0x8000)
+ len = 4;
+ else
+ len = attr.isaat_lv;
+
+ if (len < 4)
+ {
+ plog("Attribute was too short: %d", len);
+ return STF_FAIL;
+ }
+
+ attrs->cur += len;
+ }
+
+ switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK )
+ {
+ case INTERNAL_IP4_ADDRESS:
+ {
+ struct connection *c = st->st_connection;
+ ip_address a;
+ u_int32_t *ap = (u_int32_t *)(strattr.cur);
+ a.u.v4.sin_family = AF_INET;
+
+ memcpy(&a.u.v4.sin_addr.s_addr, ap
+ , sizeof(a.u.v4.sin_addr.s_addr));
+
+ if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0
+ || isanyaddr(&c->spd.this.host_srcip))
+ {
+ char srcip[ADDRTOT_BUF];
+
+ c->spd.this.host_srcip = a;
+ addrtot(&a, 0, srcip, sizeof(srcip));
+ plog("setting virtual IP source address to %s", srcip);
+ }
+
+ /* setting client subnet as srcip/32 */
+ addrtosubnet(&a, &c->spd.this.client);
+ c->spd.this.has_client = TRUE;
+ }
+ resp |= LELEM(attr.isaat_af_type);
+ break;
+ case INTERNAL_IP4_NETMASK:
+ case INTERNAL_IP4_DNS:
+ case INTERNAL_IP4_SUBNET:
+ case INTERNAL_IP4_NBNS:
+ resp |= LELEM(attr.isaat_af_type);
+ break;
+ default:
+ plog("unsupported mode cfg attribute %s received."
+ , enum_show(&modecfg_attr_names, (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK )));
+ break;
+ }
+ }
+ }
+
+ /* ack things */
+ stat = modecfg_resp(st, resp
+ ,&md->rbody
+ ,ISAKMP_CFG_ACK
+ ,FALSE
+ ,isama_id);
+
+ if (stat != STF_OK)
+ {
+ /* notification payload - not exactly the right choice, but okay */
+ md->note = CERTIFICATE_UNAVAILABLE;
+ return stat;
+ }
+
+ /*
+ * we are done with this exchange, clear things so
+ * that we can start phase 2 properly
+ */
+ st->st_msgid = 0;
+
+ if (resp)
+ {
+ st->st_modecfg.vars_set = TRUE;
+ }
+ return STF_OK;
+}
+
+/* STATE_MODE_CFG_R1:
+ * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK)
+ */
+stf_status
+modecfg_inR1(struct msg_digest *md)
+{
+ struct state *const st = md->st;
+ pb_stream *attrs = &md->chain[ISAKMP_NEXT_ATTR]->pbs;
+ int set_modecfg_attrs = LEMPTY;
+ stf_status stat;
+ struct payload_digest *p;
+
+ plog("parsing ModeCfg reply");
+
+ st->st_msgid = md->hdr.isa_msgid;
+ CHECK_QUICK_HASH(md, mode_cfg_hash(hash_val,hash_pbs->roof, md->message_pbs.roof, st)
+ , "MODECFG-HASH", "MODE R1");
+
+
+ /* process the MODECFG payloads therein */
+ for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next)
+ {
+ struct isakmp_attribute attr;
+ pb_stream strattr;
+
+ attrs = &p->pbs;
+
+ switch (p->payload.attribute.isama_type)
+ {
+ default:
+ {
+ plog("Expecting MODE_CFG_ACK, got %x instead."
+ ,md->chain[ISAKMP_NEXT_ATTR]->payload.attribute.isama_type);
+ return STF_IGNORE;
+ }
+ break;
+
+ case ISAKMP_CFG_ACK:
+ /* CHECK that ACK has been received. */
+ stat = modecfg_parse_attributes(attrs, &set_modecfg_attrs);
+ if (stat != STF_OK)
+ return stat;
+ break;
+
+ case ISAKMP_CFG_REPLY:
+ while (pbs_left(attrs) > sizeof(struct isakmp_attribute))
+ {
+ if (!in_struct(&attr, &isakmp_modecfg_attribute_desc
+ , attrs, &strattr))
+ {
+ /* Skip unknown */
+ int len;
+ if (attr.isaat_af_type & 0x8000)
+ len = 4;
+ else
+ len = attr.isaat_lv;
+
+ if (len < 4)
+ {
+ plog("Attribute was too short: %d", len);
+ return STF_FAIL;
+ }
+
+ attrs->cur += len;
+ }
+
+ switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK )
+ {
+ case INTERNAL_IP4_ADDRESS:
+ {
+ struct connection *c = st->st_connection;
+ ip_address a;
+ u_int32_t *ap = (u_int32_t *)(strattr.cur);
+ a.u.v4.sin_family = AF_INET;
+
+ memcpy(&a.u.v4.sin_addr.s_addr, ap
+ , sizeof(a.u.v4.sin_addr.s_addr));
+
+ if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0
+ || isanyaddr(&c->spd.this.host_srcip))
+ {
+ char srcip[ADDRTOT_BUF];
+
+ c->spd.this.host_srcip = a;
+ addrtot(&a, 0, srcip, sizeof(srcip));
+ plog("setting virtual IP source address to %s", srcip);
+ }
+
+ /* setting client subnet as srcip/32 */
+ addrtosubnet(&a, &c->spd.this.client);
+ setportof(0, &c->spd.this.client.addr);
+ c->spd.this.has_client = TRUE;
+ }
+ /* fall through to set attribute flage */
+
+ case INTERNAL_IP4_NETMASK:
+ case INTERNAL_IP4_DNS:
+ case INTERNAL_IP4_SUBNET:
+ case INTERNAL_IP4_NBNS:
+ set_modecfg_attrs |= LELEM(attr.isaat_af_type);
+ break;
+ default:
+ plog("unsupported mode cfg attribute %s received."
+ , enum_show(&modecfg_attr_names
+ , (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK )));
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ /* we are done with this exchange, clear things so that we can start phase 2 properly */
+ st->st_msgid = 0;
+
+ if (set_modecfg_attrs)
+ {
+ st->st_modecfg.vars_set = TRUE;
+ }
+ return STF_OK;
+}