chiark / gitweb /
site: Explicitly track name resolution status
[secnet.git] / site.c
diff --git a/site.c b/site.c
index 00a3a4c9153a8477c1d56fb041f81abdde1261b0..f9c2f087823d191f2b056032ff4c5a28b3c90eef 100644 (file)
--- a/site.c
+++ b/site.c
@@ -234,6 +234,7 @@ struct site {
     string_t tunname; /* localname<->remotename by default, used in logs */
     string_t address; /* DNS name for bootstrapping, optional */
     int remoteport; /* Port for bootstrapping, optional */
     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;
     struct netlink_if *netlink;
     struct comm_if **comms;
     int ncomms;
@@ -266,6 +267,7 @@ struct site {
     uint32_t state;
     uint64_t now; /* Most recently seen time */
     bool_t allow_send_prod;
     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;
 
     /* The currently established session */
     struct data_key current;
@@ -282,6 +284,7 @@ struct site {
        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;
        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_if *chosen_transform;
     uint32_t setup_session_id;
     transport_peers setup_peers;
@@ -297,35 +300,75 @@ struct site {
     struct transform_inst_if *new_transform; /* For key setup/verify */
 };
 
     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;
 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);
     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;
+
+    va_list ap;
+    va_start(ap,fmt);
+
+    slilog_part(st->log,class,"%s: ",st->tunname);
+    vslilog_part(st->log,class,fmt,ap);
 
 
-       vsnprintf(buf,sizeof(buf),msg,ap);
-       slilog(st->log,class,"%s: %s",st->tunname,buf);
+    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);
 }
 
     va_end(ap);
 }
 
@@ -386,6 +429,14 @@ static void dispose_transform(struct transform_inst_if **transform_var)
     type=buf_unprepend_uint32((b)); \
     if (type!=(t)) return False; } while(0)
 
     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 {
     int32_t len;
     uint8_t *name;
@@ -399,6 +450,7 @@ struct msg {
     struct parsedname remote;
     struct parsedname local;
     uint32_t remote_capabilities;
     struct parsedname remote;
     struct parsedname local;
     uint32_t remote_capabilities;
+    uint16_t remote_mtu;
     int capab_transformnum;
     uint8_t *nR;
     uint8_t *nL;
     int capab_transformnum;
     uint8_t *nR;
     uint8_t *nL;
@@ -487,6 +539,9 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what)
     if ((st->local_capabilities & CAPAB_EARLY) || (type != LABEL_MSG1)) {
        buf_append_uint32(&st->buffer,st->local_capabilities);
     }
     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);
     append_string_xinfo_done(&st->buffer,&xia);
 
     buf_append_string(&st->buffer,st->remotename);
@@ -542,10 +597,15 @@ static bool_t unpick_msg(struct site *st, uint32_t type,
     CHECK_TYPE(msg,type);
     if (!unpick_name(msg,&m->remote)) return False;
     m->remote_capabilities=0;
     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 (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);
     if (!unpick_name(msg,&m->local)) return False;
     if (type==LABEL_PROD) {
        CHECK_EMPTY(msg);
@@ -703,12 +763,34 @@ static bool_t generate_msg3(struct site *st)
                        "site:MSG3");
 }
 
                        "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;
 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);
     cstring_t err;
 
     assert(msgtype==LABEL_MSG3 || msgtype==LABEL_MSG3BIS);
@@ -741,19 +823,8 @@ static bool_t process_msg3(struct site *st, struct buffer_if *msg3,
  transform_found:
     st->chosen_transform=ti;
 
  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;
        return False;
-    }
-    free(hash);
 
     /* Terminate their DH public key with a '0' */
     m.pk[m.pklen]=0;
 
     /* Terminate their DH public key with a '0' */
     m.pk[m.pklen]=0;
