Fix resolve name to resolve IPv6 addresses of link-local%ifaddr
authorJeremy Allison <jra@samba.org>
Fri, 26 Oct 2007 01:28:36 +0000 (18:28 -0700)
committerJeremy Allison <jra@samba.org>
Fri, 26 Oct 2007 01:28:36 +0000 (18:28 -0700)
Jeremy.
(This used to be commit e6609cab732d5cd5cc9a5ae50aee15147f2ec6ec)

source3/lib/interface.c
source3/lib/interfaces.c
source3/lib/util_sock.c
source3/libsmb/namequery.c

index 49bbceceb78e06053c327b5a6c6fe23dc099c56e..9d073bc08ca57e9c07f18604e886c3d29784a190 100644 (file)
@@ -88,6 +88,29 @@ bool is_local_net(const struct sockaddr_storage *from)
        return false;
 }
 
+#if defined(HAVE_IPV6)
+void setup_linklocal_scope_id(struct sockaddr_storage *pss)
+{
+       struct interface *i;
+       for (i=local_interfaces;i;i=i->next) {
+               if (addr_equal(&i->ip,pss)) {
+                       struct sockaddr_in6 *psa6 =
+                               (struct sockaddr_in6 *)pss;
+                       psa6->sin6_scope_id = if_nametoindex(i->name);
+                       return;
+               }
+       }
+       for (i=local_interfaces;i;i=i->next) {
+               if (same_net(pss, &i->ip, &i->netmask)) {
+                       struct sockaddr_in6 *psa6 =
+                               (struct sockaddr_in6 *)pss;
+                       psa6->sin6_scope_id = if_nametoindex(i->name);
+                       return;
+               }
+       }
+}
+#endif
+
 /****************************************************************************
  Check if a packet is from a local (known) net.
 **************************************************************************/
@@ -325,100 +348,6 @@ static void add_interface(const struct iface_struct *ifs)
                        &iface->netmask) ));
 }
 
