chiark / gitweb /
Protocol change: Initiate key setup on incoming packets, not outgoing ones
[secnet.git] / site.c
diff --git a/site.c b/site.c
index 8a2d69ae5b5f713b28ab8099dea4c07b47e9657e..5a4b64ab8a76db18b8575c52e34a3c29f8b02978 100644 (file)
--- a/site.c
+++ b/site.c
@@ -17,6 +17,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <limits.h>
+#include <assert.h>
 #include <sys/socket.h>
 
 #include <sys/mman.h>
 
 #define SETUP_BUFFER_LEN 2048
 
-#define DEFAULT_KEY_LIFETIME 3600000 /* One hour */
-#define DEFAULT_KEY_RENEGOTIATE_GAP 300000 /* Five minutes */
+#define DEFAULT_KEY_LIFETIME                  (3600*1000) /* [ms] */
+#define DEFAULT_KEY_RENEGOTIATE_GAP           (5*60*1000) /* [ms] */
 #define DEFAULT_SETUP_RETRIES 5
-#define DEFAULT_SETUP_TIMEOUT 2000
-#define DEFAULT_WAIT_TIME 20000
+#define DEFAULT_SETUP_RETRY_INTERVAL             (2*1000) /* [ms] */
+#define DEFAULT_WAIT_TIME                       (20*1000) /* [ms] */
 
 /* Each site can be in one of several possible states. */
 
@@ -145,15 +146,14 @@ struct site {
     struct dh_if *dh;
     struct hash_if *hash;
 
+    uint32_t index; /* Index of this site */
     int32_t setup_retries; /* How many times to send setup packets */
-    int32_t setup_timeout; /* Initial timeout for setup packets */
+    int32_t setup_retry_interval; /* Initial timeout for setup packets */
     int32_t wait_timeout; /* How long to wait if setup unsuccessful */
     int32_t key_lifetime; /* How long a key lasts once set up */
     int32_t key_renegotiate_time; /* If we see traffic (or a keepalive)
                                      after this time, initiate a new
                                      key exchange */
-    bool_t keepalive; /* Send keepalives to detect peer failure (not yet
-                        implemented) */
 
     uint8_t *setupsig; /* Expected signature of incoming MSG1 packets */
     int32_t setupsiglen; /* Allows us to discard packets quickly if
@@ -172,7 +172,7 @@ struct site {
     bool_t current_valid;
     uint64_t current_key_timeout; /* End of life of current key */
     uint64_t renegotiate_key_time; /* When we can negotiate a new key */
-    struct sockaddr_in peer; /* Current address of peer */
+    struct comm_addr peer; /* Current address of peer */
     bool_t peer_valid; /* Peer address becomes invalid when key times out,
                          but only if we have a DNS name for our peer */
 
@@ -183,7 +183,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 setup_session_id;
-    struct sockaddr_in setup_peer;
+    struct comm_addr setup_peer;
     uint8_t localN[NONCELEN]; /* Nonces for key exchange */
     uint8_t remoteN[NONCELEN];
     struct buffer_if buffer; /* Current outgoing key exchange packet */
@@ -217,7 +217,7 @@ static void slog(struct site *st, uint32_t event, cstring_t msg, ...)
        default: class=M_ERR; break;
        }
 
-       vsnprintf(buf,240,msg,ap);
+       vsnprintf(buf,sizeof(buf),msg,ap);
        st->log->log(st->log->st,class,"%s: %s",st->tunname,buf);
     }
     va_end(ap);
