diff options
Diffstat (limited to 'if-bsd.c')
-rw-r--r-- | if-bsd.c | 323 |
1 files changed, 246 insertions, 77 deletions
@@ -1,6 +1,6 @@ /* * dhcpcd - DHCP client daemon - * Copyright 2006-2008 Roy Marples <roy@marples.name> + * Copyright (c) 2006-2010 Roy Marples <roy@marples.name> * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -25,44 +25,123 @@ * SUCH DAMAGE. */ -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> #include <sys/ioctl.h> #include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/types.h> #include <arpa/inet.h> +#include <net/if.h> #include <net/if_dl.h> -#include <net/if_types.h> #include <net/route.h> #include <netinet/in.h> +#ifdef __DragonFly__ +# include <netproto/802_11/ieee80211_ioctl.h> +#elif __APPLE__ + /* FIXME: Add apple includes so we can work out SSID */ +#else +# include <net80211/ieee80211_ioctl.h> +#endif #include <errno.h> +#include <fnmatch.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <syslog.h> #include <unistd.h> #include "config.h" #include "common.h" +#include "configure.h" #include "dhcp.h" +#include "if-options.h" #include "net.h" -/* Darwin doesn't define this for some very odd reason */ -#ifndef SA_SIZE -# define SA_SIZE(sa) \ - ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ - sizeof(long) : \ - 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +/* FIXME: Why do we need to check for sa_family 255 */ +#define COPYOUT(sin, sa) \ + sin.s_addr = ((sa) != NULL) ? \ + (((struct sockaddr_in *)(void *)sa)->sin_addr).s_addr : 0 + +static int r_fd = -1; +static char *link_buf; +static ssize_t link_buflen; + +int +if_init(_unused struct interface *iface) +{ + /* BSD promotes secondary address by default */ + return 0; +} + +int +if_conf(_unused struct interface *iface) +{ + /* No extra checks needed on BSD */ + return 0; +} + +int +init_sockets(void) +{ + if ((socket_afnet = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + return -1; + set_cloexec(socket_afnet); + if ((r_fd = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) + return -1; + set_cloexec(r_fd); + return 0; +} + +int +getifssid(const char *ifname, char *ssid) +{ + int retval = -1; +#if defined(SIOCG80211NWID) + struct ifreq ifr; + struct ieee80211_nwid nwid; +#elif defined(IEEE80211_IOC_SSID) + struct ieee80211req ireq; + char nwid[IEEE80211_NWID_LEN + 1]; #endif +#if defined(SIOCG80211NWID) /* NetBSD */ + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + memset(&nwid, 0, sizeof(nwid)); + ifr.ifr_data = (void *)&nwid; + if (ioctl(socket_afnet, SIOCG80211NWID, &ifr) == 0) { + retval = nwid.i_len; + memcpy(ssid, nwid.i_nwid, nwid.i_len); + ssid[nwid.i_len] = '\0'; + } +#elif defined(IEEE80211_IOC_SSID) /* FreeBSD */ + memset(&ireq, 0, sizeof(ireq)); + strlcpy(ireq.i_name, ifname, sizeof(ireq.i_name)); + ireq.i_type = IEEE80211_IOC_SSID; + ireq.i_val = -1; + ireq.i_data = &nwid; + if (ioctl(socket_afnet, SIOCG80211, &ireq) == 0) { + retval = ireq.i_len; + memcpy(ssid, nwid, ireq.i_len); + ssid[ireq.i_len] = '\0'; + } +#endif + return retval; +} + int -if_address(const char *ifname, const struct in_addr *address, - const struct in_addr *netmask, const struct in_addr *broadcast, - int action) +if_address(const struct interface *iface, const struct in_addr *address, + const struct in_addr *netmask, const struct in_addr *broadcast, + int action) { - int s; int retval; struct ifaliasreq ifa; union { @@ -70,39 +149,36 @@ if_address(const char *ifname, const struct in_addr *address, struct sockaddr_in *sin; } _s; - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - return -1; - memset(&ifa, 0, sizeof(ifa)); - strlcpy(ifa.ifra_name, ifname, sizeof(ifa.ifra_name)); + strlcpy(ifa.ifra_name, iface->name, sizeof(ifa.ifra_name)); -#define ADDADDR(_var, _addr) \ - _s.sa = &_var; \ - _s.sin->sin_family = AF_INET; \ - _s.sin->sin_len = sizeof(*_s.sin); \ - memcpy(&_s.sin->sin_addr, _addr, sizeof(_s.sin->sin_addr)); +#define ADDADDR(_var, _addr) { \ + _s.sa = &_var; \ + _s.sin->sin_family = AF_INET; \ + _s.sin->sin_len = sizeof(*_s.sin); \ + memcpy(&_s.sin->sin_addr, _addr, sizeof(_s.sin->sin_addr)); \ + } ADDADDR(ifa.ifra_addr, address); ADDADDR(ifa.ifra_mask, netmask); - if (action >= 0) { + if (action >= 0 && broadcast) { ADDADDR(ifa.ifra_broadaddr, broadcast); } #undef ADDADDR if (action < 0) - retval = ioctl(s, SIOCDIFADDR, &ifa); + retval = ioctl(socket_afnet, SIOCDIFADDR, &ifa); else - retval = ioctl(s, SIOCAIFADDR, &ifa); - close(s); + retval = ioctl(socket_afnet, SIOCAIFADDR, &ifa); return retval; } +/* ARGSUSED4 */ int if_route(const struct interface *iface, const struct in_addr *dest, - const struct in_addr *net, const struct in_addr *gate, - _unused int metric, int action) + const struct in_addr *net, const struct in_addr *gate, + _unused int metric, int action) { - int s; union sockunion { struct sockaddr sa; struct sockaddr_in sin; @@ -121,21 +197,18 @@ if_route(const struct interface *iface, const struct in_addr *dest, size_t l; int retval = 0; -#define ADDSU(_su) { \ - l = SA_SIZE(&(_su.sa)); \ - memcpy(bp, &(_su), l); \ - bp += l; \ -} -#define ADDADDR(_addr) { \ - memset (&su, 0, sizeof(su)); \ - su.sin.sin_family = AF_INET; \ - su.sin.sin_len = sizeof(su.sin); \ - memcpy (&su.sin.sin_addr, _addr, sizeof(su.sin.sin_addr)); \ - ADDSU(su); \ -} - - if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) - return -1; +#define ADDSU(_su) { \ + l = ROUNDUP(_su.sa.sa_len); \ + memcpy(bp, &(_su), l); \ + bp += l; \ + } +#define ADDADDR(_a) { \ + memset (&su, 0, sizeof(su)); \ + su.sin.sin_family = AF_INET; \ + su.sin.sin_len = sizeof(su.sin); \ + memcpy (&su.sin.sin_addr, _a, sizeof(su.sin.sin_addr)); \ + ADDSU(su); \ + } memset(&rtm, 0, sizeof(rtm)); rtm.hdr.rtm_version = RTM_VERSION; @@ -194,41 +267,72 @@ if_route(const struct interface *iface, const struct in_addr *dest, ADDADDR(&iface->addr); rtm.hdr.rtm_msglen = l = bp - (char *)&rtm; - if (write(s, &rtm, l) == -1) + if (write(r_fd, &rtm, l) == -1) retval = -1; - close(s); return retval; } int -open_link_socket(struct interface *iface) +open_link_socket(void) { int fd; fd = socket(PF_ROUTE, SOCK_RAW, 0); - if (fd == -1) - return -1; - set_cloexec(fd); - if (iface->link_fd != -1) - close(iface->link_fd); - iface->link_fd = fd; - return 0; + if (fd != -1) { + set_cloexec(fd); + set_nonblock(fd); + } + return fd; +} + +static void +get_addrs(int type, char *cp, struct sockaddr **sa) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (type & (1 << i)) { + sa[i] = (struct sockaddr *)cp; +#ifdef DEBUG + printf ("got %d %d %s\n", i, sa[i]->sa_family, + inet_ntoa(((struct sockaddr_in *)sa[i])-> + sin_addr)); +#endif + ADVANCE(cp, sa[i]); + } else + sa[i] = NULL; + } } -#define BUFFER_LEN 2048 int -link_changed(struct interface *iface) +manage_link(int fd) { - char buffer[2048], *p; + char *p, *e, *cp; + char ifname[IF_NAMESIZE]; ssize_t bytes; struct rt_msghdr *rtm; + struct if_announcemsghdr *ifan; struct if_msghdr *ifm; - int i; + struct ifa_msghdr *ifam; + struct rt rt; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + int len; +#ifdef RTM_CHGADDR + struct sockaddr_dl sdl; + unsigned char *hwaddr; +#endif - if ((i = if_nametoindex(iface->name)) == -1) - return -1; for (;;) { - bytes = recv(iface->link_fd, buffer, BUFFER_LEN, MSG_DONTWAIT); + if (ioctl(fd, FIONREAD, &len) == -1) + return -1; + if (link_buflen < len) { + p = realloc(link_buf, len); + if (p == NULL) + return -1; + link_buf = p; + link_buflen = len; + } + bytes = read(fd, link_buf, link_buflen); if (bytes == -1) { if (errno == EAGAIN) return 0; @@ -236,20 +340,85 @@ link_changed(struct interface *iface) continue; return -1; } - for (p = buffer; bytes > 0; - bytes -= ((struct rt_msghdr *)p)->rtm_msglen, - p += ((struct rt_msghdr *)p)->rtm_msglen) - { - rtm = (struct rt_msghdr *)p; - if (rtm->rtm_type != RTM_IFINFO) - continue; - ifm = (struct if_msghdr *)p; - if (ifm->ifm_index != i) - continue; - - /* Link changed */ - return 1; + e = link_buf + bytes; + for (p = link_buf; p < e; p += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)(void *)p; + switch(rtm->rtm_type) { +#ifdef RTM_IFANNOUNCE + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *)(void *)p; + switch(ifan->ifan_what) { + case IFAN_ARRIVAL: + handle_interface(1, ifan->ifan_name); + break; + case IFAN_DEPARTURE: + handle_interface(-1, ifan->ifan_name); + break; + } + break; +#endif + case RTM_IFINFO: + ifm = (struct if_msghdr *)(void *)p; + memset(ifname, 0, sizeof(ifname)); + if (if_indextoname(ifm->ifm_index, ifname)) + handle_interface(0, ifname); + break; + case RTM_DELETE: + if (!(rtm->rtm_addrs & RTA_DST) || + !(rtm->rtm_addrs & RTA_GATEWAY) || + !(rtm->rtm_addrs & RTA_NETMASK)) + break; + if (rtm->rtm_pid == getpid()) + break; + cp = (char *)(void *)(rtm + 1); + sa = (struct sockaddr *)(void *)cp; + if (sa->sa_family != AF_INET) + break; + get_addrs(rtm->rtm_addrs, cp, rti_info); + rt.iface = NULL; + rt.next = NULL; + COPYOUT(rt.dest, rti_info[RTAX_DST]); + COPYOUT(rt.net, rti_info[RTAX_NETMASK]); + COPYOUT(rt.gate, rti_info[RTAX_GATEWAY]); + route_deleted(&rt); + break; +#ifdef RTM_CHGADDR + case RTM_CHGADDR: /* FALLTHROUGH */ +#endif + case RTM_DELADDR: /* FALLTHROUGH */ + case RTM_NEWADDR: + ifam = (struct ifa_msghdr *)(void *)p; + if (!if_indextoname(ifam->ifam_index, ifname)) + break; + cp = (char *)(void *)(ifam + 1); + get_addrs(ifam->ifam_addrs, cp, rti_info); + if (rti_info[RTAX_IFA] == NULL) + break; + switch (rti_info[RTAX_IFA]->sa_family) { +#ifdef RTM_CHGADDR + case AF_LINK: + if (rtm->rtm_type != RTM_CHGADDR) + break; + memcpy(&sdl, rti_info[RTAX_IFA], + rti_info[RTAX_IFA]->sa_len); + hwaddr = xmalloc(sdl.sdl_alen); + memcpy(hwaddr, LLADDR(&sdl), + sdl.sdl_alen); + handle_hwaddr(ifname, hwaddr, + sdl.sdl_alen); + break; +#endif + case AF_INET: + case 255: /* FIXME: Why 255? */ + COPYOUT(rt.dest, rti_info[RTAX_IFA]); + COPYOUT(rt.net, rti_info[RTAX_NETMASK]); + COPYOUT(rt.gate, rti_info[RTAX_BRD]); + handle_ifa(rtm->rtm_type, ifname, + &rt.dest, &rt.net, &rt.gate); + break; + } + break; + } } } - return 0; } |