-/****************************************************************************
- Create a struct sockaddr_storage with the netmask bits set to 1.
-****************************************************************************/
-
-bool make_netmask(struct sockaddr_storage *pss_out,
-                       const struct sockaddr_storage *pss_in,
-                       unsigned long masklen)
-{
-       *pss_out = *pss_in;
-       /* Now apply masklen bits of mask. */
-#if defined(HAVE_IPV6)
-       if (pss_in->ss_family == AF_INET6) {
-               char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
-               unsigned int i;
-
-               if (masklen > 128) {
-                       return false;
-               }
-               for (i = 0; masklen >= 8; masklen -= 8, i++) {
-                       *p++ = 0xff;
-               }
-               /* Deal with the partial byte. */
-               *p++ &= (0xff & ~(0xff>>masklen));
-               i++;
-               for (;i < sizeof(struct in6_addr); i++) {
-                       *p++ = '\0';
-               }
-               return true;
-       }
-#endif
-       if (pss_in->ss_family == AF_INET) {
-               if (masklen > 32) {
-                       return false;
-               }
-               ((struct sockaddr_in *)pss_out)->sin_addr.s_addr =
-                       htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL));
-               return true;
-       }
-       return false;
-}
-
-/****************************************************************************
- Create a struct sockaddr_storage set to the broadcast or network adress from
- an incoming sockaddr_storage.
-****************************************************************************/
-
-static void make_bcast_or_net(struct sockaddr_storage *pss_out,
-                       const struct sockaddr_storage *pss_in,
-                       const struct sockaddr_storage *nmask,
-                       bool make_bcast)
-{
-       unsigned int i = 0, len = 0;
-       char *pmask = NULL;
-       char *p = NULL;
-       *pss_out = *pss_in;
-
-       /* Set all zero netmask bits to 1. */
-#if defined(HAVE_IPV6)
-       if (pss_in->ss_family == AF_INET6) {
-               p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
-               pmask = (char *)&((struct sockaddr_in6 *)nmask)->sin6_addr;
-               len = 16;
-       }
-#endif
-       if (pss_in->ss_family == AF_INET) {
-               p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr;
-               pmask = (char *)&((struct sockaddr_in *)nmask)->sin_addr;
-               len = 4;
-       }
-
-       for (i = 0; i < len; i++, p++, pmask++) {
-               if (make_bcast) {
-                       *p = (*p & *pmask) | (*pmask ^ 0xff);
-               } else {
-                       /* make_net */
-                       *p = (*p & *pmask);
-               }
-       }
-}
-
-static void make_bcast(struct sockaddr_storage *pss_out,
-                       const struct sockaddr_storage *pss_in,
-                       const struct sockaddr_storage *nmask)
-{
-       make_bcast_or_net(pss_out, pss_in, nmask, true);
-}
-
-static void make_net(struct sockaddr_storage *pss_out,
-                       const struct sockaddr_storage *pss_in,
-                       const struct sockaddr_storage *nmask)
-{
-       make_bcast_or_net(pss_out, pss_in, nmask, false);
-}
-
 /****************************************************************************
  Interpret a single element from a interfaces= config line.
 
index ee5d9dfb5429a5ce0f32f4bd42dc84b3b700993e..48fe2490345ee34315d3d9dbaee6c84db1fca762 100644 (file)
 #include "interfaces.h"
 #include "lib/replace/replace.h"
 
+/****************************************************************************
+ Utility functions.
+****************************************************************************/
+
+/****************************************************************************
+ Create a struct sockaddr_storage with the netmask bits set to 1.
+****************************************************************************/
+
+bool make_netmask(struct sockaddr_storage *pss_out,
+                       const struct sockaddr_storage *pss_in,
+                       unsigned long masklen)
+{
+       *pss_out = *pss_in;
+       /* Now apply masklen bits of mask. */
+#if defined(HAVE_IPV6)
+       if (pss_in->ss_family == AF_INET6) {
+               char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
+               unsigned int i;
+
+               if (masklen > 128) {
+                       return false;
+               }
+               for (i = 0; masklen >= 8; masklen -= 8, i++) {
+                       *p++ = 0xff;
+               }
+               /* Deal with the partial byte. */
+               *p++ &= (0xff & ~(0xff>>masklen));
+               i++;
+               for (;i < sizeof(struct in6_addr); i++) {
+                       *p++ = '\0';
+               }
+               return true;
+       }
+#endif
+       if (pss_in->ss_family == AF_INET) {
+               if (masklen > 32) {
+                       return false;
+               }
+               ((struct sockaddr_in *)pss_out)->sin_addr.s_addr =
+                       htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL));
+               return true;
+       }
+       return false;
+}
+
+/****************************************************************************
+ Create a struct sockaddr_storage set to the broadcast or network adress from
+ an incoming sockaddr_storage.
+****************************************************************************/
+
+static void make_bcast_or_net(struct sockaddr_storage *pss_out,
+                       const struct sockaddr_storage *pss_in,
+                       const struct sockaddr_storage *nmask,
+                       bool make_bcast)
+{
+       unsigned int i = 0, len = 0;
+       char *pmask = NULL;
+       char *p = NULL;
+       *pss_out = *pss_in;
+
+       /* Set all zero netmask bits to 1. */
+#if defined(HAVE_IPV6)
+       if (pss_in->ss_family == AF_INET6) {
+               p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
+               pmask = (char *)&((struct sockaddr_in6 *)nmask)->sin6_addr;
+               len = 16;
+       }
+#endif
+       if (pss_in->ss_family == AF_INET) {
+               p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr;
+               pmask = (char *)&((struct sockaddr_in *)nmask)->sin_addr;
+               len = 4;
+       }
+
+       for (i = 0; i < len; i++, p++, pmask++) {
+               if (make_bcast) {
+                       *p = (*p & *pmask) | (*pmask ^ 0xff);
+               } else {
+                       /* make_net */
+                       *p = (*p & *pmask);
+               }
+       }
+}
+
+void make_bcast(struct sockaddr_storage *pss_out,
+                       const struct sockaddr_storage *pss_in,
+                       const struct sockaddr_storage *nmask)
+{
+       make_bcast_or_net(pss_out, pss_in, nmask, true);
+}
+
+void make_net(struct sockaddr_storage *pss_out,
+                       const struct sockaddr_storage *pss_in,
+                       const struct sockaddr_storage *nmask)
+{
+       make_bcast_or_net(pss_out, pss_in, nmask, false);
+}
+
 /****************************************************************************
  Try the "standard" getifaddrs/freeifaddrs interfaces.
  Also gets IPv6 interfaces.
@@ -137,11 +235,20 @@ static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
                memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
                memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
 
-               if ((ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) &&
-                               ifptr->ifa_broadaddr) {
-                       memcpy(&ifaces[total].bcast,
-                               ifptr->ifa_broadaddr,
-                               copy_size);
+               if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
+                       if (ifptr->ifa_broadaddr) {
+                               memcpy(&ifaces[total].bcast,
+                                       ifptr->ifa_broadaddr,
+                                       copy_size);
+                       } else {
+                               /* For some reason ifptr->ifa_broadaddr
+                                * is null. Make one from ifa_addr and
+                                * ifa_netmask.
+                                */
+                               make_bcast(&ifaces[total].bcast,
+                                       &ifaces[total].ip,
+                                       &ifaces[total].netmask);
+                       }
                } else if ((ifaces[total].flags & IFF_POINTOPOINT) &&
                               ifptr->ifa_dstaddr ) {
                        memcpy(&ifaces[total].bcast,
index 8a85f7a5c5586be3855fe0794085a3a0af89d399..c30f21eeb7f53ee43f03d513f2aaa25d434ccf56 100644 (file)
@@ -64,7 +64,7 @@ bool is_ipaddress(const char *str)
 }
 
 /****************************************************************************
- Is a sockaddr_storage a broadcast address ? 
+ Is a sockaddr_storage a broadcast address ?
 ****************************************************************************/
 
 bool is_broadcast_addr(const struct sockaddr_storage *pss)
@@ -1478,6 +1478,10 @@ int open_socket_out(int type,
        if (pss->ss_family == AF_INET6) {
                struct sockaddr_in6 *psa6 = (struct sockaddr_in6 *)&sock_out;
                psa6->sin6_port = htons(port);
+               if (psa6->sin6_scope_id == 0 &&
+                               IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) {
+                       setup_linklocal_scope_id(&sock_out);
+               }
        }
 #endif
        if (pss->ss_family == AF_INET) {
index 34fe09b8c2988e475d4af827daefb09b1656de83..90e6be6a909ec530beb8c65e1c1b0e5df5a3ff66 100644 (file)
@@ -1594,6 +1594,32 @@ bool resolve_name(const char *name,
        char *sitename = NULL;
        int count = 0;
 
+#if defined(HAVE_IPV6)
+       unsigned int if_idx = 0;
+       const char *p = strchr_m(name, '%');
+
+       if (p && (if_idx = if_nametoindex(p+1)) != 0) {
+               char *newname = SMB_STRDUP(name);
+               if (!newname) {
+                       return false;
+               }
+               newname[PTR_DIFF(p,name)] = '\0';
+               if (is_ipaddress(newname) &&
+                               interpret_string_addr(return_ss,
+                                       newname, AI_NUMERICHOST)) {
+                       struct sockaddr_in6 *psa6 =
+                               (struct sockaddr_in6 *)&return_ss;
+                       if (psa6->sin6_scope_id == 0 &&
+                                       IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) {
+                               psa6->sin6_scope_id = if_idx;
+                       }
+                       SAFE_FREE(newname);
+                       return true;
+               }
+               SAFE_FREE(newname);
+       }
+#endif
+
        if (is_ipaddress(name)) {
                return interpret_string_addr(return_ss, name, AI_NUMERICHOST);
        }