@@ -249,10 +249,10 @@ struct msg {
     uint8_t *nR;
     uint8_t *nL;
     int32_t pklen;
-    uint8_t *pk;
+    char *pk;
     int32_t hashlen;
     int32_t siglen;
-    uint8_t *sig;
+    char *sig;
 };
 
 /* Build any of msg1 to msg4. msg5 and msg6 are built from the inside
@@ -268,7 +268,7 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what)
     buffer_init(&st->buffer,0);
     buf_append_uint32(&st->buffer,
        (type==LABEL_MSG1?0:st->setup_session_id));
-    buf_append_uint32(&st->buffer,(uint32_t)st);
+    buf_append_uint32(&st->buffer,st->index);
     buf_append_uint32(&st->buffer,type);
     buf_append_string(&st->buffer,st->localname);
     buf_append_string(&st->buffer,st->remotename);
@@ -372,7 +372,7 @@ static bool_t generate_msg1(struct site *st)
 }
 
 static bool_t process_msg1(struct site *st, struct buffer_if *msg1,
-                          struct sockaddr_in *src)
+                          const struct comm_addr *src)
 {
     struct msg m;
 
@@ -395,7 +395,7 @@ static bool_t generate_msg2(struct site *st)
 }
 
 static bool_t process_msg2(struct site *st, struct buffer_if *msg2,
-                          struct sockaddr_in *src)
+                          const struct comm_addr *src)
 {
     struct msg m;
     cstring_t err;
@@ -419,7 +419,7 @@ static bool_t generate_msg3(struct site *st)
 }
 
 static bool_t process_msg3(struct site *st, struct buffer_if *msg3,
-                          struct sockaddr_in *src)
+                          const struct comm_addr *src)
 {
     struct msg m;
     uint8_t *hash;
@@ -470,7 +470,7 @@ static bool_t generate_msg4(struct site *st)
 }
 
 static bool_t process_msg4(struct site *st, struct buffer_if *msg4,
-                          struct sockaddr_in *src)
+                          const struct comm_addr *src)
 {
     struct msg m;
     uint8_t *hash;
@@ -542,7 +542,7 @@ static bool_t generate_msg5(struct site *st)
     st->new_transform->forwards(st->new_transform->st,&st->buffer,
                                &transform_err);
     buf_prepend_uint32(&st->buffer,LABEL_MSG5);
-    buf_prepend_uint32(&st->buffer,(uint32_t)st);
+    buf_prepend_uint32(&st->buffer,st->index);
     buf_prepend_uint32(&st->buffer,st->setup_session_id);
 
     st->retries=st->setup_retries;
@@ -550,7 +550,7 @@ static bool_t generate_msg5(struct site *st)
 }
 
 static bool_t process_msg5(struct site *st, struct buffer_if *msg5,
-                          struct sockaddr_in *src)
+                          const struct comm_addr *src)
 {
     struct msg0 m;
     cstring_t transform_err;
@@ -591,7 +591,7 @@ static bool_t generate_msg6(struct site *st)
     st->new_transform->forwards(st->new_transform->st,&st->buffer,
                                &transform_err);
     buf_prepend_uint32(&st->buffer,LABEL_MSG6);
-    buf_prepend_uint32(&st->buffer,(uint32_t)st);
+    buf_prepend_uint32(&st->buffer,st->index);
     buf_prepend_uint32(&st->buffer,st->setup_session_id);
 
     st->retries=1; /* Peer will retransmit MSG5 if this packet gets lost */
@@ -599,7 +599,7 @@ static bool_t generate_msg6(struct site *st)
 }
 
 static bool_t process_msg6(struct site *st, struct buffer_if *msg6,
-                          struct sockaddr_in *src)
+                          const struct comm_addr *src)
 {
     struct msg0 m;
     cstring_t transform_err;
@@ -627,7 +627,7 @@ static bool_t process_msg6(struct site *st, struct buffer_if *msg6,
 }
 
 static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
-                          struct sockaddr_in *src)
+                          const struct comm_addr *src)
 {
     struct msg0 m;
     cstring_t transform_err;
@@ -653,12 +653,13 @@ static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
        /* We must forget about the current session. */
        delete_key(st,"request from peer",LOG_SEC);
        return True;
-       break;
     case LABEL_MSG9:
        /* Deliver to netlink layer */
        st->netlink->deliver(st->netlink->st,msg0);
+       /* 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");
        return True;
-       break;
     default:
        slog(st,LOG_SEC,"incoming encrypted message of type %08x "
             "(unknown)",type);
@@ -668,7 +669,7 @@ static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
 }
 
 static void dump_packet(struct site *st, struct buffer_if *buf,
-                       struct sockaddr_in *addr, bool_t incoming)
+                       const struct comm_addr *addr, bool_t incoming)
 {
     uint32_t dest=ntohl(*(uint32_t *)buf->start);
     uint32_t source=ntohl(*(uint32_t *)(buf->start+4));
@@ -690,7 +691,7 @@ static bool_t send_msg(struct site *st)
     if (st->retries>0) {
        dump_packet(st,&st->buffer,&st->setup_peer,False);
        st->comm->sendmsg(st->comm->st,&st->buffer,&st->setup_peer);
-       st->timeout=st->now+st->setup_timeout;
+       st->timeout=st->now+st->setup_retry_interval;
        st->retries--;
        return True;
     } else {
@@ -711,9 +712,10 @@ static void site_resolve_callback(void *sst, struct in_addr *address)
     }
     if (address) {
        memset(&st->setup_peer,0,sizeof(st->setup_peer));
-       st->setup_peer.sin_family=AF_INET;
-       st->setup_peer.sin_port=htons(st->remoteport);
-       st->setup_peer.sin_addr=*address;
+       st->setup_peer.comm=st->comm;
+       st->setup_peer.sin.sin_family=AF_INET;
+       st->setup_peer.sin.sin_port=htons(st->remoteport);
+       st->setup_peer.sin.sin_addr=*address;
        enter_new_state(st,SITE_SENTMSG1);
     } else {
        /* Resolution failed */
@@ -876,7 +878,7 @@ static bool_t enter_new_state(struct site *st, uint32_t next)
     r= gen(st) && send_msg(st);
 
     hacky_par_end(&r,
-                 st->setup_retries, st->setup_timeout,
+                 st->setup_retries, st->setup_retry_interval,
                  send_msg, st);
     
     if (r) {
@@ -909,8 +911,9 @@ static bool_t send_msg7(struct site *st, cstring_t reason)
        st->current_transform->forwards(st->current_transform->st,
                                        &st->buffer, &transform_err);
        buf_prepend_uint32(&st->buffer,LABEL_MSG0);
-       buf_prepend_uint32(&st->buffer,(uint32_t)st);
+       buf_prepend_uint32(&st->buffer,st->index);
        buf_prepend_uint32(&st->buffer,st->remote_session_id);
+       dump_packet(st,&st->buffer,&st->peer,False);
        st->comm->sendmsg(st->comm->st,&st->buffer,&st->peer);
        BUF_FREE(&st->buffer);
        return True;
@@ -926,7 +929,6 @@ static void enter_state_wait(struct site *st)
     slog(st,LOG_STATE,"entering state WAIT");
     st->timeout=st->now+st->wait_timeout;
     st->state=SITE_WAIT;
-    st->peer_valid=False;
     set_link_quality(st);
     BUF_FREE(&st->buffer); /* will have had an outgoing packet in it */
     /* XXX Erase keys etc. */
@@ -1005,14 +1007,11 @@ static void site_outgoing(void *sst, struct buffer_if *buf)
            st->current_transform->forwards(st->current_transform->st,
                                            buf, &transform_err);
            buf_prepend_uint32(buf,LABEL_MSG0);
-           buf_prepend_uint32(buf,(uint32_t)st);
+           buf_prepend_uint32(buf,st->index);
            buf_prepend_uint32(buf,st->remote_session_id);
            st->comm->sendmsg(st->comm->st,buf,&st->peer);
        }
        BUF_FREE(buf);
-       /* See whether we should start negotiating a new key */
-       if (st->now > st->renegotiate_key_time)
-           initiate_key_setup(st,"outgoing packet in renegotiation window");
        return;
     }
 
