chiark / gitweb /
site: transport peers: Notes on multi-address-family (IPv6) support
[secnet.git] / site.c
diff --git a/site.c b/site.c
index 379ab0d96976aefc90f5928c60a6e5b492165144..f035e79e08ea1c11890a4776e5deebd210884fdb 100644 (file)
--- a/site.c
+++ b/site.c
@@ -143,44 +143,80 @@ static struct flagstr log_event_table[]={
 /***** TRANSPORT PEERS declarations *****/
 
 /* Details of "mobile peer" semantics:
+
+ | Note: this comment is wishful thinking right now.  It will be
+ | implemented in subsequent commits.
+
+   - We use the same data structure for the different configurations,
+     but manage it with different algorithms.
    
-   - We record mobile_peers_max peer address/port numbers ("peers")
-     for key setup, and separately mobile_peers_max for data
-     transfer.  If these lists fill up, we retain the newest peers.
-     (For non-mobile peers we only record one of each.)
+   - We record up to mobile_peers_max peer address/port numbers
+     ("peers") for key setup, and separately up to mobile_peers_max
+     for data transfer.
+
+   - In general, we make a new set of addrs (see below) when we start
+     a new key exchange; the key setup addrs become the data transport
+     addrs when key setup complets.
+
+   If our peer is mobile:
+
+   - We send to all recent addresses of incoming packets, plus
+     initially all configured addresses (which we also expire).
 
-   - Outgoing packets are sent to every recorded peer in the
-     applicable list.
+   - So, we record addrs of good incoming packets, as follows:
+      1. expire any peers last seen >120s ("mobile-peer-expiry") ago
+      2. add the peer of the just received packet to the applicable list
+         (possibly evicting the oldest entries to make room)
+     NB that we do not expire peers until an incoming packet arrives.
 
-   - Data transfer peers are straightforward: whenever we successfully
-     process a data packet, we record the peer.  Also, whenever we
-     successfully complete a key setup, we merge the key setup
+   - If the peer has a configured address or name, we record them the
+     same way, but only as a result of our own initiation of key
+     setup.  (We might evict some incoming packet addrs to make room.)
+
+   - The default number of addrs to keep is 3, or 4 if we have a
+     configured name or address.  That's space for two configured
+     addresses (one IPv6 and one IPv4), plus two received addresses.
+
+   - Outgoing packets are sent to every recorded address in the
+     applicable list.  Any unsupported[1] addresses are deleted from
+     the list right away.  (This should only happen to configured
+     addresses, of course, but there is no need to check that.)
+
+   - When we successfully complete a key setup, we merge the key setup
      peers into the data transfer peers.
 
-     (For "non-mobile" peers we simply copy the peer used for
-     successful key setup, and don't change the peer otherwise.)
+   [1] An unsupported address is one for whose AF we don't have a
+     socket (perhaps because we got EAFNOSUPPORT or some such) or for
+     which sendto gives ENETUNREACH.
+
+   If neither end is mobile:
 
-   - Key setup peers are slightly more complicated.
+   - When peer initiated the key exchange, we use the incoming packet
+     address.
 
-     Whenever we receive and successfully process a key exchange
-     packet, we record the peer.
+   - When we initiate the key exchange, we try configured addresses
+     until we get one which isn't unsupported then fixate on that.
 
-     Whenever we try to initiate a key setup, we copy the list of data
-     transfer peers and use it for key setup.  But we also look to see
-     if the config supplies an address and port number and if so we
-     add that as a key setup peer (possibly evicting one of the data
-     transfer peers we just copied).
+   - When we complete a key setup, we replace the data transport peers
+     with those from the key setup.
 
-     (For "non-mobile" peers, if we if we have a configured peer
-     address and port, we always use that; otherwise if we have a
-     current data peer address we use that; otherwise we do not
-     attempt to initiate a key setup for lack of a peer address.)
+   If we are mobile:
 
-   "Record the peer" means
-    1. expire any peers last seen >120s ("mobile-peer-expiry") ago
-    2. add the peer of the just received packet to the applicable list
-       (possibly evicting older entries)
-   NB that we do not expire peers until an incoming packet arrives.
+   - We can't tell when local network setup changes so we can't cache
+     the unsupported addrs and completely remove the spurious calls to
+     sendto, but we can optimise things a bit by deprioritising addrs
+     which seem to be unsupported.
+
+   - Use only configured addresses.  (Except, that if our peer
+     initiated a key exchange we use the incoming packet address until
+     our name resolution completes.)
+
+   - When we send a packet, try each address in turn; if addr
+     supported, put that address to the end of the list for future
+     packets, and go onto the next address.
+
+   - When we complete a key setup, we replace the data transport peers
+     with those from the key setup.
 
    */
 
@@ -198,18 +234,35 @@ typedef struct {
     transport_peer peers[MAX_MOBILE_PEERS_MAX];
 } transport_peers;
 
+/* Basic operations on transport peer address sets */
 static void transport_peers_clear(struct site *st, transport_peers *peers);
 static int transport_peers_valid(transport_peers *peers);
 static void transport_peers_copy(struct site *st, transport_peers *dst,
                                 const transport_peers *src);
 
+/* Record address of incoming setup packet; resp. data packet. */
 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);
+
+/* Initialise the setup addresses.  Called before we send the first
+ * packet in a key exchange.  If we are the initiator, as a result of
+ * resolve completing (or being determined not to be relevant) or an
+ * incoming PROD; if we are the responder, as a result of the MSG1. */
 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 *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);
