chiark / gitweb /
changelog: start 0.6.8
[secnet.git] / polypath.c
index ac6a18b7e95d5972a6cdabbe45509df741edcaf4..adac598b3505784f2a3065ad17e49fcb3a15c06f 100644 (file)
@@ -7,7 +7,7 @@
  *
  * 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
@@ -27,6 +27,7 @@
 
 #include <adns.h>
 #include <ctype.h>
+#include <limits.h>
 
 #ifdef CONFIG_IPV6
 
@@ -45,7 +46,8 @@ struct polypath {
     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;
@@ -53,6 +55,10 @@ struct polypath {
     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
@@ -61,7 +67,7 @@ static const char *const default_loopback_ifname_pats[] = {
     "!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[] = {
@@ -93,7 +99,7 @@ static bool_t ifname_search_pats(struct polypath *st, struct cloc loc,
     const char *const *pati;
     for (pati=pats; *pati; pati++) {
        const char *pat=*pati;
-       if (*pat=='!' || *pat=='+') { *want_io=*pat; pat++; }
+       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);
@@ -119,6 +125,21 @@ static char ifname_wanted(struct polypath *st, struct cloc loc,
     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)
 {
@@ -151,7 +172,8 @@ typedef void bad_fn_type(struct polypath *st, void *badctx,
 
 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 {
@@ -222,7 +244,6 @@ static void polypath_process_monitor_line(struct polypath *st, char *orgl,
 
     char want=ifname_wanted(st,st->uc.cc.loc,ifname);
     if (want=='!') DONT("unwanted interface name");
-    assert(want=='+');
 
     switch (ia.sa.sa_family) {
     case AF_INET6: {
@@ -273,18 +294,20 @@ static void polypath_process_monitor_line(struct polypath *st, char *orgl,
 #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;
@@ -317,30 +340,39 @@ static bool_t polypath_make_socket(struct polypath *st,
 
 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);
@@ -391,7 +423,7 @@ static void polypath_record_ifaddr(struct polypath *st,
        free(interf);
     }
 
-    dump_pria(st,0);
+    dump_pria(st,interfs,0,0);
 }
 
 static void subproc_problem(struct polypath *st,
@@ -442,6 +474,49 @@ static void polypath_afterpoll_monitor(void *state, struct pollfd *fds,
 }
 
 /* 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;
+    }
+    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)
@@ -449,36 +524,16 @@ static bool_t polypath_sendmsg(void *commst, struct buffer_if *buf,
     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;
 }
@@ -565,6 +620,7 @@ struct privsep_mdata {
     bool_t add;
     char ifname[100];
     union iaddr ia;
+    char want; /* `+' or `@' */
 };
 
 static void papp_bad(struct polypath *st, void *badctx,
@@ -608,7 +664,8 @@ static void polypath_afterpoll_privsep(void *state, struct pollfd *fds,
                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;
@@ -661,7 +718,8 @@ static void polypath_afterpoll_privsep(void *state, struct pollfd *fds,
 
 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
@@ -682,6 +740,7 @@ static void privsep_handle_ifaddr(struct polypath *st,
     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);
 
@@ -809,7 +868,9 @@ static void polypath_phase_childpersist(void *sst, uint32_t newphase)
     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);
 }
 
@@ -827,6 +888,8 @@ static list_t *polypath_apply(closure_t *self, struct cloc loc,
     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;
 
@@ -846,7 +909,8 @@ static list_t *polypath_apply(closure_t *self, struct cloc loc,
     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");