From 55bc002ad69bde86b8baa8e82f9a03f77d0f9375 Mon Sep 17 00:00:00 2001 From: "Nicholas J. Kain" Date: Tue, 29 Mar 2011 14:37:45 -0400 Subject: [PATCH] Use netlink for getting interface mac and index in ndhc instead of ioctl. --- ndhc/CMakeLists.txt | 1 + ndhc/ndhc.c | 12 ++- ndhc/netlink.c | 217 ++++++++++++++++++++++++++++++++++++++++++++ ndhc/netlink.h | 9 ++ ndhc/socket.c | 51 ----------- ndhc/socket.h | 2 - 6 files changed, 237 insertions(+), 55 deletions(-) create mode 100644 ndhc/netlink.c create mode 100644 ndhc/netlink.h diff --git a/ndhc/CMakeLists.txt b/ndhc/CMakeLists.txt index 60496bb..5d0d4aa 100644 --- a/ndhc/CMakeLists.txt +++ b/ndhc/CMakeLists.txt @@ -11,6 +11,7 @@ set(NDHC_SRCS ifchange.c dhcpmsg.c arp.c + netlink.c ndhc.c ) diff --git a/ndhc/ndhc.c b/ndhc/ndhc.c index 08a26f6..73e46c0 100644 --- a/ndhc/ndhc.c +++ b/ndhc/ndhc.c @@ -52,6 +52,8 @@ #include "ifchange.h" #include "socket.h" #include "arp.h" +#include "netlink.h" + #include "log.h" #include "chroot.h" #include "cap.h" @@ -361,9 +363,14 @@ int main(int argc, char **argv) write_pid(pidfile); } - if (read_interface(client_config.interface, &client_config.ifindex, - NULL, client_config.arp) < 0) + if (nl_open() < 0) { + log_line("FATAL - failed to open netlink socket"); exit(EXIT_FAILURE); + } + if (nl_getifdata(client_config.interface) < 0) { + log_line("FATAL - failed to get interface MAC and index"); + exit(EXIT_FAILURE); + } if (!client_config.clientid) { client_config.clientid = xmalloc(6 + 3); @@ -391,5 +398,6 @@ int main(int argc, char **argv) do_work(); + nl_close(); return EXIT_SUCCESS; } diff --git a/ndhc/netlink.c b/ndhc/netlink.c new file mode 100644 index 0000000..0bfbddd --- /dev/null +++ b/ndhc/netlink.c @@ -0,0 +1,217 @@ +/* + * Copyright 2011 Nicholas J. Kain + * Copyright 2006, 2007 Stefan Rompf + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netlink.h" +#include "config.h" +#include "log.h" + +// OS_REMOVED -> exit +// OS_SHUT -> exit +// OS_DOWN -> action +// OS_UP -> action + +#define NLMSG_RECVSIZE 8192 + +static int nl_socket = -1; +static unsigned int nl_seq; + +/* internal callback handling */ +static void (*nlcb_function)(struct nlmsghdr *msg, void **args); +static void *nlcb_args[3]; +static __u32 nlcb_pid; +static unsigned int nlcb_seq; +static char nlcb_run; + +int nl_open() { + struct sockaddr_nl nlsock = { + .nl_family = AF_NETLINK, + .nl_pad = 0, + .nl_pid = getpid(), + .nl_groups = RTMGRP_LINK + }; + + nlcb_pid = nlsock.nl_pid; + + assert(nl_socket == -1); + + nl_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + + if (nl_socket == -1) return -1; + + if (bind(nl_socket, (const struct sockaddr *)&nlsock, sizeof(nlsock))) + goto err_close; + + if (fcntl(nl_socket, F_SETFD, FD_CLOEXEC)) + goto err_close; + + return 0; + + err_close: + nl_close(); + return -1; +} + +void nl_close() { + close(nl_socket); + nl_socket = -1; +} + +void nl_queryifstatus(int ifidx) { + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + } req; + + req.hdr.nlmsg_len = sizeof(req); + req.hdr.nlmsg_type = RTM_GETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = nlcb_pid; + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_index = ifidx; /* Doesn't work... */ + req.ifinfo.ifi_flags = IFF_UP; + req.ifinfo.ifi_change = 0xffffffff; + + send(nl_socket, &req, sizeof(req),0); +} + + +static void nl_handlemsg(struct nlmsghdr *msg, unsigned int len) { + if (len < sizeof(*msg)) return; + + while(NLMSG_OK(msg,len)) { + if (nlcb_run && + nlcb_pid == msg->nlmsg_pid && + nlcb_seq == msg->nlmsg_seq) { + nlcb_function(msg, nlcb_args); + + if (msg->nlmsg_type == NLMSG_DONE || + msg->nlmsg_type == NLMSG_ERROR) nlcb_run = 0; + } + + if (NLMSG_PAYLOAD(msg, msg->nlmsg_len) >= sizeof(struct ifinfomsg)) { + struct ifinfomsg *ifinfo = NLMSG_DATA(msg); + + switch(msg->nlmsg_type) { + case RTM_NEWLINK: + if (ifinfo->ifi_index != client_config.ifindex) + break; + if (ifinfo->ifi_flags & IFF_UP) { + if (ifinfo->ifi_flags & IFF_RUNNING) + printf("XXX_OS_UP() NYI\n"); + else + printf("XXX_OS_DOWN() NYI\n"); + } else + printf("XXX_OS_SHUT() NYI\n"); + break; + case RTM_DELLINK: + if (ifinfo->ifi_index != client_config.ifindex) + break; + printf("XXX_OS_REMOVED() NYI\n"); + break; + default: + break; + } + } + msg = NLMSG_NEXT(msg,len); + } +} + +static void nl_sync_dump() { + char c[NLMSG_RECVSIZE]; + struct nlmsghdr *msg = (struct nlmsghdr *)c; + int n; + + nlcb_seq = nl_seq; + for(nlcb_run = 1; nlcb_run;) { + n = recv(nl_socket, c, NLMSG_RECVSIZE, 0); + assert(n >= 0); + nl_handlemsg(msg,n); + } +} + +// Callback function for getting interface mac address and index. +static void copy_ifdata(struct nlmsghdr *msg, void **args) { + struct ifinfomsg *ifinfo = NLMSG_DATA(msg); + struct rtattr *rta = IFLA_RTA(ifinfo); + int len = NLMSG_PAYLOAD(msg, sizeof(*ifinfo)); + int found = 0; + + if (msg->nlmsg_type != RTM_NEWLINK) + return; + if (client_config.ifindex) + return; + + for(; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + switch(rta->rta_type) { + case IFLA_IFNAME: + if (!strncmp(client_config.interface, + (char *)RTA_DATA(rta), RTA_PAYLOAD(rta))) { + client_config.ifindex = ifinfo->ifi_index; + log_line("adapter index %d", ifinfo->ifi_index); + found |= 1; + } + break; + case IFLA_ADDRESS: + if (found != 1) + break; + /* We can only handle ethernet like devices with 6 octet MAC */ + if (RTA_PAYLOAD(rta) == 6) { + memcpy(client_config.arp, RTA_DATA(rta), 6); + log_line("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", + client_config.arp[0], client_config.arp[1], + client_config.arp[2], client_config.arp[3], + client_config.arp[4], client_config.arp[5]); + found |= 2; + } + break; + } + } + if (found == 3) + nlcb_args[0] = (void *)1; +} + +// Gets interface mac address and index (synchronous). +int nl_getifdata(const char *ifname) { + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + } req; + + req.hdr.nlmsg_len = sizeof(req); + req.hdr.nlmsg_type = RTM_GETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = nlcb_pid; + req.ifinfo.ifi_family = AF_UNSPEC; + + if (send(nl_socket, &req, sizeof(req), 0) != sizeof(req)) return -1; + + nlcb_function = copy_ifdata; + nlcb_args[0] = NULL; + + nl_sync_dump(); + + return nlcb_args[0]?0:-1; +} diff --git a/ndhc/netlink.h b/ndhc/netlink.h new file mode 100644 index 0000000..95ac4e5 --- /dev/null +++ b/ndhc/netlink.h @@ -0,0 +1,9 @@ +#ifndef NK_NETLINK_H_ +#define NK_NETLINK_H_ + +int nl_open(); +void nl_close(); +void nl_queryifstatus(int ifidx); +int nl_getifdata(const char *ifname); + +#endif /* NK_NETLINK_H_ */ diff --git a/ndhc/socket.c b/ndhc/socket.c index 66bf6e3..2c20a48 100644 --- a/ndhc/socket.c +++ b/ndhc/socket.c @@ -47,57 +47,6 @@ int set_sock_nonblock(int fd) return ret; } -/* Given an interface name in @interface, return its index number, - * IPv4 address, and MAC in @ifindex, @addr (optional), and @mac.*/ -int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *mac) -{ - int fd, ret = -1; - struct ifreq ifr; - struct sockaddr_in *our_ip; - - memset(&ifr, 0, sizeof(struct ifreq)); - if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) { - log_error("socket failed!: %s", strerror(errno)); - goto out; - } - - ifr.ifr_addr.sa_family = AF_INET; - strlcpy(ifr.ifr_name, interface, IFNAMSIZ); - - if (addr) { - if (ioctl(fd, SIOCGIFADDR, &ifr)) { - log_error("Couldn't get IP for %s.", strerror(errno)); - goto out_fd; - } - our_ip = (struct sockaddr_in *) &ifr.ifr_addr; - *addr = our_ip->sin_addr.s_addr; - log_line("%s (our ip) = %s", ifr.ifr_name, - inet_ntoa(our_ip->sin_addr)); - } - - if (ioctl(fd, SIOCGIFINDEX, &ifr)) { - log_error("SIOCGIFINDEX failed!: %s", strerror(errno)); - goto out_fd; - } - - log_line("adapter index %d", ifr.ifr_ifindex); - *ifindex = ifr.ifr_ifindex; - - if (ioctl(fd, SIOCGIFHWADDR, &ifr)) { - log_error("Couldn't get MAC for %s", strerror(errno)); - goto out_fd; - } - - memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); - log_line("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - ret = 0; - out_fd: - close(fd); - out: - return ret; -} - /* Returns fd of new listen socket bound to @ip:@port on interface @inf * on success, or -1 on failure. */ int listen_socket(unsigned int ip, int port, char *inf) diff --git a/ndhc/socket.h b/ndhc/socket.h index 6f46a6e..c28f396 100644 --- a/ndhc/socket.h +++ b/ndhc/socket.h @@ -4,8 +4,6 @@ #include int set_sock_nonblock(int fd); -int read_interface(char *interface, int *ifindex, uint32_t *addr, - uint8_t *mac); int listen_socket(unsigned int ip, int port, char *inf); int raw_socket(int ifindex);