Attached is a patch that seems to work on my system,
against 2.0.48.

I am not completely clear on apache memory management,
and I think it may leak the char* device memory allocated
in the url-parsing code.  I am also not sure that the url
parsing code handles all cases correctly.  It
does handle this at least:  eth4:192.168.1.5:80

There is printf debugging still in the patch.

Please consider this patch, or some other method of
achieving the SO_BINDTODEVICE call in a configurable
manner.

One question:  I tried make distclean before making a
recursive diff, but not all of the auto-generated files
were cleaned up.  Is there a better way to make diffs?

Thanks,
Ben

--
Ben Greear <[EMAIL PROTECTED]>
Candela Technologies Inc  http://www.candelatech.com

diff -u -r -N httpd-2.0.48/include/ap_listen.h httpd-2.0.48.ben/include/ap_listen.h
--- httpd-2.0.48/include/ap_listen.h    2003-02-03 09:31:29.000000000 -0800
+++ httpd-2.0.48.ben/include/ap_listen.h        2004-02-11 20:24:23.000000000 -0800
@@ -91,6 +91,10 @@
      * Is this socket currently active 
      */
     int active;
+
+    /** The device to bind to, empty string for no binding.
+     */
+    char bind_device[16];
 /* more stuff here, like which protocol is bound to the port */
 };
 
diff -u -r -N httpd-2.0.48/include/httpd.h httpd-2.0.48.ben/include/httpd.h
--- httpd-2.0.48/include/httpd.h        2003-10-24 09:19:31.000000000 -0700
+++ httpd-2.0.48.ben/include/httpd.h    2004-02-11 19:51:43.000000000 -0800
@@ -1043,6 +1043,8 @@
     apr_port_t host_port;
     /** The name given in <VirtualHost> */
     char *virthost;
+    /** The device name to bind this server to */
+    char *host_device; 
 };
 
 /** A structure to store information for each virtual server */
diff -u -r -N httpd-2.0.48/server/listen.c httpd-2.0.48.ben/server/listen.c
--- httpd-2.0.48/server/listen.c        2003-03-30 20:30:52.000000000 -0800
+++ httpd-2.0.48.ben/server/listen.c    2004-02-12 22:31:49.000000000 -0800
@@ -165,7 +165,7 @@
     ap_sock_disable_nagle(s);
 #endif
 
