summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlastair Houghton <alastair@coriolis-systems.com>2014-05-01 11:06:19 +0100
committerAlastair Houghton <alastair@coriolis-systems.com>2014-05-01 11:06:19 +0100
commitf467fc035e73917639dec3e9332f989b6b8f656d (patch)
treec649a5e9b99bdd9d66679bf85d3064270f7ad42e
parent20beadd1263f6702686584870e875fb474f1b259 (diff)
downloadnetifaces-f467fc035e73917639dec3e9332f989b6b8f656d.tar.gz
Added gateway detection code for PF_SOCKET and sysctl(). Also fixed some Solaris related issues.
-rw-r--r--README.rst44
-rw-r--r--netifaces.c609
-rw-r--r--setup.py132
3 files changed, 765 insertions, 20 deletions
diff --git a/README.rst b/README.rst
index 8fd7ced..135a552 100644
--- a/README.rst
+++ b/README.rst
@@ -116,20 +116,42 @@ instance, be on a Mac, in which case:
No, that isn't an exceptionally long Ethernet MAC address---it's a FireWire
address.
-3. This is great! What platforms does it work on?
---------------------------------------------------
+As of version 0.10.0, you can also obtain a list of gateways on your
+machine:::
+
+>>> netifaces.gateways()
+{2: [('10.0.1.1', 'en0', True), ('10.2.1.1', 'en1', False)], 30: [('fe80::1', 'en0', True)], 'default': { 2: ('10.0.1.1', 'en0'), 30: ('fe80::1', 'en0') }}
+
+This dictionary is keyed on address family---in this case, ``AF_INET``---and
+each entry is a list of gateways as ``(address, interface, is_default)`` tuples.
+Notice that here we have two separate gateways for IPv4 (``AF_INET``); some
+operating systems support configurations like this and can either route packets
+based on their source, or based on administratively configured routing tables.
+
+For convenience, we also allow you to index the dictionary with the special
+value ``'default'``, which returns a dictionary mapping address families to the
+default gateway in each case. Thus you can get the default IPv4 gateway with
-Well, see, here's the thing. It's been tested on Mac OS X, and it seems to
-work. (OS X helpfully has some of the ``SIOCGIFxxx`` ioctls, which means
-that most of those have been tested too, the only glaring exception being the
-``SIOCGIFHWADDR`` ioctl, which OS X just doesn't have.)
+>>> gws = netifaces.gateways()
+>>> gws['default'][netifaces.AF_INET]
+('10.0.1.1', 'en0')
-It should probably work on most of the other UNIX-like systems with relatively
-minor changes. If you do have to change something, send it to me at
-<alastair AT alastairs-place.net> and I'll see if I can merge it in.
+Do note that there may be no default gateway for any given address family;
+this is currently very common for IPv6 and much less common for IPv4 but it
+can happen even for ``AF_INET``.
+
+BTW, if you're trying to configure your machine to have multiple gateways for
+the same address family, it's a very good idea to check the documentation for
+your operating system *very* carefully, as some systems become extremely
+confused or route packets in a non-obvious manner.
+
+3. This is great! What platforms does it work on?
+--------------------------------------------------
-It also works just fine on Windows, using the ``GetAdaptersAddresses()``
-function.
+It gets regular testing on OS X, Linux and Windows. It has also been used
+successfully on Solaris, and it's expected to work properly on other UNIX-like
+systems as well. If you are running something that is not supported, and
+wish to contribute a patch, please use BitBucket to send a pull request.
4. What license is this under?
------------------------------
diff --git a/netifaces.c b/netifaces.c
index 021a584..7b0a850 100644
--- a/netifaces.c
+++ b/netifaces.c
@@ -27,6 +27,21 @@
# include <net/if.h>
# include <netdb.h>
+# if HAVE_PF_ROUTE
+# include <net/route.h>
+# endif
+
+# if HAVE_SYSCTL_CTL_NET
+# include <sys/sysctl.h>
+# include <net/route.h>
+# endif
+
+# if HAVE_PF_NETLINK
+# include <asm/types.h>
+# include <linux/netlink.h>
+# include <linux/rtnetlink.h>
+# endif
+
# if HAVE_SOCKET_IOCTLS
# include <sys/ioctl.h>
# include <netinet/in.h>
@@ -137,6 +152,9 @@ static int af_to_len(int af)
#if defined(AF_IRDA) && HAVE_SOCKADDR_IRDA
case AF_IRDA: return sizeof (struct sockaddr_irda);
#endif
+#if defined(AF_LINK) && HAVE_SOCKADDR_DL
+ case AF_LINK: return sizeof (struct sockaddr_dl);
+#endif
}
return sizeof (struct sockaddr);
}
@@ -418,19 +436,19 @@ compare_bits (const void *pva,
#endif
static int
-add_to_family (PyObject *result, int family, PyObject *dict)
+add_to_family (PyObject *result, int family, PyObject *obj)
{
PyObject *py_family;
PyObject *list;
- if (!PyDict_Size (dict))
+ if (!PyObject_Size (obj))
return TRUE;
py_family = PyInt_FromLong (family);
list = PyDict_GetItem (result, py_family);
if (!py_family) {
- Py_DECREF (dict);
+ Py_DECREF (obj);
Py_XDECREF (list);
return FALSE;
}
@@ -438,17 +456,17 @@ add_to_family (PyObject *result, int family, PyObject *dict)
if (!list) {
list = PyList_New (1);
if (!list) {
- Py_DECREF (dict);
+ Py_DECREF (obj);
Py_DECREF (py_family);
return FALSE;
}
- PyList_SET_ITEM (list, 0, dict);
+ PyList_SET_ITEM (list, 0, obj);
PyDict_SetItem (result, py_family, list);
Py_DECREF (list);
} else {
- PyList_Append (list, dict);
- Py_DECREF (dict);
+ PyList_Append (list, obj);
+ Py_DECREF (obj);
}
return TRUE;
@@ -650,8 +668,6 @@ ifaddrs (PyObject *self, PyObject *args)
if (iRet == 0)
bcast = PyString_FromString (buffer);
- else
- printf ("%d\n", iRet);
break;
}
@@ -1167,6 +1183,565 @@ interfaces (PyObject *self)
return result;
}
+static PyObject *
+gateways (PyObject *self)
+{
+ PyObject *result, *defaults;
+
+#if defined(WIN32)
+ #error Implement me
+#elif defined(HAVE_PF_NETLINK)
+ #error Implement me
+#elif defined(HAVE_SYSCTL_CTL_NET)
+ int mib[] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS,
+ RTF_UP | RTF_GATEWAY };
+ size_t len;
+ char *buffer = NULL, *ptr, *end;
+ int ret;
+ char ifnamebuf[IF_NAMESIZE];
+ char *ifname;
+
+ result = PyDict_New();
+ defaults = PyDict_New();
+ PyDict_SetItemString (result, "default", defaults);
+ Py_DECREF(defaults);
+
+ /* Remembering that the routing table may change while we're reading it,
+ we need to do this in a loop until we succeed. */
+ do {
+ if (sysctl (mib, 6, 0, &len, 0, 0) < 0) {
+ PyErr_SetFromErrno (PyExc_OSError);
+ free (buffer);
+ Py_DECREF (result);
+ return NULL;
+ }
+
+ ptr = realloc(buffer, len);
+ if (!ptr) {
+ PyErr_NoMemory();
+ free (buffer);
+ Py_DECREF (result);
+ return NULL;
+ }
+
+ buffer = ptr;
+
+ ret = sysctl (mib, 6, buffer, &len, 0, 0);
+ } while (ret != 0 || errno == ENOMEM || errno == EINTR);
+
+ if (ret < 0) {
+ PyErr_SetFromErrno (PyExc_OSError);
+ free (buffer);
+ Py_DECREF (result);
+ return NULL;
+ }
+
+ ptr = buffer;
+ end = buffer + len;
+
+ while (ptr + sizeof (struct rt_msghdr) <= end) {
+ struct rt_msghdr *msg = (struct rt_msghdr *)ptr;
+ char *msgend = (char *)msg + msg->rtm_msglen;
+ int addrs = msg->rtm_addrs;
+ int addr = RTA_DST;
+ PyObject *pyifname;
+
+ if (msgend > end)
+ break;
+
+ ifname = if_indextoname (msg->rtm_index, ifnamebuf);
+
+ if (!ifname) {
+ ptr = msgend;
+ continue;
+ }
+
+ pyifname = PyString_FromString (ifname);
+
+ ptr = (char *)(msg + 1);
+ while (ptr + sizeof (struct sockaddr) <= msgend && addrs) {
+ struct sockaddr *sa = (struct sockaddr *)ptr;
+ int len = SA_LEN(sa);
+
+ if (!len)
+ len = 4;
+ else
+ len = (len + 3) & ~3;
+
+ if (ptr + len > msgend)
+ break;
+
+ while (!(addrs & addr))
+ addr <<= 1;
+
+ addrs &= ~addr;
+
+ if (addr == RTA_DST) {
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ if (sin->sin_addr.s_addr != INADDR_ANY)
+ break;
+#ifdef AF_INET6
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ if (memcmp (&sin6->sin6_addr, &in6addr_any, sizeof (in6addr_any)) != 0)
+ break;
+#endif
+ } else {
+ break;
+ }
+ }
+
+ if (addr == RTA_GATEWAY) {
+ char buffer[256];
+ PyObject *tuple = NULL;
+ PyObject *deftuple = NULL;
+
+ if (string_from_sockaddr (sa, buffer, sizeof(buffer)) == 0) {
+ PyObject *pyaddr = PyString_FromString (buffer);
+#if HAVE_RTF_IFSCOPE
+ PyObject *isdefault = PyBool_FromLong (!(msg->rtm_flags & RTF_IFSCOPE));
+#else
+ PyObject *isdefault = Py_INCREF(Py_True);
+#endif
+ tuple = PyTuple_Pack (3, pyaddr, pyifname, isdefault);
+
+ if (PyBool_Check (isdefault))
+ deftuple = PyTuple_Pack (2, pyaddr, pyifname);
+
+ Py_DECREF (pyaddr);
+ Py_DECREF (isdefault);
+ }
+
+ if (tuple && !add_to_family (result, sa->sa_family, tuple)) {
+ Py_DECREF (deftuple);
+ Py_DECREF (result);
+ Py_DECREF (pyifname);
+ free (buffer);
+ return NULL;
+ }
+
+ if (deftuple) {
+ PyObject *pyfamily = PyInt_FromLong (sa->sa_family);
+
+ PyDict_SetItem (defaults, pyfamily, deftuple);
+
+ Py_DECREF (pyfamily);
+ Py_DECREF (deftuple);
+ }
+ }
+
+ /* These are aligned on a 4-byte boundary */
+ ptr += len;
+ }
+
+ Py_DECREF (pyifname);
+ ptr = msgend;
+ }
+
+ free (buffer);
+#elif defined(HAVE_PF_ROUTE)
+ /* The PF_ROUTE code will only retrieve gateway information for AF_INET and
+ AF_INET6. This is because it would need to loop through all possible
+ values, and the messages it needs to send in each case are potentially
+ different. */
+ int pagesize = getpagesize();
+ int bufsize = pagesize < 8192 ? 8192 : pagesize;
+ struct rt_msghdr *pmsg;
+ int s;
+ int seq = 0;
+ int pid = getpid();
+ ssize_t ret;
+ struct sockaddr_in *sin_dst, *sin_netmask;
+ struct sockaddr_dl *sdl_ifp;
+ struct sockaddr_in6 *sin6_dst;
+ size_t msglen;
+ char ifnamebuf[IF_NAMESIZE];
+ char *ifname;
+ int skip;
+
+ result = PyDict_New();
+ defaults = PyDict_New();
+ PyDict_SetItemString (result, "default", defaults);
+ Py_DECREF(defaults);
+
+ pmsg = (struct rt_msghdr *)malloc (bufsize);
+
+ if (!pmsg) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ s = socket (PF_ROUTE, SOCK_RAW, 0);
+
+ if (s < 0) {
+ PyErr_SetFromErrno (PyExc_OSError);
+ free (pmsg);
+ return NULL;
+ }
+
+ msglen = (sizeof (struct rt_msghdr)
+ + 2 * sizeof (struct sockaddr_in)
+ + sizeof (struct sockaddr_dl));
+ memset (pmsg, 0, msglen);
+
+ /* AF_INET first */
+ pmsg->rtm_msglen = msglen;
+ pmsg->rtm_type = RTM_GET;
+ pmsg->rtm_index = 0;
+ pmsg->rtm_flags = RTF_UP | RTF_GATEWAY;
+ pmsg->rtm_version = RTM_VERSION;
+ pmsg->rtm_seq = ++seq;
+ pmsg->rtm_pid = 0;
+ pmsg->rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP;
+
+ sin_dst = (struct sockaddr_in *)(pmsg + 1);
+ sin_netmask = (struct sockaddr_in *)(sin_dst + 1);
+ sdl_ifp = (struct sockaddr_dl *)(sin_netmask + 1);
+
+ sin_dst->sin_family = AF_INET;
+ sin_netmask->sin_family = AF_INET;
+ sdl_ifp->sdl_family = AF_LINK;
+
+#if HAVE_SOCKADDR_SA_LEN
+ sin_dst->sin_len = sizeof (struct sockaddr_in);
+ sin_netmask->sin_len = sizeof (struct sockaddr_in);
+ sdl_ifp->sdl_len = sizeof (struct sockaddr_dl);
+#endif
+
+ skip = 0;
+ if (send (s, pmsg, msglen, 0) < 0) {
+ if (errno == ESRCH)
+ skip = 1;
+ else {
+ PyErr_SetFromErrno (PyExc_OSError);
+ close (s);
+ free (pmsg);
+ return NULL;
+ }
+ }
+
+ while (!skip && !(pmsg->rtm_flags & RTF_DONE)) {
+ char *ptr;
+ char *msgend;
+ int addrs;
+ int addr;
+ struct sockaddr_in *dst = NULL;
+ struct sockaddr_in *gw = NULL;
+ struct sockaddr_dl *ifp = NULL;
+ PyObject *tuple = NULL;
+ PyObject *deftuple = NULL;
+
+ do {
+ ret = recv (s, pmsg, bufsize, 0);
+ } while ((ret < 0 && errno == EINTR)
+ || (ret > 0 && (pmsg->rtm_seq != seq || pmsg->rtm_pid != pid)));
+
+ if (ret < 0) {
+ PyErr_SetFromErrno (PyExc_OSError);
+ close (s);
+ free (pmsg);
+ return NULL;
+ }
+
+ if (pmsg->rtm_errno != 0) {
+ if (pmsg->rtm_errno == ESRCH)
+ skip = 1;
+ else {
+ errno = pmsg->rtm_errno;
+ PyErr_SetFromErrno (PyExc_OSError);
+ close (s);
+ free (pmsg);
+ return NULL;
+ }
+ }
+
+ if (skip)
+ break;
+
+ ptr = (char *)(pmsg + 1);
+ msgend = (char *)pmsg + pmsg->rtm_msglen;
+ addrs = pmsg->rtm_addrs;
+ addr = RTA_DST;
+ while (ptr + sizeof (struct sockaddr) <= msgend && addrs) {
+ struct sockaddr *sa = (struct sockaddr *)ptr;
+ int len = SA_LEN(sa);
+
+ if (!len)
+ len = 4;
+ else
+ len = (len + 3) & ~3;
+
+ if (ptr + len > msgend)
+ break;
+
+ while (!(addrs & addr))
+ addr <<= 1;
+
+ addrs &= ~addr;
+
+ switch (addr) {
+ case RTA_DST:
+ dst = (struct sockaddr_in *)sa;
+ break;
+ case RTA_GATEWAY:
+ gw = (struct sockaddr_in *)sa;
+ break;
+ case RTA_IFP:
+ ifp = (struct sockaddr_dl *)sa;
+ break;
+ }
+
+ ptr += len;
+ }
+
+ if ((dst && dst->sin_family != AF_INET)
+ || (gw && gw->sin_family != AF_INET)
+ || (ifp && ifp->sdl_family != AF_LINK)) {
+ dst = gw = NULL;
+ ifp = NULL;
+ }
+
+ if (dst && dst->sin_addr.s_addr == INADDR_ANY)
+ dst = NULL;
+
+ if (!dst && gw && ifp) {
+ char buffer[256];
+
+ if (ifp->sdl_index)
+ ifname = if_indextoname (ifp->sdl_index, ifnamebuf);
+ else {
+ memcpy (ifnamebuf, ifp->sdl_data, ifp->sdl_nlen);
+ ifnamebuf[ifp->sdl_nlen] = '\0';
+ ifname = ifnamebuf;
+ }
+
+ if (string_from_sockaddr ((struct sockaddr *)gw,
+ buffer, sizeof(buffer)) == 0) {
+ PyObject *pyifname = PyString_FromString (ifname);
+ PyObject *pyaddr = PyString_FromString (buffer);
+#if HAVE_RTF_IFSCOPE
+ PyObject *isdefault = PyBool_FromLong (!(pmsg->rtm_flags & RTF_IFSCOPE));
+#else
+ PyObject *isdefault = Py_True;
+ Py_INCREF(isdefault);
+#endif
+
+ tuple = PyTuple_Pack (3, pyaddr, pyifname, isdefault);
+
+ if (PyBool_Check (isdefault))
+ deftuple = PyTuple_Pack (2, pyaddr, pyifname);
+
+ Py_DECREF (pyaddr);
+ Py_DECREF (pyifname);
+ Py_DECREF (isdefault);
+ }
+
+ if (tuple && !add_to_family (result, AF_INET, tuple)) {
+ Py_DECREF (deftuple);
+ Py_DECREF (result);
+ free (pmsg);
+ return NULL;
+ }
+
+ if (deftuple) {
+ PyObject *pyfamily = PyInt_FromLong (AF_INET);
+
+ PyDict_SetItem (defaults, pyfamily, deftuple);
+
+ Py_DECREF (pyfamily);
+ Py_DECREF (deftuple);
+ }
+ }
+ }
+
+#ifdef AF_INET6
+ /* AF_INET6 now */
+ msglen = (sizeof (struct rt_msghdr)
+ + sizeof (struct sockaddr_in6)
+ + sizeof (struct sockaddr_dl));
+ memset (pmsg, 0, msglen);
+
+ pmsg->rtm_msglen = msglen;
+ pmsg->rtm_type = RTM_GET;
+ pmsg->rtm_index = 0;
+ pmsg->rtm_flags = RTF_UP | RTF_GATEWAY;
+ pmsg->rtm_version = RTM_VERSION;
+ pmsg->rtm_seq = ++seq;
+ pmsg->rtm_pid = 0;
+ pmsg->rtm_addrs = RTA_DST | RTA_IFP;
+
+ sin6_dst = (struct sockaddr_in6 *)(pmsg + 1);
+ sdl_ifp = (struct sockaddr_dl *)(sin6_dst + 1);
+
+ sin6_dst->sin6_family = AF_INET6;
+ sin6_dst->sin6_addr = in6addr_any;
+ sdl_ifp->sdl_family = AF_LINK;
+
+#if HAVE_SOCKADDR_SA_LEN
+ sin6_dst->sin6_len = sizeof (struct sockaddr_in6);
+ sdl_ifp->sdl_len = sizeof (struct sockaddr_dl);
+#endif
+
+ skip = 0;
+ if (send (s, pmsg, msglen, 0) < 0) {
+ if (errno == ESRCH)
+ skip = 1;
+ else {
+ PyErr_SetFromErrno (PyExc_OSError);
+ close (s);
+ free (pmsg);
+ return NULL;
+ }
+ }
+
+ while (!skip && !(pmsg->rtm_flags & RTF_DONE)) {
+ char *ptr;
+ char *msgend;
+ int addrs;
+ int addr;
+ struct sockaddr_in6 *dst = NULL;
+ struct sockaddr_in6 *gw = NULL;
+ struct sockaddr_dl *ifp = NULL;
+ PyObject *tuple = NULL;
+ PyObject *deftuple = NULL;
+
+ do {
+ ret = recv (s, pmsg, bufsize, 0);
+ } while ((ret < 0 && errno == EINTR)
+ || (ret > 0 && (pmsg->rtm_seq != seq || pmsg->rtm_pid != pid)));
+
+ if (ret < 0) {
+ PyErr_SetFromErrno (PyExc_OSError);
+ close (s);
+ free (pmsg);
+ return NULL;
+ }
+
+ if (pmsg->rtm_errno != 0) {
+ if (pmsg->rtm_errno == ESRCH)
+ skip = 1;
+ else {
+ errno = pmsg->rtm_errno;
+ PyErr_SetFromErrno (PyExc_OSError);
+ close (s);
+ free (pmsg);
+ return NULL;
+ }
+ }
+
+ if (skip)
+ break;
+
+ ptr = (char *)(pmsg + 1);
+ msgend = (char *)pmsg + pmsg->rtm_msglen;
+ addrs = pmsg->rtm_addrs;
+ addr = RTA_DST;
+ while (ptr + sizeof (struct sockaddr) <= msgend && addrs) {
+ struct sockaddr *sa = (struct sockaddr *)ptr;
+ int len = SA_LEN(sa);
+
+ if (!len)
+ len = 4;
+ else
+ len = (len + 3) & ~3;
+
+ if (ptr + len > msgend)
+ break;
+
+ while (!(addrs & addr))
+ addr <<= 1;
+
+ addrs &= ~addr;
+
+ switch (addr) {
+ case RTA_DST:
+ dst = (struct sockaddr_in6 *)sa;
+ break;
+ case RTA_GATEWAY:
+ gw = (struct sockaddr_in6 *)sa;
+ break;
+ case RTA_IFP:
+ ifp = (struct sockaddr_dl *)sa;
+ break;
+ }
+
+ ptr += len;
+ }
+
+ if ((dst && dst->sin6_family != AF_INET6)
+ || (gw && gw->sin6_family != AF_INET6)
+ || (ifp && ifp->sdl_family != AF_LINK)) {
+ dst = gw = NULL;
+ ifp = NULL;
+ }
+
+ if (dst && memcmp (&dst->sin6_addr, &in6addr_any,
+ sizeof(struct in6_addr)) == 0)
+ dst = NULL;
+
+ if (!dst && gw && ifp) {
+ char buffer[256];
+
+ if (ifp->sdl_index)
+ ifname = if_indextoname (ifp->sdl_index, ifnamebuf);
+ else {
+ memcpy (ifnamebuf, ifp->sdl_data, ifp->sdl_nlen);
+ ifnamebuf[ifp->sdl_nlen] = '\0';
+ ifname = ifnamebuf;
+ }
+
+ if (string_from_sockaddr ((struct sockaddr *)gw,
+ buffer, sizeof(buffer)) == 0) {
+ PyObject *pyifname = PyString_FromString (ifname);
+ PyObject *pyaddr = PyString_FromString (buffer);
+#if HAVE_RTF_IFSCOPE
+ PyObject *isdefault = PyBool_FromLong (!(pmsg->rtm_flags & RTF_IFSCOPE));
+#else
+ PyObject *isdefault = Py_True;
+ Py_INCREF (isdefault);
+#endif
+
+ tuple = PyTuple_Pack (3, pyaddr, pyifname, isdefault);
+
+ if (PyBool_Check (isdefault))
+ deftuple = PyTuple_Pack (2, pyaddr, pyifname);
+
+ Py_DECREF (pyaddr);
+ Py_DECREF (pyifname);
+ Py_DECREF (isdefault);
+ }
+
+ if (tuple && !add_to_family (result, AF_INET6, tuple)) {
+ Py_DECREF (deftuple);
+ Py_DECREF (result);
+ free (pmsg);
+ return NULL;
+ }
+
+ if (deftuple) {
+ PyObject *pyfamily = PyInt_FromLong (AF_INET6);
+
+ PyDict_SetItem (defaults, pyfamily, deftuple);
+
+ Py_DECREF (pyfamily);
+ Py_DECREF (deftuple);
+ }
+ }
+ }
+#endif /* AF_INET6 */
+
+ free (pmsg);
+#else
+ /* If we don't know how to implement this on your platform, we raise an
+ exception. */
+ PyErr_SetString (PyExc_OSError,
+ "Unable to obtain gateway information on your platform.");
+#endif
+
+ return result;
+}
+
static PyMethodDef methods[] = {
{ "ifaddresses", (PyCFunction)ifaddrs, METH_VARARGS,
"Obtain information about the specified network interface.\n"
@@ -1176,6 +1751,20 @@ static PyMethodDef methods[] = {
"that family that are attached to the network interface." },
{ "interfaces", (PyCFunction)interfaces, METH_NOARGS,
"Obtain a list of the interfaces available on this machine." },
+ { "gateways", (PyCFunction)gateways, METH_NOARGS,
+ "Obtain a list of the gateways on this machine.\n"
+"\n"
+"Returns a dict whose keys are equal to the address family constants,\n"
+"e.g. netifaces.AF_INET, and whose values are a list of tuples of the\n"
+"format (<address>, <interface>, <is_default>).\n"
+"\n"
+"There is also a special entry with the key 'default', which you can use\n"
+"to quickly obtain the default gateway for a particular address family.\n"
+"\n"
+"There may in general be multiple gateways; different address\n"
+"families may have different gateway settings (e.g. AF_INET vs AF_INET6)\n"
+"and on some systems it's also possible to have interface-specific\n"
+"default gateways.\n" },
{ NULL, NULL, 0, NULL }
};
@@ -1494,6 +2083,8 @@ MODULE_INIT(netifaces)
PyModule_AddObject(m, "address_families", address_family_dict);
// Add-in the version number from setup.py
+#undef STR
+#undef _STR
#define _STR(x) #x
#define STR(x) _STR(x)
diff --git a/setup.py b/setup.py
index 2c0015d..4da0674 100644
--- a/setup.py
+++ b/setup.py
@@ -97,6 +97,8 @@ class my_build_ext(build_ext):
os.dup2(mystderr, 2)
except CompileError:
return False
+ except LinkError:
+ return False
except DistutilsExecError:
return False
return result
@@ -392,6 +394,136 @@ class my_build_ext(build_ext):
results['have_sockaddrs'] = result
+ # Reading routing tables is very OS dependent; check for a few
+ # different approaches.
+ output("checking for routing socket support...", end='')
+
+ result = results.get('have_pf_route', None)
+ if result is not None:
+ cached = '(cached)'
+ else:
+ cached = ''
+
+ testrig = """
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <net/route.h>
+
+ int main (void) {
+ int s = socket (PF_ROUTE, SOCK_RAW, 0);
+ return 0;
+ }
+ """
+
+ result = self.test_build(testrig)
+
+ if result:
+ output('yes. %s' % cached)
+ self.compiler.define_macro('HAVE_PF_ROUTE', 1)
+ else:
+ output('no. %s' % cached)
+
+ results['have_pf_route'] = result
+
+ output("checking for sysctl(CTL_NET...) support...", end='')
+
+ result = results.get('have_sysctl_ctl_net', None)
+ if result is not None:
+ cached = '(cached)'
+ else:
+ cached = ''
+
+ testrig = """
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/sysctl.h>
+ #include <net/route.h>
+
+ int main (void) {
+ int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS,
+ RTF_UP | RTF_GATEWAY };
+ return 0;
+ }
+ """
+
+ result = self.test_build(testrig)
+
+ if result:
+ output('yes. %s' % cached)
+ self.compiler.define_macro('HAVE_SYSCTL_CTL_NET', 1)
+ else:
+ output('no. %s' % cached)
+
+ results['have_sysctl_ctl_net'] = result
+
+ output("checking for netlink support...", end='')
+
+ result = results.get('have_pf_netlink', None)
+ if result is not None:
+ cached = '(cached)'
+ else:
+ cached = ''
+
+ testrig = """
+ #include <asm/types.h>
+ #include <linux/netlink.h>
+ #include <linux/rtnetlink.h>
+ #include <sys/socket.h>
+
+ int main (void) {
+ int s = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ return 0;
+ }
+ """
+
+ result = self.test_build(testrig)
+
+ if result:
+ output('yes. %s' % cached)
+ self.compiler.define_macro('HAVE_PF_NETLINK', 1)
+ else:
+ output('no. %s' % cached)
+
+ results['have_pf_netlink'] = result
+
+ if results['have_pf_netlink']:
+ output('will use netlink to read routing table')
+ elif results['have_sysctl_ctl_net']:
+ output('will use sysctl() to read routing table')
+ elif results['have_pf_route']:
+ output('will use routing socket to read routing table')
+
+ # Also check for RTF_IFSCOPE
+ output("checking for RTF_IFSCOPE...", end='')
+
+ result = results.get('have_rtf_ifscope', None)
+ if result is not None:
+ cached = '(cached)'
+ else:
+ cached = ''
+
+ testrig = """
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/sysctl.h>
+ #include <net/route.h>
+
+ int main (void) {
+ int scope = RTF_IFSCOPE;
+ return 0;
+ }
+ """
+
+ result = self.test_build(testrig)
+
+ if result:
+ output('yes. %s' % cached)
+ self.compiler.define_macro('HAVE_RTF_IFSCOPE', 1)
+ else:
+ output('no. %s' % cached)
+
+ results['have_rtf_ifscope'] = result
+
# Save the results to our config.cache file
myfile = open(cache_file, 'wb')
try: