[PATCH 5/6] libsnmp/transports: Add support for interface binding

Bart Van Assche <[email protected]>
Newsgroups gmane.network.net-snmp.devel
Message-ID <[email protected]>
---
 include/net-snmp/library/snmpIPBaseDomain.h |  2 +
 snmplib/transports/snmpIPBaseDomain.c       | 41 +++++++++++++++++++--
 snmplib/transports/snmpIPv4BaseDomain.c     |  2 +
 snmplib/transports/snmpIPv6BaseDomain.c     |  2 +
 snmplib/transports/snmpTCPDomain.c          |  7 ++++
 snmplib/transports/snmpTCPIPv6Domain.c      |  6 +++
 snmplib/transports/snmpUDPIPv4BaseDomain.c  | 13 ++++++-
 snmplib/transports/snmpUDPIPv6Domain.c      | 13 ++++++-
 8 files changed, 79 insertions(+), 7 deletions(-)

diff --git a/include/net-snmp/library/snmpIPBaseDomain.h b/include/net-snmp/library/snmpIPBaseDomain.h
index 6eaf37ac4253..635c748ac930 100644
--- a/include/net-snmp/library/snmpIPBaseDomain.h
+++ b/include/net-snmp/library/snmpIPBaseDomain.h
@@ -26,9 +26,11 @@ struct netsnmp_ep {
  */
 struct netsnmp_ep_str {
     char     addr[64];
+    char     iface[16];
     uint16_t port;
 };
 
 int netsnmp_parse_ep_str(struct netsnmp_ep_str *ep_str, const char *endpoint);
+int netsnmp_bindtodevice(int fd, const char *iface);
 
 #endif /* _SNMPIPBASEDOMAIN_H_ */
diff --git a/snmplib/transports/snmpIPBaseDomain.c b/snmplib/transports/snmpIPBaseDomain.c
index ac94ba73840d..ea0a291c8529 100644
--- a/snmplib/transports/snmpIPBaseDomain.c
+++ b/snmplib/transports/snmpIPBaseDomain.c
@@ -3,6 +3,7 @@
 #include <net-snmp/library/system.h>
 #include <net-snmp/library/snmpIPBaseDomain.h>
 #include <ctype.h>
+#include <errno.h>
 #include <stdlib.h>
 
 static int isnumber(const char *cp)
@@ -23,7 +24,7 @@ static int isnumber(const char *cp)
  */
 int netsnmp_parse_ep_str(struct netsnmp_ep_str *ep_str, const char *endpoint)
 {
-    char *dup, *cp, *addrstr = NULL, *portstr = NULL;
+    char *dup, *cp, *addrstr = NULL, *iface = NULL, *portstr = NULL;
     unsigned port;
 
     if (!endpoint)
@@ -46,9 +47,16 @@ int netsnmp_parse_ep_str(struct netsnmp_ep_str *ep_str, const char *endpoint)
             } else {
                 goto invalid;
             }
-        } else if (*cp != ':') {
+        } else if (*cp != '@' && *cp != ':') {
             addrstr = cp;
-            cp = strrchr(cp, ':');
+            cp = strchr(addrstr, '@');
+            if (!cp)
+                cp = strrchr(addrstr, ':');
+        }
+        if (cp && *cp == '@') {
+            *cp = '\0';
+            iface = cp + 1;
+            cp = strchr(cp + 1, ':');
         }
         if (cp && *cp == ':') {
             *cp++ = '\0';
@@ -62,6 +70,8 @@ int netsnmp_parse_ep_str(struct netsnmp_ep_str *ep_str, const char *endpoint)
 
     if (addrstr)
         strlcpy(ep_str->addr, addrstr, sizeof(ep_str->addr));
+    if (iface)
+        strlcpy(ep_str->iface, iface, sizeof(ep_str->iface));
     if (portstr) {
         port = atoi(portstr);
         if (port > 0 && port <= 0xffff)
@@ -77,3 +87,28 @@ invalid:
     free(dup);
     return 0;
 }
+
+int netsnmp_bindtodevice(int fd, const char *iface)
+{
+    /* If no interface name has been specified, report success. */
+    if (!iface || iface[0] == '\0')
+        return 0;
+
+#ifdef HAVE_SO_BINDTODEVICE
+    /*
+     * +1 to work around the Linux kernel bug that the passed in name is not
+     * '\0'-terminated.
+     */
+    int ifacelen = strlen(iface) + 1;
+    int ret;
+
+    ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface, ifacelen);
+    if (ret < 0)
+        snmp_log(LOG_ERR, "Binding socket to interface %s failed: %s\n", iface,
+                 strerror(errno));
+    return ret;
+#else
+    errno = EINVAL;
+    return -1;
+#endif
+}
diff --git a/snmplib/transports/snmpIPv4BaseDomain.c b/snmplib/transports/snmpIPv4BaseDomain.c
index d8537fbdc912..c3f2671cbe98 100644
--- a/snmplib/transports/snmpIPv4BaseDomain.c
+++ b/snmplib/transports/snmpIPv4BaseDomain.c
@@ -108,6 +108,8 @@ netsnmp_sockaddr_in3(struct netsnmp_ep *ep,
         if (netsnmp_parse_ep_str(&ep_str, inpeername)) {
             if (ep_str.port)
                 addr->sin_port = htons(ep_str.port);
+            if (ep_str.iface[0])
+                strlcpy(ep->iface, ep_str.iface, sizeof(ep->iface));
             if (strcmp(ep_str.addr, "255.255.255.255") == 0 ) {
                 /*
                  * The explicit broadcast address hack
diff --git a/snmplib/transports/snmpIPv6BaseDomain.c b/snmplib/transports/snmpIPv6BaseDomain.c
index c2baacc5d42c..1a87ee97767e 100644
--- a/snmplib/transports/snmpIPv6BaseDomain.c
+++ b/snmplib/transports/snmpIPv6BaseDomain.c
@@ -309,6 +309,8 @@ netsnmp_sockaddr_in6_3(struct netsnmp_ep *ep,
                     !netsnmp_resolve_v6_hostname(&addr->sin6_addr, ep_str.addr))
                     return 0;
             }
+            if (ep_str.iface[0])
+                strlcpy(ep->iface, ep_str.iface, sizeof(ep->iface));
             if (ep_str.port)
                 addr->sin6_port = htons(ep_str.port);
         }
diff --git a/snmplib/transports/snmpTCPDomain.c b/snmplib/transports/snmpTCPDomain.c
index ac9b66a16fd5..cfe6b13d75de 100644
--- a/snmplib/transports/snmpTCPDomain.c
+++ b/snmplib/transports/snmpTCPDomain.c
@@ -227,6 +227,13 @@ netsnmp_tcp_transport(const struct netsnmp_ep *ep, int local)
 		   sizeof(opt));
 
         if (!socket_initialized) {
+            rc = netsnmp_bindtodevice(t->sock, ep->iface);
+            if (rc != 0) {
+                DEBUGMSGTL(("netsnmp_tcpbase",
+                            "failed to bind to iface %s: %s\n",
+                            ep->iface, strerror(errno)));
+                goto err;
+            }
             rc = bind(t->sock, (const struct sockaddr *)addr, sizeof(*addr));
             if (rc != 0)
                 goto err;
diff --git a/snmplib/transports/snmpTCPIPv6Domain.c b/snmplib/transports/snmpTCPIPv6Domain.c
index cdff5fab6843..47bf5747840b 100644
--- a/snmplib/transports/snmpTCPIPv6Domain.c
+++ b/snmplib/transports/snmpTCPIPv6Domain.c
@@ -231,6 +231,12 @@ netsnmp_tcp6_transport(const struct netsnmp_ep *ep, int local)
         setsockopt(t->sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
 
         if (!socket_initialized) {
+            rc = netsnmp_bindtodevice(t->sock, ep->iface);
+            if (rc != 0) {
+                DEBUGMSGTL(("netsnmp_tcp6", "failed to bind to iface %s: %s\n",
+                            ep->iface, strerror(errno)));
+                goto err;
+            }
             rc = bind(t->sock, (const struct sockaddr *)addr, sizeof(*addr));
             if (rc != 0)
                 goto err;
diff --git a/snmplib/transports/snmpUDPIPv4BaseDomain.c b/snmplib/transports/snmpUDPIPv4BaseDomain.c
index 7f8b68108d6d..eb30ea99f7ec 100644
--- a/snmplib/transports/snmpUDPIPv4BaseDomain.c
+++ b/snmplib/transports/snmpUDPIPv4BaseDomain.c
@@ -208,16 +208,25 @@ netsnmp_udpipv4base_transport_bind(netsnmp_transport *t,
                     t->sock, str));
         free(str);
     }
+    rc = netsnmp_bindtodevice(t->sock, ep->iface);
+    if (rc != 0) {
+        DEBUGMSGTL(("netsnmp_udpbase", "failed to bind to iface %s: %s\n",
+                    ep->iface, strerror(errno)));
+        goto err;
+    }
     rc = bind(t->sock, (const struct sockaddr *)addr, sizeof(*addr));
     if ( rc != 0 ) {
         DEBUGMSGTL(("netsnmp_udpbase",
                     "failed to bind for clientaddr: %d %s\n",
                     errno, strerror(errno)));
-        netsnmp_socketbase_close(t);
-        return 1;
+        goto err;
     }
 
     return 0;
+
+err:
+    netsnmp_socketbase_close(t);
+    return 1;
 }
 
 void
diff --git a/snmplib/transports/snmpUDPIPv6Domain.c b/snmplib/transports/snmpUDPIPv6Domain.c
index 1f8c3765f21d..083edf099253 100644
--- a/snmplib/transports/snmpUDPIPv6Domain.c
+++ b/snmplib/transports/snmpUDPIPv6Domain.c
@@ -312,15 +312,24 @@ netsnmp_udp6_transport_bind(netsnmp_transport *t,
                     t->sock, str));
         free(str);
     }
+    rc = netsnmp_bindtodevice(t->sock, ep->iface);
+    if (rc != 0) {
+        DEBUGMSGTL(("netsnmp_udp6", "failed to bind to iface %s: %s\n",
+                    ep->iface, strerror(errno)));
+        goto err;
+    }
     rc = bind(t->sock, (const struct sockaddr *)addr, sizeof(*addr));
     if (rc != 0) {
         DEBUGMSGTL(("netsnmp_udp6", "failed to bind for clientaddr: %d %s\n",
                     errno, strerror(errno)));
-        netsnmp_socketbase_close(t);
-        return -1;
+        goto err;
     }
 
     return 0;
+
+err:
+    netsnmp_socketbase_close(t);
+    return -1;
 }
 
 int
-- 
2.19.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.