-    if ((stat = apr_bind(s, server->bind_addr)) != APR_SUCCESS) {
+    if ((stat = apr_bind(s, server->bind_addr, server->bind_device)) != APR_SUCCESS) {
         ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, stat, p,
                       "make_sock: could not bind to address %pI",
                       server->bind_addr);
@@ -253,7 +253,7 @@
         if ((sock_rv = apr_socket_create(&tmp_sock, APR_INET6, SOCK_STREAM, p)) 
             == APR_SUCCESS &&
             apr_sockaddr_info_get(&sa, NULL, APR_INET6, 0, 0, p) == APR_SUCCESS &&
-            apr_bind(tmp_sock, sa) == APR_SUCCESS) { 
+            apr_bind(tmp_sock, sa, NULL) == APR_SUCCESS) { 
             default_family = APR_INET6;
         }
         else {
@@ -267,7 +267,8 @@
 }
 
 
-static const char *alloc_listener(process_rec *process, char *addr, apr_port_t port)
+static const char *alloc_listener(process_rec *process, char *addr, apr_port_t port,
+                                  const char* device)
 {
     ap_listen_rec **walk;
     ap_listen_rec *new;
@@ -275,6 +276,14 @@
     apr_port_t oldport;
     apr_sockaddr_t *sa;
 
+    printf("addr: %p  port: %d  device: %p\n", addr, port, device);
+    if (addr) {
+       printf("addr -:%s:-\n", addr);
+    }
+    if (device) {
+       printf("device -:%s:-\n", device);
+    }
+
     if (!addr) { /* don't bind to specific interface */
         find_default_family(process->pool);
         switch(default_family) {
@@ -313,6 +322,15 @@
     /* this has to survive restarts */
     new = apr_palloc(process->pool, sizeof(ap_listen_rec));
     new->active = 0;
+
+    if (device) {
+       strncpy(new->bind_device, device, sizeof(new->bind_device));
+       new->bind_device[sizeof(new->bind_device) - 1] = 0;
+    }
+    else {
+       new->bind_device[0] = '\0';
+    }
+    
     if ((status = apr_sockaddr_info_get(&new->bind_addr, addr, APR_UNSPEC,
                                         port, 0, process->pool))
         != APR_SUCCESS) {
@@ -413,7 +431,7 @@
 
 const char *ap_set_listener(cmd_parms *cmd, void *dummy, const char *ips)
 {
-    char *host, *scope_id;
+    char *host, *scope_id, *device;
     apr_port_t port;
     apr_status_t rv;
     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
@@ -422,7 +440,7 @@
         return err;
     }
 
-    rv = apr_parse_addr_port(&host, &scope_id, &port, ips, cmd->pool);
+    rv = apr_parse_addr_port(&device, &host, &scope_id, &port, ips, cmd->pool);
     if (rv != APR_SUCCESS) {
         return "Invalid address or port";
     }
@@ -440,7 +458,7 @@
         return "Port must be specified";
     }
 
-    return alloc_listener(cmd->server->process, host, port);
+    return alloc_listener(cmd->server->process, host, port, device);
 }
 
 const char *ap_set_listenbacklog(cmd_parms *cmd, void *dummy, const char *arg)
diff -u -r -N httpd-2.0.48/server/rfc1413.c httpd-2.0.48.ben/server/rfc1413.c
--- httpd-2.0.48/server/rfc1413.c       2003-02-03 09:32:01.000000000 -0800
+++ httpd-2.0.48.ben/server/rfc1413.c   2004-02-11 20:15:40.000000000 -0800
@@ -164,7 +164,7 @@
  * addresses from the query socket.
  */
 
-    if ((rv = apr_bind(*newsock, localsa)) != APR_SUCCESS) {
+    if ((rv = apr_bind(*newsock, localsa, NULL)) != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, srv,
                      "rfc1413: Error binding query socket to local port");
         apr_socket_close(*newsock);
diff -u -r -N httpd-2.0.48/server/vhost.c httpd-2.0.48.ben/server/vhost.c
--- httpd-2.0.48/server/vhost.c 2003-02-03 09:32:01.000000000 -0800
+++ httpd-2.0.48.ben/server/vhost.c     2004-02-11 19:47:03.000000000 -0800
@@ -183,7 +183,7 @@
 {
     apr_sockaddr_t *my_addr;
     server_addr_rec *sar;
-    char *w, *host, *scope_id;
+    char *w, *host, *scope_id, *device;
     int wild_port;
     apr_size_t wlen;
     apr_port_t port;
@@ -205,7 +205,7 @@
             wild_port = 1;
         }
     }
-    rv = apr_parse_addr_port(&host, &scope_id, &port, w, p);
+    rv = apr_parse_addr_port(&device, &host, &scope_id, &port, w, p);
     /* If the string is "80", apr_parse_addr_port() will be happy and set
      * host to NULL and port to 80, so watch out for that.
      */
@@ -246,6 +246,7 @@
     sar = apr_pcalloc(p, sizeof(server_addr_rec));
     **paddr = sar;
     *paddr = &sar->next;
+    sar->host_device = device;
     sar->host_addr = my_addr;
     sar->host_port = port;
     sar->virthost = host;
@@ -740,7 +741,7 @@
  */
 static void fix_hostname(request_rec *r)
 {
-    char *host, *scope_id;
+    char *host, *scope_id, *device;
     char *dst;
     apr_port_t port;
     apr_status_t rv;
@@ -750,7 +751,7 @@
         return;
     }
 
-    rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool);
+    rv = apr_parse_addr_port(&device, &host, &scope_id, &port, r->hostname, r->pool);
     if (rv != APR_SUCCESS || scope_id) {
         goto bad;
     }
diff -u -r -N httpd-2.0.48/srclib/apr/include/apr_network_io.h 
httpd-2.0.48.ben/srclib/apr/include/apr_network_io.h
--- httpd-2.0.48/srclib/apr/include/apr_network_io.h    2003-10-16 15:13:02.000000000 
-0700
+++ httpd-2.0.48.ben/srclib/apr/include/apr_network_io.h        2004-02-11 
20:04:56.000000000 -0800
@@ -349,14 +349,16 @@
  * Bind the socket to its associated port
  * @param sock The socket to bind 
  * @param sa The socket address to bind to
+ * @param device  The device (ie, eth2) to bind to, or NULL for no binding.
  * @remark This may be where we will find out if there is any other process
  *      using the selected port.
  */
 APR_DECLARE(apr_status_t) apr_socket_bind(apr_socket_t *sock, 
-                                          apr_sockaddr_t *sa);
+                                          apr_sockaddr_t *sa,
+                                          const char* device);
 
 /** @deprecated @see apr_socket_bind */
-APR_DECLARE(apr_status_t) apr_bind(apr_socket_t *sock, apr_sockaddr_t *sa);
+APR_DECLARE(apr_status_t) apr_bind(apr_socket_t *sock, apr_sockaddr_t *sa, const 
char* device);
 
 /**
  * Listen to a bound socket for connections.
@@ -451,12 +453,17 @@
  *   www.apache.org:8080   (hostname and port number)
  *   [fe80::1]:80          (IPv6 numeric address string only)
  *   [fe80::1%eth0]        (IPv6 numeric address string and scope id)
+ *   eth0:192.168.1.1:8080 (Specify device to bind to as well)
+ *   eth0::8080            (Specify device to bind to, use first IP on device)
+ *
  *
  * Invalid strings:
  *                         (empty string)
  *   [abc]                 (not valid IPv6 numeric address string)
  *   abc:65536             (invalid port number)
  *
+ * @param device The new buffer containing just the device name.  On output,
+ *             *device will be NULL if no device was specified.
  * @param addr The new buffer containing just the hostname.  On output, *addr 
  *             will be NULL if no hostname/IP address was specfied.
  * @param scope_id The new buffer containing just the scope id.  On output, 
@@ -472,7 +479,8 @@
  *         required, check for addr == NULL in addition to checking the 
  *         return code.
  */
-APR_DECLARE(apr_status_t) apr_parse_addr_port(char **addr,
+APR_DECLARE(apr_status_t) apr_parse_addr_port(char **device,
+                                              char **addr,
                                               char **scope_id,
                                               apr_port_t *port,
                                               const char *str,
diff -u -r -N httpd-2.0.48/srclib/apr/network_io/unix/sockaddr.c 
httpd-2.0.48.ben/srclib/apr/network_io/unix/sockaddr.c
--- httpd-2.0.48/srclib/apr/network_io/unix/sockaddr.c  2003-09-29 12:08:15.000000000 
-0700
+++ httpd-2.0.48.ben/srclib/apr/network_io/unix/sockaddr.c      2004-02-13 
00:21:45.000000000 -0800
@@ -257,16 +257,19 @@
     return APR_SUCCESS;
 }
 
-APR_DECLARE(apr_status_t) apr_parse_addr_port(char **addr,
+
+APR_DECLARE(apr_status_t) apr_parse_addr_port(char **device,
+                                              char **addr,
                                               char **scope_id,
                                               apr_port_t *port,
                                               const char *str,
                                               apr_pool_t *p)
 {
-    const char *ch, *lastchar;
+    const char *ch, *lastchar, *addr_start;
     int big_port;
     apr_size_t addrlen;
 
+    *device = NULL;       /* assume not specified */
     *addr = NULL;         /* assume not specified */
     *scope_id = NULL;     /* assume not specified */
     *port = 0;            /* assume not specified */
@@ -298,18 +301,66 @@
         }
         *port = big_port;
         lastchar = ch - 1;
+        --ch; /* back over the ':' separator */
+    }
+
+    /* I think IP-v6 addrs can have ':' in them?? */
+    printf("str: %p(%s)  lastchar: %p(%s)  ch: %p(%c)\n",
+           str, str, lastchar, lastchar, ch, *ch);
+    addrlen = 0;
+    if (*lastchar == ']') {
+       while (ch >= str && (*ch != '[')) {
+          --ch;
+          addrlen++;
+       }
+       if (*ch != '[') {
+          /* BAD, need matching bracket. */
+          return APR_EINVAL;
+       }
+       --ch;
+       addrlen++;
+    }
+    else {
+       if (*lastchar == ':') {
+          /* Was something like eth0::80, we have no address specified */
+       }
+       else {
+          while (ch >= str && (*ch != ':')) {
+             --ch;
+             addrlen++;
+          }
+       }
+    }
+
+    printf("str: %p(%s)  lastchar: %p(%s)  ch: %p(%c)  addrlen: %d\n",
+           str, str, lastchar, lastchar, ch, *ch, addrlen);
+    
+    /* *ch points to the ':' at the end of eth0 if we are using that format */
+    
+    addr_start = ch + 1;
+
+    if (addr_start > (str + 1)) {
+       apr_size_t devlen = (addr_start - 1) - str;
+       if (devlen > 0) {
+          *device = apr_palloc(p, devlen + 1);
+          memcpy(*device, str, devlen);
+          (*device)[devlen] = '\0';
+       }
     }
 
+    if (addrlen == 0) {
+       goto out;
+    }
+    
     /* now handle the hostname */
-    addrlen = lastchar - str + 1;
 
 /* XXX we don't really have to require APR_HAVE_IPV6 for this; 
  * just pass char[] for ipaddr (so we don't depend on struct in6_addr)
  * and always define APR_INET6 
  */
 #if APR_HAVE_IPV6
-    if (*str == '[') {
-        const char *end_bracket = memchr(str, ']', addrlen);
+    if (*addr_start == '[') {
+        const char *end_bracket = memchr(addr_start, ']', addrlen);
         struct in6_addr ipaddr;
         const char *scope_delim;
 
@@ -319,13 +370,13 @@
         }
 
         /* handle scope id; this is the only context where it is allowed */
-        scope_delim = memchr(str, '%', addrlen);
+        scope_delim = memchr(addr_start, '%', addrlen);
         if (scope_delim) {
             if (scope_delim == end_bracket - 1) { /* '%' without scope id */
                 *port = 0;
                 return APR_EINVAL;
             }
-            addrlen = scope_delim - str - 1;
+            addrlen = scope_delim - addr_start - 1;
             *scope_id = apr_palloc(p, end_bracket - scope_delim);
             memcpy(*scope_id, scope_delim + 1, end_bracket - scope_delim - 1);
             (*scope_id)[end_bracket - scope_delim - 1] = '\0';
@@ -336,7 +387,7 @@
 
         *addr = apr_palloc(p, addrlen + 1);
         memcpy(*addr,
-               str + 1,
+               addr_start + 1,
                addrlen);
         (*addr)[addrlen] = '\0';
         if (apr_inet_pton(AF_INET6, *addr, &ipaddr) != 1) {
@@ -353,9 +404,11 @@
          *     for bogus scope ids first.
          */
         *addr = apr_palloc(p, addrlen + 1);
-        memcpy(*addr, str, addrlen);
+        memcpy(*addr, addr_start, addrlen);
         (*addr)[addrlen] = '\0';
     }
+    
+  out:
     return APR_SUCCESS;
 }
 
diff -u -r -N httpd-2.0.48/srclib/apr/network_io/unix/sockets.c 
httpd-2.0.48.ben/srclib/apr/network_io/unix/sockets.c
--- httpd-2.0.48/srclib/apr/network_io/unix/sockets.c   2003-07-08 05:53:11.000000000 
-0700
+++ httpd-2.0.48.ben/srclib/apr/network_io/unix/sockets.c       2004-02-11 
20:41:18.000000000 -0800
@@ -164,8 +164,22 @@
     return apr_pool_cleanup_run(thesocket->cntxt, thesocket, socket_cleanup);
 }
 
-apr_status_t apr_socket_bind(apr_socket_t *sock, apr_sockaddr_t *sa)
+apr_status_t apr_socket_bind(apr_socket_t *sock, apr_sockaddr_t *sa, const char* 
device)
 {
+
+#ifdef __linux__
+    if (device && strlen(device)) {
+       // Bind to specific device.
+       apr_status_t status = 0;
+       if (setsockopt(sock->socketdes, SOL_SOCKET, SO_BINDTODEVICE,
+                      device, strlen(device) + 1)) {
+          /*printf("Could not BINDTODEVICE, device: %s  error: %s\n",
+            device, strerror(errno)); */
+          /* Soldier on, this should not be fatal. */
+       }
+    }
+#endif
+   
     if (bind(sock->socketdes, 
              (struct sockaddr *)&sa->sa, sa->salen) == -1) {
         return errno;
@@ -428,9 +442,9 @@
 }
 
 /* deprecated */
-apr_status_t apr_bind(apr_socket_t *sock, apr_sockaddr_t *sa)
+apr_status_t apr_bind(apr_socket_t *sock, apr_sockaddr_t *sa, const char* device)
 {
-    return apr_socket_bind(sock, sa);
+    return apr_socket_bind(sock, sa, device);
 }
 
 /* deprecated */
diff -u -r -N httpd-2.0.48/support/ab.c httpd-2.0.48.ben/support/ab.c
--- httpd-2.0.48/support/ab.c   2003-09-15 08:40:06.000000000 -0700
+++ httpd-2.0.48.ben/support/ab.c       2004-02-11 20:11:13.000000000 -0800
@@ -1841,6 +1841,7 @@
     char *cp;
     char *h;
     char *scope_id;
+    char *device;
     apr_status_t rv;
 
     /* Save a copy for the proxy */
@@ -1870,7 +1871,7 @@
     h = apr_palloc(cntxt, cp - url + 1);
     memcpy(h, url, cp - url);
     h[cp - url] = '\0';
-    rv = apr_parse_addr_port(&hostname, &scope_id, &port, h, cntxt);
+    rv = apr_parse_addr_port(&device, &hostname, &scope_id, &port, h, cntxt);
     if (rv != APR_SUCCESS || !hostname || scope_id) {
        return 1;
     }

Reply via email to