[gssdp/wip/master/ipv6: 3/7] net-posix: Add mac lookup for IPv6
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gssdp/wip/master/ipv6: 3/7] net-posix: Add mac lookup for IPv6
- Date: Wed, 31 Oct 2018 17:25:53 +0000 (UTC)
commit 6c301ee52258cc432280a829d063fd20e6f2999a
Author: Jens Georg <mail jensge org>
Date: Wed Oct 31 14:52:55 2018 +0100
net-posix: Add mac lookup for IPv6
libgssdp/gssdp-client.c | 4 +-
libgssdp/gssdp-net-bionic.c | 2 +-
libgssdp/gssdp-net-posix.c | 312 +++++++++++++++++++++++++++++++++++---------
libgssdp/gssdp-net-win32.c | 2 +-
libgssdp/gssdp-net.h | 2 +-
5 files changed, 252 insertions(+), 70 deletions(-)
---
diff --git a/libgssdp/gssdp-client.c b/libgssdp/gssdp-client.c
index 3136bf6..e072db8 100644
--- a/libgssdp/gssdp-client.c
+++ b/libgssdp/gssdp-client.c
@@ -734,7 +734,7 @@ gssdp_client_add_cache_entry (GSSDPClient *client,
priv = gssdp_client_get_instance_private (client);
- hwaddr = gssdp_net_arp_lookup (&priv->device, ip_address);
+ hwaddr = gssdp_net_mac_lookup (&priv->device, ip_address);
if (hwaddr)
g_hash_table_insert (priv->user_agent_cache,
@@ -762,7 +762,7 @@ gssdp_client_guess_user_agent (GSSDPClient *client,
priv = gssdp_client_get_instance_private (client);
- hwaddr = gssdp_net_arp_lookup (&priv->device, ip_address);
+ hwaddr = gssdp_net_mac_lookup (&priv->device, ip_address);
if (hwaddr) {
const char *agent;
diff --git a/libgssdp/gssdp-net-bionic.c b/libgssdp/gssdp-net-bionic.c
index ee070f6..5168cdb 100644
--- a/libgssdp/gssdp-net-bionic.c
+++ b/libgssdp/gssdp-net-bionic.c
@@ -81,7 +81,7 @@ gssdp_net_query_ifindex (GSSDPNetworkDevice *device)
}
char *
-gssdp_net_arp_lookup (GSSDPNetworkDevice *device, const char *ip_address)
+gssdp_net_mac_lookup (GSSDPNetworkDevice *device, const char *ip_address)
{
#if defined(__linux__)
struct arpreq req;
diff --git a/libgssdp/gssdp-net-posix.c b/libgssdp/gssdp-net-posix.c
index 05edabd..2be5b8c 100644
--- a/libgssdp/gssdp-net-posix.c
+++ b/libgssdp/gssdp-net-posix.c
@@ -38,8 +38,9 @@
#include <stdlib.h>
#include <string.h>
-#ifdef __linux__
-#include <net/if_arp.h>
+#if defined(__linux__)
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
#endif
gboolean
@@ -83,50 +84,175 @@ gssdp_net_query_ifindex (GSSDPNetworkDevice *device)
#endif
}
+#if defined(__linux__)
+struct nl_req_s {
+ struct nlmsghdr hdr;
+ struct ndmsg gen;
+};
+
+#define NLMSG_IS_VALID(msg,len) \
+ (NLMSG_OK(msg,len) && (msg->nlmsg_type != NLMSG_DONE))
+
+#define RT_ATTR_OK(a,l) \
+ ((l > 0) && RTA_OK (a, l))
+
char *
-gssdp_net_arp_lookup (GSSDPNetworkDevice *device, const char *ip_address)
+gssdp_net_mac_lookup (GSSDPNetworkDevice *device, const char *ip_address)
{
-#if defined(__linux__)
- struct arpreq req;
- struct sockaddr_in *sin;
int fd = -1;
+ int saved_errno;
+ int status;
+ struct sockaddr_nl sa, dest;
+ struct nl_req_s req;
+ char *result = NULL;
+ int seq = rand();
+ GInetAddress *addr = NULL;
+ struct iovec iov;
+ struct msghdr msg;
+ char buf[8196];
+ unsigned char *data = NULL;
+ gssize data_length = -1;
+
+ /* Create the netlink socket */
+ fd = socket (PF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE);
+ saved_errno = errno;
+
+ if (fd == -1) {
+ g_debug ("Failed to create netlink socket: %s",
+ g_strerror (saved_errno));
+ goto out;
+ }
+ memset (&sa, 0, sizeof (sa));
+ sa.nl_family = AF_NETLINK;
+ status = bind (fd, (struct sockaddr *) &sa, sizeof (sa));
+ saved_errno = errno;
+ if (status == -1) {
+ g_debug ("Failed ot bind to netlink socket: %s",
+ g_strerror (saved_errno));
+
+ goto out;
+ }
+
+ /* Query the current neighbour table */
memset (&req, 0, sizeof (req));
+ memset (&dest, 0, sizeof (dest));
+ memset (&msg, 0, sizeof (msg));
- /* FIXME: Update when we support IPv6 properly */
- sin = (struct sockaddr_in *) &req.arp_pa;
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = inet_addr (ip_address);
+ dest.nl_family = AF_NETLINK;
+ req.hdr.nlmsg_len = NLMSG_LENGTH (sizeof (struct ndmsg));
+ req.hdr.nlmsg_seq = seq;
+ req.hdr.nlmsg_type = RTM_GETNEIGH;
+ req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- /* copy name, leave place for nul terminator */;
- strncpy (req.arp_dev, device->iface_name, sizeof (req.arp_dev) - 1);
+ addr = g_inet_address_new_from_string (ip_address);
+ req.gen.ndm_family = g_inet_address_get_family (addr);
- fd = socket (AF_INET, SOCK_STREAM, 0);
- if (fd < 0)
- return g_strdup (ip_address);
+ iov.iov_base = &req;
+ iov.iov_len = req.hdr.nlmsg_len;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = &dest;
+ msg.msg_namelen = sizeof (dest);
+
+ status = sendmsg (fd, (struct msghdr *) &msg, 0);
+ saved_errno = errno;
- if (ioctl (fd, SIOCGARP, (caddr_t) &req) < 0) {
- return NULL;
+ if (status < 0) {
+ g_debug ("Failed to send netlink message: %s",
+ g_strerror (saved_errno));
+
+ goto out;
}
- close (fd);
-
- if (req.arp_flags & ATF_COM) {
- unsigned char *buf = (unsigned char *) req.arp_ha.sa_data;
-
- return g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
- buf[0],
- buf[1],
- buf[2],
- buf[3],
- buf[4],
- buf[5]);
+
+ /* Receive the answers until error or nothing more to read */
+ while (TRUE) {
+ ssize_t len;
+ struct nlmsghdr *header = (struct nlmsghdr *) buf;
+
+ len = recv (fd, buf, sizeof (buf), 0);
+ saved_errno = errno;
+ if (len < 0) {
+ if (saved_errno != EWOULDBLOCK && saved_errno != EAGAIN) {
+ g_debug ("Failed to receive netlink msg: %s",
+ g_strerror (saved_errno));
+ }
+
+ break;
+ }
+
+ for (; NLMSG_IS_VALID (header, len); header = NLMSG_NEXT (header, len)) {
+ struct ndmsg *msg;
+ struct rtattr *rtattr;
+ int rtattr_len;
+
+ if (header->nlmsg_type != RTM_NEWNEIGH)
+ continue;
+
+ msg = NLMSG_DATA (header);
+
+ rtattr = IFA_RTA (msg);
+ rtattr_len = IFA_PAYLOAD (header);
+
+ while (RT_ATTR_OK (rtattr, rtattr_len)) {
+ if (rtattr->rta_type == NDA_DST) {
+ GInetAddress *entry_addr = g_inet_address_new_from_bytes (RTA_DATA
(rtattr),
+ g_inet_address_get_family (addr));
+ gboolean equal = g_inet_address_equal (addr, entry_addr);
+ g_clear_object (&entry_addr);
+
+ if (!equal) {
+ g_clear_pointer (&data, g_free);
+ break;
+ }
+ } else if (rtattr->rta_type == NDA_LLADDR) {
+ g_clear_pointer (&data, g_free);
+ data_length = RTA_PAYLOAD (rtattr);
+ data = g_memdup (RTA_DATA (rtattr), data_length);
+ }
+
+ rtattr = RTA_NEXT (rtattr, rtattr_len);
+ }
+
+ if (data != NULL)
+ break;
+ }
+
+ if (data != NULL)
+ break;
+
}
- return g_strdup (ip_address);
+ if (data != NULL) {
+ gssize i;
+ GString *mac_str = g_string_new ("");
+ for (i = 0; i < data_length; i++) {
+ if (i > 0) {
+ g_string_append_c (mac_str, ':');
+ }
+ g_string_append_printf (mac_str, "%02x", data[i]);
+ }
+
+ result = g_string_free (mac_str, FALSE);
+ }
+out:
+ g_clear_pointer (&data, g_free);
+ g_clear_object (&addr);
+ if (fd >= 0)
+ close (fd);
+
+ if (result == NULL)
+ return g_strdup (ip_address);
+ else
+ return result;
+}
#else
+char *
+gssdp_net_mac_lookup (GSSDPNetworkDevice *device, const char *ip_address)
return g_strdup (ip_address);
-#endif
}
+#endif
static const char *
sockaddr_to_string(struct sockaddr *addr,
@@ -171,35 +297,36 @@ gssdp_net_get_host_ip (GSSDPNetworkDevice *device)
return FALSE;
}
+ /*
+ * First, check all the devices. Filter out everything that is not UP or
+ * a PtP device or matches a supported family (FIXME: Questionable; it might
+ * be useful to do SSDP on a PtP device, though)
+ */
for (ifa = ifa_list; ifa != NULL; ifa = ifa->ifa_next) {
- if (ifa->ifa_addr == NULL)
+ /* Can happen for weird sa_family */
+ if (ifa->ifa_addr == NULL) {
continue;
+ }
+ /* We are really interested in AF_INET* only */
family = ifa->ifa_addr->sa_family;
if (family != AF_INET && family != AF_INET6) {
- continue;
+ continue;
}
- if (device->iface_name &&
+ else if (device->iface_name &&
!g_str_equal (device->iface_name, ifa->ifa_name)) {
- g_debug ("Skipping %s because it does not match %s",
- ifa->ifa_name,
- device->iface_name);
- continue;
- } else if (!(ifa->ifa_flags & IFF_UP)) {
- g_debug ("Skipping %s because it is not up",
- ifa->ifa_name);
- continue;
- } else if ((ifa->ifa_flags & IFF_POINTOPOINT)) {
- g_debug ("Skipping %s because it is point-to-point",
- ifa->ifa_name);
continue;
}
- /* Loopback and IPv6 interfaces go at the bottom on the list */
+ else if (!(ifa->ifa_flags & IFF_UP))
+ continue;
+
+ else if ((ifa->ifa_flags & IFF_POINTOPOINT))
+ continue;
- if ((ifa->ifa_flags & IFF_LOOPBACK) ||
- family == AF_INET6) {
+ /* Loopback and legacy IP interfaces go at the bottom on the list */
+ if ((ifa->ifa_flags & IFF_LOOPBACK) || family == AF_INET6) {
g_debug ("Found %s(%s), appending",
ifa->ifa_name,
sockaddr_to_string (ifa->ifa_addr,
@@ -216,35 +343,89 @@ gssdp_net_get_host_ip (GSSDPNetworkDevice *device)
}
}
+ /*
+ * Now go through the devices we consider worthy
+ */
+ family = G_SOCKET_FAMILY_INVALID;
+
+ if (device->host_addr) {
+ family = g_inet_address_get_family (device->host_addr);
+ }
+
+ if (family == G_SOCKET_FAMILY_IPV6 &&
+ !g_inet_address_get_is_link_local (device->host_addr) &&
+ !g_inet_address_get_is_site_local (device->host_addr) &&
+ !g_inet_address_get_is_loopback (device->host_addr)) {
+ char *addr = g_inet_address_to_string (device->host_addr);
+ /* FIXME: Discard the address, but use the interface */
+ g_warning("Invalid IP address given: %s, discarding",
+ addr);
+ g_free (addr);
+ g_clear_object (&device->host_addr);
+ }
+
for (ifaceptr = up_ifaces;
ifaceptr != NULL;
ifaceptr = ifaceptr->next) {
- char ip[INET6_ADDRSTRLEN];
- char net[INET6_ADDRSTRLEN];
- const char *p, *q;
- struct sockaddr_in *s4, *s4_mask;
- struct in_addr net_addr;
+ const char *q = NULL;
+ struct sockaddr_in *s4;
+ struct sockaddr_in6 *s6;
const guint8 *bytes;
ifa = ifaceptr->data;
- if (ifa->ifa_addr->sa_family != AF_INET) {
+ /* There was an address given for the client, but
+ * the address families don't match -> skip
+ */
+ if (family != G_SOCKET_FAMILY_INVALID &&
+ ifa->ifa_addr->sa_family != family) {
continue;
}
- s4 = (struct sockaddr_in *) ifa->ifa_addr;
- p = inet_ntop (AF_INET, &s4->sin_addr, ip, sizeof (ip));
- device->host_ip = g_strdup (p);
+ if (device->host_addr == NULL) {
+ switch (ifa->ifa_addr->sa_family) {
+ case AF_INET:
+ /* legacy IP: Easy, just take the first
+ * address we can find */
+ s4 = (struct sockaddr_in *) ifa->ifa_addr;
+ bytes = (const guint8 *) &s4->sin_addr;
+ device->host_addr = g_inet_address_new_from_bytes
+ (bytes, G_SOCKET_FAMILY_IPV4);
+#ifndef HAVE_PKTINFO
+ {
+ struct sockaddr_in *s4_mask;
+ char net[INET6_ADDRSTRLEN];
+ struct in_addr net_addr;
+ s4_mask = (struct sockaddr_in *) ifa->ifa_netmask;
+ memcpy (&(device->mask), s4_mask, sizeof (struct sockaddr_in));
+ net_addr.s_addr = (in_addr_t) s4->sin_addr.s_addr &
+ (in_addr_t) s4_mask->sin_addr.s_addr;
+ q = inet_ntop (AF_INET, &net_addr, net, sizeof (net));
+ }
+#endif
+ break;
+ case AF_INET6:
+ /* IP: Bit more complicated. We have to select a link-local or
+ * ULA address */
+ s6 = (struct sockaddr_in6 *) ifa->ifa_addr;
+ bytes = (const guint8 *) &s6->sin6_addr;
+ device->host_addr = g_inet_address_new_from_bytes
+ (bytes, G_SOCKET_FAMILY_IPV6);
+ if (!g_inet_address_get_is_link_local (device->host_addr) &&
+ !g_inet_address_get_is_site_local (device->host_addr)) {
+ g_clear_object (&device->host_addr);
+
+ continue;
+ }
+#ifndef HAVE_PKTINFO
+ /* FIXME: Todo */
+#endif
- bytes = (const guint8 *) &s4->sin_addr;
- device->host_addr = g_inet_address_new_from_bytes
- (bytes, G_SOCKET_FAMILY_IPV4);
+ default:
+ continue;
+ }
- s4_mask = (struct sockaddr_in *) ifa->ifa_netmask;
- memcpy (&(device->mask), s4_mask, sizeof (struct sockaddr_in));
- net_addr.s_addr = (in_addr_t) s4->sin_addr.s_addr &
- (in_addr_t) s4_mask->sin_addr.s_addr;
- q = inet_ntop (AF_INET, &net_addr, net, sizeof (net));
+ }
if (device->iface_name == NULL)
@@ -253,6 +434,7 @@ gssdp_net_get_host_ip (GSSDPNetworkDevice *device)
device->network = g_strdup (q);
device->index = gssdp_net_query_ifindex (device);
+
break;
}
diff --git a/libgssdp/gssdp-net-win32.c b/libgssdp/gssdp-net-win32.c
index df5b861..5bc582b 100644
--- a/libgssdp/gssdp-net-win32.c
+++ b/libgssdp/gssdp-net-win32.c
@@ -106,7 +106,7 @@ gssdp_net_query_ifindex (GSSDPNetworkDevice *device)
}
char *
-gssdp_net_arp_lookup (GSSDPNetworkDevice *device, const char *ip_address)
+gssdp_net_mac_lookup (GSSDPNetworkDevice *device, const char *ip_address)
{
/* TODO: Is there a way to make this work? */
/* GetIpNetTable / GetIpNetTable2 for Vista (ipv6) */
diff --git a/libgssdp/gssdp-net.h b/libgssdp/gssdp-net.h
index 02f2901..f4d247c 100644
--- a/libgssdp/gssdp-net.h
+++ b/libgssdp/gssdp-net.h
@@ -59,7 +59,7 @@ G_GNUC_INTERNAL int
gssdp_net_query_ifindex (GSSDPNetworkDevice *device);
G_GNUC_INTERNAL char*
-gssdp_net_arp_lookup (GSSDPNetworkDevice *device,
+gssdp_net_mac_lookup (GSSDPNetworkDevice *device,
const char *ip_address);
#endif /* GSSDP_NET_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]