diff --git a/CMakeLists.txt b/CMakeLists.txt index af9a7e6..2b5a129 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -s -std=gnu99 -pedantic -Wall -lcap -DHAVE include_directories("${PROJECT_SOURCE_DIR}/ncmlib") add_subdirectory(ncmlib) -include_directories("${PROJECT_SOURCE_DIR}/libmnl") -add_subdirectory(libmnl) add_subdirectory(ifchd) add_subdirectory(ndhc) diff --git a/ndhc/CMakeLists.txt b/ndhc/CMakeLists.txt index a40c411..ff69b71 100644 --- a/ndhc/CMakeLists.txt +++ b/ndhc/CMakeLists.txt @@ -5,4 +5,4 @@ cmake_minimum_required (VERSION 2.6) file(GLOB NDHC_SRCS "*.c") add_executable(ndhc ${NDHC_SRCS}) -target_link_libraries(ndhc ncmlib libmnl) +target_link_libraries(ndhc ncmlib) diff --git a/ndhc/ndhc.c b/ndhc/ndhc.c index 8c44f3d..e7091fd 100644 --- a/ndhc/ndhc.c +++ b/ndhc/ndhc.c @@ -1,5 +1,5 @@ /* ndhc.c - DHCP client - * Time-stamp: <2011-07-03 05:43:58 njk> + * Time-stamp: <2011-07-03 17:19:25 njk> * * (c) 2004-2011 Nicholas J. Kain * @@ -47,6 +47,7 @@ #include "sys.h" #include "ifchange.h" #include "arp.h" +#include "nl.h" #include "netlink.h" #include "leasefile.h" @@ -308,7 +309,7 @@ int main(int argc, char **argv) write_pid(pidfile); } - if (nl_open(&cs) < 0) { + if ((cs.nlFd = nl_open(NETLINK_ROUTE, RTMGRP_LINK, &nlportid)) < 0) { log_line("FATAL - failed to open netlink socket"); exit(EXIT_FAILURE); } diff --git a/ndhc/netlink.c b/ndhc/netlink.c index 6362821..7d2814d 100644 --- a/ndhc/netlink.c +++ b/ndhc/netlink.c @@ -21,8 +21,6 @@ #include #include #include -#include -#include #include #include #include @@ -32,37 +30,16 @@ #include #include #include -#include #include #include "netlink.h" #include "ifchange.h" #include "arp.h" #include "log.h" +#include "nl.h" -static struct mnl_socket *mls; - -static void nl_close(struct client_state_t *cs) -{ - mnl_socket_close(mls); - cs->nlFd = -1; -} - -int nl_open(struct client_state_t *cs) -{ - assert(cs->nlFd == -1); - if ((mls = mnl_socket_open(NETLINK_ROUTE)) == (struct mnl_socket *)-1) - return -1; - cs->nlFd = mnl_socket_get_fd(mls); - if (fcntl(cs->nlFd, F_SETFD, FD_CLOEXEC)) - goto err_close; - if (mnl_socket_bind(mls, RTMGRP_LINK, 0)) - goto err_close; - return 0; - err_close: - nl_close(cs); - return -1; -} +static char nlbuf[8192]; +int nlportid; static void restart_if(struct client_state_t *cs) { @@ -84,50 +61,34 @@ static void sleep_if(struct client_state_t *cs) cs->timeout = -1; } -static int nl_process_msgs_attr(const struct nlattr *attr, void *data) -{ - const struct nlattr **tb = data; - int type = mnl_attr_get_type(attr); - // skip unsupported attribute in user-space - if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) - return MNL_CB_OK; - switch (type) { - case IFLA_IFNAME: - if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { - log_warning("nl: IFLA_IFNAME failed validation."); - return MNL_CB_ERROR; - } - break; - case IFLA_ADDRESS: - if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) { - log_warning("nl: IFLA_ADDRESS failed validation."); - return MNL_CB_ERROR; - } - break; - } +static int nlrtattr_assign(struct nlattr *attr, int type, void *data) +{ + struct nlattr **tb = data; + if (type >= IFLA_MAX) + return 0; tb[type] = attr; - return MNL_CB_OK; + return 0; } static void get_if_index_and_mac(const struct nlmsghdr *nlh, - struct ifinfomsg *ifm, - const struct nlattr **tb) + struct ifinfomsg *ifm) { - mnl_attr_parse(nlh, sizeof *ifm, nl_process_msgs_attr, tb); + struct nlattr *tb[IFLA_MAX] = {0}; + nl_attr_parse(nlh, sizeof *ifm, nlrtattr_assign, tb); if (!tb[IFLA_IFNAME]) return; - if (!strcmp(client_config.interface, mnl_attr_get_str(tb[IFLA_IFNAME]))) { + if (!strcmp(client_config.interface, nlattr_get_data(tb[IFLA_IFNAME]))) { client_config.ifindex = ifm->ifi_index; if (!tb[IFLA_ADDRESS]) suicide("FATAL: adapter %s lacks a hardware address"); - int maclen = mnl_attr_get_len(tb[IFLA_ADDRESS]) - 4; + int maclen = nlattr_get_len(tb[IFLA_ADDRESS]) - 4; if (maclen != 6) suicide("FATAL: adapter hardware address length should be 6, but is %u", maclen); const unsigned char *mac = - (unsigned char *)mnl_attr_get_str(tb[IFLA_ADDRESS]); + (unsigned char *)nlattr_get_data(tb[IFLA_ADDRESS]); log_line("%s hardware address %x:%x:%x:%x:%x:%x", client_config.interface, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -137,14 +98,13 @@ static void get_if_index_and_mac(const struct nlmsghdr *nlh, static int nl_process_msgs(const struct nlmsghdr *nlh, void *data) { - struct nlattr *tb[IFLA_MAX+1] = {0}; - struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); + struct ifinfomsg *ifm = nlmsg_get_data(nlh); struct client_state_t *cs = data; switch(nlh->nlmsg_type) { case RTM_NEWLINK: if (!client_config.ifindex) - get_if_index_and_mac(nlh, ifm, (const struct nlattr **)tb); + get_if_index_and_mac(nlh, ifm); if (ifm->ifi_index != client_config.ifindex) break; // IFF_UP corresponds to ifconfig down or ifconfig up. @@ -194,41 +154,50 @@ static int nl_process_msgs(const struct nlmsghdr *nlh, void *data) default: break; } - return MNL_CB_OK; + return 1; } void handle_nl_message(struct client_state_t *cs) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - int ret; + ssize_t ret; assert(cs->nlFd != -1); do { - ret = mnl_socket_recvfrom(mls, buf, sizeof buf); - ret = mnl_cb_run(buf, ret, 0, 0, nl_process_msgs, cs); + ret = nl_recv_buf(cs->nlFd, nlbuf, sizeof nlbuf); + if (ret == -1) + break; + if (nl_foreach_nlmsg(nlbuf, ret, nlportid, nl_process_msgs, cs) == -1) + break; } while (ret > 0); - if (ret == -1) - log_line("nl callback function returned error: %s", strerror(errno)); } int nl_getifdata(const char *ifname, struct client_state_t *cs) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; + char buf[8192]; + struct nlmsghdr *nlh = (struct nlmsghdr *)buf; struct ifinfomsg *ifinfo; - unsigned int seq; + size_t msgsize = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof *ifinfo); - nlh = mnl_nlmsg_put_header(buf); + memset(buf, 0, msgsize); + nlh->nlmsg_len = msgsize; nlh->nlmsg_type = RTM_GETLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; - nlh->nlmsg_seq = seq = time(NULL); - - ifinfo = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg)); + nlh->nlmsg_seq = time(NULL); + ifinfo = (struct ifinfomsg *)((char *)buf + NLMSG_HDRLEN); ifinfo->ifi_family = AF_UNSPEC; - if (mnl_socket_sendto(mls, nlh, nlh->nlmsg_len) < 0) + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + }; + if (sendto(cs->nlFd, buf, nlh->nlmsg_len, 0, (struct sockaddr *)&addr, + sizeof addr) == -1) return -1; + // This is rather ugly, but hey! + if (fcntl(cs->nlFd, F_SETFL, fcntl(cs->nlFd, F_GETFL) & ~O_NONBLOCK) == -1) + suicide("nl_getifdata: failed to remove O_NONBLOCK"); handle_nl_message(cs); + if (fcntl(cs->nlFd, F_SETFL, fcntl(cs->nlFd, F_GETFL) | O_NONBLOCK) == -1) + suicide("nl_getifdata: failed to restore O_NONBLOCK"); return 0; } diff --git a/ndhc/netlink.h b/ndhc/netlink.h index fe5bc30..80df66c 100644 --- a/ndhc/netlink.h +++ b/ndhc/netlink.h @@ -20,6 +20,7 @@ #ifndef NK_NETLINK_H_ #define NK_NETLINK_H_ +#include #include "state.h" enum { @@ -30,7 +31,8 @@ enum { IFS_REMOVED }; -int nl_open(struct client_state_t *cs); +extern int nlportid; + void handle_nl_message(struct client_state_t *cs); int nl_getifdata(const char *ifname, struct client_state_t *cs); diff --git a/ndhc/nl.c b/ndhc/nl.c new file mode 100644 index 0000000..69b889b --- /dev/null +++ b/ndhc/nl.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "nl.h" + +void nl_attr_parse(const struct nlmsghdr *nlh, size_t offset, + nl_attr_parse_fn workfn, void *data) +{ + struct nlattr *attr; + for (attr = (struct nlattr *) + ((char *)nlh + NLMSG_HDRLEN + NLMSG_ALIGN(offset)); + nl_attr_ok(attr, (char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len) - + (char *)attr); + attr = (struct nlattr *)((char *)attr + NLMSG_ALIGN(attr->nla_len))) + { + int type = attr->nla_type & NLA_TYPE_MASK; + if (workfn(attr, type, data) < 0) + break; + } +} + +ssize_t nl_recv_buf(int fd, char *buf, size_t blen) +{ + struct sockaddr_nl addr; + struct iovec iov = { + .iov_base = buf, + .iov_len = blen, + }; + struct msghdr msg = { + .msg_name = &addr, + .msg_namelen = sizeof addr, + .msg_iov = &iov, + .msg_iovlen = 1, + }; + size_t ret = recvmsg(fd, &msg, 0); + if (ret == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + log_error("nl_fill_buf: recvmsg failed: %s", strerror(errno)); + return -1; + } + if (msg.msg_flags & MSG_TRUNC) { + log_error("nl_fill_buf: buffer not long enough for message"); + return -1; + } + if (msg.msg_namelen != sizeof addr) { + log_error("nl_fill_buf: response was not of the same address family"); + return -1; + } + return ret; +} + +int nl_foreach_nlmsg(char *buf, size_t blen, int portid, + nlmsg_foreach_fn pfn, void *fnarg) +{ + const struct nlmsghdr *nlh = (const struct nlmsghdr *)buf; + + assert(pfn); + while (NLMSG_OK(nlh, blen)) { + // PortID should be zero for messages from the kernel. + if (nlh->nlmsg_pid && nlh->nlmsg_pid != portid) + continue; + // XXX don't bother with sequence # tracking (0 = kernel, ours = ??) + + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + pfn(nlh, fnarg); + } else { + switch (nlh->nlmsg_type) { + case NLMSG_ERROR: + log_line("nl: received a NLMSG_ERROR: %s", + strerror(nlmsg_get_error(nlh))); + return -1; + case NLMSG_DONE: + return 0; + case NLMSG_OVERRUN: + log_line("nl: received a NLMSG_OVERRUN"); + case NLMSG_NOOP: + default: + break; + } + } + nlh = NLMSG_NEXT(nlh, blen); + } + return 0; +} + +int nl_open(int nltype, int nlgroup, int *nlportid) +{ + int fd; + fd = socket(AF_NETLINK, SOCK_RAW, nltype); + if (fd == -1) { + log_error("nl_open: socket failed: %s", strerror(errno)); + return -1; + } + if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1) { + log_error("nl_open: set non-blocking failed: %s", strerror(errno)); + goto err_close; + } + if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { + log_error("nl_open: set close-on-exec failed: %s", strerror(errno)); + goto err_close; + } + socklen_t al; + struct sockaddr_nl nlsock = { + .nl_family = AF_NETLINK, + .nl_groups = nlgroup, + }; + if (bind(fd, (struct sockaddr *)&nlsock, sizeof nlsock) == -1) { + log_error("nl_open: bind to group failed: %s", strerror(errno)); + goto err_close; + } + al = sizeof nlsock; + if (getsockname(fd, (struct sockaddr *)&nlsock, &al) == -1) { + log_error("nl_open: getsockname failed: %s", strerror(errno)); + goto err_close; + } + if (al != sizeof nlsock) { + log_error("nl_open: bound socket doesn't have right family size"); + goto err_close; + } + if (nlsock.nl_family != AF_NETLINK) { + log_error("nl_open: bound socket isn't AF_NETLINK"); + goto err_close; + } + if (nlportid) + *nlportid = nlsock.nl_pid; + return fd; + err_close: + close(fd); + return -1; +} + diff --git a/ndhc/nl.h b/ndhc/nl.h new file mode 100644 index 0000000..edf0912 --- /dev/null +++ b/ndhc/nl.h @@ -0,0 +1,55 @@ +#ifndef NK_NL_H_ +#define NK_NL_H_ + +// Limited netlink code. The horrors... + +#include +//#include + +static inline int nl_attr_ok(const struct nlattr *attr, size_t len) +{ + if (len < sizeof *attr) + return 0; + if (attr->nla_len < sizeof *attr) + return 0; + if (attr->nla_len > len) + return 0; + return 1; +} + +static inline size_t nlattr_get_len(const struct nlattr *attr) +{ + return attr->nla_len; +} + +static inline void *nlattr_get_data(const struct nlattr *attr) +{ + return (char *)attr + NLA_HDRLEN; +} + +static inline void *nlmsg_get_data(const struct nlmsghdr *nlh) +{ + return (char *)nlh + NLMSG_HDRLEN; +} + +static inline int nlmsg_get_error(const struct nlmsghdr *nlh) +{ + const struct nlmsgerr *err = nlmsg_get_data(nlh); + if (nlh->nlmsg_len < sizeof(struct nlmsgerr) + NLMSG_HDRLEN) + return EBADMSG; + return err->error & 0x7fffffff; +} + +typedef int (*nl_attr_parse_fn)(struct nlattr *attr, int type, void *data); +void nl_attr_parse(const struct nlmsghdr *nlh, size_t offset, + nl_attr_parse_fn workfn, void *data); + +ssize_t nl_recv_buf(int fd, char *buf, size_t blen); + +typedef int (*nlmsg_foreach_fn)(const struct nlmsghdr *, void *); +int nl_foreach_nlmsg(char *buf, size_t blen, int portid, + nlmsg_foreach_fn pfn, void *fnarg); + +int nl_open(int nltype, int nlgroup, int *nlportid); + +#endif /* NK_NL_H_ */