static void transport_setup_msgok(struct site *st, const struct comm_addr *a);
static void transport_data_msgok(struct site *st, const struct comm_addr *a);
static bool_t transport_compute_setupinit_peers(struct site *st,
- const struct comm_addr *configured_addr /* 0 if none or not found */);
+ const struct comm_addr *configured_addr /* 0 if none or not found */,
+ const struct comm_addr *prod_hint_addr /* 0 if none */);
static void transport_record_peer(struct site *st, transport_peers *peers,
const struct comm_addr *addr, const char *m);
/* configuration information */
string_t localname;
string_t remotename;
- bool_t peer_mobile; /* Mobile client support */
+ bool_t local_mobile, peer_mobile; /* Mobile client support */
int32_t transport_peers_max;
string_t tunname; /* localname<->remotename by default, used in logs */
string_t address; /* DNS name for bootstrapping, optional */
int remoteport; /* Port for bootstrapping, optional */
+ uint32_t mtu_target;
struct netlink_if *netlink;
struct comm_if **comms;
int ncomms;
/* runtime information */
uint32_t state;
uint64_t now; /* Most recently seen time */
+ bool_t allow_send_prod;
+ bool_t resolving;
/* The currently established session */
struct data_key current;
timeout before we can listen for another setup packet); perhaps
we should keep a list of 'bad' sources for setup packets. */
uint32_t remote_capabilities;
+ uint16_t remote_adv_mtu;
struct transform_if *chosen_transform;
uint32_t setup_session_id;
transport_peers setup_peers;
struct transform_inst_if *new_transform; /* For key setup/verify */
};
+static uint32_t event_log_priority(struct site *st, uint32_t event)
+{
+ if (!(event&st->log_events))
+ return 0;
+ switch(event) {
+ case LOG_UNEXPECTED: return M_INFO;
+ case LOG_SETUP_INIT: return M_INFO;
+ case LOG_SETUP_TIMEOUT: return M_NOTICE;
+ case LOG_ACTIVATE_KEY: return M_INFO;
+ case LOG_TIMEOUT_KEY: return M_INFO;
+ case LOG_SEC: return M_SECURITY;
+ case LOG_STATE: return M_DEBUG;
+ case LOG_DROP: return M_DEBUG;
+ case LOG_DUMP: return M_DEBUG;
+ case LOG_ERROR: return M_ERR;
+ case LOG_PEER_ADDRS: return M_DEBUG;
+ default: return M_ERR;
+ }
+}
+
+static void vslog(struct site *st, uint32_t event, cstring_t msg, va_list ap)
+FORMAT(printf,3,0);
+static void vslog(struct site *st, uint32_t event, cstring_t msg, va_list ap)
+{
+ uint32_t class;
+
+ class=event_log_priority(st, event);
+ if (class) {
+ slilog_part(st->log,class,"%s: ",st->tunname);
+ vslilog_part(st->log,class,msg,ap);
+ slilog_part(st->log,class,"\n");
+ }
+}
+
static void slog(struct site *st, uint32_t event, cstring_t msg, ...)
FORMAT(printf,3,4);
static void slog(struct site *st, uint32_t event, cstring_t msg, ...)
{
va_list ap;
- char buf[240];
- uint32_t class;
-
va_start(ap,msg);
+ vslog(st,event,msg,ap);
+ va_end(ap);
+}
- if (event&st->log_events) {
- switch(event) {
- case LOG_UNEXPECTED: class=M_INFO; break;
- case LOG_SETUP_INIT: class=M_INFO; break;
- case LOG_SETUP_TIMEOUT: class=M_NOTICE; break;
- case LOG_ACTIVATE_KEY: class=M_INFO; break;
- case LOG_TIMEOUT_KEY: class=M_INFO; break;
- case LOG_SEC: class=M_SECURITY; break;
- case LOG_STATE: class=M_DEBUG; break;
- case LOG_DROP: class=M_DEBUG; break;
- case LOG_DUMP: class=M_DEBUG; break;
- case LOG_ERROR: class=M_ERR; break;
- case LOG_PEER_ADDRS: class=M_DEBUG; break;
- default: class=M_ERR; break;
- }
+static void logtimeout(struct site *st, const char *fmt, ...)
+FORMAT(printf,2,3);
+static void logtimeout(struct site *st, const char *fmt, ...)
+{
+ uint32_t class=event_log_priority(st,LOG_SETUP_TIMEOUT);
+ if (!class)
+ return;
- vsnprintf(buf,sizeof(buf),msg,ap);
- slilog(st->log,class,"%s: %s",st->tunname,buf);
+ va_list ap;
+ va_start(ap,fmt);
+
+ slilog_part(st->log,class,"%s: ",st->tunname);
+ vslilog_part(st->log,class,fmt,ap);
+
+ const char *delim;
+ int i;
+ for (i=0, delim=" (tried ";
+ i<st->setup_peers.npeers;
+ i++, delim=", ") {
+ transport_peer *peer=&st->setup_peers.peers[i];
+ const char *s=comm_addr_to_string(&peer->addr);
+ slilog_part(st->log,class,"%s%s",delim,s);
}
+
+ slilog_part(st->log,class,")\n");
va_end(ap);
}
const char *reason /* may be 0 meaning don't log*/,
const char *which /* ignored if !reasonn */,
uint32_t loglevel /* ignored if !reasonn */);
-static bool_t initiate_key_setup(struct site *st, cstring_t reason);
+static bool_t initiate_key_setup(struct site *st, cstring_t reason,
+ const struct comm_addr *prod_hint);
static void enter_state_run(struct site *st);
static bool_t enter_state_resolve(struct site *st);
static bool_t enter_new_state(struct site *st,uint32_t next);
type=buf_unprepend_uint32((b)); \
if (type!=(t)) return False; } while(0)
+static _Bool type_is_msg34(uint32_t type)
+{
+ return
+ type == LABEL_MSG3 ||
+ type == LABEL_MSG3BIS ||
+ type == LABEL_MSG4;
+}
+
struct parsedname {
int32_t len;
uint8_t *name;
struct parsedname remote;
struct parsedname local;
uint32_t remote_capabilities;
+ uint16_t remote_mtu;
int capab_transformnum;
uint8_t *nR;
uint8_t *nL;
if ((st->local_capabilities & CAPAB_EARLY) || (type != LABEL_MSG1)) {
buf_append_uint32(&st->buffer,st->local_capabilities);
}
+ if (type_is_msg34(type)) {
+ buf_append_uint16(&st->buffer,st->mtu_target);
+ }
append_string_xinfo_done(&st->buffer,&xia);
buf_append_string(&st->buffer,st->remotename);
CHECK_TYPE(msg,type);
if (!unpick_name(msg,&m->remote)) return False;
m->remote_capabilities=0;
+ m->remote_mtu=0;
if (m->remote.extrainfo.size) {
CHECK_AVAIL(&m->remote.extrainfo,4);
m->remote_capabilities=buf_unprepend_uint32(&m->remote.extrainfo);
}
+ if (type_is_msg34(type) && m->remote.extrainfo.size) {
+ CHECK_AVAIL(&m->remote.extrainfo,2);
+ m->remote_mtu=buf_unprepend_uint16(&m->remote.extrainfo);
+ }
if (!unpick_name(msg,&m->local)) return False;
+ if (type==LABEL_PROD) {
+ CHECK_EMPTY(msg);
+ return True;
+ }
CHECK_AVAIL(msg,NONCELEN);
m->nR=buf_unprepend(msg,NONCELEN);
if (type==LABEL_MSG1) {
process an incoming MSG1, and that the MSG1 has correct values
of A and B. */
- transport_record_peer(st,&st->setup_peers,src,"msg1");
st->setup_session_id=m->source;
st->remote_capabilities=m->remote_capabilities;
memcpy(st->remoteN,m->nR,NONCELEN);
"site:MSG3");
}
+static bool_t process_msg3_msg4(struct site *st, struct msg *m)
+{
+ uint8_t *hash;
+ void *hst;
+
+ /* Check signature and store g^x mod m */
+ hash=safe_malloc(st->hash->len, "process_msg3_msg4");
+ hst=st->hash->init();
+ st->hash->update(hst,m->hashstart,m->hashlen);
+ st->hash->final(hst,hash);
+ /* Terminate signature with a '0' - cheating, but should be ok */
+ m->sig[m->siglen]=0;
+ if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m->sig)) {
+ slog(st,LOG_SEC,"msg3/msg4 signature failed check!");
+ free(hash);
+ return False;
+ }
+ free(hash);
+
+ st->remote_adv_mtu=m->remote_mtu;
+
+ return True;
+}
+
static bool_t process_msg3(struct site *st, struct buffer_if *msg3,
const struct comm_addr *src, uint32_t msgtype)
{
struct msg m;
- uint8_t *hash;
- void *hst;
cstring_t err;
assert(msgtype==LABEL_MSG3 || msgtype==LABEL_MSG3BIS);
transform_found:
st->chosen_transform=ti;
- /* Check signature and store g^x mod m */
- hash=safe_malloc(st->hash->len, "process_msg3");
- hst=st->hash->init();
- st->hash->update(hst,m.hashstart,m.hashlen);
- st->hash->final(hst,hash);
- /* Terminate signature with a '0' - cheating, but should be ok */
- m.sig[m.siglen]=0;
- if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m.sig)) {
- slog(st,LOG_SEC,"msg3 signature failed check!");
- free(hash);
+ if (!process_msg3_msg4(st,&m))
return False;
- }
- free(hash);
/* Terminate their DH public key with a '0' */
m.pk[m.pklen]=0;
const struct comm_addr *src)
{
struct msg m;
- uint8_t *hash;
- void *hst;
cstring_t err;
if (!unpick_msg(st,LABEL_MSG4,msg4,&m)) return False;
return False;
}
- /* Check signature and store g^x mod m */
- hash=safe_malloc(st->hash->len, "process_msg4");
- hst=st->hash->init();
- st->hash->update(hst,m.hashstart,m.hashlen);
- st->hash->final(hst,hash);
- /* Terminate signature with a '0' - cheating, but should be ok */
- m.sig[m.siglen]=0;
- if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m.sig)) {
- slog(st,LOG_SEC,"msg4 signature failed check!");
- free(hash);
+ if (!process_msg3_msg4(st,&m))
return False;
- }
- free(hash);
/* Terminate their DH public key with a '0' */
m.pk[m.pklen]=0;
slog(st,LOG_SEC,"transform: %s (aux: %s, new: %s)",
transform_err,auxkey_err,newkey_err);
- initiate_key_setup(st,"incoming message would not decrypt");
+ initiate_key_setup(st,"incoming message would not decrypt",0);
send_nak(src,m.dest,m.source,m.type,msg0,"message would not decrypt");
return False;
transport_data_msgok(st,src);
/* See whether we should start negotiating a new key */
if (st->now > st->renegotiate_key_time)
- initiate_key_setup(st,"incoming packet in renegotiation window");
+ initiate_key_setup(st,"incoming packet in renegotiation window",0);
return True;
default:
slog(st,LOG_SEC,"incoming encrypted message of type %08x "
st->retries--;
return True;
} else if (st->state==SITE_SENTMSG5) {
- slog(st,LOG_SETUP_TIMEOUT,"timed out sending MSG5, stashing new key");
+ logtimeout(st,"timed out sending MSG5, stashing new key");
/* We stash the key we have produced, in case it turns out that
* our peer did see our MSG5 after all and starts using it. */
/* This is a bit like some of activate_new_key */
enter_state_wait(st);
return False;
} else {
- slog(st,LOG_SETUP_TIMEOUT,"timed out sending key setup packet "
+ logtimeout(st,"timed out sending key setup packet "
"(in state %s)",state_name(st->state));
enter_state_wait(st);
return False;
struct site *st=sst;
struct comm_addr ca_buf, *ca_use;
- if (st->state!=SITE_RESOLVE) {
- slog(st,LOG_UNEXPECTED,"site_resolve_callback called unexpectedly");
- return;
- }
+ st->resolving=False;
+
if (address) {
FILLZERO(ca_buf);
ca_buf.comm=st->comms[0];
ca_buf.sin.sin_port=htons(st->remoteport);
ca_buf.sin.sin_addr=*address;
ca_use=&ca_buf;
+ slog(st,LOG_STATE,"resolution of %s completed: %s",
+ st->address, comm_addr_to_string(ca_use));;
} else {
slog(st,LOG_ERROR,"resolution of %s failed",st->address);
ca_use=0;
}
- if (transport_compute_setupinit_peers(st,ca_use)) {
- enter_new_state(st,SITE_SENTMSG1);
- } else {
- /* Can't figure out who to try to to talk to */
- slog(st,LOG_SETUP_INIT,"key exchange failed: cannot find peer address");
- enter_state_run(st);
+
+ switch (st->state) {
+ case SITE_RESOLVE:
+ if (transport_compute_setupinit_peers(st,ca_use,0)) {
+ enter_new_state(st,SITE_SENTMSG1);
+ } else {
+ /* Can't figure out who to try to to talk to */
+ slog(st,LOG_SETUP_INIT,
+ "key exchange failed: cannot find peer address");
+ enter_state_run(st);
+ }
+ break;
+ case SITE_SENTMSG1: case SITE_SENTMSG2:
+ case SITE_SENTMSG3: case SITE_SENTMSG4:
+ case SITE_SENTMSG5:
+ if (ca_use) {
+ /* We start using the address immediately for data too.
+ * It's best to store it in st->peers now because we might
+ * go via SENTMSG5, WAIT, and a MSG0, straight into using
+ * the new key (without updating the data peer addrs). */
+ transport_record_peer(st,&st->peers,ca_use,"resolved data");
+ transport_record_peer(st,&st->setup_peers,ca_use,"resolved setup");
+ } else if (st->local_mobile) {
+ /* We can't let this rest because we may have a peer
+ * address which will break in the future. */
+ slog(st,LOG_SETUP_INIT,"resolution of %s failed: "
+ "abandoning key exchange",st->address);
+ enter_state_wait(st);
+ } else {
+ slog(st,LOG_SETUP_INIT,"resolution of %s failed: "
+ " continuing to use source address of peer's packets"
+ " for key exchange and ultimately data",
+ st->address);
+ }
+ break;
+ case SITE_RUN:
+ if (ca_use) {
+ slog(st,LOG_SETUP_INIT,"resolution of %s completed tardily,"
+ " updating peer address(es)",st->address);
+ transport_record_peer(st,&st->peers,ca_use,"resolved tardily");
+ } else if (st->local_mobile) {
+ /* Not very good. We should queue (another) renegotiation
+ * so that we can update the peer address. */
+ st->key_renegotiate_time=st->now+st->wait_timeout;
+ } else {
+ slog(st,LOG_SETUP_INIT,"resolution of %s failed: "
+ " continuing to use source address of peer's packets",
+ st->address);
+ }
+ break;
+ case SITE_WAIT:
+ case SITE_STOP:
+ /* oh well */
+ break;
}
}
-static bool_t initiate_key_setup(struct site *st, cstring_t reason)
+static bool_t initiate_key_setup(struct site *st, cstring_t reason,
+ const struct comm_addr *prod_hint)
{
+ /* Reentrancy hazard: can call enter_new_state/enter_state_* */
if (st->state!=SITE_RUN) return False;
slog(st,LOG_SETUP_INIT,"initiating key exchange (%s)",reason);
if (st->address) {
slog(st,LOG_SETUP_INIT,"resolving peer address");
return enter_state_resolve(st);
- } else if (transport_compute_setupinit_peers(st,0)) {
+ } else if (transport_compute_setupinit_peers(st,0,prod_hint)) {
return enter_new_state(st,SITE_SENTMSG1);
}
slog(st,LOG_SETUP_INIT,"key exchange failed: no address for peer");
transport_peers_copy(st,&st->peers,&st->setup_peers);
st->current.remote_session_id=st->setup_session_id;
- slog(st,LOG_ACTIVATE_KEY,"new key activated");
+ /* Compute the inter-site MTU. This is min( our_mtu, their_mtu ).
+ * But their mtu be unspecified, in which case we just use ours. */
+ uint32_t intersite_mtu=
+ MIN(st->mtu_target, st->remote_adv_mtu ?: ~(uint32_t)0);
+ st->netlink->set_mtu(st->netlink->st,intersite_mtu);
+
+ slog(st,LOG_ACTIVATE_KEY,"new key activated"
+ " (mtu ours=%"PRId32" theirs=%"PRId32" intersite=%"PRId32")",
+ st->mtu_target, st->remote_adv_mtu, intersite_mtu);
enter_state_run(st);
}
set_link_quality(st);
}
+static bool_t ensure_resolving(struct site *st)
+{
+ /* Reentrancy hazard: may call site_resolve_callback and hence
+ * enter_new_state, enter_state_* and generate_msg*. */
+ if (st->resolving)
+ return True;
+
+ assert(st->address);
+
+ /* resolver->request might reentrantly call site_resolve_callback
+ * which will clear st->resolving, so we need to set it beforehand
+ * rather than afterwards; also, it might return False, in which
+ * case we have to clear ->resolving again. */
+ st->resolving=True;
+ bool_t ok = st->resolver->request(st->resolver->st,st->address,
+ site_resolve_callback,st);
+ if (!ok)
+ st->resolving=False;
+
+ return ok;
+}
+
static bool_t enter_state_resolve(struct site *st)
{
+ /* Reentrancy hazard! See ensure_resolving. */
state_assert(st,st->state==SITE_RUN);
slog(st,LOG_STATE,"entering state RESOLVE");
st->state=SITE_RESOLVE;
- st->resolver->request(st->resolver->st,st->address,
- site_resolve_callback,st);
- return True;
+ return ensure_resolving(st);
}
static bool_t enter_new_state(struct site *st, uint32_t next)
/* XXX Erase keys etc. */
}
+static void generate_prod(struct site *st, struct buffer_if *buf)
+{
+ buffer_init(buf,0);
+ buf_append_uint32(buf,0);
+ buf_append_uint32(buf,0);
+ buf_append_uint32(buf,LABEL_PROD);
+ buf_append_string(buf,st->localname);
+ buf_append_string(buf,st->remotename);
+}
+
+static void generate_send_prod(struct site *st,
+ const struct comm_addr *source)
+{
+ if (!st->allow_send_prod) return; /* too soon */
+ if (!(st->state==SITE_RUN || st->state==SITE_RESOLVE ||
+ st->state==SITE_WAIT)) return; /* we'd ignore peer's MSG1 */
+
+ slog(st,LOG_SETUP_INIT,"prodding peer for key exchange");
+ st->allow_send_prod=0;
+ generate_prod(st,&st->scratch);
+ dump_packet(st,&st->scratch,source,False);
+ source->comm->sendmsg(source->comm->st, &st->scratch, source);
+}
+
static inline void site_settimeout(uint64_t timeout, int *timeout_io)
{
if (timeout) {
return;
}
+ st->allow_send_prod=1;
+
/* In all other states we consider delivering the packet if we have
a valid key and a valid address to send it to. */
if (current_valid(st) && transport_peers_valid(&st->peers)) {
slog(st,LOG_DROP,"discarding outgoing packet of size %d",buf->size);
BUF_FREE(buf);
- initiate_key_setup(st,"outgoing packet");
+ initiate_key_setup(st,"outgoing packet",0);
}
static bool_t named_for_us(struct site *st, const struct buffer_if *buf_in,
}
/* This function is called by the communication device to deliver
- packets from our peers. */
+ packets from our peers.
+ It should return True if the packet is recognised as being for
+ this current site instance (and should therefore not be processed
+ by other sites), even if the packet was otherwise ignored. */
static bool_t site_incoming(void *sst, struct buffer_if *buf,
const struct comm_addr *source)
{
if (st->state==SITE_RUN || st->state==SITE_RESOLVE ||
st->state==SITE_WAIT) {
/* We should definitely process it */
+ transport_record_peer(st,&st->setup_peers,source,"msg1");
if (process_msg1(st,buf,source,&named_msg)) {
slog(st,LOG_SETUP_INIT,"key setup initiated by peer");
- enter_new_state(st,SITE_SENTMSG2);
+ bool_t entered=enter_new_state(st,SITE_SENTMSG2);
+ if (entered && st->address && st->local_mobile)
+ /* We must do this as the very last thing, because
+ the resolver callback might reenter us. */
+ ensure_resolving(st);
} else {
slog(st,LOG_ERROR,"failed to process incoming msg1");
}
"priority => use incoming msg1");
if (process_msg1(st,buf,source,&named_msg)) {
BUF_FREE(&st->buffer); /* Free our old message 1 */
+ transport_setup_msgok(st,source);
enter_new_state(st,SITE_SENTMSG2);
} else {
slog(st,LOG_ERROR,"failed to process an incoming "
BUF_FREE(buf);
return True;
}
+ if (msgtype==LABEL_PROD) {
+ if (!named_for_us(st,buf,msgtype,&named_msg))
+ return False;
+ dump_packet(st,buf,source,True);
+ if (st->state!=SITE_RUN) {
+ slog(st,LOG_DROP,"ignoring PROD when not in state RUN");
+ } else if (current_valid(st)) {
+ slog(st,LOG_DROP,"ignoring PROD when we think we have a key");
+ } else {
+ initiate_key_setup(st,"peer sent PROD packet",source);
+ }
+ BUF_FREE(buf);
+ return True;
+ }
if (dest==st->index) {
/* Explicitly addressed to us */
if (msgtype!=LABEL_MSG0) dump_packet(st,buf,source,True);
/* If the source is our current peer then initiate a key setup,
because our peer's forgotten the key */
if (get_uint32(buf->start+4)==st->current.remote_session_id) {
- initiate_key_setup(st,"received a NAK");
+ bool_t initiated;
+ initiated = initiate_key_setup(st,"received a NAK",0);
+ if (!initiated) generate_send_prod(st,source);
} else {
slog(st,LOG_SEC,"bad incoming NAK");
}
st->remotename=dict_read_string(dict, "name", True, "site", loc);
st->peer_mobile=dict_read_bool(dict,"mobile",False,"site",loc,False);
- bool_t local_mobile=
+ st->local_mobile=
dict_read_bool(dict,"local-mobile",False,"site",loc,False);
/* Sanity check (which also allows the 'sites' file to include
if (strcmp(st->localname,st->remotename)==0) {
Message(M_DEBUG,"site %s: local-name==name -> ignoring this site\n",
st->localname);
- if (st->peer_mobile != local_mobile)
+ if (st->peer_mobile != st->local_mobile)
cfgfatal(loc,"site","site %s's peer-mobile=%d"
" but our local-mobile=%d\n",
- st->localname, st->peer_mobile, local_mobile);
+ st->localname, st->peer_mobile, st->local_mobile);
free(st);
return NULL;
}
- if (st->peer_mobile && local_mobile) {
+ if (st->peer_mobile && st->local_mobile) {
Message(M_WARNING,"site %s: site is mobile but so are we"
" -> ignoring this site\n", st->remotename);
free(st);
st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc);
st->hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc);
-#define DEFAULT(D) (st->peer_mobile || local_mobile \
+#define DEFAULT(D) (st->peer_mobile || st->local_mobile \
? DEFAULT_MOBILE_##D : DEFAULT_##D)
#define CFG_NUMBER(k,D) dict_read_number(dict,(k),False,"site",loc,DEFAULT(D));
st->setup_retries= CFG_NUMBER("setup-retries", SETUP_RETRIES);
st->setup_retry_interval= CFG_NUMBER("setup-timeout", SETUP_RETRY_INTERVAL);
st->wait_timeout= CFG_NUMBER("wait-time", WAIT_TIME);
+ st->mtu_target= dict_read_number(dict,"mtu-target",False,"site",loc,0);
st->mobile_peer_expiry= dict_read_number(
dict,"mobile-peer-expiry",False,"site",loc,DEFAULT_MOBILE_PEER_EXPIRY);
st->log_events=string_list_to_word(dict_lookup(dict,"log-events"),
log_event_table,"site");
+ st->resolving=False;
+ st->allow_send_prod=0;
+
st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5,
"site_apply");
sprintf(st->tunname,"%s<->%s",st->localname,st->remotename);
buffer_new(&st->buffer,SETUP_BUFFER_LEN);
- buffer_new(&st->scratch,0);
+ buffer_new(&st->scratch,SETUP_BUFFER_LEN);
BUF_ALLOC(&st->scratch,"site:scratch");
/* We are interested in poll(), but only for timeouts. We don't have
}
/* We need to register the remote networks with the netlink device */
- st->netlink->reg(st->netlink->st, site_outgoing, st);
+ uint32_t netlink_mtu; /* local virtual interface mtu */
+ st->netlink->reg(st->netlink->st, site_outgoing, st, &netlink_mtu);
+ if (!st->mtu_target)
+ st->mtu_target=netlink_mtu;
for (i=0; i<st->ncomms; i++)
st->comms[i]->request_notify(st->comms[i]->st, st, site_incoming);
i++, (argp+=stride?stride:sizeof(*args))) {
const struct comm_addr *ca=(void*)argp;
slog(st, LOG_PEER_ADDRS, " args: addrs[%d]=%s",
- i, ca->comm->addr_to_string(ca->comm->st,ca));
+ i, comm_addr_to_string(ca));
}
for (i=0; i<dst->npeers; i++) {
struct timeval diff;
timersub(tv_now,&dst->peers[i].last,&diff);
const struct comm_addr *ca=&dst->peers[i].addr;
slog(st, LOG_PEER_ADDRS, " peers: addrs[%d]=%s T-%ld.%06ld",
- i, ca->comm->addr_to_string(ca->comm->st,ca),
+ i, comm_addr_to_string(ca),
(unsigned long)diff.tv_sec, (unsigned long)diff.tv_usec);
}
}
changed=1;
if (peers->npeers==st->transport_peers_max)
- slot=st->transport_peers_max;
+ slot=st->transport_peers_max-1;
else
slot=peers->npeers++;
}
static bool_t transport_compute_setupinit_peers(struct site *st,
- const struct comm_addr *configured_addr /* 0 if none or not found */) {
+ const struct comm_addr *configured_addr /* 0 if none or not found */,
+ const struct comm_addr *prod_hint_addr /* 0 if none */) {
- if (!configured_addr && !transport_peers_valid(&st->peers))
+ if (!configured_addr && !prod_hint_addr &&
+ !transport_peers_valid(&st->peers))
return False;
slog(st,LOG_SETUP_INIT,
- (!configured_addr ? "using only %d old peer address(es)"
- : "using configured address, and/or perhaps %d old peer address(es)"),
+ "using:%s%s %d old peer address(es)",
+ configured_addr ? " configured address;" : "",
+ prod_hint_addr ? " PROD hint address;" : "",
st->peers.npeers);
- /* Non-mobile peers havve st->peers.npeers==0 or ==1, since they
+ /* Non-mobile peers have st->peers.npeers==0 or ==1, since they
* have transport_peers_max==1. The effect is that this code
* always uses the configured address if supplied, or otherwise
- * the existing data peer if one exists; this is as desired. */
+ * the address of the incoming PROD, or the existing data peer if
+ * one exists; this is as desired. */
transport_peers_copy(st,&st->setup_peers,&st->peers);
+ if (prod_hint_addr)
+ transport_record_peer(st,&st->setup_peers,prod_hint_addr,"prod");
+
if (configured_addr)
transport_record_peer(st,&st->setup_peers,configured_addr,"setupinit");