1/* 2 * dhcpcd - DHCP client daemon 3 * Copyright (c) 2006-2010 Roy Marples <roy@marples.name> 4 * All rights reserved 5 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/types.h> 29#include <sys/ioctl.h> 30#include <sys/param.h> 31#include <sys/socket.h> 32#include <sys/time.h> 33 34#include <arpa/inet.h> 35#include <net/if.h> 36#include <net/if_arp.h> 37#ifdef AF_LINK 38# include <net/if_dl.h> 39# include <net/if_types.h> 40#endif 41#include <netinet/in_systm.h> 42#include <netinet/in.h> 43#include <netinet/ip.h> 44#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */ 45#include <netinet/udp.h> 46#undef __FAVOR_BSD 47#ifdef AF_PACKET 48# include <netpacket/packet.h> 49#endif 50#ifdef SIOCGIFMEDIA 51# include <net/if_media.h> 52#endif 53 54#include <ctype.h> 55#include <errno.h> 56#include <ifaddrs.h> 57#include <fnmatch.h> 58#include <stddef.h> 59#include <stdio.h> 60#include <stdlib.h> 61#include <string.h> 62#include <unistd.h> 63 64#include "common.h" 65#include "dhcp.h" 66#include "if-options.h" 67#include "net.h" 68 69#include <rump/rump_syscalls.h> 70 71static char hwaddr_buffer[(HWADDR_LEN * 3) + 1]; 72 73int socket_afnet = -1; 74 75int 76inet_ntocidr(struct in_addr address) 77{ 78 int cidr = 0; 79 uint32_t mask = htonl(address.s_addr); 80 81 while (mask) { 82 cidr++; 83 mask <<= 1; 84 } 85 return cidr; 86} 87 88int 89inet_cidrtoaddr(int cidr, struct in_addr *addr) 90{ 91 int ocets; 92 93 if (cidr < 1 || cidr > 32) { 94 errno = EINVAL; 95 return -1; 96 } 97 ocets = (cidr + 7) / 8; 98 99 addr->s_addr = 0; 100 if (ocets > 0) { 101 memset(&addr->s_addr, 255, (size_t)ocets - 1); 102 memset((unsigned char *)&addr->s_addr + (ocets - 1), 103 (256 - (1 << (32 - cidr) % 8)), 1); 104 } 105 106 return 0; 107} 108 109uint32_t 110get_netmask(uint32_t addr) 111{ 112 uint32_t dst; 113 114 if (addr == 0) 115 return 0; 116 117 dst = htonl(addr); 118 if (IN_CLASSA(dst)) 119 return ntohl(IN_CLASSA_NET); 120 if (IN_CLASSB(dst)) 121 return ntohl(IN_CLASSB_NET); 122 if (IN_CLASSC(dst)) 123 return ntohl(IN_CLASSC_NET); 124 125 return 0; 126} 127 128char * 129hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen) 130{ 131 char *p = hwaddr_buffer; 132 size_t i; 133 134 for (i = 0; i < hwlen && i < HWADDR_LEN; i++) { 135 if (i > 0) 136 *p ++= ':'; 137 p += snprintf(p, 3, "%.2x", hwaddr[i]); 138 } 139 140 *p ++= '\0'; 141 142 return hwaddr_buffer; 143} 144 145size_t 146hwaddr_aton(unsigned char *buffer, const char *addr) 147{ 148 char c[3]; 149 const char *p = addr; 150 unsigned char *bp = buffer; 151 size_t len = 0; 152 153 c[2] = '\0'; 154 while (*p) { 155 c[0] = *p++; 156 c[1] = *p++; 157 /* Ensure that digits are hex */ 158 if (isxdigit((unsigned char)c[0]) == 0 || 159 isxdigit((unsigned char)c[1]) == 0) 160 { 161 errno = EINVAL; 162 return 0; 163 } 164 /* We should have at least two entries 00:01 */ 165 if (len == 0 && *p == '\0') { 166 errno = EINVAL; 167 return 0; 168 } 169 /* Ensure that next data is EOL or a separator with data */ 170 if (!(*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) { 171 errno = EINVAL; 172 return 0; 173 } 174 if (*p) 175 p++; 176 if (bp) 177 *bp++ = (unsigned char)strtol(c, NULL, 16); 178 len++; 179 } 180 return len; 181} 182 183struct interface * 184init_interface(const char *ifname) 185{ 186 struct ifreq ifr; 187 struct interface *iface = NULL; 188 189 memset(&ifr, 0, sizeof(ifr)); 190 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 191 if (rump_sys_ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == -1) 192 goto eexit; 193 194 iface = xzalloc(sizeof(*iface)); 195 strlcpy(iface->name, ifname, sizeof(iface->name)); 196 iface->flags = ifr.ifr_flags; 197 /* We reserve the 100 range for virtual interfaces, if and when 198 * we can work them out. */ 199 iface->metric = 200 + if_nametoindex(iface->name); 200 if (getifssid(ifname, iface->ssid) != -1) { 201 iface->wireless = 1; 202 iface->metric += 100; 203 } 204 205 if (rump_sys_ioctl(socket_afnet, SIOCGIFMTU, &ifr) == -1) 206 goto eexit; 207 /* Ensure that the MTU is big enough for DHCP */ 208 if (ifr.ifr_mtu < MTU_MIN) { 209 ifr.ifr_mtu = MTU_MIN; 210 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 211 if (rump_sys_ioctl(socket_afnet, SIOCSIFMTU, &ifr) == -1) 212 goto eexit; 213 } 214 215 /* 0 is a valid fd, so init to -1 */ 216 iface->raw_fd = -1; 217 iface->udp_fd = -1; 218 iface->arp_fd = -1; 219 goto exit; 220 221eexit: 222 free(iface); 223 iface = NULL; 224exit: 225 return iface; 226} 227 228int 229carrier_status(struct interface *iface) 230{ 231 int ret; 232 struct ifreq ifr; 233#ifdef SIOCGIFMEDIA 234 struct ifmediareq ifmr; 235#endif 236#ifdef __linux__ 237 char *p; 238#endif 239 240 memset(&ifr, 0, sizeof(ifr)); 241 strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name)); 242#ifdef __linux__ 243 /* We can only test the real interface up */ 244 if ((p = strchr(ifr.ifr_name, ':'))) 245 *p = '\0'; 246#endif 247 248 if (rump_sys_ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == -1) 249 return -1; 250 iface->flags = ifr.ifr_flags; 251 252 ret = -1; 253#ifdef SIOCGIFMEDIA 254 memset(&ifmr, 0, sizeof(ifmr)); 255 strlcpy(ifmr.ifm_name, iface->name, sizeof(ifmr.ifm_name)); 256 if (rump_sys_ioctl(socket_afnet, SIOCGIFMEDIA, &ifmr) != -1 && 257 ifmr.ifm_status & IFM_AVALID) 258 ret = (ifmr.ifm_status & IFM_ACTIVE) ? 1 : 0; 259#endif 260 if (ret == -1) 261 ret = (ifr.ifr_flags & IFF_RUNNING) ? 1 : 0; 262 return ret; 263} 264 265int 266up_interface(struct interface *iface) 267{ 268 struct ifreq ifr; 269 int retval = -1; 270#ifdef __linux__ 271 char *p; 272#endif 273 274 memset(&ifr, 0, sizeof(ifr)); 275 strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name)); 276#ifdef __linux__ 277 /* We can only bring the real interface up */ 278 if ((p = strchr(ifr.ifr_name, ':'))) 279 *p = '\0'; 280#endif 281 if (rump_sys_ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == 0) { 282 if ((ifr.ifr_flags & IFF_UP)) 283 retval = 0; 284 else { 285 ifr.ifr_flags |= IFF_UP; 286 if (rump_sys_ioctl(socket_afnet, SIOCSIFFLAGS, &ifr) == 0) 287 retval = 0; 288 } 289 iface->flags = ifr.ifr_flags; 290 } 291 return retval; 292} 293 294int 295do_address(const char *ifname, 296 struct in_addr *addr, struct in_addr *net, struct in_addr *dst, int act) 297{ 298 struct ifaddrs *ifaddrs, *ifa; 299 const struct sockaddr_in *a, *n, *d; 300 int retval; 301 302 if (getifaddrs(&ifaddrs) == -1) 303 return -1; 304 305 retval = 0; 306 for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { 307 if (ifa->ifa_addr == NULL || 308 ifa->ifa_addr->sa_family != AF_INET || 309 strcmp(ifa->ifa_name, ifname) != 0) 310 continue; 311 a = (const struct sockaddr_in *)(void *)ifa->ifa_addr; 312 n = (const struct sockaddr_in *)(void *)ifa->ifa_netmask; 313 if (ifa->ifa_flags & IFF_POINTOPOINT) 314 d = (const struct sockaddr_in *)(void *) 315 ifa->ifa_dstaddr; 316 else 317 d = NULL; 318 if (act == 1) { 319 addr->s_addr = a->sin_addr.s_addr; 320 net->s_addr = n->sin_addr.s_addr; 321 if (dst) { 322 if (ifa->ifa_flags & IFF_POINTOPOINT) 323 dst->s_addr = d->sin_addr.s_addr; 324 else 325 dst->s_addr = INADDR_ANY; 326 } 327 retval = 1; 328 break; 329 } 330 if (addr->s_addr == a->sin_addr.s_addr && 331 (net == NULL || net->s_addr == n->sin_addr.s_addr)) 332 { 333 retval = 1; 334 break; 335 } 336 } 337 freeifaddrs(ifaddrs); 338 return retval; 339} 340 341int 342do_mtu(const char *ifname, short int mtu) 343{ 344 struct ifreq ifr; 345 int r; 346 347 memset(&ifr, 0, sizeof(ifr)); 348 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 349 ifr.ifr_mtu = mtu; 350 r = rump_sys_ioctl(socket_afnet, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr); 351 if (r == -1) 352 return -1; 353 return ifr.ifr_mtu; 354} 355 356void 357free_routes(struct rt *routes) 358{ 359 struct rt *r; 360 361 while (routes) { 362 r = routes->next; 363 free(routes); 364 routes = r; 365 } 366} 367 368struct udp_dhcp_packet 369{ 370 struct ip ip; 371 struct udphdr udp; 372 struct dhcp_message dhcp; 373}; 374const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet); 375 376static uint16_t 377checksum(const void *data, uint16_t len) 378{ 379 const uint8_t *addr = data; 380 uint32_t sum = 0; 381 382 while (len > 1) { 383 sum += addr[0] * 256 + addr[1]; 384 addr += 2; 385 len -= 2; 386 } 387 388 if (len == 1) 389 sum += *addr * 256; 390 391 sum = (sum >> 16) + (sum & 0xffff); 392 sum += (sum >> 16); 393 394 sum = htons(sum); 395 396 return ~sum; 397} 398 399ssize_t 400make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length, 401 struct in_addr source, struct in_addr dest) 402{ 403 struct udp_dhcp_packet *udpp; 404 struct ip *ip; 405 struct udphdr *udp; 406 407 udpp = xzalloc(sizeof(*udpp)); 408 ip = &udpp->ip; 409 udp = &udpp->udp; 410 411 /* OK, this is important :) 412 * We copy the data to our packet and then create a small part of the 413 * ip structure and an invalid ip_len (basically udp length). 414 * We then fill the udp structure and put the checksum 415 * of the whole packet into the udp checksum. 416 * Finally we complete the ip structure and ip checksum. 417 * If we don't do the ordering like so then the udp checksum will be 418 * broken, so find another way of doing it! */ 419 420 memcpy(&udpp->dhcp, data, length); 421 422 ip->ip_p = IPPROTO_UDP; 423 ip->ip_src.s_addr = source.s_addr; 424 if (dest.s_addr == 0) 425 ip->ip_dst.s_addr = INADDR_BROADCAST; 426 else 427 ip->ip_dst.s_addr = dest.s_addr; 428 429 udp->uh_sport = htons(DHCP_CLIENT_PORT); 430 udp->uh_dport = htons(DHCP_SERVER_PORT); 431 udp->uh_ulen = htons(sizeof(*udp) + length); 432 ip->ip_len = udp->uh_ulen; 433 udp->uh_sum = checksum(udpp, sizeof(*udpp)); 434 435 ip->ip_v = IPVERSION; 436 ip->ip_hl = sizeof(*ip) >> 2; 437 ip->ip_id = arc4random() & UINT16_MAX; 438 ip->ip_ttl = IPDEFTTL; 439 ip->ip_len = htons(sizeof(*ip) + sizeof(*udp) + length); 440 ip->ip_sum = checksum(ip, sizeof(*ip)); 441 442 *packet = (uint8_t *)udpp; 443 return sizeof(*ip) + sizeof(*udp) + length; 444} 445 446ssize_t 447get_udp_data(const uint8_t **data, const uint8_t *udp) 448{ 449 struct udp_dhcp_packet packet; 450 451 memcpy(&packet, udp, sizeof(packet)); 452 *data = udp + offsetof(struct udp_dhcp_packet, dhcp); 453 return ntohs(packet.ip.ip_len) - 454 sizeof(packet.ip) - 455 sizeof(packet.udp); 456} 457 458int 459valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from) 460{ 461 struct udp_dhcp_packet packet; 462 uint16_t bytes, udpsum; 463 464 if (data_len < sizeof(packet.ip)) { 465 if (from) 466 from->s_addr = INADDR_ANY; 467 errno = EINVAL; 468 return -1; 469 } 470 memcpy(&packet, data, MIN(data_len, sizeof(packet))); 471 if (from) 472 from->s_addr = packet.ip.ip_src.s_addr; 473 if (data_len > sizeof(packet)) { 474 errno = EINVAL; 475 return -1; 476 } 477 if (checksum(&packet.ip, sizeof(packet.ip)) != 0) { 478 errno = EINVAL; 479 return -1; 480 } 481 482 bytes = ntohs(packet.ip.ip_len); 483 if (data_len < bytes) { 484 errno = EINVAL; 485 return -1; 486 } 487 udpsum = packet.udp.uh_sum; 488 packet.udp.uh_sum = 0; 489 packet.ip.ip_hl = 0; 490 packet.ip.ip_v = 0; 491 packet.ip.ip_tos = 0; 492 packet.ip.ip_len = packet.udp.uh_ulen; 493 packet.ip.ip_id = 0; 494 packet.ip.ip_off = 0; 495 packet.ip.ip_ttl = 0; 496 packet.ip.ip_sum = 0; 497 if (udpsum && checksum(&packet, bytes) != udpsum) { 498 errno = EINVAL; 499 return -1; 500 } 501 502 return 0; 503} 504