Merge remote-tracking branch 'mdw/mdw/powm-sec'
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 25 Apr 2017 12:05:53 +0000 (13:05 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 25 Apr 2017 12:05:53 +0000 (13:05 +0100)
13 files changed:
Makefile.in
README
comm-common.c
comm-common.h
conffile.c
debian/changelog
netlink.c
polypath.c
secnet.c
secnet.h
site.c
udp.c
util.c

index 6ef9f2a..406efde 100644 (file)
@@ -127,6 +127,8 @@ conffile.yy.h:      conffile.yy.c
 conffile.tab.c:        conffile.y
 # End of manual dependencies section
 
+conffile.yy.o: ALL_CFLAGS += -Wno-sign-compare
+
 secnet:        $(OBJECTS)
        $(MAKE) version.o # *.o $(filter-out %.o, $^)
        $(CC) $(LDFLAGS) $(ALL_CFLAGS) -o $@ $(OBJECTS) version.o $(LDLIBS)
diff --git a/README b/README
index a01156c..e56c444 100644 (file)
--- a/README
+++ b/README
@@ -236,14 +236,18 @@ polypath: dict argument
   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
@@ -272,6 +276,14 @@ parameter set to `true'.  When the local site site is not marked
 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
@@ -390,7 +402,7 @@ site: dict argument
     [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
@@ -444,6 +456,9 @@ site: dict argument
     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
index a384ba6..4bbc871 100644 (file)
 #include "secnet.h"
 #include "comm-common.h"
 
+struct comm_clientinfo *comm_clientinfo_ignore(void *state, dict_t *dict,
+                                              struct cloc cloc)
+{
+    return 0;
+}
+
 void comm_request_notify(void *commst, void *nst, comm_notify_fn *fn)
 {
     struct commcommon *st=commst;
index b517f95..ebb71b8 100644 (file)
@@ -40,6 +40,8 @@ struct commcommon { /* must be first so that void* is comm_common* */
     struct buffer_if *rbuf;
 };
 
+struct comm_clientinfo *comm_clientinfo_ignore(void *state, dict_t*,
+                                              struct cloc cloc);
 void comm_request_notify(void *commst, void *nst, comm_notify_fn *fn);
 void comm_release_notify(void *commst, void *nst, comm_notify_fn *fn);
 
@@ -55,6 +57,7 @@ void comm_apply(struct commcommon *cc, void *st);
     NEW(st);                                           \
     (cc)->loc=loc;                                     \
     (cc)->cl.description=desc;                         \
+    (cc)->ops.clientinfo=comm_clientinfo_ignore;       \
     (cc)->ops.sendmsg=prefix##sendmsg;                 \
     (cc)->ops.addr_to_string=prefix##addr_to_string;   \
     comm_apply((cc),(st))
index bf4891e..7f0202c 100644 (file)
@@ -767,6 +767,21 @@ bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
     return r;
 }
 
+dict_t *dict_read_dict(dict_t *dict, cstring_t key, bool_t required,
+                      cstring_t desc, struct cloc loc)
+{
+    item_t *i;
+    dict_t *r;
+
+    i=dict_find_item(dict,key,required,desc,loc);
+    if (!i) return NULL;
+    if (i->type!=t_dict) {
+       cfgfatal(loc,desc,"\"%s\" must be a dictionary\n",key);
+    }
+    r=i->data.dict;
+    return r;
+}
+
 uint32_t string_to_word(cstring_t s, struct cloc loc,
                        struct flagstr *f, cstring_t desc)
 {
index 95d4554..f4227a0 100644 (file)
@@ -1,3 +1,14 @@
+secnet (0.4.1~~) unstable; urgency=medium
+
+  * Implement comm-info and dedicated-interface-addr feature, for
+    benefit of hippotat.
+  * Implement `keepalive' site option, to try to keep link always up.
+  * When printing messages about dropping IPv6, do not print anything
+    about ihl.  (Check the IP version field first!)
+  * When turning on debug, turn on verbose too.
+
+ --
+
 secnet (0.4.0) unstable; urgency=low
 
   Debugging improvements:
index 9556e60..83b470e 100644 (file)
--- a/netlink.c
+++ b/netlink.c
@@ -487,8 +487,8 @@ static bool_t netlink_check(struct netlink *st, struct buffer_if *buf,
     struct iphdr *iph=(struct iphdr *)buf->start;
     int32_t len;
 
-    if (iph->ihl < 5) BAD("ihl %u",iph->ihl);
     if (iph->version != 4) BAD("version %u",iph->version);
+    if (iph->ihl < 5) BAD("ihl %u",iph->ihl);
     if (buf->size < iph->ihl*4) BAD("size %"PRId32"<%u*4",buf->size,iph->ihl);
     if (ip_fast_csum((uint8_t *)iph, iph->ihl)!=0) BAD("csum");
     len=ntohs(iph->tot_len);
index cea465e..025909a 100644 (file)
@@ -45,7 +45,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 +54,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 +66,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[] = {
@@ -83,7 +88,7 @@ static const char *polypath_addr_to_string(void *commst,
 }
 
 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
@@ -93,9 +98,8 @@ 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=='!') { *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;
@@ -105,13 +109,13 @@ static bool_t ifname_search_pats(struct polypath *st, struct cloc loc,
     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;
@@ -120,6 +124,21 @@ static bool_t 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)
 {
@@ -152,7 +171,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 {
@@ -221,8 +241,8 @@ static void polypath_process_monitor_line(struct polypath *st, char *orgl,
        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: {
@@ -273,18 +293,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 +339,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 +422,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,42 +473,69 @@ 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;
+       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;
 }
@@ -564,6 +622,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,
@@ -607,7 +666,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;
@@ -660,7 +720,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
@@ -681,6 +742,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);
 
@@ -808,7 +870,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);
 }
 
@@ -826,6 +890,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;
 
@@ -845,7 +911,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");
 
index 8fb8aff..1762fd7 100644 (file)
--- a/secnet.c
+++ b/secnet.c
@@ -127,6 +127,9 @@ static void parse_options(int argc, char **argv)
            exit(0);
            break;
 
+       case 'd':
+           message_level|=M_DEBUG_CONFIG|M_DEBUG_PHASE|M_DEBUG;
+           /* fall through */
        case 'v':
            message_level|=M_INFO|M_NOTICE|M_WARNING|M_ERR|M_SECURITY|
                M_FATAL;
@@ -136,10 +139,6 @@ static void parse_options(int argc, char **argv)
            message_level&=(~M_WARNING);
            break;
 
-       case 'd':
-           message_level|=M_DEBUG_CONFIG|M_DEBUG_PHASE|M_DEBUG;
-           break;
-
        case 'f':
            message_level=M_FATAL;
            break;
index 83ee97f..8c628f6 100644 (file)
--- a/secnet.h
+++ b/secnet.h
@@ -171,6 +171,8 @@ extern uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
   /* return value can safely be assigned to int32_t */
 extern bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
                             cstring_t desc, struct cloc loc, bool_t def);
+extern dict_t *dict_read_dict(dict_t *dict, cstring_t key, bool_t required,
+                       cstring_t desc, struct cloc loc);
 const char **dict_read_string_array(dict_t *dict, cstring_t key,
                                    bool_t required, cstring_t desc,
                                    struct cloc loc, const char *const *def);
@@ -435,6 +437,15 @@ struct comm_addr {
     int ix; /* see comment `Re comm_addr.ix' in udp.c */
 };
 
+struct comm_clientinfo; /* private for comm */
+
+typedef struct comm_clientinfo *comm_clientinfo_fn(void *state, dict_t*,
+                                                  struct cloc cloc);
+/* A comm client may call this during configuration, and then pass
+ * the resulting comm_clientinfo* to some or all sendmsg calls.
+ * The semantics depend on the dict and defined by the comm, and
+ * should be documented in README. */
+
 /* Return True if the packet was processed, and shouldn't be passed to
    any other potential receivers. (buf is freed iff True returned.) */
 typedef bool_t comm_notify_fn(void *state, struct buffer_if *buf,
@@ -444,7 +455,8 @@ typedef void comm_request_notify_fn(void *commst, void *nst,
 typedef void comm_release_notify_fn(void *commst, void *nst,
                                    comm_notify_fn *fn);
 typedef bool_t comm_sendmsg_fn(void *commst, struct buffer_if *buf,
-                              const struct comm_addr *dest);
+                              const struct comm_addr *dest,
+                              struct comm_clientinfo* /* 0 OK */);
   /* Only returns false if (we know that) the local network
    * environment is such that this address cannot work; transient
    * or unknown/unexpected failures return true. */
@@ -453,6 +465,7 @@ typedef const char *comm_addr_to_string_fn(void *commst,
         /* Returned string is in a static buffer. */
 struct comm_if {
     void *st;
+    comm_clientinfo_fn *clientinfo;
     comm_request_notify_fn *request_notify;
     comm_release_notify_fn *release_notify;
     comm_sendmsg_fn *sendmsg;
diff --git a/site.c b/site.c
index 70cc316..dcac0ba 100644 (file)
--- a/site.c
+++ b/site.c
@@ -296,6 +296,7 @@ struct site {
 /* 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 */
@@ -304,6 +305,7 @@ struct site {
     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;
@@ -1146,6 +1148,10 @@ static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
     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 */
@@ -1178,6 +1184,22 @@ static void dump_packet(struct site *st, struct buffer_if *buf,
               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;
@@ -1429,6 +1451,9 @@ static void enter_state_run(struct site *st)
     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)
@@ -1602,7 +1627,7 @@ static void generate_send_prod(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);
 }
 
@@ -1967,6 +1992,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     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);
@@ -2016,6 +2043,14 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
 
     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);
@@ -2349,8 +2384,7 @@ void transport_xmit(struct site *st, transport_peers *peers,
     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) {
diff --git a/udp.c b/udp.c
index 451e6e9..1535f29 100644 (file)
--- a/udp.c
+++ b/udp.c
@@ -201,7 +201,8 @@ static void udp_socks_afterpoll(void *state, struct pollfd *fds, int nfds)
 }
 
 static bool_t udp_sendmsg(void *commst, struct buffer_if *buf,
-                         const struct comm_addr *dest)
+                         const struct comm_addr *dest,
+                         struct comm_clientinfo *clientinfo)
 {
     struct udp *st=commst;
     struct udpcommon *uc=&st->uc;
diff --git a/util.c b/util.c
index a91c073..7036061 100644 (file)
--- a/util.c
+++ b/util.c
@@ -448,7 +448,7 @@ void send_nak(const struct comm_addr *dest, uint32_t our_index,
                " %s; sending NAK\n",
                comm_addr_to_string(dest),
                our_index, their_index, msgtype, logwhy);
-    dest->comm->sendmsg(dest->comm->st, buf, dest);
+    dest->comm->sendmsg(dest->comm->st, buf, dest, 0);
 }
 
 int consttime_memeq(const void *s1in, const void *s2in, size_t n)