buffer (buffer closure): buffer for incoming packets
authbind (string): optional, path to authbind-helper program
max-interfaces (number): optional, max number of different interfaces to
- use (also, maximum steady-state amount of packet multiplication)
+ use (also, maximum steady-state amount of packet multiplication);
+ interfaces marked with `@' do not count.
interfaces (string list): which interfaces to process; each entry is
- optionally `!' or `+' followed by a glob pattern (which is applied to a
- prospective interface using fnmatch with no flags). If no list is
- specified, or the list ends with a `!' entry, a default list is
- used/appended: "!tun*","!tap*","!sl*","!userv*","!lo","*". Patterns
- which do not start with `*' or an alphanumeric need to be preceded
- by `!' or `+'.
+ optionally `!' or `+' or `@' followed by a glob pattern (which is
+ applied to a prospective interface using fnmatch with no flags).
+ `+' or nothing means to process normally. `!' means to ignore;
+ `@' means to use only in conjunction with dedicated-interface-addr.
+ If no list is specified, or the list ends with a `!' entry, a
+ default list is used/appended:
+ "!tun*","!tap*","!sl*","!userv*","!lo","@hippo*","*".
+ Patterns which do not start with `*' or an alphanumeric need to be
+ preceded by `!' or `+' or `@'.
monitor-command (string list): Program to use to monitor appearance
and disappearance of addresses on local network interfaces. Should
produce lines of the form `+|-<ifname> 4|6 <addr>' where <addr> is
mobile the address selection machinery might fixate on an unsuitable
address.
+polypath takes site-specific informtion as passed to the `comm-info'
+site closure parameter. The entries understood in the dictionary
+are:
+ dedicated-interface-addr (string): IPv4 or IPv6 address
+ literal. Interfaces specified with `@' in `interfaces' will be
+ used for the corresponding site iff the interface local address
+ is this address.
+
For an interface to work with polypath, it must either have a suitable
default route, or be a point-to-point interface. In the general case
this might mean that the host would have to have multiple default
[half key-lifetime, or key-lifetime minus 5 mins (mobile: 12 hours),
whichever is longer].
keepalive (bool): if True then attempt always to keep a valid session key.
- Not actually currently implemented. [false]
+ [false]
log-events (string list): types of events to log for this site
unexpected: unexpected key setup packets (may be late retransmissions)
setup-init: start of attempt to setup a session key
should be reflected in the local private interface MTU, ie the mtu
parameter to netlink). If this parameter is not set, or is set
to 0, the default is to use the local private link mtu.
+ comm-info (dict): Information for the comm, used when this site
+ wants to transmit. If the comm does not support this, it is
+ ignored.
Links involving mobile peers have some different tuning parameter
default values, which are generally more aggressive about retrying key
const char *const *ifname_pats;
const char *const *monitor_command;
bool_t permit_loopback;
- LIST_HEAD(,interf) interfs;
+ LIST_HEAD(interf_list, interf) interfs_general;
+ struct interf_list interfs_dedicated;
struct buffer_if lbuf;
int monitor_fd;
pid_t monitor_pid;
int privsep_ipcsock_fd;
};
+struct comm_clientinfo {
+ union iaddr dedicated; /* might be AF_UNSPEC */
+};
+
static void polypath_phase_shutdown(void *sst, uint32_t newphase);
#define LG 0, st->uc.cc.cl.description, &st->uc.cc.loc
"!lo", 0
};
static const char *const default_ifname_pats[] = {
- "!tun*","!tap*","!sl*","!userv*", "*", 0
+ "!tun*","!tap*","!sl*","!userv*", "@hippo*", "*", 0
};
static const char *const default_monitor_command[] = {
}
static bool_t ifname_search_pats(struct polypath *st, struct cloc loc,
- const char *ifname, bool_t *want_io,
+ const char *ifname, char *want_io,
const char *const *pats) {
/* Returns True iff we found a list entry, in which case *want_io
* is set to the sense of that entry. Otherwise *want_io is set
const char *const *pati;
for (pati=pats; *pati; pati++) {
const char *pat=*pati;
- if (*pat=='!') { *want_io=False; pat++; }
- else if (*pat=='+') { *want_io=True; pat++; }
- else if (*pat=='*' || isalnum((unsigned char)*pat)) { *want_io=True; }
+ if (*pat=='!' || *pat=='+' || *pat=='@') { *want_io=*pat; pat++; }
+ else if (*pat=='*' || isalnum((unsigned char)*pat)) { *want_io='+'; }
else cfgfatal(loc,"polypath","invalid interface name pattern `%s'",pat);
int match=fnmatch(pat,ifname,0);
if (match==0) return True;
return False;
}
-static bool_t ifname_wanted(struct polypath *st, struct cloc loc,
- const char *ifname) {
- bool_t want=False; /* pretend an empty cfg ends with !<doesn'tmatch> */
+static char ifname_wanted(struct polypath *st, struct cloc loc,
+ const char *ifname) {
+ char want='!'; /* pretend an empty cfg ends with !<doesn'tmatch> */
if (ifname_search_pats(st,loc,ifname,&want, st->ifname_pats))
return want;
- if (want) /* last pattern was positive, do not search default */
- return False;
+ if (want!='!') /* last pattern was positive, do not search default */
+ return '!';
if (!st->permit_loopback &&
ifname_search_pats(st,loc,ifname,&want, default_loopback_ifname_pats))
return want;
abort();
}
+static struct comm_clientinfo *polypath_clientinfo(void *state,
+ dict_t *dict, struct cloc cloc) {
+ struct comm_clientinfo *clientinfo;
+
+ NEW(clientinfo);
+ FILLZERO(*clientinfo);
+ clientinfo->dedicated.sa.sa_family=AF_UNSPEC;
+
+ item_t *item = dict_find_item(dict,"dedicated-interface-addr",
+ False,"polypath",cloc);
+ if (item) string_item_to_iaddr(item,0,&clientinfo->dedicated,
+ "polypath");
+ return clientinfo;
+}
+
static int polypath_beforepoll(void *state, struct pollfd *fds, int *nfds_io,
int *timeout_io)
{
typedef void polypath_ppml_callback_type(struct polypath *st,
bad_fn_type *bad, void *badctx,
- bool_t add, const char *ifname, const char *ifaddr,
+ bool_t add, char want,
+ const char *ifname, const char *ifaddr,
const union iaddr *ia, int fd /* -1 if none yet */);
struct ppml_bad_ctx {
goto out; \
}while(0)
- if (!ifname_wanted(st,st->uc.cc.loc,ifname))
- DONT("unwanted interface name");
+ char want=ifname_wanted(st,st->uc.cc.loc,ifname);
+ if (want=='!') DONT("unwanted interface name");
switch (ia.sa.sa_family) {
case AF_INET6: {
#undef DONT
/* OK, process it */
- callback(st, bad,badctx, add,ifname,ifaddr,&ia,-1);
+ callback(st, bad,badctx, add,want, ifname,ifaddr,&ia,-1);
out:;
}
-static void dump_pria(struct polypath *st, const char *ifname)
+static void dump_pria(struct polypath *st, struct interf_list *interfs,
+ const char *ifname, char want)
{
#ifdef POLYPATH_DEBUG
struct interf *interf;
if (ifname)
- lg_perror(LG,M_DEBUG,0, "polypath record ifaddr `%s'",ifname);
- LIST_FOREACH(interf, &st->interfs, entry) {
+ lg_perror(LG,M_DEBUG,0, "polypath record ifaddr `%s' (%c)",
+ ifname, want);
+ LIST_FOREACH(interf, interfs, entry) {
lg_perror(LG,M_DEBUG,0, " polypath interface `%s', nsocks=%d",
interf->name, interf->socks.n_socks);
int i;
static void polypath_record_ifaddr(struct polypath *st,
bad_fn_type *bad, void *badctx,
- bool_t add, const char *ifname,
+ bool_t add, char want,
+ const char *ifname,
const char *ifaddr,
const union iaddr *ia, int fd)
{
struct udpcommon *uc=&st->uc;
struct interf *interf=0;
+ int max_interfs;
struct udpsock *us=0;
- dump_pria(st,ifname);
+ struct interf_list *interfs;
+ switch (want) {
+ case '+': interfs=&st->interfs_general; max_interfs=st->max_interfs; break;
+ case '@': interfs=&st->interfs_dedicated; max_interfs=INT_MAX; break;
+ default: fatal("polypath: got bad want (%#x, %s)", want, ifname);
+ }
+
+ dump_pria(st,interfs,ifname,want);
int n_ifs=0;
- LIST_FOREACH(interf,&st->interfs,entry) {
+ LIST_FOREACH(interf,interfs,entry) {
if (!strcmp(interf->name,ifname))
goto found_interf;
n_ifs++;
}
/* not found */
- if (n_ifs==st->max_interfs) BAD("too many interfaces");
+ if (n_ifs==max_interfs) BAD("too many interfaces");
interf=malloc(sizeof(*interf));
if (!interf) BADE("malloc for new interface",errno);
interf->name=0;
interf->socks.n_socks=0;
FILLZERO(interf->experienced_xmit_noaf);
- LIST_INSERT_HEAD(&st->interfs,interf,entry);
+ LIST_INSERT_HEAD(interfs,interf,entry);
interf->name=strdup(ifname);
udp_socks_register(&st->uc,&interf->socks,interf->name);
if (!interf->name) BADE("strdup interface name",errno);
free(interf);
}
- dump_pria(st,0);
+ dump_pria(st,interfs,0,0);
}
static void subproc_problem(struct polypath *st,
}
/* Actual udp packet sending work */
+
+static void polypath_sendmsg_interf(struct polypath *st,
+ struct interf *interf,
+ struct buffer_if *buf,
+ const struct comm_addr *dest,
+ const union iaddr *dedicated,
+ bool_t *allreasonable)
+{
+ int i;
+ int af=dest->ia.sa.sa_family;
+ bool_t wanted=False, attempted=False, reasonable=False;
+
+ for (i=0; i<interf->socks.n_socks; i++) {
+ struct udpsock *us=&interf->socks.socks[i];
+ if (dedicated && !iaddr_equal(dedicated, &us->addr, True))
+ continue;
+ wanted=True;
+ if (af != us->addr.sa.sa_family)
+ continue;
+ attempted=True;
+ int r=sendto(us->fd,buf->start,buf->size,
+ 0,&dest->ia.sa,iaddr_socklen(&dest->ia));
+ udp_sock_experienced(0,&st->uc,&interf->socks,us,
+ &dest->ia,af, r,errno);
+ if (r>=0) {
+ reasonable=True;
+ break;
+ }
+ if (!(errno==EAFNOSUPPORT || errno==ENETUNREACH))
+ reasonable=True;
+ lg_perror(LG,M_DEBUG,errno,"%s [%s] xmit %"PRIu32" bytes to %s",
+ interf->name,iaddr_to_string(&us->addr),
+ buf->size,iaddr_to_string(&dest->ia));
+ }
+ if (!wanted)
+ return;
+
+ if (!attempted)
+ if (!interf->experienced_xmit_noaf[af]++)
+ lg_perror(LG,M_WARNING,0,
+ "%s has no suitable address to transmit %s",
+ interf->name, af_name(af));
+
+ *allreasonable *= reasonable;
+}
+
static bool_t polypath_sendmsg(void *commst, struct buffer_if *buf,
- const struct comm_addr *dest)
+ const struct comm_addr *dest,
+ struct comm_clientinfo *clientinfo)
{
struct polypath *st=commst;
struct interf *interf;
bool_t allreasonable=True;
- int af=dest->ia.sa.sa_family;
- LIST_FOREACH(interf,&st->interfs,entry) {
- int i;
- bool_t attempted=False, reasonable=False;
- for (i=0; i<interf->socks.n_socks; i++) {
- struct udpsock *us=&interf->socks.socks[i];
- if (af != us->addr.sa.sa_family)
- continue;
- attempted=True;
- int r=sendto(us->fd,buf->start,buf->size,
- 0,&dest->ia.sa,iaddr_socklen(&dest->ia));
- udp_sock_experienced(0,&st->uc,&interf->socks,us,
- &dest->ia,af, r,errno);
- if (r>=0) {
- reasonable=True;
- break;
- }
- if (!(errno==EAFNOSUPPORT || errno==ENETUNREACH))
- reasonable=True;
- lg_perror(LG,M_DEBUG,errno,"%s [%s] xmit %"PRIu32" bytes to %s",
- interf->name,iaddr_to_string(&us->addr),
- buf->size,iaddr_to_string(&dest->ia));
+ LIST_FOREACH(interf,&st->interfs_general,entry) {
+ polypath_sendmsg_interf(st,interf,buf,dest,
+ 0, &allreasonable);
+ }
+ if (clientinfo && clientinfo->dedicated.sa.sa_family != AF_UNSPEC) {
+ LIST_FOREACH(interf,&st->interfs_dedicated,entry) {
+ polypath_sendmsg_interf(st,interf,buf,dest,
+ &clientinfo->dedicated, &allreasonable);
}
- if (!attempted)
- if (!interf->experienced_xmit_noaf[af]++)
- lg_perror(LG,M_WARNING,0,
- "%s has no suitable address to transmit %s",
- interf->name, af_name(af));
- allreasonable *= reasonable;
}
return allreasonable;
}
bool_t add;
char ifname[100];
union iaddr ia;
+ char want; /* `+' or `@' */
};
static void papp_bad(struct polypath *st, void *badctx,
fatal("polypath (privsep): got message data but bad AF %d",af);
const char *addr_str=iaddr_to_string(&mdata->ia);
polypath_record_ifaddr(st,papp_bad,(void*)addr_str,
- mdata->add,mdata->ifname,addr_str,
+ mdata->add,mdata->want,
+ mdata->ifname,addr_str,
&mdata->ia, st->privsep_incoming_fd);
st->privsep_incoming_fd=-1;
st->lbuf.size=0;
static void privsep_handle_ifaddr(struct polypath *st,
bad_fn_type *bad, void *badctx,
- bool_t add, const char *ifname,
+ bool_t add, char want,
+ const char *ifname,
const char *ifaddr,
const union iaddr *ia, int fd_dummy)
/* In child: handles discovered wanted interfaces, making sockets
size_t l=strlen(ifname);
if (l>=sizeof(mdata.ifname)) BAD("interface name too long");
strcpy(mdata.ifname,ifname);
+ mdata.want=want;
COPY_OBJ(mdata.ia,*ia);
struct polypath *st=sst;
struct interf *interf;
- LIST_FOREACH(interf,&st->interfs,entry)
+ LIST_FOREACH(interf,&st->interfs_general,entry)
+ udp_socks_childpersist(&st->uc,&interf->socks);
+ LIST_FOREACH(interf,&st->interfs_dedicated,entry)
udp_socks_childpersist(&st->uc,&interf->socks);
}
COMM_APPLY_STANDARD(st,&st->uc.cc,"polypath",args);
UDP_APPLY_STANDARD(st,&st->uc,"polypath");
+ st->uc.cc.ops.clientinfo = polypath_clientinfo;
+
struct udpcommon *uc=&st->uc;
struct commcommon *cc=&uc->cc;
st->permit_loopback=dict_read_bool(d,"permit-loopback",False,
"polypath",cc->loc,False);
- LIST_INIT(&st->interfs);
+ LIST_INIT(&st->interfs_general);
+ LIST_INIT(&st->interfs_dedicated);
buffer_new(&st->lbuf,ADNS_ADDR2TEXT_BUFLEN+100);
BUF_ALLOC(&st->lbuf,"polypath lbuf");
/* configuration information */
string_t localname;
string_t remotename;
+ bool_t keepalive;
bool_t local_mobile, peer_mobile; /* Mobile client support */
int32_t transport_peers_max;
string_t tunname; /* localname<->remotename by default, used in logs */
uint32_t mtu_target;
struct netlink_if *netlink;
struct comm_if **comms;
+ struct comm_clientinfo **commclientinfos;
int ncomms;
struct resolver_if *resolver;
struct log_if *log;
case LABEL_MSG7:
/* We must forget about the current session. */
delete_keys(st,"request from peer",LOG_SEC);
+ /* probably, the peer is shutting down, and this is going to fail,
+ * but we need to be trying to bring the link up again */
+ if (st->keepalive)
+ initiate_key_setup(st,"peer requested key teardown",0);
return True;
case LABEL_MSG9:
/* Deliver to netlink layer */
ok?"":" - fail");
}
+static bool_t comm_addr_sendmsg(struct site *st,
+ const struct comm_addr *dest,
+ struct buffer_if *buf)
+{
+ int i;
+ struct comm_clientinfo *commclientinfo = 0;
+
+ for (i=0; i < st->ncomms; i++) {
+ if (st->comms[i] == dest->comm) {
+ commclientinfo = st->commclientinfos[i];
+ break;
+ }
+ }
+ return dest->comm->sendmsg(dest->comm->st, buf, dest, commclientinfo);
+}
+
static uint32_t site_status(void *st)
{
return 0;
memset(st->dhsecret,0,st->dh->len);
memset(st->sharedsecret,0,st->sharedsecretlen);
set_link_quality(st);
+
+ if (st->keepalive && !current_valid(st))
+ initiate_key_setup(st, "keepalive", 0);
}
static bool_t ensure_resolving(struct site *st)
slog(st,LOG_SETUP_INIT,"prodding peer for key exchange");
st->allow_send_prod=0;
generate_prod(st,&st->scratch);
- bool_t ok = source->comm->sendmsg(source->comm->st, &st->scratch, source);
+ bool_t ok = comm_addr_sendmsg(st, source, &st->scratch);
dump_packet(st,&st->scratch,source,False,ok);
}
st->localname=dict_read_string(dict, "local-name", True, "site", loc);
st->remotename=dict_read_string(dict, "name", True, "site", loc);
+ st->keepalive=dict_read_bool(dict,"keepalive",False,"site",loc,False);
+
st->peer_mobile=dict_read_bool(dict,"mobile",False,"site",loc,False);
st->local_mobile=
dict_read_bool(dict,"local-mobile",False,"site",loc,False);
GET_CLOSURE_LIST("comm",comms,ncomms,CL_COMM);
+ NEW_ARY(st->commclientinfos, st->ncomms);
+ dict_t *comminfo = dict_read_dict(dict,"comm-info",False,"site",loc);
+ for (i=0; i<st->ncomms; i++) {
+ st->commclientinfos[i] =
+ !comminfo ? 0 :
+ st->comms[i]->clientinfo(st->comms[i],comminfo,loc);
+ }
+
st->resolver=find_cl_if(dict,"resolver",CL_RESOLVER,True,"site",loc);
st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc);
st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc);
int nfailed=0;
for (slot=0; slot<peers->npeers; slot++) {
transport_peer *peer=&peers->peers[slot];
- bool_t ok =
- peer->addr.comm->sendmsg(peer->addr.comm->st, buf, &peer->addr);
+ bool_t ok = comm_addr_sendmsg(st, &peer->addr, buf);
if (candebug)
dump_packet(st, buf, &peer->addr, False, ok);
if (!ok) {