@@ -777,8 +848,6 @@ static bool_t process_msg4(struct site *st, struct buffer_if *msg4,
                           const struct comm_addr *src)
 {
     struct msg m;
                           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;
     cstring_t err;
 
     if (!unpick_msg(st,LABEL_MSG4,msg4,&m)) return False;
@@ -787,19 +856,8 @@ static bool_t process_msg4(struct site *st, struct buffer_if *msg4,
        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;
        return False;
-    }
-    free(hash);
 
     /* Terminate their DH public key with a '0' */
     m.pk[m.pklen]=0;
 
     /* Terminate their DH public key with a '0' */
     m.pk[m.pklen]=0;
@@ -1060,7 +1118,7 @@ static bool_t send_msg(struct site *st)
        st->retries--;
        return True;
     } else if (st->state==SITE_SENTMSG5) {
        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 */
        /* 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 */
@@ -1078,7 +1136,7 @@ static bool_t send_msg(struct site *st)
        enter_state_wait(st);
        return False;
     } else {
        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;
            "(in state %s)",state_name(st->state));
        enter_state_wait(st);
        return False;
@@ -1090,6 +1148,8 @@ static void site_resolve_callback(void *sst, struct in_addr *address)
     struct site *st=sst;
     struct comm_addr ca_buf, *ca_use;
 
     struct site *st=sst;
     struct comm_addr ca_buf, *ca_use;
 
+    st->resolving=False;
+
     if (st->state!=SITE_RESOLVE) {
        slog(st,LOG_UNEXPECTED,"site_resolve_callback called unexpectedly");
        return;
     if (st->state!=SITE_RESOLVE) {
        slog(st,LOG_UNEXPECTED,"site_resolve_callback called unexpectedly");
        return;
@@ -1117,6 +1177,7 @@ static void site_resolve_callback(void *sst, struct in_addr *address)
 static bool_t initiate_key_setup(struct site *st, cstring_t reason,
                                 const struct comm_addr *prod_hint)
 {
 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) {
     if (st->state!=SITE_RUN) return False;
     slog(st,LOG_SETUP_INIT,"initiating key exchange (%s)",reason);
     if (st->address) {
@@ -1149,7 +1210,15 @@ static void activate_new_key(struct site *st)
     transport_peers_copy(st,&st->peers,&st->setup_peers);
     st->current.remote_session_id=st->setup_session_id;
 
     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);
 }
 
     enter_state_run(st);
 }
 
@@ -1219,14 +1288,33 @@ static void enter_state_run(struct site *st)
     set_link_quality(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;
+
+    /* 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)
 {
 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;
     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)
 }
 
 static bool_t enter_new_state(struct site *st, uint32_t next)
@@ -1769,6 +1857,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     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->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->mobile_peer_expiry= dict_read_number(
        dict,"mobile-peer-expiry",False,"site",loc,DEFAULT_MOBILE_PEER_EXPIRY);
@@ -1795,6 +1884,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     st->log_events=string_list_to_word(dict_lookup(dict,"log-events"),
                                       log_event_table,"site");
 
     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,
     st->allow_send_prod=0;
 
     st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5,
@@ -1837,7 +1927,10 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     }
 
     /* We need to register the remote networks with the netlink device */
     }
 
     /* We need to register the remote networks with the netlink device */
-    st->netlink->reg(st->netlink->st, site_outgoing, st, 0);
+    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);
     
     for (i=0; i<st->ncomms; i++)
        st->comms[i]->request_notify(st->comms[i]->st, st, site_incoming);
@@ -1882,14 +1975,14 @@ static void transport_peers_debug(struct site *st, transport_peers *dst,
         i++, (argp+=stride?stride:sizeof(*args))) {
        const struct comm_addr *ca=(void*)argp;
        slog(st, LOG_PEER_ADDRS, " args: addrs[%d]=%s",
         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",
     }
     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);
     }
 }
             (unsigned long)diff.tv_sec, (unsigned long)diff.tv_usec);
     }
 }
@@ -1926,7 +2019,7 @@ static void transport_record_peer(struct site *st, transport_peers *peers,
 
     changed=1;
     if (peers->npeers==st->transport_peers_max)
 
     changed=1;
     if (peers->npeers==st->transport_peers_max)
-       slot=st->transport_peers_max;
+       slot=st->transport_peers_max-1;
     else
        slot=peers->npeers++;
 
     else
        slot=peers->npeers++;