From 51331cf32a54dd6f69b897ebd779b87084d0418c Mon Sep 17 00:00:00 2001 From: Brooks Davis Date: Mon, 10 Sep 2001 02:05:10 +0000 Subject: [PATCH] Add support for monitor mode. This means that after enabling the correct mode via ancontrol, you can use bpf to sniff raw 802.11 frames. Who want's to port AirSnort. ;-) Submitted by: Doug Ambrisko (author) David Wolfskill (port to current) --- sys/dev/an/if_an.c | 348 ++++++++++++++++++++++++--------- usr.sbin/ancontrol/ancontrol.8 | 18 ++ usr.sbin/ancontrol/ancontrol.c | 23 ++- 3 files changed, 291 insertions(+), 98 deletions(-) diff --git a/sys/dev/an/if_an.c b/sys/dev/an/if_an.c index 406be2117596..7749d86a7d63 100644 --- a/sys/dev/an/if_an.c +++ b/sys/dev/an/if_an.c @@ -102,6 +102,7 @@ #endif #include +#include #include #include #include @@ -169,6 +170,68 @@ static int an_media_change __P((struct ifnet *)); static void an_media_status __P((struct ifnet *, struct ifmediareq *)); static int an_dump = 0; +static char an_conf[256]; + +/* sysctl vars */ +SYSCTL_NODE(_machdep, OID_AUTO, an, CTLFLAG_RD, 0, "dump RID"); + +static int +sysctl_an_dump(SYSCTL_HANDLER_ARGS) +{ + int error, r, last; + char *s = an_conf; + + last = an_dump; + bzero(an_conf, sizeof(an_conf)); + + switch (an_dump){ + case 0: + strcat(an_conf, "off"); + break; + case 1: + strcat(an_conf, "type"); + break; + case 2: + strcat(an_conf, "dump"); + break; + default: + snprintf(an_conf, 5, "%x", an_dump); + break; + } + + error = sysctl_handle_string(oidp, an_conf, sizeof(an_conf), req); + + if (strncmp(an_conf,"off", 4) == 0){ + an_dump = 0; + } + if (strncmp(an_conf,"dump", 4) == 0){ + an_dump = 1; + } + if (strncmp(an_conf,"type", 4) == 0){ + an_dump = 2; + } + if (*s == 'f'){ + r = 0; + for(;;s++){ + if((*s >= '0') && (*s <= '9')){ + r = r * 16 + (*s - '0'); + }else if ((*s >= 'a') && (*s <= 'f')) { + r = r * 16 + (*s - 'a' + 10); + }else{ + break; + } + } + an_dump = r; + } + if (an_dump != last) + printf("Sysctl changed for Aironet driver\n"); + + return error; +} + +SYSCTL_PROC(_machdep, OID_AUTO, an_dump, CTLTYPE_STRING | CTLFLAG_RW, + 0, sizeof(an_conf), sysctl_an_dump, "A", ""); + /* * We probe for an Aironet 4500/4800 card by attempting to * read the default SSID list. On reset, the first entry in @@ -303,6 +366,8 @@ int an_attach(sc, unit, flags) sc->an_gone = 0; sc->an_associated = 0; + sc->an_monitor = 0; + sc->an_was_monitor = 0; /* Reset the NIC. */ an_reset(sc); @@ -424,90 +489,160 @@ int an_attach(sc, unit, flags) return(0); } -static void an_rxeof(sc) - struct an_softc *sc; +static void +an_rxeof(sc) + struct an_softc *sc; { - struct ifnet *ifp; - struct ether_header *eh; -#ifdef ANCACHE - struct an_rxframe rx_frame; -#endif - struct an_rxframe_802_3 rx_frame_802_3; - struct mbuf *m; - int id, error = 0; + struct ifnet *ifp; + struct ether_header *eh; + struct ieee80211_frame *ih; + struct an_rxframe rx_frame; + struct an_rxframe_802_3 rx_frame_802_3; + struct mbuf *m; + int len, id, error = 0; + int ieee80211_header_len; + u_char *bpf_buf; + u_short fc1; ifp = &sc->arpcom.ac_if; id = CSR_READ_2(sc, AN_RX_FID); - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) { - ifp->if_ierrors++; - return; - } - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - m_freem(m); - ifp->if_ierrors++; - return; - } + if (sc->an_monitor && (ifp->if_flags & IFF_PROMISC)) { + /* read raw 802.11 packet */ + bpf_buf = sc->buf_802_11; - m->m_pkthdr.rcvif = ifp; + /* read header */ + if (an_read_data(sc, id, 0x0, (caddr_t)&rx_frame, + sizeof(rx_frame))) { + ifp->if_ierrors++; + return; + } - eh = mtod(m, struct ether_header *); + /* + * skip beacon by default since this increases the + * system load a lot + */ + + if(!(sc->an_monitor & AN_MONITOR_INCLUDE_BEACON) && + (rx_frame.an_frame_ctl & IEEE80211_FC0_SUBTYPE_BEACON)){ + return; + } + + if (sc->an_monitor & AN_MONITOR_AIRONET_HEADER){ + len = rx_frame.an_rx_payload_len + + sizeof(rx_frame); + /* Check for insane frame length */ + if (len > sizeof(sc->buf_802_11)) { + printf("an%d: oversized packet received (%d, %d)\n", + sc->an_unit, len, MCLBYTES); + ifp->if_ierrors++; + return; + } + + bcopy((char *)&rx_frame, + bpf_buf, sizeof(rx_frame)); + + error = an_read_data(sc, id, sizeof(rx_frame), + (caddr_t)bpf_buf+sizeof(rx_frame), + rx_frame.an_rx_payload_len); + }else{ + fc1=rx_frame.an_frame_ctl >> 8; + ieee80211_header_len = sizeof(struct ieee80211_frame); + if ((fc1 & IEEE80211_FC1_DIR_TODS) && + (fc1 & IEEE80211_FC1_DIR_FROMDS)) { + ieee80211_header_len += ETHER_ADDR_LEN; + } + + len = rx_frame.an_rx_payload_len + + ieee80211_header_len; + /* Check for insane frame length */ + if (len > sizeof(sc->buf_802_11)) { + printf("an%d: oversized packet received (%d, %d)\n", + sc->an_unit, len, MCLBYTES); + ifp->if_ierrors++; + return; + } + + ih = (struct ieee80211_frame *)bpf_buf; + + bcopy((char *)&rx_frame.an_frame_ctl, + (char *)ih, ieee80211_header_len); + + error = an_read_data(sc, id, sizeof(rx_frame) + + rx_frame.an_gaplen, + (caddr_t)ih +ieee80211_header_len, + rx_frame.an_rx_payload_len); + } + /* dump raw 802.11 packet to bpf and skip ip stack */ + if (ifp->if_bpf != NULL) { + bpf_tap(ifp, bpf_buf, len); + } + } else { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + ifp->if_ierrors++; + return; + } + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + ifp->if_ierrors++; + return; + } + m->m_pkthdr.rcvif = ifp; + /* Read Ethenet encapsulated packet */ #ifdef ANCACHE - /* Read NIC frame header */ - if (an_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { - ifp->if_ierrors++; - return; - } + /* Read NIC frame header */ + if (an_read_data(sc, id, 0, (caddr_t) & rx_frame, sizeof(rx_frame))) { + ifp->if_ierrors++; + return; + } #endif - /* Read in the 802_3 frame header */ - if (an_read_data(sc, id, 0x34, (caddr_t)&rx_frame_802_3, - sizeof(rx_frame_802_3))) { - ifp->if_ierrors++; - return; - } + /* Read in the 802_3 frame header */ + if (an_read_data(sc, id, 0x34, (caddr_t) & rx_frame_802_3, + sizeof(rx_frame_802_3))) { + ifp->if_ierrors++; + return; + } + if (rx_frame_802_3.an_rx_802_3_status != 0) { + ifp->if_ierrors++; + return; + } + /* Check for insane frame length */ + if (rx_frame_802_3.an_rx_802_3_payload_len > MCLBYTES) { + ifp->if_ierrors++; + return; + } + m->m_pkthdr.len = m->m_len = + rx_frame_802_3.an_rx_802_3_payload_len + 12; - if (rx_frame_802_3.an_rx_802_3_status != 0) { - ifp->if_ierrors++; - return; - } + eh = mtod(m, struct ether_header *); - /* Check for insane frame length */ - if (rx_frame_802_3.an_rx_802_3_payload_len > MCLBYTES) { - ifp->if_ierrors++; - return; - } + bcopy((char *)&rx_frame_802_3.an_rx_dst_addr, + (char *)&eh->ether_dhost, ETHER_ADDR_LEN); + bcopy((char *)&rx_frame_802_3.an_rx_src_addr, + (char *)&eh->ether_shost, ETHER_ADDR_LEN); - m->m_pkthdr.len = m->m_len = - rx_frame_802_3.an_rx_802_3_payload_len + 12; + /* in mbuf header type is just before payload */ + error = an_read_data(sc, id, 0x44, (caddr_t)&(eh->ether_type), + rx_frame_802_3.an_rx_802_3_payload_len); - - bcopy((char *)&rx_frame_802_3.an_rx_dst_addr, - (char *)&eh->ether_dhost, ETHER_ADDR_LEN); - bcopy((char *)&rx_frame_802_3.an_rx_src_addr, - (char *)&eh->ether_shost, ETHER_ADDR_LEN); + if (error) { + m_freem(m); + ifp->if_ierrors++; + return; + } + ifp->if_ipackets++; - /* in mbuf header type is just before payload */ - error = an_read_data(sc, id, 0x44, (caddr_t)&(eh->ether_type), - rx_frame_802_3.an_rx_802_3_payload_len); - - if (error != 0) { - m_freem(m); - ifp->if_ierrors++; - return; - } - - ifp->if_ipackets++; - - /* Receive packet. */ - m_adj(m, sizeof(struct ether_header)); + /* Receive packet. */ + m_adj(m, sizeof(struct ether_header)); #ifdef ANCACHE - an_cache_store(sc, eh, m, rx_frame.an_rx_signal_strength); + an_cache_store(sc, eh, m, rx_frame.an_rx_signal_strength); #endif - ether_input(ifp, eh, m); + ether_input(ifp, eh, m); + } } static void an_txeof(sc, status) @@ -517,10 +652,6 @@ static void an_txeof(sc, status) struct ifnet *ifp; int id, i; - /* TX DONE enable lan monitor DJA - an_enable_sniff(); - */ - ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; @@ -1025,27 +1156,38 @@ static void an_setdef(sc, areq) sc->an_tx_rate = sp->an_val; break; case AN_RID_WEP_TEMP: - /* Disable the MAC. */ - an_cmd(sc, AN_CMD_DISABLE, 0); - - /* Just write the Key, we don't want to save it */ - an_write_record(sc, (struct an_ltv_gen *)areq); - - /* Turn the MAC back on. */ - an_cmd(sc, AN_CMD_ENABLE, 0); - - break; case AN_RID_WEP_PERM: - /* Disable the MAC. */ an_cmd(sc, AN_CMD_DISABLE, 0); - /* Just write the Key, the card will save it in this mode */ + /* Write the key */ an_write_record(sc, (struct an_ltv_gen *)areq); /* Turn the MAC back on. */ an_cmd(sc, AN_CMD_ENABLE, 0); + break; + case AN_RID_MONITOR_MODE: + cfg = (struct an_ltv_genconfig *)areq; + bpfdetach(ifp); + if (ng_ether_detach_p != NULL) + (*ng_ether_detach_p) (ifp); + sc->an_monitor = cfg->an_len; + + if (sc->an_monitor & AN_MONITOR){ + if (sc->an_monitor & AN_MONITOR_AIRONET_HEADER){ + bpfattach(ifp, DLT_AIRONET_HEADER, + sizeof(struct ether_header)); + } else { + bpfattach(ifp, DLT_IEEE802_11, + sizeof(struct ether_header)); + } + } else { + bpfattach(ifp, DLT_EN10MB, + sizeof(struct ether_header)); + if (ng_ether_attach_p != NULL) + (*ng_ether_attach_p) (ifp); + } break; default: printf("an%d: unknown RID: %x\n", sc->an_unit, areq->an_type); @@ -1062,16 +1204,21 @@ static void an_setdef(sc, areq) } /* - * We can't change the NIC configuration while the MAC is enabled, - * so in order to turn on RX monitor mode, we have to turn the MAC - * off first. + * Derived from Linux driver to enable promiscious mode. */ + static void an_promisc(sc, promisc) struct an_softc *sc; int promisc; { - an_cmd(sc, AN_CMD_SET_MODE, promisc ? 0xffff : 0); + if(sc->an_was_monitor) + an_reset(sc); + if (sc->an_monitor ||sc->an_was_monitor ) + an_init(sc); + sc->an_was_monitor = sc->an_monitor; + an_cmd(sc, AN_CMD_SET_MODE, promisc ? 0xffff : 0); + return; } @@ -1634,13 +1781,19 @@ static void an_init(xsc) if (ifp->if_flags & IFF_MULTICAST) sc->an_config.an_rxmode = AN_RXMODE_BC_MC_ADDR; - /* Initialize promisc mode. */ - /* Kills card DJA can't TX packet in sniff mode - if (ifp->if_flags & IFF_PROMISC) - sc->an_config.an_rxmode |= AN_RXMODE_LAN_MONITOR_CURBSS; - */ - - sc->an_rxmode = sc->an_config.an_rxmode; + if (ifp->if_flags & IFF_PROMISC) { + if (sc->an_monitor & AN_MONITOR) { + if (sc->an_monitor & AN_MONITOR_ANY_BSS) { + sc->an_config.an_rxmode |= + AN_RXMODE_80211_MONITOR_ANYBSS | + AN_RXMODE_NO_8023_HEADER; + } else { + sc->an_config.an_rxmode |= + AN_RXMODE_80211_MONITOR_CURBSS | + AN_RXMODE_NO_8023_HEADER; + } + } + } /* Set the ssid list */ sc->an_ssidlist.an_type = AN_RID_SSIDLIST; @@ -1713,6 +1866,15 @@ static void an_start(ifp) if (!sc->an_associated) return; + if (sc->an_monitor && (ifp->if_flags & IFF_PROMISC)) { + for(;;) { + IF_DEQUEUE(&ifp->if_snd, m0); + if (m0 == NULL) + break; + } + return; + } + idx = sc->an_rdata.an_tx_prod; bzero((char *)&tx_frame_802_3, sizeof(tx_frame_802_3)); diff --git a/usr.sbin/ancontrol/ancontrol.8 b/usr.sbin/ancontrol/ancontrol.8 index 150ede83218f..f88baa8a7efc 100644 --- a/usr.sbin/ancontrol/ancontrol.8 +++ b/usr.sbin/ancontrol/ancontrol.8 @@ -96,6 +96,8 @@ .Nm .Fl i Ar iface Fl r Ar RTS threshold .Nm +.Fl i Ar iface Fl M Ar 0-15 (set monitor mode) +.Nm .Fl h .Sh DESCRIPTION The @@ -394,6 +396,22 @@ need to be retransmitted instead of the whole packet. The fragmentation threshold can be anything from 64 to 2312 bytes. The default is 2312. +.It Fl i Ar iface Fl M Ar 0-15 +Set monitor mode via bit mask, meaning: +.Bl -tag -offset indent -compact -width 0x000000 +.Em "Bit Mask Meaning" +.It 0 +to not dump 802.11 packet. +.It 1 +to enable 802.11 monitor. +.It 2 +to monitor any SSID. +.It 4 +to not skip beacons, monitor beacons produces a high system load. +.It 8 +to enable full Aironet header returned via BPF. +Note it appears that a SSID must be set. +.El .It Fl i Ar iface Fl r Ar RTS threshold Set the RTS/CTS threshold for a given interface. This controls the diff --git a/usr.sbin/ancontrol/ancontrol.c b/usr.sbin/ancontrol/ancontrol.c index 15ef266efc9d..07fe1709339c 100644 --- a/usr.sbin/ancontrol/ancontrol.c +++ b/usr.sbin/ancontrol/ancontrol.c @@ -125,6 +125,7 @@ int main __P((int, char **)); #define ACT_SET_KEY_TYPE 34 #define ACT_SET_KEYS 35 #define ACT_ENABLE_TX_KEY 36 +#define ACT_SET_MONITOR_MODE 37 static void an_getval(iface, areq) const char *iface; @@ -283,6 +284,7 @@ static void an_dumpstatus(iface) an_printhex((char *)&sts->an_errcode, 1); printf("\nSignal quality:\t\t"); an_printhex((char *)&sts->an_cur_signal_quality, 1); + printf("\nSignal strength:\t[ %d%% ]",sts->an_normalized_rssi); /* * XXX: This uses the old definition of the rate field (units of * 500kbps). Technically the new definition is that this field @@ -839,6 +841,7 @@ static void usage(p) fprintf(stderr, "\t%s -i iface -c val (set ad-hoc channel)\n", p); fprintf(stderr, "\t%s -i iface -f val (set frag threshold)\n", p); fprintf(stderr, "\t%s -i iface -r val (set RTS threshold)\n", p); + fprintf(stderr, "\t%s -i iface -M 0-15 (set monitor mode)\n", p); #ifdef ANCACHE fprintf(stderr, "\t%s -i iface -Q print signal quality cache\n", p); fprintf(stderr, "\t%s -i iface -Z zero out signal cache\n", p); @@ -977,6 +980,10 @@ static void an_setconfig(iface, act, arg) cfg->an_authtype = (cfg->an_authtype & ~AN_AUTHTYPE_MASK) | atoi(arg); break; + case ACT_SET_MONITOR_MODE: + areq.an_type = AN_RID_MONITOR_MODE; + cfg->an_len = atoi(arg); /* mode is put in length */ + break; default: errx(1, "unknown action"); break; @@ -1282,18 +1289,20 @@ static void an_readkeyinfo(iface) printf("WEP Key status:\n"); areq.an_type = AN_RID_WEP_TEMP; /* read first key */ - for(i=0; i<4; i++){ + for(i=0; i<5; i++){ areq.an_len = sizeof(struct an_ltv_key); an_getval(iface, &areq); + if(k->kindex == 0xffff) + break; switch (k->klen){ case 0: - printf("\tKey %d is unset\n",i); + printf("\tKey %d is unset\n",k->kindex); break; case 5: - printf("\tKey %d is set 40 bits\n",i); + printf("\tKey %d is set 40 bits\n",k->kindex); break; case 13: - printf("\tKey %d is set 128 bits\n",i); + printf("\tKey %d is set 128 bits\n",k->kindex); break; default: printf("\tWEP Key %d has an unknown size %d\n", @@ -1369,7 +1378,7 @@ int main(argc, argv) opterr = 1; while ((ch = getopt(argc, argv, - "ANISCTht:a:e:o:s:n:v:d:j:b:c:r:p:w:m:l:k:K:W:QZ")) != -1) { + "ANISCTht:a:e:o:s:n:v:d:j:b:c:r:p:w:m:l:k:K:W:QZM:")) != -1) { switch(ch) { case 'Z': #ifdef ANCACHE @@ -1532,6 +1541,10 @@ int main(argc, argv) act = ACT_SET_WAKE_DURATION; arg = optarg; break; + case 'M': + act = ACT_SET_MONITOR_MODE; + arg = optarg; + break; case 'h': default: usage(p);