+        const struct comm_addr *incoming_packet_addr /* 0 if none */);
+
+/* Called if we are the responder in a key setup, when the resolve
+ * completes.  transport_compute_setupinit_peers will hvae been called
+ * earlier.  If _complete is called, we are still doing the key setup
+ * (and we should use the new values for both the rest of the key
+ * setup and the ongoing data exchange); if _tardy is called, the key
+ * setup is done (either completed or not) and only the data peers are
+ * relevant */
+static void transport_resolve_complete(struct site *st,
+                                      const struct comm_addr *a);
+static void transport_resolve_complete_tardy(struct site *st,
+                                            const struct comm_addr *ca_use);
 
 static void transport_xmit(struct site *st, transport_peers *peers,
                           struct buffer_if *buf, bool_t candebug);
@@ -545,9 +598,9 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what)
     append_string_xinfo_done(&st->buffer,&xia);
 
     buf_append_string(&st->buffer,st->remotename);
-    memcpy(buf_append(&st->buffer,NONCELEN),st->localN,NONCELEN);
+    BUF_ADD_OBJ(append,&st->buffer,st->localN);
     if (type==LABEL_MSG1) return True;
-    memcpy(buf_append(&st->buffer,NONCELEN),st->remoteN,NONCELEN);
+    BUF_ADD_OBJ(append,&st->buffer,st->remoteN);
     if (type==LABEL_MSG2) return True;
 
     if (hacky_par_mid_failnow()) return False;
@@ -1182,8 +1235,7 @@ static void site_resolve_callback(void *sst, struct in_addr *address)
             * 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");
+           transport_resolve_complete(st,ca_use);
        } else if (st->local_mobile) {
            /* We can't let this rest because we may have a peer
             * address which will break in the future. */
@@ -1201,7 +1253,7 @@ static void site_resolve_callback(void *sst, struct in_addr *address)
        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");
+           transport_resolve_complete_tardy(st,ca_use);
        } else if (st->local_mobile) {
            /* Not very good.  We should queue (another) renegotiation
             * so that we can update the peer address. */
@@ -1325,8 +1377,8 @@ static void enter_state_run(struct site *st)
 
     st->setup_session_id=0;
     transport_peers_clear(st,&st->setup_peers);
-    memset(st->localN,0,NONCELEN);
-    memset(st->remoteN,0,NONCELEN);
+    FILLZERO(st->localN);
+    FILLZERO(st->remoteN);
     dispose_transform(&st->new_transform);
     memset(st->dhsecret,0,st->dh->len);
     memset(st->sharedsecret,0,st->sharedsecretlen);
@@ -1627,11 +1679,11 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf,
        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");
+           transport_compute_setupinit_peers(st,0,source);
            if (process_msg1(st,buf,source,&named_msg)) {
                slog(st,LOG_SETUP_INIT,"key setup initiated by peer");
                bool_t entered=enter_new_state(st,SITE_SENTMSG2);
-               if (entered && st->address)
+               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);
@@ -1693,7 +1745,7 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf,
               because our peer's forgotten the key */
            if (get_uint32(buf->start+4)==st->current.remote_session_id) {
                bool_t initiated;
-               initiated = initiate_key_setup(st,"received a NAK",0);
+               initiated = initiate_key_setup(st,"received a NAK",source);
                if (!initiated) generate_send_prod(st,source);
            } else {
                slog(st,LOG_SEC,"bad incoming NAK");
@@ -2091,16 +2143,16 @@ static void transport_record_peer(struct site *st, transport_peers *peers,
 
 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 *prod_hint_addr /* 0 if none */) {
+        const struct comm_addr *incoming_packet_addr /* 0 if none */) {
 
-    if (!configured_addr && !prod_hint_addr &&
+    if (!configured_addr && !incoming_packet_addr &&
        !transport_peers_valid(&st->peers))
        return False;
 
     slog(st,LOG_SETUP_INIT,
         "using:%s%s %d old peer address(es)",
         configured_addr ? " configured address;" : "",
-        prod_hint_addr ? " PROD hint address;" : "",
+        incoming_packet_addr ? " incoming packet address;" : "",
         st->peers.npeers);
 
     /* Non-mobile peers have st->peers.npeers==0 or ==1, since they
@@ -2111,8 +2163,9 @@ static bool_t transport_compute_setupinit_peers(struct site *st,
 
     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 (incoming_packet_addr)
+       transport_record_peer(st,&st->setup_peers,incoming_packet_addr,
+                             "incoming");
 
     if (configured_addr)
        transport_record_peer(st,&st->setup_peers,configured_addr,"setupinit");
@@ -2145,6 +2198,17 @@ static void transport_peers_copy(struct site *st, transport_peers *dst,
                          src->npeers, &src->peers->addr, sizeof(*src->peers));
 }
 
+static void transport_resolve_complete(struct site *st,
+                                      const struct comm_addr *ca_use) {
+    transport_record_peer(st,&st->peers,ca_use,"resolved data");
+    transport_record_peer(st,&st->setup_peers,ca_use,"resolved setup");
+}
+
+static void transport_resolve_complete_tardy(struct site *st,
+                                            const struct comm_addr *ca_use) {
+    transport_record_peer(st,&st->peers,ca_use,"resolved tardily");
+}
+
 void transport_xmit(struct site *st, transport_peers *peers,
                    struct buffer_if *buf, bool_t candebug) {
     int slot;