diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:20 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:20 -0800 |
commit | d97c47cad830d00c9da685cc4ea157d6185f6c97 (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /net.c | |
parent | 4c5a5fb53bccceff331bae70f748bf9b4609fe0a (diff) | |
download | android_external_dhcpcd-d97c47cad830d00c9da685cc4ea157d6185f6c97.tar.gz android_external_dhcpcd-d97c47cad830d00c9da685cc4ea157d6185f6c97.tar.bz2 android_external_dhcpcd-d97c47cad830d00c9da685cc4ea157d6185f6c97.zip |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'net.c')
-rw-r--r-- | net.c | 707 |
1 files changed, 0 insertions, 707 deletions
@@ -1,707 +0,0 @@ -/* - * 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 <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/time.h> - -#include <net/if.h> -#include <net/if_arp.h> -#include <arpa/inet.h> -#include <netinet/in_systm.h> -#ifdef __linux__ -#include <netinet/ether.h> -#include <netpacket/packet.h> -#endif -#include <netinet/in.h> -#include <netinet/ip.h> -#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */ -#include <netinet/udp.h> -#undef __FAVOR_BSD -#ifdef SIOCGIFMEDIA -#include <net/if_media.h> -#endif -#include <arpa/inet.h> -#ifdef AF_LINK -# include <net/if_dl.h> -#endif - -#include <ctype.h> -#include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "config.h" -#include "common.h" -#include "dhcp.h" -#include "logger.h" -#include "net.h" -#include "signals.h" - -int -inet_ntocidr(struct in_addr address) -{ - int cidr = 0; - uint32_t mask = htonl(address.s_addr); - - while (mask) { - cidr++; - mask <<= 1; - } - - return cidr; -} - -int -inet_cidrtoaddr(int cidr, struct in_addr *addr) -{ - int ocets; - - if (cidr < 0 || cidr > 32) { - errno = EINVAL; - return -1; - } - ocets = (cidr + 7) / 8; - - addr->s_addr = 0; - if (ocets > 0) { - memset(&addr->s_addr, 255, (size_t)ocets - 1); - memset((unsigned char *)&addr->s_addr + (ocets - 1), - (256 - (1 << (32 - cidr) % 8)), 1); - } - - return 0; -} - -uint32_t -get_netmask(uint32_t addr) -{ - uint32_t dst; - - if (addr == 0) - return 0; - - dst = htonl(addr); - if (IN_CLASSA(dst)) - return ntohl(IN_CLASSA_NET); - if (IN_CLASSB (dst)) - return ntohl(IN_CLASSB_NET); - if (IN_CLASSC (dst)) - return ntohl(IN_CLASSC_NET); - - return 0; -} - -char * -hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen) -{ - static char buffer[(HWADDR_LEN * 3) + 1]; - char *p = buffer; - size_t i; - - for (i = 0; i < hwlen && i < HWADDR_LEN; i++) { - if (i > 0) - *p ++= ':'; - p += snprintf(p, 3, "%.2x", hwaddr[i]); - } - - *p ++= '\0'; - - return buffer; -} - -size_t -hwaddr_aton(unsigned char *buffer, const char *addr) -{ - char c[3]; - const char *p = addr; - unsigned char *bp = buffer; - size_t len = 0; - - c[2] = '\0'; - while (*p) { - c[0] = *p++; - c[1] = *p++; - /* Ensure that digits are hex */ - if (isxdigit((unsigned char)c[0]) == 0 || - isxdigit((unsigned char)c[1]) == 0) - { - errno = EINVAL; - return 0; - } - /* We should have at least two entries 00:01 */ - if (len == 0 && *p == '\0') { - errno = EINVAL; - return 0; - } - /* Ensure that next data is EOL or a seperator with data */ - if (!(*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) { - errno = EINVAL; - return 0; - } - if (*p) - p++; - if (bp) - *bp++ = (unsigned char)strtol(c, NULL, 16); - len++; - } - return len; -} - -int -do_interface(const char *ifname, - _unused unsigned char *hwaddr, _unused size_t *hwlen, - struct in_addr *addr, struct in_addr *net, int get) -{ - int s; - struct ifconf ifc; - int retval = 0, found = 0; - int len = 10 * sizeof(struct ifreq); - int lastlen = 0; - char *p; - union { - char *buffer; - struct ifreq *ifr; - } ifreqs; - struct sockaddr_in address; - struct ifreq *ifr; - struct sockaddr_in netmask; -#ifdef AF_LINK - struct sockaddr_dl *sdl; -#endif - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - return -1; - - /* Not all implementations return the needed buffer size for - * SIOGIFCONF so we loop like so for all until it works */ - memset(&ifc, 0, sizeof(ifc)); - for (;;) { - ifc.ifc_len = len; - ifc.ifc_buf = xmalloc((size_t)len); - if (ioctl(s, SIOCGIFCONF, &ifc) == -1) { - if (errno != EINVAL || lastlen != 0) { - close(s); - free(ifc.ifc_buf); - return -1; - } - } else { - if (ifc.ifc_len == lastlen) - break; - lastlen = ifc.ifc_len; - } - - free(ifc.ifc_buf); - ifc.ifc_buf = NULL; - len *= 2; - } - - for (p = (char *)ifc.ifc_buf; p < (char *)ifc.ifc_buf + ifc.ifc_len;) { - /* Cast the ifc buffer to an ifreq cleanly */ - ifreqs.buffer = p; - ifr = ifreqs.ifr; - -#ifndef __linux__ - if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru)) - p += offsetof(struct ifreq, ifr_ifru) + - ifr->ifr_addr.sa_len; - else -#endif - p += sizeof(*ifr); - - if (strcmp(ifname, ifr->ifr_name) != 0) - continue; - - found = 1; - -#ifdef AF_LINK - if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) { - sdl = xmalloc(ifr->ifr_addr.sa_len); - memcpy(sdl, &ifr->ifr_addr, ifr->ifr_addr.sa_len); - *hwlen = sdl->sdl_alen; - memcpy(hwaddr, LLADDR(sdl), *hwlen); - free(sdl); - retval = 1; - break; - } -#endif - - if (ifr->ifr_addr.sa_family == AF_INET) { - memcpy(&address, &ifr->ifr_addr, sizeof(address)); - if (ioctl(s, SIOCGIFNETMASK, ifr) == -1) - continue; - memcpy(&netmask, &ifr->ifr_addr, sizeof(netmask)); - if (get) { - addr->s_addr = address.sin_addr.s_addr; - net->s_addr = netmask.sin_addr.s_addr; - retval = 1; - break; - } else { - if (address.sin_addr.s_addr == addr->s_addr && - (!net || - netmask.sin_addr.s_addr == net->s_addr)) - { - retval = 1; - break; - } - } - } - - } - - if (!found) - errno = ENXIO; - close(s); - free(ifc.ifc_buf); - return retval; -} - -int -up_interface(const char *ifname) -{ - int s; - struct ifreq ifr; - int retval = -1; -#ifdef __linux__ - char *p; -#endif - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - return -1; - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); -#ifdef __linux__ - /* We can only bring the real interface up */ - if ((p = strchr(ifr.ifr_name, ':'))) - *p = '\0'; -#endif - if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) { - if ((ifr.ifr_flags & IFF_UP)) - retval = 0; - else { - ifr.ifr_flags |= IFF_UP; - if (ioctl(s, SIOCSIFFLAGS, &ifr) == 0) - retval = 0; - } - } - close(s); - return retval; -} - -int -carrier_status(const char *ifname) -{ - int s; - struct ifreq ifr; - int retval = -1; -#ifdef SIOCGIFMEDIA - struct ifmediareq ifmr; -#endif -#ifdef __linux__ - char *p; -#endif - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - return -1; - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); -#ifdef __linux__ - /* We can only test the real interface up */ - if ((p = strchr(ifr.ifr_name, ':'))) - *p = '\0'; -#endif - if ((retval = ioctl(s, SIOCGIFFLAGS, &ifr)) == 0) { - if (ifr.ifr_flags & IFF_UP && ifr.ifr_flags & IFF_RUNNING) - retval = 1; - else - retval = 0; - } - -#ifdef SIOCGIFMEDIA - if (retval == 1) { - memset(&ifmr, 0, sizeof(ifmr)); - strncpy(ifmr.ifm_name, ifr.ifr_name, sizeof(ifmr.ifm_name)); - if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1 && - ifmr.ifm_status & IFM_AVALID) - { - if (!(ifmr.ifm_status & IFM_ACTIVE)) - retval = 0; - } - } -#endif - close(s); - return retval; -} - -struct interface * -read_interface(const char *ifname, _unused int metric) -{ - int s; - struct ifreq ifr; - struct interface *iface = NULL; - unsigned char *hwaddr = NULL; - size_t hwlen = 0; - sa_family_t family = 0; - - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - return NULL; - -#ifdef __linux__ - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1) - goto eexit; - - switch (ifr.ifr_hwaddr.sa_family) { - case ARPHRD_ETHER: - case ARPHRD_IEEE802: - hwlen = ETHER_ADDR_LEN; - break; - case ARPHRD_IEEE1394: - hwlen = EUI64_ADDR_LEN; - case ARPHRD_INFINIBAND: - hwlen = INFINIBAND_ADDR_LEN; - break; - } - - hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN); - memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, hwlen); - family = ifr.ifr_hwaddr.sa_family; -#else - ifr.ifr_metric = metric; - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - if (ioctl(s, SIOCSIFMETRIC, &ifr) == -1) - goto eexit; - - hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN); - if (do_interface(ifname, hwaddr, &hwlen, NULL, NULL, 0) != 1) - goto eexit; - - family = ARPHRD_ETHER; -#endif - - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - if (ioctl(s, SIOCGIFMTU, &ifr) == -1) - goto eexit; - - /* Ensure that the MTU is big enough for DHCP */ - if (ifr.ifr_mtu < MTU_MIN) { - ifr.ifr_mtu = MTU_MIN; - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - if (ioctl(s, SIOCSIFMTU, &ifr) == -1) - goto eexit; - } - - if (up_interface(ifname) != 0) - goto eexit; - - iface = xzalloc(sizeof(*iface)); - strlcpy(iface->name, ifname, IF_NAMESIZE); - snprintf(iface->leasefile, PATH_MAX, LEASEFILE, ifname); - memcpy(&iface->hwaddr, hwaddr, hwlen); - iface->hwlen = hwlen; - - iface->family = family; - iface->arpable = !(ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)); - - /* 0 is a valid fd, so init to -1 */ - iface->raw_fd = -1; - iface->udp_fd = -1; - iface->arp_fd = -1; - iface->link_fd = -1; - -eexit: - close(s); - free(hwaddr); - return iface; -} - -int -do_mtu(const char *ifname, short int mtu) -{ - struct ifreq ifr; - int r; - int s; - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - return -1; - - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - ifr.ifr_mtu = mtu; - r = ioctl(s, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr); - close(s); - if (r == -1) - return -1; - return ifr.ifr_mtu; -} - -void -free_routes(struct rt *routes) -{ - struct rt *r; - - while (routes) { - r = routes->next; - free(routes); - routes = r; - } -} - -int -open_udp_socket(struct interface *iface) -{ - int s; - union sockunion { - struct sockaddr sa; - struct sockaddr_in sin; - } su; - int n; -#ifdef SO_BINDTODEVICE - struct ifreq ifr; -#endif - - if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) - return -1; - - n = 1; - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) - goto eexit; -#ifdef SO_BINDTODEVICE - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name)); - if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1) - goto eexit; -#endif - /* As we don't use this socket for receiving, set the - * receive buffer to 1 */ - n = 1; - if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1) - goto eexit; - memset(&su, 0, sizeof(su)); - su.sin.sin_family = AF_INET; - su.sin.sin_port = htons(DHCP_CLIENT_PORT); - su.sin.sin_addr.s_addr = iface->addr.s_addr; - if (bind(s, &su.sa, sizeof(su)) == -1) - goto eexit; - - iface->udp_fd = s; - set_cloexec(s); - return 0; - -eexit: - close(s); - return -1; -} - -ssize_t -send_packet(const struct interface *iface, struct in_addr to, - const uint8_t *data, ssize_t len) -{ - union sockunion { - struct sockaddr sa; - struct sockaddr_in sin; - } su; - - memset(&su, 0, sizeof(su)); - su.sin.sin_family = AF_INET; - su.sin.sin_addr.s_addr = to.s_addr; - su.sin.sin_port = htons(DHCP_SERVER_PORT); - - return sendto(iface->udp_fd, data, len, 0, &su.sa, sizeof(su)); -} - -struct udp_dhcp_packet -{ - struct ip ip; - struct udphdr udp; - struct dhcp_message dhcp; -}; -const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet); - -static uint16_t -checksum(const void *data, uint16_t len) -{ - const uint8_t *addr = data; - uint32_t sum = 0; - - while (len > 1) { - sum += addr[0] * 256 + addr[1]; - addr += 2; - len -= 2; - } - - if (len == 1) - sum += *addr * 256; - - sum = (sum >> 16) + (sum & 0xffff); - sum += (sum >> 16); - - sum = htons(sum); - - return ~sum; -} - -ssize_t -make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length, - struct in_addr source, struct in_addr dest) -{ - struct udp_dhcp_packet *udpp; - struct ip *ip; - struct udphdr *udp; - - udpp = xzalloc(sizeof(*udpp)); - ip = &udpp->ip; - udp = &udpp->udp; - - /* OK, this is important :) - * We copy the data to our packet and then create a small part of the - * ip structure and an invalid ip_len (basically udp length). - * We then fill the udp structure and put the checksum - * of the whole packet into the udp checksum. - * Finally we complete the ip structure and ip checksum. - * If we don't do the ordering like so then the udp checksum will be - * broken, so find another way of doing it! */ - - memcpy(&udpp->dhcp, data, length); - - ip->ip_p = IPPROTO_UDP; - ip->ip_src.s_addr = source.s_addr; - if (dest.s_addr == 0) - ip->ip_dst.s_addr = INADDR_BROADCAST; - else - ip->ip_dst.s_addr = dest.s_addr; - - udp->uh_sport = htons(DHCP_CLIENT_PORT); - udp->uh_dport = htons(DHCP_SERVER_PORT); - udp->uh_ulen = htons(sizeof(*udp) + length); - ip->ip_len = udp->uh_ulen; - udp->uh_sum = checksum(udpp, sizeof(*udpp)); - - ip->ip_v = IPVERSION; - ip->ip_hl = 5; - ip->ip_id = 0; - ip->ip_tos = IPTOS_LOWDELAY; - ip->ip_len = htons (sizeof(*ip) + sizeof(*udp) + length); - ip->ip_id = 0; - ip->ip_off = htons(IP_DF); /* Don't fragment */ - ip->ip_ttl = IPDEFTTL; - - ip->ip_sum = checksum(ip, sizeof(*ip)); - - *packet = (uint8_t *)udpp; - return sizeof(*ip) + sizeof(*udp) + length; -} - -ssize_t -get_udp_data(const uint8_t **data, const uint8_t *udp) -{ - struct udp_dhcp_packet packet; - - memcpy(&packet, udp, sizeof(packet)); - *data = udp + offsetof(struct udp_dhcp_packet, dhcp); - return ntohs(packet.ip.ip_len) - sizeof(packet.ip) - sizeof(packet.udp); -} - -int -valid_udp_packet(const uint8_t *data) -{ - struct udp_dhcp_packet packet; - uint16_t bytes; - uint16_t ipsum; - uint16_t iplen; - uint16_t udpsum; - struct in_addr source; - struct in_addr dest; - int retval = 0; - - memcpy(&packet, data, sizeof(packet)); - bytes = ntohs(packet.ip.ip_len); - ipsum = packet.ip.ip_sum; - iplen = packet.ip.ip_len; - udpsum = packet.udp.uh_sum; - - if (0 != checksum(&packet.ip, sizeof(packet.ip))) { - errno = EINVAL; - return -1; - } - - packet.ip.ip_sum = 0; - memcpy(&source, &packet.ip.ip_src, sizeof(packet.ip.ip_src)); - memcpy(&dest, &packet.ip.ip_dst, sizeof(packet.ip.ip_dst)); - memset(&packet.ip, 0, sizeof(packet.ip)); - packet.udp.uh_sum = 0; - - packet.ip.ip_p = IPPROTO_UDP; - memcpy(&packet.ip.ip_src, &source, sizeof(packet.ip.ip_src)); - memcpy(&packet.ip.ip_dst, &dest, sizeof(packet.ip.ip_dst)); - packet.ip.ip_len = packet.udp.uh_ulen; - if (udpsum && udpsum != checksum(&packet, bytes)) { - errno = EINVAL; - retval = -1; - } - - return retval; -} - -int -send_arp(const struct interface *iface, int op, in_addr_t sip, in_addr_t tip) -{ - struct arphdr *arp; - size_t arpsize; - uint8_t *p; - int retval; - - arpsize = sizeof(*arp) + 2 * iface->hwlen + 2 * sizeof(sip); - arp = xmalloc(arpsize); - arp->ar_hrd = htons(iface->family); - arp->ar_pro = htons(ETHERTYPE_IP); - arp->ar_hln = iface->hwlen; - arp->ar_pln = sizeof(sip); - arp->ar_op = htons(op); - p = (uint8_t *)arp; - p += sizeof(*arp); - memcpy(p, iface->hwaddr, iface->hwlen); - p += iface->hwlen; - memcpy(p, &sip, sizeof(sip)); - p += sizeof(sip); - /* ARP requests should ignore this */ - retval = iface->hwlen; - while (retval--) - *p++ = '\0'; - memcpy(p, &tip, sizeof(tip)); - p += sizeof(tip); - retval = send_raw_packet(iface, ETHERTYPE_ARP, arp, arpsize); - free(arp); - return retval; -} |