*
* secnet is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version d of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* secnet is distributed in the hope that it will be useful, but
#include <adns.h>
#include <ctype.h>
+#include <limits.h>
#ifdef CONFIG_IPV6
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,
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");