diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2005-04-13 18:12:24 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2005-04-13 18:12:24 +0000 |
commit | fbd9185cd25b95eabb882ad60bd5546ac6bfc50d (patch) | |
tree | d646baeb201f349c253e6db5e2dbabb2d950f753 /usr.sbin/hostapd/hostapd.c | |
parent | 9e547b0e368e4f157730b7c08e4e626b8df8038b (diff) |
add the initial version of the OpenBSD hostapd daemon
ok deraadt@
Diffstat (limited to 'usr.sbin/hostapd/hostapd.c')
-rw-r--r-- | usr.sbin/hostapd/hostapd.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/usr.sbin/hostapd/hostapd.c b/usr.sbin/hostapd/hostapd.c new file mode 100644 index 00000000000..05b2d8f71ae --- /dev/null +++ b/usr.sbin/hostapd/hostapd.c @@ -0,0 +1,453 @@ +/* $OpenBSD: hostapd.c,v 1.1 2005/04/13 18:12:23 reyk Exp $ */ + +/* + * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/queue.h> +#include <sys/stat.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#include <net/if_llc.h> +#include <net/bpf.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <event.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +#include "hostapd.h" + +void hostapd_usage(void); +void hostapd_udp_init(struct hostapd_config *); +void hostapd_sig_handler(int); + +struct hostapd_config hostapd_cfg; + +extern char *__progname; + +/* defined in event(3) to terminate the event loop */ +extern volatile sig_atomic_t event_gotterm; + +void +hostapd_usage(void) +{ + fprintf(stderr, "usage: %s [-dvb] [-a interface] [-D macro=value] " + "[-f file] [-i interface]\n", + __progname); + exit(EXIT_FAILURE); +} + +void +hostapd_log(u_int level, const char *fmt, ...) +{ + va_list ap; + + if (level > hostapd_cfg.c_verbose) + return; + + va_start(ap, fmt); + if (hostapd_cfg.c_debug) + vfprintf(stderr, fmt, ap); + else + vsyslog(LOG_INFO, fmt, ap); + fflush(stderr); + va_end(ap); +} + +void +hostapd_fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (hostapd_cfg.c_debug) + vfprintf(stderr, fmt, ap); + else + vsyslog(LOG_ERR, fmt, ap); + fflush(stderr); + va_end(ap); + + hostapd_cleanup(&hostapd_cfg); + + exit(EXIT_FAILURE); +} + +int +hostapd_check_file_secrecy(int fd, const char *fname) +{ + struct stat st; + + if (fstat(fd, &st)) { + hostapd_log(HOSTAPD_LOG, + "cannot stat %s\n", fname); + return (-1); + } + + if (st.st_uid != 0 && st.st_uid != getuid()) { + hostapd_log(HOSTAPD_LOG, + "%s: owner not root or current user\n", fname); + return (-1); + } + + if (st.st_mode & (S_IRWXG | S_IRWXO)) { + hostapd_log(HOSTAPD_LOG, + "%s: group/world readable/writeable\n", fname); + return (-1); + } + + return (0); +} + +int +hostapd_bpf_open(u_int flags) +{ + u_int i; + int fd = -1; + char *dev; + struct bpf_version bpv; + + /* + * Try to open the next available BPF device + */ + for (i = 0; i < 255; i++) { + if (asprintf(&dev, "/dev/bpf%u", i) == -1) + hostapd_fatal("failed to allocate buffer\n"); + + if ((fd = open(dev, flags)) != -1) + break; + + free(dev); + } + + if (fd == -1) { + free(dev); + hostapd_fatal("unable to open BPF device\n"); + } + + free(dev); + + /* + * Get and validate the BPF version + */ + + if (ioctl(fd, BIOCVERSION, &bpv) == -1) + hostapd_fatal("failed to get BPF version: %s\n", + strerror(errno)); + + if (bpv.bv_major != BPF_MAJOR_VERSION || + bpv.bv_minor < BPF_MINOR_VERSION) + hostapd_fatal("invalid BPF version\n"); + + return (fd); +} + +void +hostapd_udp_init(struct hostapd_config *cfg) +{ + struct ifreq ifr; + struct sockaddr_in *addr, baddr; + struct ip_mreq mreq; + int brd = 1; + + bzero(&ifr, sizeof(ifr)); + + /* + * Open a listening UDP socket + */ + + if ((cfg->c_iapp_udp = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + hostapd_fatal("unable to open udp socket\n"); + + cfg->c_flags |= HOSTAPD_CFG_F_UDP; + + strlcpy(ifr.ifr_name, cfg->c_iapp_iface, sizeof(ifr.ifr_name)); + + if (ioctl(cfg->c_iapp_udp, SIOCGIFADDR, &ifr) == -1) + hostapd_fatal("UDP ioctl %s on \"%s\" failed: %s\n", + "SIOCGIFADDR", ifr.ifr_name, strerror(errno)); + + addr = (struct sockaddr_in *)&ifr.ifr_addr; + cfg->c_iapp_addr.sin_family = AF_INET; + cfg->c_iapp_addr.sin_addr.s_addr = addr->sin_addr.s_addr; + cfg->c_iapp_addr.sin_port = htons(IAPP_PORT); + + if (ioctl(cfg->c_iapp_udp, SIOCGIFBRDADDR, &ifr) == -1) + hostapd_fatal("UDP ioctl %s on \"%s\" failed: %s\n", + "SIOCGIFBRDADDR", ifr.ifr_name, strerror(errno)); + + addr = (struct sockaddr_in *)&ifr.ifr_addr; + cfg->c_iapp_broadcast.sin_family = AF_INET; + cfg->c_iapp_broadcast.sin_addr.s_addr = addr->sin_addr.s_addr; + cfg->c_iapp_broadcast.sin_port = htons(IAPP_PORT); + + baddr.sin_family = AF_INET; + baddr.sin_addr.s_addr = htonl(INADDR_ANY); + baddr.sin_port = htons(IAPP_PORT); + + if (bind(cfg->c_iapp_udp, (struct sockaddr *)&baddr, + sizeof(baddr)) == -1) + hostapd_fatal("failed to bind UDP socket: %s\n", + strerror(errno)); + + /* + * The revised 802.11F standard requires IAPP messages to be + * send via multicast to the group 224.0.1.178. Nevertheless, + * some implementations still use broadcasts for IAPP + * messages. + */ + if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST) { + /* + * Enable broadcast + */ + + hostapd_log(HOSTAPD_LOG_DEBUG, "using broadcast mode\n"); + + if (setsockopt(cfg->c_iapp_udp, SOL_SOCKET, SO_BROADCAST, + &brd, sizeof(brd)) == -1) + hostapd_fatal("failed to enable broadcast on socket\n"); + } else { + /* + * Enable multicast + */ + + hostapd_log(HOSTAPD_LOG_DEBUG, "using multicast mode\n"); + + bzero(&mreq, sizeof(mreq)); + + cfg->c_iapp_multicast.sin_family = AF_INET; + cfg->c_iapp_multicast.sin_addr.s_addr = + inet_addr(IAPP_MCASTADDR); + cfg->c_iapp_multicast.sin_port = htons(IAPP_PORT); + + mreq.imr_multiaddr.s_addr = + cfg->c_iapp_multicast.sin_addr.s_addr; + mreq.imr_interface.s_addr = + cfg->c_iapp_addr.sin_addr.s_addr; + + if (setsockopt(cfg->c_iapp_udp, IPPROTO_IP, + IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) + hostapd_fatal("failed to add multicast membership to %s:" + " %s\n", + IAPP_MCASTADDR, strerror(errno)); + } +} + +void +hostapd_sig_handler(int sig) +{ + switch (sig) { + case SIGALRM: + case SIGTERM: + case SIGQUIT: + case SIGINT: + /* This will terminate libevent's main loop */ + event_gotterm = 1; + } +} + +void +hostapd_cleanup(struct hostapd_config *cfg) +{ + struct ip_mreq mreq; + + if (cfg->c_flags & HOSTAPD_CFG_F_PRIV && + (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST) == 0 && + cfg->c_apme_n == 0) { + /* + * Disable multicast and let the kernel unsubscribe + * from the multicast group. + */ + + bzero(&mreq, sizeof(mreq)); + + mreq.imr_multiaddr.s_addr = + inet_addr(IAPP_MCASTADDR); + mreq.imr_interface.s_addr = + cfg->c_iapp_addr.sin_addr.s_addr; + + if (setsockopt(cfg->c_iapp_udp, IPPROTO_IP, + IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) + hostapd_log(HOSTAPD_LOG, "failed to remove multicast" + " membership to %s: %s\n", + IAPP_MCASTADDR, strerror(errno)); + } + + if ((cfg->c_flags & HOSTAPD_CFG_F_PRIV) == 0 && + cfg->c_flags & HOSTAPD_CFG_F_APME) { + /* Shutdown the Host AP protocol handler */ + hostapd_iapp_term(&hostapd_cfg); + } + + hostapd_log(HOSTAPD_LOG_VERBOSE, "bye!\n"); + + if (!cfg->c_debug) + closelog(); + + /* Close all open file descriptors and sockets */ + closefrom(0); +} + +int +main(int argc, char *argv[]) +{ + struct hostapd_config *cfg = &hostapd_cfg; + char *iapp_iface = NULL, *hostap_iface = NULL, *config = NULL; + int ch; + + bzero(cfg, sizeof(struct hostapd_config)); + + /* + * Get and parse command line options + */ + while ((ch = getopt(argc, argv, "a:bf:D:di:v")) != -1) { + switch (ch) { + case 'a': + hostap_iface = optarg; + cfg->c_flags |= HOSTAPD_CFG_F_APME; + break; + case 'b': + cfg->c_flags |= HOSTAPD_CFG_F_BRDCAST; + break; + case 'f': + config = optarg; + break; + case 'D': + if (hostapd_parse_symset(optarg) < 0) + hostapd_fatal("could not parse macro definition %s", + optarg); + break; + case 'd': + cfg->c_debug++; + break; + case 'i': + iapp_iface = optarg; + cfg->c_flags |= HOSTAPD_CFG_F_IAPP; + break; + case 'v': + cfg->c_verbose++; + break; + default: + hostapd_usage(); + } + } + + if (config == NULL) + strlcpy(cfg->c_config, HOSTAPD_CONFIG, sizeof(cfg->c_config)); + else + strlcpy(cfg->c_config, config, sizeof(cfg->c_config)); + + if (iapp_iface != NULL) + strlcpy(cfg->c_iapp_iface, iapp_iface, + sizeof(cfg->c_iapp_iface)); + + if (hostap_iface != NULL) + strlcpy(cfg->c_apme_iface, hostap_iface, + sizeof(cfg->c_apme_iface)); + + if (!cfg->c_debug) { + openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + daemon(0, 0); + } + + /* Parse the configuration file */ + hostapd_parse_file(cfg); + + if ((cfg->c_flags & HOSTAPD_CFG_F_IAPP) == 0) + hostapd_usage(); + + if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0) + strlcpy(cfg->c_apme_iface, "<none>", sizeof(cfg->c_apme_iface)); + + /* + * Setup the hostapd handlers + */ + hostapd_udp_init(cfg); + hostapd_llc_init(cfg); + + if (cfg->c_flags & HOSTAPD_CFG_F_APME) + hostapd_apme_init(cfg); + else + hostapd_log(HOSTAPD_LOG, "%s/%s: running without a Host AP\n", + cfg->c_apme_iface, cfg->c_iapp_iface); + + /* Drop all privileges in an unprivileged child process */ + hostapd_priv_init(cfg); + + setproctitle("Host AP: %s, IAPP: %s", + cfg->c_apme_iface, cfg->c_iapp_iface); + + /* + * Unprivileged child process + */ + + event_init(); + + /* + * Set signal handlers + */ + signal(SIGALRM, hostapd_sig_handler); + signal(SIGTERM, hostapd_sig_handler); + signal(SIGQUIT, hostapd_sig_handler); + signal(SIGINT, hostapd_sig_handler); + signal(SIGHUP, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + + /* Initialize the IAPP protcol handler */ + hostapd_iapp_init(cfg); + + /* + * Schedule the Host AP listener + */ + if (cfg->c_flags & HOSTAPD_CFG_F_APME) { + event_set(&cfg->c_apme_ev, cfg->c_apme_raw, + EV_READ | EV_PERSIST, hostapd_apme_input, cfg); + event_add(&cfg->c_apme_ev, NULL); + } + + /* + * Schedule the IAPP listener + */ + event_set(&cfg->c_iapp_udp_ev, cfg->c_iapp_udp, EV_READ | EV_PERSIST, + hostapd_iapp_input, cfg); + event_add(&cfg->c_iapp_udp_ev, NULL); + + hostapd_log(HOSTAPD_LOG, "%s/%s: starting hostapd with pid %u\n", + cfg->c_apme_iface, cfg->c_iapp_iface, getpid()); + + /* Run event loop */ + event_dispatch(); + + /* Executed after the event loop has been terminated */ + hostapd_cleanup(cfg); + + return(EXIT_SUCCESS); +} |