diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:29:22 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:29:22 -0800 |
commit | f7c5421560640d23fc10803b9d59a9ff1d83e467 (patch) | |
tree | d6ae69d0d3f4a4d760a3254ec326bca4a8afacfe /if-linux.c | |
parent | d97c47cad830d00c9da685cc4ea157d6185f6c97 (diff) | |
download | android_external_dhcpcd-f7c5421560640d23fc10803b9d59a9ff1d83e467.tar.gz android_external_dhcpcd-f7c5421560640d23fc10803b9d59a9ff1d83e467.tar.bz2 android_external_dhcpcd-f7c5421560640d23fc10803b9d59a9ff1d83e467.zip |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'if-linux.c')
-rw-r--r-- | if-linux.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/if-linux.c b/if-linux.c new file mode 100644 index 0000000..1009270 --- /dev/null +++ b/if-linux.c @@ -0,0 +1,379 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright 2006-2008 Roy Marples <roy@marples.name> + * All rights reserved + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <asm/types.h> /* Needed for 2.4 kernels */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/param.h> + +#include <arpa/inet.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <netinet/ether.h> +#include <netpacket/packet.h> + +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#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" + +#define BUFFERLEN 256 + +int +open_link_socket(struct interface *iface) +{ + int fd; + struct sockaddr_nl nl; + + if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) + return -1; + memset(&nl, 0, sizeof(nl)); + nl.nl_family = AF_NETLINK; + 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; +} + +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); + for (;;) { + bytes = recv(fd, buffer, BUFFERLEN, flags); + if (bytes == -1) { + if (errno == EAGAIN) { + r = 0; + goto eexit; + } + if (errno == EINTR) + continue; + 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; + } + } + +eexit: + free(buffer); + return r; +} + +static int +err_netlink(struct nlmsghdr *nlm, _unused const char *ifname) +{ + struct nlmsgerr *err; + int l; + + 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; +} + +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); + } + + if (strncmp(ifname, ifn, sizeof(ifn)) == 0) + return 1; + return 0; +} + +int +link_changed(struct interface *iface) +{ + return get_netlink(iface->link_fd, MSG_DONTWAIT, + &link_netlink, iface->name); +} + +static int +send_netlink(struct nlmsghdr *hdr) +{ + int fd, r; + struct sockaddr_nl nl; + struct iovec iov; + struct msghdr msg; + static unsigned int seq; + + 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; + + 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) \ + ((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len))) + +static int +add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type, + const void *data, int alen) +{ + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + errno = ENOBUFS; + return -1; + } + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + + return 0; +} + +static int +add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data) +{ + int len = RTA_LENGTH(sizeof(data)); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { + errno = ENOBUFS; + return -1; + } + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), &data, sizeof(data)); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + + return 0; +} + +struct nlma +{ + struct nlmsghdr hdr; + struct ifaddrmsg ifa; + char buffer[64]; +}; + +struct nlmr +{ + struct nlmsghdr hdr; + struct rtmsg rt; + char buffer[256]; +}; + +int +if_address(const char *ifname, + const struct in_addr *address, const struct in_addr *netmask, + const struct in_addr *broadcast, int action) +{ + struct nlma *nlm; + int retval = 0; + + nlm = xzalloc(sizeof(*nlm)); + nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + nlm->hdr.nlmsg_flags = NLM_F_REQUEST; + if (action >= 0) { + nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; + nlm->hdr.nlmsg_type = RTM_NEWADDR; + } else + nlm->hdr.nlmsg_type = RTM_DELADDR; + if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) { + free(nlm); + errno = ENODEV; + return -1; + } + nlm->ifa.ifa_family = AF_INET; + nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask); + /* This creates the aliased interface */ + add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL, + ifname, strlen(ifname) + 1); + add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL, + &address->s_addr, sizeof(address->s_addr)); + if (action >= 0) + add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST, + &broadcast->s_addr, sizeof(broadcast->s_addr)); + + if (send_netlink(&nlm->hdr) == -1) + retval = -1; + free(nlm); + return retval; +} + +int +if_route(const char *ifname, + const struct in_addr *destination, const struct in_addr *netmask, + const struct in_addr *gateway, int metric, int action) +{ + struct nlmr *nlm; + unsigned int ifindex; + int retval = 0; + + + if (!(ifindex = if_nametoindex(ifname))) { + errno = ENODEV; + return -1; + } + + nlm = xzalloc(sizeof(*nlm)); + 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; + else if (action > 0) + /* + * 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 */; + else + nlm->hdr.nlmsg_type = RTM_DELROUTE; + nlm->hdr.nlmsg_flags |= NLM_F_REQUEST; + nlm->rt.rtm_family = AF_INET; + nlm->rt.rtm_table = RT_TABLE_MAIN; + + if (action < 0) + nlm->rt.rtm_scope = RT_SCOPE_NOWHERE; + else { + nlm->hdr.nlmsg_flags |= NLM_F_CREATE /*| NLM_F_EXCL*/; + nlm->rt.rtm_protocol = RTPROT_BOOT; + if (gateway->s_addr == INADDR_ANY) + nlm->rt.rtm_scope = RT_SCOPE_LINK; + else + nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE; + nlm->rt.rtm_type = RTN_UNICAST; + } + + nlm->rt.rtm_dst_len = inet_ntocidr(*netmask); + add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST, + &destination->s_addr, sizeof(destination->s_addr)); + add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY, + &gateway->s_addr, sizeof(gateway->s_addr)); + + add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex); + add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric); + + if (send_netlink(&nlm->hdr) == -1) + retval = -1; + free(nlm); + return retval; +} |