The attached patch is against Squid 3.5. Given Squid 3.5's status as a stable release, this probably won't be integrated into the vanilla Squid release, but I'm posting here in case anyone finds it useful, or if someone wants to port it to Squid 4.


Squid currently supports ACLs and logformat specifiers which rely on the EUI-48 (MAC address) for IPv4 traffic, and EUI-64 for IPv6 traffic.

For IPv4, Squid queries the ARP cache for the client's address. For IPv6, Squid extracts the EUI-64 from site-local SLAAC addresses. This isn't going to work for most clients, since site-local addresses are rarely used in the real world. This patch brings the IPv6 functionality in line with the IPv4 functionality by querying the neighbour table using rtnetlink.

Open question: we could also pull the EUI-64 from a global scope SLAAC address. Would it be trustworthy enough? Is it worth doing? Since most clients now use privacy extensions it's probably not worthwhile.


Notes:

- We have to examine the entire neighbour table since (as far as I can tell) the kernel doesn't allow querying a specific IP address. This could be slow if there are a lot of neighbours.

- The IPv4 neighbour table can be retrieved in the same way, so there is scope for unifying the IPv6 and IPv4 code.

- The neighbour table contains MAC addresses (i.e. EUI-48), not EUI-64 addresses. This patch converts the retrieved EUI-48 into an EUI-64 by inserting 0xfffe into the middle.

- If the IPv4/IPv6 code is to be unified in the future, consider converting everything to EUI-64 instead of making a distinction between EUI-48 and EUI-64.

- This code is useful where users are being authenticated through a mechanism other than HTTP proxy auth. For example, a client can identify itself through a captive portal, but then use a combination of IPv4 and numerous IPv6 addresses (due to privacy extensions) thereafter. The client can be linked back to their portal login through their MAC, irrespective of the IP address they are using for any given request.

- Obviously the client needs to be on the same layer 2 network as Squid, so this doesn't help in situations where clients are behind a router.

--
 - Steve Hill
   Technical Director
   Opendium    Online Safety / Web Filtering    http://www.opendium.com

   Enquiries                 Support
   ---------                 -------
   [email protected]        [email protected]
   +44-1792-824568           +44-1792-825748
Index: squid/trunk/source/src/eui/Eui64.cc
===================================================================
--- squid/trunk/source/src/eui/Eui64.cc	(revision 529)
+++ squid/trunk/source/src/eui/Eui64.cc	(working copy)
@@ -12,6 +12,11 @@
 
 #if USE_SQUID_EUI
 
+#if _SQUID_LINUX_
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#endif
+
 #include "compat/eui64_aton.h"
 #include "Debug.h"
 #include "eui/Eui64.h"
@@ -77,19 +82,95 @@
 bool
 Eui::Eui64::lookupNdp(const Ip::Address &c)
 {
-#if 0 /* no actual lookup coded yet */
+    bool success = false;
 
-    /* no OS yet supported for NDP protocol lookup */
-    debugs(28, DBG_CRITICAL, "ERROR: ARP / MAC / EUI-* operations not supported on this operating system.");
+#if _SQUID_LINUX_
+    int rtnetlink_sock;
+    struct {
+        struct nlmsghdr hdr;
+        struct ndmsg ndm;
+    } req;
+    struct sockaddr_nl sa;
+    struct iovec iov;
+    struct msghdr msg;
+    char buf[16384];
+    ssize_t buff_fill;
+    struct nlmsghdr * nl_reply_hdr;
+    struct nlmsghdr * nl_reply_msg;
+    struct rtattr * rt_reply_attr;
+    size_t rt_reply_len;
+    bool found_ip = 0;
+    bool done = 0;
+    struct in6_addr ip;
+    void * mac_ptr;
 
-    /*
-     * Address was not found on any interface
-     */
-    debugs(28, 3, "id=" << (void*)this << ' ' << c << " NOT found");
-#endif /* 0 */
+    c.getInAddr(ip);
 
-    clear();
-    return false;
+    // Prepare rtnetlink request
+    req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+    req.hdr.nlmsg_type = RTM_GETNEIGH;
+    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+    req.hdr.nlmsg_seq = 0;
+    req.hdr.nlmsg_pid = 0;
+    req.ndm.ndm_family = AF_INET6;
+    req.ndm.ndm_state = 0;
+    
+    iov.iov_base = &req;
+    iov.iov_len = req.hdr.nlmsg_len;
+            
+    msg.msg_name = &sa;
+    msg.msg_namelen = sizeof(sa);
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = NULL;
+    msg.msg_controllen = 0;
+    msg.msg_flags = 0;
+
+    // Send rtnetlink request
+    rtnetlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+    if (rtnetlink_sock >= 0) {
+        memset(&sa, 0, sizeof(sa));
+        sa.nl_family = AF_NETLINK;
+        if (sendmsg(rtnetlink_sock, &msg, 0) >= 0) {
+            // Loop through all the responses
+            while ((! done) && ((buff_fill = recv(rtnetlink_sock, buf, sizeof(buf), 0)) >= 0)) {
+                mac_ptr = NULL;
+                for (nl_reply_hdr = (struct nlmsghdr *) buf; (! done) && NLMSG_OK(nl_reply_hdr, buff_fill); nl_reply_hdr = NLMSG_NEXT(nl_reply_hdr, buff_fill)) {
+                    if (nl_reply_hdr->nlmsg_type == NLMSG_DONE) done = 1;
+                    else if (nl_reply_hdr->nlmsg_type == RTM_NEWNEIGH) {
+                        nl_reply_msg = (struct nlmsghdr *) NLMSG_DATA(nl_reply_hdr);
+                        rt_reply_len = RTM_PAYLOAD(nl_reply_hdr);
+
+                        for (rt_reply_attr = RTM_RTA(nl_reply_msg); (! done) && RTA_OK(rt_reply_attr, rt_reply_len); rt_reply_attr = RTA_NEXT(rt_reply_attr, rt_reply_len)) {
+                            if (rt_reply_attr->rta_type == NDA_DST) {
+                                if (memcmp(ip.s6_addr, RTA_DATA(rt_reply_attr), 16)) {
+                                    // IP doesn't match - don't loop through the rest of the RTAs
+                                    break;
+                                } else found_ip = 1;
+                            } else if (rt_reply_attr->rta_type == NDA_LLADDR) mac_ptr = RTA_DATA(rt_reply_attr);
+
+                            if (found_ip && mac_ptr) {
+                                // We've found the right IP and its MAC address.
+                                // Insert 0xfffe to turn the MAC (EUI48) into an EUI64.
+                                memcpy(eui, mac_ptr, 3);
+                                eui[3] = 0xff;
+                                eui[4] = 0xfe;
+                                memcpy(eui+5, ((char *) mac_ptr) + 3, 3);
+                                done = 1;
+                                success = 1;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        close(rtnetlink_sock);
+    }
+    if (! success) debugs(28, 4, "id=" << (void*)this << " NDP Fail on " << c);
+#endif
+
+    if (! success) clear();
+    return success;
 }
 
 #endif /* USE_SQUID_EUI */
_______________________________________________
squid-dev mailing list
[email protected]
http://lists.squid-cache.org/listinfo/squid-dev

Reply via email to