diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:04:11 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:04:11 -0800 |
commit | 4c5a5fb53bccceff331bae70f748bf9b4609fe0a (patch) | |
tree | d6ae69d0d3f4a4d760a3254ec326bca4a8afacfe /if-linux.c | |
parent | e95877ecfa1170d77b1ec1f66752725cdda01b64 (diff) | |
download | external_dhcpcd-4c5a5fb53bccceff331bae70f748bf9b4609fe0a.zip external_dhcpcd-4c5a5fb53bccceff331bae70f748bf9b4609fe0a.tar.gz external_dhcpcd-4c5a5fb53bccceff331bae70f748bf9b4609fe0a.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'if-linux.c')
-rw-r--r-- | if-linux.c | 241 |
1 files changed, 146 insertions, 95 deletions
@@ -46,128 +46,178 @@ #include <string.h> #include <unistd.h> +/* Support older kernels */ +#ifndef IFLA_WIRELESS +# define IFLA_WIRELSSS (IFLFA_MASTER + 1) +#endif + #include "config.h" #include "common.h" #include "dhcp.h" #include "net.h" -/* This netlink stuff is overly compex IMO. - * The BSD implementation is much cleaner and a lot less code. - * send_netlink handles the actual transmission so we can work out - * if there was an error or not. */ #define BUFFERLEN 256 -static int -send_netlink(struct nlmsghdr *hdr) + +int +open_link_socket(struct interface *iface) { - int s; - pid_t mypid = getpid (); + int fd; struct sockaddr_nl nl; - struct iovec iov; - struct msghdr msg; - static unsigned int seq; - char *buffer = NULL; - ssize_t bytes; - union - { - char *buffer; - struct nlmsghdr *nlm; - } h; - int len, l; - struct nlmsgerr *err; - if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) + if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) return -1; - memset(&nl, 0, sizeof(nl)); nl.nl_family = AF_NETLINK; - if (bind(s, (struct sockaddr *)&nl, sizeof(nl)) == -1) - goto eexit; - - memset(&iov, 0, sizeof(iov)); - iov.iov_base = hdr; - iov.iov_len = hdr->nlmsg_len; - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &nl; - msg.msg_namelen = sizeof(nl); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - /* Request a reply */ - hdr->nlmsg_flags |= NLM_F_ACK; - hdr->nlmsg_seq = ++seq; + nl.nl_groups = RTMGRP_LINK; + if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) + return -1; + set_cloexec(fd); + if (iface->link_fd != -1) + close(iface->link_fd); + iface->link_fd = fd; + return 0; +} - if (sendmsg(s, &msg, 0) == -1) - goto eexit; +static int +get_netlink(int fd, int flags, + int (*callback)(struct nlmsghdr *, const char *), + const char *ifname) +{ + char *buffer = NULL; + ssize_t bytes; + struct nlmsghdr *nlm; + int r = -1; buffer = xzalloc(sizeof(char) * BUFFERLEN); - iov.iov_base = buffer; - for (;;) { - iov.iov_len = BUFFERLEN; - bytes = recvmsg(s, &msg, 0); - + bytes = recv(fd, buffer, BUFFERLEN, flags); if (bytes == -1) { + if (errno == EAGAIN) { + r = 0; + goto eexit; + } if (errno == EINTR) continue; goto eexit; } - - if (bytes == 0) { - errno = ENODATA; - goto eexit; + for (nlm = (struct nlmsghdr *)buffer; + NLMSG_OK(nlm, (size_t)bytes); + nlm = NLMSG_NEXT(nlm, bytes)) + { + r = callback(nlm, ifname); + if (r != 0) + goto eexit; } + } - if (msg.msg_namelen != sizeof(nl)) { - errno = EBADMSG; - goto eexit; - } +eexit: + free(buffer); + return r; +} - for (h.buffer = buffer; bytes >= (signed) sizeof(*h.nlm); ) { - len = h.nlm->nlmsg_len; - l = len - sizeof(*h.nlm); - err = (struct nlmsgerr *)NLMSG_DATA(h.nlm); +static int +err_netlink(struct nlmsghdr *nlm, _unused const char *ifname) +{ + struct nlmsgerr *err; + int l; - if (l < 0 || len > bytes) { - errno = EBADMSG; - goto eexit; - } + if (nlm->nlmsg_type != NLMSG_ERROR) + return 0; + l = nlm->nlmsg_len - sizeof(*nlm); + if ((size_t)l < sizeof(*err)) { + errno = EBADMSG; + return -1; + } + err = (struct nlmsgerr *)NLMSG_DATA(nlm); + if (err->error == 0) + return l; + errno = -err->error; + return -1; +} - /* Ensure it's our message */ - if (nl.nl_pid != 0 || - (pid_t)h.nlm->nlmsg_pid != mypid || - h.nlm->nlmsg_seq != seq) - { - /* Next Message */ - bytes -= NLMSG_ALIGN(len); - h.buffer += NLMSG_ALIGN(len); - continue; - } +static int +link_netlink(struct nlmsghdr *nlm, const char *ifname) +{ + int len; + struct rtattr *rta; + struct ifinfomsg *ifi; + char ifn[IF_NAMESIZE + 1]; + + if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK) + return 0; + len = nlm->nlmsg_len - sizeof(*nlm); + if ((size_t)len < sizeof(*ifi)) { + errno = EBADMSG; + return -1; + } + ifi = NLMSG_DATA(nlm); + if (ifi->ifi_flags & IFF_LOOPBACK) + return 0; + rta = (struct rtattr *) ((char *)ifi + NLMSG_ALIGN(sizeof(*ifi))); + len = NLMSG_PAYLOAD(nlm, sizeof(*ifi)); + *ifn = '\0'; + while (RTA_OK(rta, len)) { + switch (rta->rta_type) { + case IFLA_WIRELESS: + /* Ignore wireless messages */ + if (nlm->nlmsg_type == RTM_NEWLINK && + ifi->ifi_change == 0) + return 0; + break; + case IFLA_IFNAME: + strlcpy(ifn, RTA_DATA(rta), sizeof(ifn)); + break; + } + rta = RTA_NEXT(rta, len); + } - /* We get an NLMSG_ERROR back with a code of zero for success */ - if (h.nlm->nlmsg_type != NLMSG_ERROR) - continue; + if (strncmp(ifname, ifn, sizeof(ifn)) == 0) + return 1; + return 0; +} - if ((unsigned)l < sizeof(*err)) { - errno = EBADMSG; - goto eexit; - } +int +link_changed(struct interface *iface) +{ + return get_netlink(iface->link_fd, MSG_DONTWAIT, + &link_netlink, iface->name); +} - if (err->error == 0) { - close(s); - free(buffer); - return l; - } +static int +send_netlink(struct nlmsghdr *hdr) +{ + int fd, r; + struct sockaddr_nl nl; + struct iovec iov; + struct msghdr msg; + static unsigned int seq; - errno = -err->error; - goto eexit; - } + if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) + return -1; + memset(&nl, 0, sizeof(nl)); + nl.nl_family = AF_NETLINK; + if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) { + close(fd); + return -1; } + memset(&iov, 0, sizeof(iov)); + iov.iov_base = hdr; + iov.iov_len = hdr->nlmsg_len; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &nl; + msg.msg_namelen = sizeof(nl); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + /* Request a reply */ + hdr->nlmsg_flags |= NLM_F_ACK; + hdr->nlmsg_seq = ++seq; -eexit: - close(s); - free(buffer); - return -1; + if (sendmsg(fd, &msg, 0) != -1) + r = get_netlink(fd, 0, &err_netlink, NULL); + else + r = -1; + close(fd); + return r; } #define NLMSG_TAIL(nmsg) \ @@ -285,15 +335,16 @@ if_route(const char *ifname, nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlm->hdr.nlmsg_type = RTM_NEWROUTE; if (action == 0) - nlm->hdr.nlmsg_flags = NLM_F_REPLACE; + nlm->hdr.nlmsg_flags = NLM_F_REPLACE; else if (action > 0) /* - * commented out NLM_F_EXCL here and - * below. We sometimes keep one interface up while - * we are configuring the other one, and this flag + * ers@google: + * commented out NLM_F_EXCL here and below. We + * sometimes keep one interface up while we are + * configuring the other one, and this flag * causes route addition to fail. */ - nlm->hdr.nlmsg_flags = NLM_F_CREATE /* | NLM_F_EXCL*/; + nlm->hdr.nlmsg_flags = NLM_F_CREATE /* | NLM_F_EXCL */; else nlm->hdr.nlmsg_type = RTM_DELROUTE; nlm->hdr.nlmsg_flags |= NLM_F_REQUEST; |