X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=secnet.git;a=blobdiff_plain;f=site.c;h=3ac23722e650c854b0fdcd22e9c1b4d451575986;hp=9b079806e9d58dc514c48dbf58af28b1001a4265;hb=53f4e66603bcc2d21c70ad7a69db5fda6da5d645;hpb=51fc5a74420dc89f160dcaf67c04c246e80cf326 diff --git a/site.c b/site.c index 9b07980..3ac2372 100644 --- a/site.c +++ b/site.c @@ -40,7 +40,6 @@ #define DEFAULT_MOBILE_WAIT_TIME (10*1000) /* [ms] */ #define DEFAULT_MOBILE_PEER_EXPIRY (2*60) /* [s] */ -#define DEFAULT_MOBILE_PEERS_MAX 3 /* send at most this many copies (default) */ /* Each site can be in one of several possible states. */ @@ -144,9 +143,6 @@ static struct flagstr log_event_table[]={ /* 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. @@ -220,8 +216,6 @@ static struct flagstr log_event_table[]={ */ -#define MAX_MOBILE_PEERS_MAX 5 /* send at most this many copies, compiled max */ - typedef struct { struct timeval last; struct comm_addr addr; @@ -231,7 +225,7 @@ typedef struct { /* configuration information */ /* runtime information */ int npeers; - transport_peer peers[MAX_MOBILE_PEERS_MAX]; + transport_peer peers[MAX_PEER_ADDRS]; } transport_peers; /* Basic operations on transport peer address sets */ @@ -249,7 +243,8 @@ static void transport_data_msgok(struct site *st, const struct comm_addr *a); * 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 *configured_addrs /* 0 if none or not found */, + int n_configured_addrs /* 0 if none or not found */, const struct comm_addr *incoming_packet_addr /* 0 if none */); /* Called if we are the responder in a key setup, when the resolve @@ -260,9 +255,9 @@ static bool_t transport_compute_setupinit_peers(struct site *st, * 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); + const struct comm_addr *addrs, int naddrs); static void transport_resolve_complete_tardy(struct site *st, - const struct comm_addr *ca_use); + const struct comm_addr *addrs, int naddrs); static void transport_xmit(struct site *st, transport_peers *peers, struct buffer_if *buf, bool_t candebug); @@ -285,7 +280,7 @@ struct site { 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 */ + cstring_t *addresses; /* DNS name or address(es) for bootstrapping, optional */ int remoteport; /* Port for bootstrapping, optional */ uint32_t mtu_target; struct netlink_if *netlink; @@ -320,7 +315,10 @@ struct site { uint32_t state; uint64_t now; /* Most recently seen time */ bool_t allow_send_prod; - bool_t resolving; + int resolving_count; + int resolving_n_results_all; + int resolving_n_results_stored; + struct comm_addr resolving_results[MAX_PEER_ADDRS]; /* The currently established session */ struct data_key current; @@ -435,6 +433,7 @@ 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 void decrement_resolving_count(struct site *st, int by); static bool_t enter_new_state(struct site *st,uint32_t next); static void enter_state_wait(struct site *st); static void activate_new_key(struct site *st); @@ -1195,30 +1194,57 @@ static bool_t send_msg(struct site *st) } } -static void site_resolve_callback(void *sst, struct in_addr *address) +static void site_resolve_callback(void *sst, const struct comm_addr *addrs, + int stored_naddrs, int all_naddrs, + const char *address, const char *failwhy) { struct site *st=sst; - struct comm_addr ca_buf, *ca_use; - - st->resolving=False; - - if (address) { - FILLZERO(ca_buf); - ca_buf.comm=st->comms[0]; - ca_buf.sin.sin_family=AF_INET; - 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));; + + if (!stored_naddrs) { + slog(st,LOG_ERROR,"resolution of %s failed: %s",address,failwhy); } else { - slog(st,LOG_ERROR,"resolution of %s failed",st->address); - ca_use=0; + slog(st,LOG_PEER_ADDRS,"resolution of %s completed, %d addrs, eg: %s", + address, all_naddrs, comm_addr_to_string(&addrs[0]));; + + int space=st->transport_peers_max-st->resolving_n_results_stored; + int n_tocopy=MIN(stored_naddrs,space); + COPY_ARRAY(st->resolving_results + st->resolving_n_results_stored, + addrs, + n_tocopy); + st->resolving_n_results_stored += n_tocopy; + st->resolving_n_results_all += all_naddrs; + } + + decrement_resolving_count(st,1); +} + +static void decrement_resolving_count(struct site *st, int by) +{ + assert(st->resolving_count>0); + st->resolving_count-=by; + + if (st->resolving_count) + return; + + /* OK, we are done with them all. Handle combined results. */ + + const struct comm_addr *addrs=st->resolving_results; + int naddrs=st->resolving_n_results_stored; + assert(naddrs<=st->transport_peers_max); + + if (naddrs) { + if (naddrs != st->resolving_n_results_all) { + slog(st,LOG_SETUP_INIT,"resolution of supplied addresses/names" + " yielded too many results (%d > %d), some ignored", + st->resolving_n_results_all, naddrs); + } + slog(st,LOG_STATE,"resolution completed, %d addrs, eg: %s", + naddrs, comm_addr_to_string(&addrs[0]));; } switch (st->state) { case SITE_RESOLVE: - if (transport_compute_setupinit_peers(st,ca_use,0)) { + if (transport_compute_setupinit_peers(st,addrs,naddrs,0)) { enter_new_state(st,SITE_SENTMSG1); } else { /* Can't figure out who to try to to talk to */ @@ -1230,38 +1256,36 @@ static void site_resolve_callback(void *sst, struct in_addr *address) case SITE_SENTMSG1: case SITE_SENTMSG2: case SITE_SENTMSG3: case SITE_SENTMSG4: case SITE_SENTMSG5: - if (ca_use) { + if (naddrs) { /* 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_resolve_complete(st,ca_use); + transport_resolve_complete(st,addrs,naddrs); } 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); + slog(st,LOG_SETUP_INIT,"resolution failed: " + "abandoning key exchange"); enter_state_wait(st); } else { - slog(st,LOG_SETUP_INIT,"resolution of %s failed: " + slog(st,LOG_SETUP_INIT,"resolution failed: " " continuing to use source address of peer's packets" - " for key exchange and ultimately data", - st->address); + " for key exchange and ultimately data"); } break; case SITE_RUN: - if (ca_use) { - slog(st,LOG_SETUP_INIT,"resolution of %s completed tardily," - " updating peer address(es)",st->address); - transport_resolve_complete_tardy(st,ca_use); + if (naddrs) { + slog(st,LOG_SETUP_INIT,"resolution completed tardily," + " updating peer address(es)"); + transport_resolve_complete_tardy(st,addrs,naddrs); } 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); + slog(st,LOG_SETUP_INIT,"resolution failed: " + " continuing to use source address of peer's packets"); } break; case SITE_WAIT: @@ -1277,10 +1301,10 @@ static bool_t initiate_key_setup(struct site *st, cstring_t reason, /* 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"); + if (st->addresses) { + slog(st,LOG_SETUP_INIT,"resolving peer address(es)"); return enter_state_resolve(st); - } else if (transport_compute_setupinit_peers(st,0,prod_hint)) { + } else if (transport_compute_setupinit_peers(st,0,0,prod_hint)) { return enter_new_state(st,SITE_SENTMSG1); } slog(st,LOG_SETUP_INIT,"key exchange failed: no address for peer"); @@ -1359,7 +1383,7 @@ static void set_link_quality(struct site *st) quality=LINK_QUALITY_UP; else if (st->state==SITE_WAIT || st->state==SITE_STOP) quality=LINK_QUALITY_DOWN; - else if (st->address) + else if (st->addresses) quality=LINK_QUALITY_DOWN_CURRENT_ADDRESS; else if (transport_peers_valid(&st->peers)) quality=LINK_QUALITY_DOWN_STALE_ADDRESS; @@ -1389,22 +1413,37 @@ 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) + if (st->resolving_count) return True; - assert(st->address); + assert(st->addresses); /* 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; + * which will decrement st->resolving, so we need to increment it + * twice beforehand to prevent decrement from thinking we're + * finished, and decrement it ourselves. Alternatively if + * everything fails then there are no callbacks due and we simply + * set it to 0 and return false.. */ + st->resolving_n_results_stored=0; + st->resolving_n_results_all=0; + st->resolving_count+=2; + const char **addrp=st->addresses; + const char *address; + bool_t anyok=False; + for (; (address=*addrp++); ) { + bool_t ok = st->resolver->request(st->resolver->st,address, + st->remoteport,st->comms[0], + site_resolve_callback,st); + if (ok) + st->resolving_count++; + anyok|=ok; + } + if (!anyok) { + st->resolving_count=0; + return False; + } + decrement_resolving_count(st,2); + return True; } static bool_t enter_state_resolve(struct site *st) @@ -1561,7 +1600,7 @@ static int site_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, { struct site *st=sst; - *nfds_io=0; /* We don't use any file descriptors */ + BEFOREPOLL_WANT_FDS(0); /* We don't use any file descriptors */ st->now=*now; /* Work out when our next timeout is. The earlier of 'timeout' or @@ -1679,11 +1718,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_compute_setupinit_peers(st,0,source); + transport_compute_setupinit_peers(st,0,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 && st->local_mobile) + if (entered && st->addresses && st->local_mobile) /* We must do this as the very last thing, because the resolver callback might reenter us. */ ensure_resolving(st); @@ -1943,8 +1982,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc); st->privkey=find_cl_if(dict,"local-key",CL_RSAPRIVKEY,True,"site",loc); - st->address=dict_read_string(dict, "address", False, "site", loc); - if (st->address) + st->addresses=dict_read_string_array(dict,"address",False,"site",loc,0); + if (st->addresses) st->remoteport=dict_read_number(dict,"port",True,"site",loc,0); else st->remoteport=0; st->pubkey=find_cl_if(dict,"key",CL_RSAPUBKEY,True,"site",loc); @@ -1967,12 +2006,14 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->mobile_peer_expiry= dict_read_number( dict,"mobile-peer-expiry",False,"site",loc,DEFAULT_MOBILE_PEER_EXPIRY); - st->transport_peers_max= !st->peer_mobile ? 1 : dict_read_number( - dict,"mobile-peers-max",False,"site",loc,DEFAULT_MOBILE_PEERS_MAX); + const char *peerskey= st->peer_mobile + ? "mobile-peers-max" : "static-peers-max"; + st->transport_peers_max= dict_read_number( + dict,peerskey,False,"site",loc, st->addresses ? 4 : 3); if (st->transport_peers_max<1 || - st->transport_peers_max>MAX_MOBILE_PEERS_MAX) { - cfgfatal(loc,"site","mobile-peers-max must be in range 1.." - STRING(MAX_MOBILE_PEERS_MAX) "\n"); + st->transport_peers_max>MAX_PEER_ADDRS) { + cfgfatal(loc,"site", "%s must be in range 1.." + STRING(MAX_PEER_ADDRS) "\n", peerskey); } if (st->key_lifetime < DEFAULT(KEY_RENEGOTIATE_GAP)*2) @@ -1989,7 +2030,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->resolving=False; + st->resolving_count=0; st->allow_send_prod=0; st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5, @@ -2008,7 +2049,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, /* We are interested in poll(), but only for timeouts. We don't have any fds of our own. */ - register_for_poll(st, site_beforepoll, site_afterpoll, 0, "site"); + register_for_poll(st, site_beforepoll, site_afterpoll, "site"); st->timeout=0; st->remote_capabilities=0; @@ -2092,11 +2133,6 @@ static void transport_peers_debug(struct site *st, transport_peers *dst, } } -static bool_t transport_addrs_equal(const struct comm_addr *a, - const struct comm_addr *b) { - return !memcmp(a,b,sizeof(*a)); -} - static void transport_peers_expire(struct site *st, transport_peers *peers) { /* peers must be sorted first */ int previous_peers=peers->npeers; @@ -2120,7 +2156,7 @@ static bool_t transport_peer_record_one(struct site *st, transport_peers *peers, return 0; for (search=0; searchnpeers; search++) - if (transport_addrs_equal(&peers->peers[search].addr, ca)) + if (comm_addr_equal(&peers->peers[search].addr, ca)) return 1; peers->peers[peers->npeers].addr = *ca; @@ -2139,7 +2175,7 @@ static void transport_record_peers(struct site *st, transport_peers *peers, * Caller must first call transport_peers_expire. */ if (naddrs==1 && peers->npeers>=1 && - transport_addrs_equal(&addrs[0], &peers->peers[0].addr)) { + comm_addr_equal(&addrs[0], &peers->peers[0].addr)) { /* optimisation, also avoids debug for trivial updates */ peers->peers[0].last = *tv_now; return; @@ -2174,24 +2210,24 @@ static void transport_expire_record_peers(struct site *st, } 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_addrs /* 0 if none or not found */, + int n_configured_addrs /* 0 if none or not found */, const struct comm_addr *incoming_packet_addr /* 0 if none */) { - - if (!configured_addr && !incoming_packet_addr && + if (!n_configured_addrs && !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;" : "", + "using: %d configured addr(s);%s %d old peer addrs(es)", + n_configured_addrs, incoming_packet_addr ? " incoming packet address;" : "", st->peers.npeers); - /* 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 address of the incoming PROD, or the existing data peer if - * one exists; this is as desired. */ + /* Non-mobile peers try addresses until one is plausible. The + * effect is that this code always tries first the configured + * address if supplied, or otherwise the address of the incoming + * PROD, or finally the existing data peer if one exists; this is + * as desired. */ transport_peers_copy(st,&st->setup_peers,&st->peers); transport_peers_expire(st,&st->setup_peers); @@ -2200,9 +2236,9 @@ static bool_t transport_compute_setupinit_peers(struct site *st, transport_record_peers(st,&st->setup_peers, incoming_packet_addr,1, "incoming"); - if (configured_addr) + if (n_configured_addrs) transport_record_peers(st,&st->setup_peers, - configured_addr,1, "setupinit"); + configured_addrs,n_configured_addrs, "setupinit"); assert(transport_peers_valid(&st->setup_peers)); return True; @@ -2233,14 +2269,19 @@ static void transport_peers_copy(struct site *st, transport_peers *dst, } static void transport_resolve_complete(struct site *st, - const struct comm_addr *ca) { - transport_expire_record_peers(st,&st->peers,ca,1,"resolved data"); - transport_expire_record_peers(st,&st->setup_peers,ca,1,"resolved setup"); + const struct comm_addr *addrs, + int naddrs) { + transport_expire_record_peers(st,&st->peers,addrs,naddrs, + "resolved data"); + transport_expire_record_peers(st,&st->setup_peers,addrs,naddrs, + "resolved setup"); } static void transport_resolve_complete_tardy(struct site *st, - const struct comm_addr *ca) { - transport_expire_record_peers(st,&st->peers,ca,1,"resolved tardily"); + const struct comm_addr *addrs, + int naddrs) { + transport_expire_record_peers(st,&st->peers,addrs,naddrs, + "resolved tardily"); } static void transport_peers__copy_by_mask(transport_peer *out, int *nout_io, @@ -2263,7 +2304,7 @@ void transport_xmit(struct site *st, transport_peers *peers, int slot; transport_peers_expire(st, peers); unsigned failed=0; /* bitmask */ - assert(MAX_MOBILE_PEERS_MAX < sizeof(unsigned)*CHAR_BIT); + assert(MAX_PEER_ADDRS < sizeof(unsigned)*CHAR_BIT); int nfailed=0; for (slot=0; slotnpeers; slot++) {