summaryrefslogtreecommitdiff
path: root/usr.sbin/hostapd/handle.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/hostapd/handle.c')
-rw-r--r--usr.sbin/hostapd/handle.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/usr.sbin/hostapd/handle.c b/usr.sbin/hostapd/handle.c
new file mode 100644
index 00000000000..a82f37a4eb8
--- /dev/null
+++ b/usr.sbin/hostapd/handle.c
@@ -0,0 +1,299 @@
+/* $OpenBSD: handle.c,v 1.1 2005/06/17 19:13:35 reyk Exp $ */
+
+/*
+ * Copyright (c) 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 <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 <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hostapd.h"
+
+int hostapd_handle_frame(struct hostapd_config *, struct hostapd_frame *,
+ u_int8_t *, const u_int);
+int hostapd_handle_action(struct hostapd_config *, struct hostapd_frame *,
+ u_int8_t *, u_int8_t *, u_int8_t *, u_int8_t *, const u_int);
+void hostapd_handle_addr(const u_int32_t, u_int32_t *, u_int8_t *,
+ u_int8_t *, struct hostapd_table *);
+void hostapd_handle_ref(u_int, u_int, u_int8_t *, u_int8_t *, u_int8_t *,
+ u_int8_t *);
+
+int
+hostapd_handle_input(struct hostapd_config *cfg, u_int8_t *buf, u_int len)
+{
+ int ret;
+ struct hostapd_frame *frame;
+
+ TAILQ_FOREACH(frame, &cfg->c_frames, f_entries) {
+ if ((ret = hostapd_handle_frame(cfg, frame, buf, len)) != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+void
+hostapd_handle_addr(const u_int32_t mask, u_int32_t *flags, u_int8_t *addr,
+ u_int8_t *maddr, struct hostapd_table *table)
+{
+ int ret = 0;
+
+ if ((*flags & mask) & HOSTAPD_FRAME_TABLE) {
+ if (hostapd_entry_lookup(table, addr) == NULL)
+ ret = 1;
+ } else if (bcmp(addr, maddr, IEEE80211_ADDR_LEN) != 0)
+ ret = 1;
+
+ if ((ret == 1 && (*flags & mask) & HOSTAPD_FRAME_N) ||
+ (ret == 0 && ((*flags & mask) & HOSTAPD_FRAME_N) == 0))
+ *flags &= ~mask;
+}
+
+void
+hostapd_handle_ref(u_int flags, u_int shift, u_int8_t *wfrom, u_int8_t *wto,
+ u_int8_t *wbssid, u_int8_t *addr)
+{
+ if (flags & (HOSTAPD_ACTION_F_REF_FROM << shift))
+ bcopy(wfrom, addr, IEEE80211_ADDR_LEN);
+ else if (flags & (HOSTAPD_ACTION_F_REF_TO << shift))
+ bcopy(wto, addr, IEEE80211_ADDR_LEN);
+ else if (flags & (HOSTAPD_ACTION_F_REF_BSSID << shift))
+ bcopy(wbssid, addr, IEEE80211_ADDR_LEN);
+}
+
+int
+hostapd_handle_frame(struct hostapd_config *cfg, struct hostapd_frame *frame,
+ u_int8_t *buf, const u_int len)
+{
+ struct ieee80211_frame *wh;
+ struct hostapd_ieee80211_frame *mh;
+ u_int8_t *wfrom, *wto, *wbssid;
+ struct timeval t_now;
+ u_int32_t flags;
+ int offset;
+
+ if ((offset = hostapd_apme_offset(cfg, buf, len)) < 0)
+ return (0);
+ wh = (struct ieee80211_frame *)(buf + offset);
+
+ mh = &frame->f_frame;
+ flags = frame->f_flags;
+
+ /* Handle optional limit */
+ if (frame->f_limit.tv_sec || frame->f_limit.tv_usec) {
+ gettimeofday(&t_now, NULL);
+ if (timercmp(&t_now, &frame->f_then, <))
+ return (0);
+ timeradd(&t_now, &frame->f_limit, &frame->f_then);
+ }
+
+ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ wfrom = wh->i_addr2;
+ wto = wh->i_addr1;
+ wbssid = wh->i_addr3;
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ wfrom = wh->i_addr2;
+ wto = wh->i_addr3;
+ wbssid = wh->i_addr1;
+ break;
+ case IEEE80211_FC1_DIR_FROMDS:
+ wfrom = wh->i_addr3;
+ wto = wh->i_addr1;
+ wbssid = wh->i_addr2;
+ break;
+ default:
+ case IEEE80211_FC1_DIR_DSTODS:
+ return (0);
+ }
+
+ if (flags & HOSTAPD_FRAME_F_TYPE) {
+ /* type $type */
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ (mh->i_fc[0] & IEEE80211_FC0_TYPE_MASK))
+ flags &= ~HOSTAPD_FRAME_F_TYPE;
+ } else if (flags & HOSTAPD_FRAME_F_TYPE_N) {
+ /* type !$type */
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
+ (mh->i_fc[0] & IEEE80211_FC0_TYPE_MASK))
+ flags &= ~HOSTAPD_FRAME_F_TYPE_N;
+ }
+
+ if (flags & HOSTAPD_FRAME_F_SUBTYPE) {
+ /* subtype $subtype */
+ if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ (mh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK))
+ flags &= ~HOSTAPD_FRAME_F_SUBTYPE;
+ } else if (flags & HOSTAPD_FRAME_F_SUBTYPE_N) {
+ /* subtype !$subtype */
+ if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) !=
+ (mh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK))
+ flags &= ~HOSTAPD_FRAME_F_SUBTYPE_N;
+ }
+
+ if (flags & HOSTAPD_FRAME_F_DIR) {
+ /* dir $dir */
+ if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) ==
+ (mh->i_fc[1] & IEEE80211_FC1_DIR_MASK))
+ flags &= ~HOSTAPD_FRAME_F_DIR;
+ } else if (flags & HOSTAPD_FRAME_F_DIR_N) {
+ /* dir !$dir */
+ if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
+ (mh->i_fc[1] & IEEE80211_FC1_DIR_MASK))
+ flags &= ~HOSTAPD_FRAME_F_DIR_N;
+ }
+
+ /* from/to/bssid [!]$addr/<table> */
+ hostapd_handle_addr(HOSTAPD_FRAME_F_FROM_M, &flags, wfrom,
+ mh->i_from, frame->f_from);
+ hostapd_handle_addr(HOSTAPD_FRAME_F_TO_M, &flags, wto,
+ mh->i_to, frame->f_to);
+ hostapd_handle_addr(HOSTAPD_FRAME_F_BSSID_M, &flags, wbssid,
+ mh->i_bssid, frame->f_bssid);
+
+ /* Handle if frame matches */
+ if ((flags & HOSTAPD_FRAME_F_M) != 0)
+ return (0);
+
+ if (hostapd_handle_action(cfg, frame, wfrom, wto, wbssid, buf,
+ len) != 0)
+ return (0);
+
+ return ((frame->f_flags & HOSTAPD_FRAME_F_RET_M) >>
+ HOSTAPD_FRAME_F_RET_S);
+}
+
+int
+hostapd_handle_action(struct hostapd_config *cfg, struct hostapd_frame *frame,
+ u_int8_t *wfrom, u_int8_t *wto, u_int8_t *wbssid, u_int8_t *buf,
+ const u_int len)
+{
+ struct hostapd_action_data *action = &frame->f_action_data;
+ struct hostapd_node node;
+ u_int8_t *lladdr = NULL;
+ int ret = 0, offset;
+
+ switch (frame->f_action) {
+ case HOSTAPD_ACTION_RADIOTAP:
+ /* Send IAPP frame with radiotap/pcap payload */
+ if ((ret = hostapd_iapp_radiotap(cfg, buf, len)) != 0)
+ return (ret);
+
+ if ((frame->f_action_flags & HOSTAPD_ACTION_VERBOSE) == 0)
+ return (0);
+
+ hostapd_log(HOSTAPD_LOG,
+ "%s: sent IAPP frame HOSTAPD_%s (%u bytes)\n",
+ cfg->c_iapp_iface, cfg->c_apme_dlt ==
+ DLT_IEEE802_11_RADIO ? "RADIOTAP" : "PCAP", len);
+ break;
+
+ case HOSTAPD_ACTION_LOG:
+ /* Log frame to syslog/stderr */
+ hostapd_printf("%s: ", cfg->c_apme_iface);
+
+ hostapd_print_ieee80211(cfg->c_apme_dlt, frame->f_action_flags &
+ HOSTAPD_ACTION_VERBOSE, buf, len);
+
+ /* Flush output buffer */
+ hostapd_printf(NULL);
+ break;
+
+ case HOSTAPD_ACTION_DELNODE:
+ case HOSTAPD_ACTION_ADDNODE:
+ bzero(&node, sizeof(node));
+
+ if (action->a_flags & HOSTAPD_ACTION_F_REF_FROM)
+ lladdr = wfrom;
+ else if (action->a_flags & HOSTAPD_ACTION_F_REF_TO)
+ lladdr = wto;
+ else if (action->a_flags & HOSTAPD_ACTION_F_REF_BSSID)
+ lladdr = wbssid;
+ else
+ lladdr = action->a_lladdr;
+
+ bcopy(lladdr, &node.ni_macaddr, IEEE80211_ADDR_LEN);
+
+ if (HOSTAPD_ACTION_DELNODE)
+ ret = hostapd_apme_delnode(cfg, &node);
+ else
+ ret = hostapd_apme_addnode(cfg, &node);
+
+ if (ret != 0) {
+ hostapd_log(HOSTAPD_LOG_DEBUG,
+ "%s: node add/delete %s failed: %s\n",
+ cfg->c_apme_iface, etheraddr_string(lladdr),
+ strerror(ret));
+ }
+ break;
+
+ case HOSTAPD_ACTION_NONE:
+ /* Nothing */
+ break;
+
+ case HOSTAPD_ACTION_RESEND:
+ /* Resend received raw IEEE 802.11 frame */
+ if ((offset = hostapd_apme_offset(cfg, buf, len)) < 0)
+ return (EINVAL);
+ if (write(cfg->c_apme_raw, buf + offset, len - offset) == -1)
+ ret = errno;
+ break;
+
+ case HOSTAPD_ACTION_FRAME:
+ if (action->a_flags & HOSTAPD_ACTION_F_REF_M) {
+ hostapd_handle_ref(action->a_flags &
+ HOSTAPD_ACTION_F_REF_FROM_M,
+ HOSTAPD_ACTION_F_REF_FROM_S, wfrom, wto, wbssid,
+ action->a_frame.i_from);
+ hostapd_handle_ref(action->a_flags &
+ HOSTAPD_ACTION_F_REF_TO_M,
+ HOSTAPD_ACTION_F_REF_TO_S, wfrom, wto, wbssid,
+ action->a_frame.i_to);
+ hostapd_handle_ref(action->a_flags &
+ HOSTAPD_ACTION_F_REF_BSSID_M,
+ HOSTAPD_ACTION_F_REF_BSSID_S, wfrom, wto, wbssid,
+ action->a_frame.i_bssid);
+ }
+
+ /* Send a raw IEEE 802.11 frame */
+ return (hostapd_apme_output(cfg, &action->a_frame));
+
+ default:
+ return (0);
+ }
+
+ return (ret);
+}