chiark / gitweb /
udp, polypath: Make specifying port optional
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 1 Oct 2014 23:19:34 +0000 (00:19 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 2 Oct 2014 15:41:57 +0000 (16:41 +0100)
There is no particular reason why a mobile site with no stable address
ought to have to bind to a particular port.  Doing so in those
configurations can bring in additional complications.

So, make specifying the port optional.

udp_make_socket calls getsockname to find what port number it got.
(We do this unconditionally as that's simplest.)

This has more complicated fallout than appears at first glance.
polypath needs to be able to match disappearances of the interface
address, which is mostly processed in terms of the configured rather
than obtained sockaddr.

In polypath, we need to compare just the addresses when removing an
interface address, because the port will have been assigned later.  We
also use the actual address in logging, rather than the one supplied
by the interface monitor.

To support these changes, we formalise udp_import_socket, and provide
a new `ignoreport' option to iaddr_equal.  The scope of the FAIL and
FAIL_LG macros in udp.c becomes a bit wider.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
README
comm-common.h
polypath.c
udp.c
util.c
util.h

diff --git a/README b/README
index 59af7a3fa714c0bc6da7bb1c815c377cb4b50c95..70b34bfa6f347dc8b193b96ba16d7322142270fe 100644 (file)
--- a/README
+++ b/README
@@ -194,8 +194,11 @@ Defines:
   udp (closure => comm closure)
 
 udp: dict argument
-  address (string list): IPv6 or IPv4 addresses to listen and send on
-  port (integer): UDP port to listen and send on
+  address (string list): IPv6 or IPv4 addresses to listen and send on;
+   default is all local addresses
+  port (integer): UDP port to listen and send on; optional if you
+   don't need to have a stable address for your peers to talk to
+   (in which case your site ought probably to have `local-mobile true').
   buffer (buffer closure): buffer for incoming packets
   authbind (string): optional, path to authbind-helper program
 
index c2171f66a86faaac12fa3a8a1b3e7e7da1962629..472cbbd746417ab06818f39ea8d44458cb394026 100644 (file)
@@ -79,7 +79,12 @@ struct udpcommon {
 
 bool_t udp_make_socket(struct udpcommon *uc, struct udpsock *us,
                       int failmsgclass);
-  /* Fills in us->fd.  Logs any errors with lg_[v]perror. */
+  /* Caller should have filled in ->addr.  Fills in us->fd,
+     ->experienced; updates ->addr.  Logs any errors with lg_[v]perror. */
+bool_t udp_import_socket(struct udpcommon *uc, struct udpsock *us,
+                        int failmsgclass, int fd);
+  /* Like udp_make_socket, but caller provides fd.  fd is not closed
+     on error */
 
 void udp_destroy_socket(struct udpcommon *uc, struct udpsock *us);
   /* Idempotent.  No errors are possible. */
@@ -97,7 +102,7 @@ void udp_socks_childpersist(struct udpcommon *uc, struct udpsocks *socks);
 #define UDP_APPLY_STANDARD(st,uc,desc)                                 \
     (uc)->use_proxy=False;                                             \
     (uc)->authbind=NULL;                                               \
-    (uc)->port=dict_read_number(d,"port",True,"udp",(uc)->cc.loc,0)
+    (uc)->port=dict_read_number(d,"port",False,"udp",(uc)->cc.loc,0)
     /* Expects in scope:  dict_t *d=...; */
 
 #endif /*COMM_COMMON_H*/
index 00b3d3492d542c6f900da4fcb3db5267694f2ad5..61e301b14132febd90c97dc8817ff8f5a7da23a7 100644 (file)
@@ -336,21 +336,23 @@ static void polypath_record_ifaddr(struct polypath *st,
            bool_t ok=polypath_make_socket(st,bad,badctx, us,ifname);
            if (!ok) goto out;
        } else {
-           FILLZERO(us->experienced);
-           us->fd=fd;
+           bool_t ok=udp_import_socket(uc,us,M_WARNING,fd);
+           if (!ok) goto out;
            fd=-1;
        }
        interf->socks.n_socks++;
+       lg_perror(LG,M_INFO,0,"using %s %s",ifname,
+                 iaddr_to_string(&us->addr));
        us=0; /* do not destroy this socket during `out' */
-       lg_perror(LG,M_INFO,0,"using %s %s",ifname,ifaddr);
     } else {
        int i;
        for (i=0;i<interf->socks.n_socks;i++)
-           if (!memcmp(&interf->socks.socks[i].addr,ia,sizeof(*ia)))
+           if (iaddr_equal(&interf->socks.socks[i].addr,ia,True))
                goto address_remove_found;
        BAD("address to remove not found");
     address_remove_found:
-       lg_perror(LG,M_INFO,0,"removed %s %s",ifname,ifaddr);
+       lg_perror(LG,M_INFO,0,"removed %s %s",ifname,
+                 iaddr_to_string(&interf->socks.socks[i].addr));
        udp_destroy_socket(&st->uc,&interf->socks.socks[i]);
        interf->socks.socks[i]=
            interf->socks.socks[--interf->socks.n_socks];
diff --git a/udp.c b/udp.c
index dca8db6c4f08b18e0e05e66e9adbbaeb0ee0a548..40348fc918f1bd9001cf17a2d6ee821308184c4b 100644 (file)
--- a/udp.c
+++ b/udp.c
@@ -121,7 +121,7 @@ static void udp_socks_afterpoll(void *state, struct pollfd *fds, int nfds)
                    /* Check that the packet came from our poxy server;
                       we shouldn't be contacted directly by anybody else
                       (since they can trivially forge source addresses) */
-                   if (!iaddr_equal(&from,&uc->proxy)) {
+                   if (!iaddr_equal(&from,&uc->proxy,False)) {
                        Message(M_INFO,"udp: received packet that's not "
                                "from the proxy\n");
                        BUF_FREE(cc->rbuf);
@@ -219,6 +219,35 @@ void udp_destroy_socket(struct udpcommon *uc, struct udpsock *us)
     }
 }
 
+#define FAIL_LG 0, cc->cl.description, &cc->loc, failmsgclass
+#define FAIL(...) do{                                          \
+        lg_perror(FAIL_LG,errno,__VA_ARGS__);  \
+       goto failed;                                            \
+    }while(0)
+
+static bool_t record_socket_gotaddr(struct udpcommon *uc, struct udpsock *us,
+                                   int failmsgclass)
+{
+    struct commcommon *cc=&uc->cc;
+    FILLZERO(us->addr);
+    socklen_t salen=sizeof(us->addr);
+    int r=getsockname(us->fd,&us->addr.sa,&salen);
+    if (r) FAIL("getsockname()");
+    if (salen>sizeof(us->addr)) { errno=0; FAIL("getsockname() length"); }
+    return True;
+
+ failed:
+    return False;
+}
+
+bool_t udp_import_socket(struct udpcommon *uc, struct udpsock *us,
+                        int failmsgclass, int fd)
+{
+    FILLZERO(us->experienced);
+    us->fd=fd;
+    return record_socket_gotaddr(uc,us,failmsgclass);
+}
+
 bool_t udp_make_socket(struct udpcommon *uc, struct udpsock *us,
                       int failmsgclass)
 {
@@ -226,12 +255,6 @@ bool_t udp_make_socket(struct udpcommon *uc, struct udpsock *us,
     struct commcommon *cc=&uc->cc;
     us->fd=-1;
 
-#define FAIL_LG 0, cc->cl.description, &cc->loc, failmsgclass
-#define FAIL(...) do{                                          \
-        lg_perror(FAIL_LG,errno,__VA_ARGS__);  \
-       goto failed;                                            \
-    }while(0)
-
     FILLZERO(us->experienced);
     us->fd=socket(addr->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
     if (us->fd<0) FAIL("socket");
@@ -303,14 +326,18 @@ bool_t udp_make_socket(struct udpcommon *uc, struct udpsock *us,
        if (bind(us->fd, &addr->sa, iaddr_socklen(addr))!=0)
            FAIL("bind (%s)",iaddr_to_string(addr));
     }
+
+    bool_t ok=record_socket_gotaddr(uc,us,failmsgclass);
+    if (!ok) goto failed;
+
     return True;
 
 failed:
     udp_destroy_socket(uc,us);
     return False;
+}
 
 #undef FAIL
-}
 
 void udp_socks_register(struct udpcommon *uc, struct udpsocks *socks)
 {
diff --git a/util.c b/util.c
index fb0a4316d8ea15534fa2ae66935170d230fc6a7b..b96ffa437e05425a8206cd01109abdb30fbc2c9e 100644 (file)
--- a/util.c
+++ b/util.c
@@ -589,19 +589,22 @@ const char *iaddr_to_string(const union iaddr *ia)
     return bufs[b];
 }
 
-bool_t iaddr_equal(const union iaddr *ia, const union iaddr *ib)
+bool_t iaddr_equal(const union iaddr *ia, const union iaddr *ib,
+                  bool_t ignoreport)
 {
     if (ia->sa.sa_family != ib->sa.sa_family)
        return 0;
     switch (ia->sa.sa_family) {
     case AF_INET:
        return ia->sin.sin_addr.s_addr == ib->sin.sin_addr.s_addr
-           && ia->sin.sin_port        == ib->sin.sin_port;
+           && (ignoreport ||
+              ia->sin.sin_port        == ib->sin.sin_port);
 #ifdef CONFIG_IPV6
     case AF_INET6:
        return !memcmp(&ia->sin6.sin6_addr, &ib->sin6.sin6_addr, 16)
-           && ia->sin6.sin6_scope_id  == ib->sin6.sin6_scope_id
-           && ia->sin6.sin6_port      == ib->sin6.sin6_port
+          &&  ia->sin6.sin6_scope_id  == ib->sin6.sin6_scope_id
+           && (ignoreport ||
+              ia->sin6.sin6_port      == ib->sin6.sin6_port)
            /* we ignore the flowinfo field */;
 #endif /* CONFIG_IPV6 */
     default:
diff --git a/util.h b/util.h
index 82ed8223ffc6aeff3a1bd70739273d5cd5704bb1..fcdc10787614673b49d6a2617f628dcf5e75815a 100644 (file)
--- a/util.h
+++ b/util.h
@@ -83,7 +83,8 @@ extern void send_nak(const struct comm_addr *dest, uint32_t our_index,
 extern int consttime_memeq(const void *s1, const void *s2, size_t n);
 
 const char *iaddr_to_string(const union iaddr *ia);
-bool_t iaddr_equal(const union iaddr *ia, const union iaddr *ib);
+bool_t iaddr_equal(const union iaddr *ia, const union iaddr *ib,
+                  bool_t ignoreport);
 int iaddr_socklen(const union iaddr *ia);
 
 void text2iaddr(const item_t *item, uint16_t port, union iaddr *ia,