summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--isys/Makefile14
-rw-r--r--isys/nl.c365
-rw-r--r--isys/nl.h31
4 files changed, 414 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 4b74434e8..2618f829e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2006-05-23 David Cantrell <dcantrell@redhat.com>
+
+ * isys/Makefile: Add nl.o, glib flags, and a target for nltest.
+ * isys/nl.h: Netlink helper functions, the header file.
+ * isys/nl.c: Netlink helper functions, the source file.
+
2006-05-23 Chris Lumens <clumens@redhat.com>
* bootloader.py (writeBootloader): Don't try to pop the wait window
diff --git a/isys/Makefile b/isys/Makefile
index 84f743fbe..3ebb0e9b5 100644
--- a/isys/Makefile
+++ b/isys/Makefile
@@ -4,8 +4,9 @@ CFLAGS += -I$(PYTHONINCLUDE) -I.. -DHAVE_NFS
OBJECTS = nfsmount.o nfsmount_clnt.o nfsmount_xdr.o imount.o getmacaddr.o \
smp.o devnodes.o cpio.o uncpio.o dasd.o \
- lang.o isofs.o dns.o linkdetect.o vio.o \
- ethtool.o getipaddr.o wireless.o eddsupport.o
+ lang.o isofs.o dns.o linkdetect.o vio.o \
+ ethtool.o getipaddr.o wireless.o eddsupport.o \
+ nl.o
SOBJECTS = $(patsubst %.o,%.lo,$(OBJECTS))
SOURCES = $(patsubst %.o,%.c,$(OBJECTS)) isys.c
LOADLIBES = -lresolv -lpci -lpopt -lpump -lext2fs -lz -lkudzu -lpci -ldevmapper
@@ -14,6 +15,10 @@ PYMODULES = _isys.so
SUBDIRS = gzlib
DIET = diet
+# using glib
+LOADLIBES += $(shell pkg-config --libs glib-2.0)
+CFLAGS += $(shell pkg-config --cflags glib-2.0)
+
DOBJECTS = $(patsubst %.o,%.do,$(OBJECTS))
GENERATED = nfs_mountversion.h
@@ -64,6 +69,7 @@ clean:
rm -f *.o *.so *.lo *.a *.pyc $(TARGET) $(SOBJECTS) $(DOBJECTS)
rm -f $(DIETLIBS) $(OBJECTS) $(GENERATED)
rm -f .depend
+ rm -f nl
for d in $(SUBDIRS); do make -C $$d clean; done
install: all
@@ -82,6 +88,10 @@ nfsmount.do: nfs_mountversion.h
nfs_mountversion.h: /usr/include/linux/nfs_mount.h
grep NFS_MOUNT_VERSION $< | sed -e 's/NFS/KERNEL_NFS/' > $@
+nltest: nl.c nl.h
+ $(CC) -c $(CFLAGS) -DTESTING nl.c -o nl.o
+ $(CC) -DTESTING nl.o -o nl $(LOADLIBES)
+
depend: nfs_mountversion.h
$(CPP) -M $(CFLAGS) $(SOURCES) > .depend
diff --git a/isys/nl.c b/isys/nl.c
new file mode 100644
index 000000000..00ced2cef
--- /dev/null
+++ b/isys/nl.c
@@ -0,0 +1,365 @@
+/*
+ * nl.c - Netlink helper functions
+ *
+ * Copyright 2006 Red Hat, Inc.
+ *
+ * David Cantrell <dcantrell@redhat.com>
+ *
+ * This software may be freely redistributed under the terms of the GNU
+ * general public license.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include "nl.h"
+
+/* A linked list of interface_info_t structures (see nl.h) */
+static GSList *interfaces = NULL;
+
+/**
+ * Not really Netlink-specific, but handy nonetheless. Takes a MAC address
+ * and converts it to the familiar hexidecimal notation for easy reading.
+ *
+ * @param mac The unsigned char MAC address value.
+ * @param buf The string to write the formatted address to.
+ * @return Pointer to buf.
+ */
+char *netlink_format_mac_addr(char *buf, unsigned char *mac) {
+ sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ return buf;
+}
+
+/**
+ * Return a human-readable IP address (either v4 or v6).
+ *
+ * @param family The address family.
+ * @param intf The interface_info_t structure with the IP address info.
+ * @param buf The buffer to write the formatted IP address to.
+ * @return A pointer to buf.
+ */
+char *netlink_format_ip_addr(int family, interface_info_t *intf, char *buf) {
+ char ipbuf[256];
+
+ memset(ipbuf, 0, sizeof(ipbuf));
+ switch (family) {
+ case AF_INET:
+ inet_ntop(family, &(intf->ip_addr), ipbuf, sizeof(ipbuf));
+ break;
+ case AF_INET6:
+ inet_ntop(family, &(intf->ip6_addr), ipbuf, sizeof(ipbuf));
+ break;
+ }
+
+ memcpy(buf, ipbuf, sizeof(ipbuf));
+ return buf;
+}
+
+/**
+ * Create a new PF_NETLINK socket for communication with the kernel Netlink
+ * layer. Open with NETLINK_ROUTE protocol since we want IPv4 and IPv6
+ * interface, address, and routing information.
+ *
+ * @return Handle to new socket or -1 on error.
+ */
+int netlink_create_socket(void) {
+ int sock;
+
+ sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock < 0) {
+ perror("netlink socket");
+ return -1;
+ }
+
+ return sock;
+}
+
+/**
+ * Send a dump request message for the specified information in the
+ * specified family type. Family may be AF_INET or AF_INET6, for
+ * example. The request type should be a GET type as specified in
+ * /usr/include/linux/rtnetlink.h (for example, RTM_GETLINK).
+ *
+ * @param sock The Netlink socket to use.
+ * @param type The Netlink request type.
+ * @param family The address family.
+ * @return The number of characters sent or -1 on error.
+ */
+int netlink_send_dump_request(int sock, int type, int family) {
+ int ret;
+ char buf[4096];
+ struct sockaddr_nl snl;
+ struct nlmsghdr *nlh;
+ struct rtgenmsg *g;
+
+ memset(&snl, 0, sizeof(snl));
+ snl.nl_family = AF_NETLINK;
+
+ memset(buf, 0, sizeof(buf));
+ nlh = (struct nlmsghdr *)buf;
+ g = (struct rtgenmsg *)(buf + sizeof(struct nlmsghdr));
+
+ nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
+ nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
+ nlh->nlmsg_type = type;
+ g->rtgen_family = family;
+
+ ret = sendto(sock, buf, nlh->nlmsg_len, 0, (struct sockaddr *)&snl,
+ sizeof(snl));
+ if (ret < 0) {
+ perror("netlink_send_dump_request sendto");
+ return -1;
+ }
+
+ return ret;
+}
+
+/**
+ * Look for an IP address for the given interface.
+ *
+ * @param index The interface index number.
+ * @param family The address family (AF_INET or AF_INET6).
+ * @param addr Pointer to where we should write the IP address.
+ * @return -1 on error, 0 on success.
+ */
+int netlink_get_interface_ip(int index, int family, void *addr) {
+ int sock, ret, len, alen;
+ char buf[4096];
+ struct nlmsghdr *nlh;
+ struct ifaddrmsg *ifa;
+ struct rtattr *rta;
+ struct rtattr *tb[IFLA_MAX+1];
+
+ /* get a socket */
+ if ((sock = netlink_create_socket()) == -1) {
+ perror("netlink_create_socket in netlink_get_interface_ip");
+ close(sock);
+ return -1;
+ }
+
+ /* send dump request */
+ if (netlink_send_dump_request(sock, RTM_GETADDR, family) == -1) {
+ perror("netlink_send_dump_request in netlink_get_interface_ip");
+ close(sock);
+ return -1;
+ }
+
+ /* read back messages */
+ memset(buf, 0, sizeof(buf));
+ ret = recvfrom(sock, buf, sizeof(buf), 0, NULL, 0);
+ if (ret < 0) {
+ perror("recvfrom in netlink_init_interfaces_table");
+ close(sock);
+ return -1;
+ }
+
+ nlh = (struct nlmsghdr *) buf;
+ while (NLMSG_OK(nlh, ret)) {
+ switch (nlh->nlmsg_type) {
+ case NLMSG_DONE:
+ break;
+ case RTM_NEWADDR:
+ break;
+ default:
+ nlh = NLMSG_NEXT(nlh, ret);
+ continue;
+ }
+
+ /* RTM_NEWADDR */
+ ifa = NLMSG_DATA(nlh);
+ rta = IFA_RTA(ifa);
+ len = NLMSG_PAYLOAD(nlh, 0); /* IFA_PAYLOAD(nlh) ???? */
+
+ if (ifa->ifa_family != family) {
+ nlh = NLMSG_NEXT(nlh, ret);
+ continue;
+ }
+
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= len)
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT(rta, len);
+ }
+
+ alen = RTA_PAYLOAD(tb[IFA_ADDRESS]);
+
+ /* write the address */
+ if (tb[IFA_ADDRESS] && ifa->ifa_index == index) {
+ memset(addr, 0, sizeof(*addr));
+ switch (family) {
+ case AF_INET:
+ memcpy(addr, (struct in_addr *)RTA_DATA(tb[IFA_ADDRESS]), alen);
+ break;
+ case AF_INET6:
+ memcpy(addr, (struct in6_addr *)RTA_DATA(tb[IFA_ADDRESS]), alen);
+ break;
+ }
+
+ close(sock);
+ return 0;
+ }
+
+ /* next netlink msg */
+ nlh = NLMSG_NEXT(nlh, ret);
+ }
+
+ close(sock);
+ return 0;
+}
+
+/**
+ * Initialize the interfaces linked list with the interface name, MAC
+ * address, and IP addresses. This function is only called once to
+ * initialize the structure, but may be called again if the structure
+ * should be reinitialized.
+ *
+ * @return 0 on succes, -1 on error.
+ */
+int netlink_init_interfaces_list(void) {
+ int sock, ret, len, alen, r;
+ char buf[4096];
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifi;
+ struct rtattr *rta;
+ struct rtattr *tb[IFLA_MAX+1];
+ interface_info_t *intfinfo;
+
+ /* get a socket */
+ if ((sock = netlink_create_socket()) == -1) {
+ perror("netlink_create_socket in netlink_init_interfaces_table");
+ close(sock);
+ return -1;
+ }
+
+ /* send dump request */
+ if (netlink_send_dump_request(sock, RTM_GETLINK, AF_NETLINK) == -1) {
+ perror("netlink_send_dump_request in netlink_init_interfaces_table");
+ close(sock);
+ return -1;
+ }
+
+ /* read back messages */
+ memset(buf, 0, sizeof(buf));
+ ret = recvfrom(sock, buf, sizeof(buf), 0, NULL, 0);
+ if (ret < 0) {
+ perror("recvfrom in netlink_init_interfaces_table");
+ close(sock);
+ return -1;
+ }
+
+ nlh = (struct nlmsghdr *) buf;
+ while (NLMSG_OK(nlh, ret)) {
+ switch (nlh->nlmsg_type) {
+ case NLMSG_DONE:
+ break;
+ case RTM_NEWLINK:
+ break;
+ default:
+ nlh = NLMSG_NEXT(nlh, ret);
+ continue;
+ }
+
+ /* RTM_NEWLINK */
+ memset(tb, 0, sizeof(tb));
+ memset(tb, 0, sizeof(struct rtattr *) * (IFLA_MAX + 1));
+
+ ifi = NLMSG_DATA(nlh);
+ rta = IFLA_RTA(ifi);
+ len = IFLA_PAYLOAD(nlh);
+
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= len)
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT(rta, len);
+ }
+
+ alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+
+ /* we have an ethernet MAC addr if alen=6 */
+ if (alen == 6) {
+ /* make some room! */
+ intfinfo = malloc(sizeof(struct _interface_info_t));
+ if (intfinfo == NULL) {
+ perror("malloc in netlink_init_interfaces_table");
+ close(sock);
+ return -1;
+ }
+
+ /* copy the interface index */
+ intfinfo->i = ifi->ifi_index;
+
+ /* copy the interface name (eth0, eth1, ...) */
+ intfinfo->name = strndup((char *) RTA_DATA(tb[IFLA_IFNAME]),
+ sizeof(RTA_DATA(tb[IFLA_IFNAME])));
+
+ /* copy the MAC addr */
+ memcpy(&intfinfo->mac, RTA_DATA(tb[IFLA_ADDRESS]), alen);
+
+ /* get the IPv4 address of this interface (if any) */
+ r = netlink_get_interface_ip(intfinfo->i, AF_INET, &intfinfo->ip_addr);
+ if (r == -1)
+ intfinfo->ip_addr.s_addr = 0;
+
+ /* get the IPv6 address of this interface (if any) */
+ r = netlink_get_interface_ip(intfinfo->i,AF_INET6,&intfinfo->ip6_addr);
+/* XXX: why this no work?
+ if (r == -1)
+ intfinfo->ip6_addr.s6_addr = 0;
+*/
+
+ /* add this interface */
+ interfaces = g_slist_append(interfaces, intfinfo);
+ }
+
+ /* next netlink msg */
+ nlh = NLMSG_NEXT(nlh, ret);
+ }
+
+ close(sock);
+ return 0;
+}
+
+#ifdef TESTING
+void print_interfaces(gpointer data, gpointer user_data) {
+ char buf[20];
+ char ipbuf[256];
+ interface_info_t *intf;
+
+ intf = (interface_info_t *) data;
+ printf("Interface %d\n", intf->i);
+ printf(" Name: %s\n", intf->name);
+ printf(" IPv4: %s\n", netlink_format_ip_addr(AF_INET, intf, ipbuf));
+ printf(" IPv6: %s\n", netlink_format_ip_addr(AF_INET6, intf, ipbuf));
+ printf(" MAC: %s\n\n", netlink_format_mac_addr(buf, intf->mac));
+
+ return;
+}
+
+int main(void) {
+ if (netlink_init_interfaces_list() == -1) {
+ fprintf(stderr, "netlink_init_interfaces_list failure: %s\n", __func__);
+ fflush(stderr);
+ return EXIT_FAILURE;
+ }
+
+ g_slist_foreach(interfaces, print_interfaces, NULL);
+
+ return EXIT_SUCCESS;
+}
+#endif
diff --git a/isys/nl.h b/isys/nl.h
new file mode 100644
index 000000000..46e70313a
--- /dev/null
+++ b/isys/nl.h
@@ -0,0 +1,31 @@
+/*
+ * nl.h - Netlink helper functions, the header file
+ *
+ * Copyright 2006 Red Hat, Inc.
+ *
+ * David Cantrell <dcantrell@redhat.com>
+ *
+ * This software may be freely redistributed under the terms of the GNU
+ * general public license.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Information per interface */
+typedef struct _interface_info_t {
+ int i; /* interface index */
+ char *name; /* name (eth0, eth1, ...) */
+ struct in_addr ip_addr; /* IPv4 address (0=none) */
+ struct in6_addr ip6_addr; /* IPv6 address (0=none) */
+ unsigned char mac[8]; /* MAC address */
+} interface_info_t;
+
+/* Function prototypes */
+char *netlink_format_mac_addr(char *buf, unsigned char *mac);
+char *netlink_format_ip_addr(int family, interface_info_t *intf, char *buf);
+int netlink_create_socket(void);
+int netlink_send_dump_request(int sock, int type, int family);
+int netlink_get_interface_ip(int index, int family, void *addr);
+int netlink_init_interfaces_list(void);