@@ -1024,7 +1023,7 @@ static void site_outgoing(void *sst, struct buffer_if *buf)
 /* This function is called by the communication device to deliver
    packets from our peers. */
 static bool_t site_incoming(void *sst, struct buffer_if *buf,
-                           struct sockaddr_in *source)
+                           const struct comm_addr *source)
 {
     struct site *st=sst;
     uint32_t dest=ntohl(*(uint32_t *)buf->start);
@@ -1078,7 +1077,7 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf,
        }
        return False; /* Not for us. */
     }
-    if (dest==(uint32_t)st) {
+    if (dest==st->index) {
        /* Explicitly addressed to us */
        uint32_t msgtype=ntohl(get_uint32(buf->start+8));
        if (msgtype!=LABEL_MSG0) dump_packet(st,buf,source,True);
@@ -1186,6 +1185,7 @@ static void site_phase_hook(void *sst, uint32_t newphase)
 static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
                          list_t *args)
 {
+    static uint32_t index_sequence;
     struct site *st;
     item_t *item;
     dict_t *dict;
@@ -1217,6 +1217,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
        free(st);
        return NULL;
     }
+    assert(index_sequence < 0xffffffffUL);
+    st->index = ++index_sequence;
     st->netlink=find_cl_if(dict,"link",CL_NETLINK,True,"site",loc);
     st->comm=find_cl_if(dict,"comm",CL_COMM,True,"site",loc);
     st->resolver=find_cl_if(dict,"resolver",CL_RESOLVER,True,"site",loc);
@@ -1236,25 +1238,24 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc);
     st->hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc);
 
-    st->key_lifetime=dict_read_number(
-       dict,"key-lifetime",False,"site",loc,DEFAULT_KEY_LIFETIME);
-    if (st->key_lifetime < DEFAULT_KEY_RENEGOTIATE_GAP)
+#define DEFAULT(D) DEFAULT_##D
+#define CFG_NUMBER(k,D) dict_read_number(dict,(k),False,"site",loc,DEFAULT(D));
+
+    st->key_lifetime=         CFG_NUMBER("key-lifetime",  KEY_LIFETIME);
+    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);
+
+    if (st->key_lifetime < DEFAULT(KEY_RENEGOTIATE_GAP)*2)
        st->key_renegotiate_time=st->key_lifetime/2;
     else
-       st->key_renegotiate_time=st->key_lifetime-DEFAULT_KEY_RENEGOTIATE_GAP;
-    st->setup_retries=dict_read_number(
-       dict,"setup-retries",False,"site",loc,DEFAULT_SETUP_RETRIES);
-    st->setup_timeout=dict_read_number(
-       dict,"setup-timeout",False,"site",loc,DEFAULT_SETUP_TIMEOUT);
-    st->wait_timeout=dict_read_number(
-       dict,"wait-time",False,"site",loc,DEFAULT_WAIT_TIME);
+       st->key_renegotiate_time=st->key_lifetime-DEFAULT(KEY_RENEGOTIATE_GAP);
     st->key_renegotiate_time=dict_read_number(
-       dict,"renegotiate-time",False,"site",loc,st->key_lifetime);
+       dict,"renegotiate-time",False,"site",loc,st->key_renegotiate_time);
     if (st->key_renegotiate_time > st->key_lifetime) {
        cfgfatal(loc,"site",
                 "renegotiate-time must be less than key-lifetime\n");
     }
-    st->keepalive=dict_read_bool(dict,"keepalive",False,"site",loc,False);
 
     st->log_events=string_list_to_word(dict_lookup(dict,"log-events"),
                                       log_event_table,"site");