snoopy: add support for wireless monitor mode packets

This commit is contained in:
Russ Cox 2008-07-24 08:04:02 -07:00
parent 7e36b43bba
commit 17b19538a4
9 changed files with 789 additions and 1 deletions

View file

@ -6,7 +6,10 @@ typedef struct Proto Proto;
#define NetS(x) ((((uchar*)x)[0]<<8) | ((uchar*)x)[1])
#define Net3(x) ((((uchar*)x)[0]<<16) | (((uchar*)x)[1]<<8) | ((uchar*)x)[2])
#define NetL(x) ((((uchar*)x)[0]<<24) | (((uchar*)x)[1]<<16) | (((uchar*)x)[2]<<8) | ((uchar*)x)[3])
#define NetL(x) (((ulong)((((uchar*)x)[0]<<24) | (((uchar*)x)[1]<<16) | (((uchar*)x)[2]<<8) | ((uchar*)x)[3]))&0xFFFFFFFFU)
#define LittleS(x) ((((uchar*)x)[1]<<8) | ((uchar*)x)[0])
#define LittleL(x) (((ulong)((((uchar*)x)[3]<<24) | (((uchar*)x)[2]<<16) | (((uchar*)x)[1]<<8) | ((uchar*)x)[0]))&0xFFFFFFFFU)
/*
* one per protocol module

173
src/cmd/ip/snoopy/llc.c Normal file
View file

@ -0,0 +1,173 @@
/*
* LLC. Only enough to dispatch to SNAP and IP.
*/
#include <u.h>
#include <libc.h>
#include <ip.h>
#include "dat.h"
#include "protos.h"
enum
{
UFmt = 3,
Gsap = 1,
IG = 1,
SFmt = 1,
UPoll = 0x10,
IsPoll = 0x100,
XidFi = 0x81,
SapNull = 0,
SapGlobal = 0xff,
Sap8021BI = 0x02,
Sap8021BG = 0x03,
SapSNA = 0x04,
SapIP = 0x06,
SapProwayNM = 0x0e,
Sap8021D = 0x42,
SapRS511 = 0x4e,
SapISO8208 = 0x7e,
SapProway = 0x8e,
SapSnap = 0xaa,
SapIpx = 0xe0,
SapNetbeui = 0xf0,
SapIsons = 0xfe,
};
static Mux p_mux[] =
{
// Linux gives llc -> snap not llc -> ip.
// If we don't tell snoopy about llc -> ip, then the default patterns
// like snoopy -h radiotap -f dns work better.
// { "ip", SapIP },
{ "snap", SapSnap },
{ 0 }
};
typedef struct Hdr Hdr;
struct Hdr
{
uchar dsap;
uchar ssap;
uchar dsapf;
uchar ssapf;
ushort ctl;
uchar isu;
int hdrlen;
};
static int
unpackhdr(uchar *p, uchar *ep, Hdr *h)
{
if(p+3 > ep)
return -1;
h->dsapf = p[0];
h->dsap = h->dsapf & ~IG;
h->ssapf = p[1];
h->ssap = h->ssapf & ~Gsap;
h->ctl = p[2];
h->hdrlen = 3;
if((h->ctl&UFmt) == UFmt)
h->isu = 1;
else{
if(p+4 > ep)
return -1;
h->hdrlen = 4;
h->ctl = LittleS(p+2);
}
return 0;
}
enum
{
Ossap,
Odsap,
Ot,
};
static Field p_fields[] =
{
{ "ssap", Fnum, Ossap, "ssap" },
{ "dsap", Fnum, Odsap, "dsap" },
{ 0 }
};
static void
p_compile(Filter *f)
{
Mux *m;
if(f->op == '='){
compile_cmp(llc.name, f, p_fields);
return;
}
for(m = p_mux; m->name != nil; m++){
if(strcmp(f->s, m->name) == 0){
f->pr = m->pr;
f->ulv = m->val;
f->subop = Ot;
return;
}
}
sysfatal("unknown llc field or protocol: %s", f->s);
}
static int
p_filter(Filter *f, Msg *m)
{
Hdr h;
memset(&h, 0, sizeof h);
if(unpackhdr(m->ps, m->pe, &h) < 0)
return 0;
m->ps += h.hdrlen;
switch(f->subop){
case Ossap:
return f->ulv == h.ssap;
case Odsap:
return f->ulv == h.dsap;
case Ot:
return f->ulv == h.ssap && f->ulv == h.dsap;
}
return 0;
}
static int
p_seprint(Msg *m)
{
Hdr h;
memset(&h, 0, sizeof h);
if(unpackhdr(m->ps, m->pe, &h) < 0)
return -1;
m->pr = &dump;
m->p = seprint(m->p, m->e, "ssap=%02x dsap=%02x ctl=%04x", h.ssap, h.dsap, h.ctl);
m->ps += h.hdrlen;
m->pr = &dump;
if(h.ssap == h.dsap){
switch(h.ssap){
case SapIP:
m->pr = &ip;
break;
case SapSnap:
m->pr = &snap;
break;
}
}
return 0;
}
Proto llc =
{
"llc",
p_compile,
p_filter,
p_seprint,
p_mux,
nil,
nil,
defaultframer
};

View file

@ -27,6 +27,11 @@ PROTOS=\
ppp_ipcp\
pppoe_sess\
pppoe_disc\
dns\
p80211\
llc\
radiotap\
snap\
POBJS=${PROTOS:%=%.$O}

367
src/cmd/ip/snoopy/p80211.c Normal file
View file

@ -0,0 +1,367 @@
/*
* IEEE 802.11.
*/
#include <u.h>
#include <libc.h>
#include <ip.h>
#include "dat.h"
#include "protos.h"
enum
{
Tmgmt = 0,
Tctl,
Tdata,
CtlPoll = 0xA,
CtlRts,
CtlCts,
CtlAck,
CtlCfEnd,
CtlCfEndAck,
Data = 0,
DataCfAck,
DataCfPoll,
DataCfAckPoll,
Nodata,
NodataCfAck,
NodataCfPoll,
NodataCfAckPoll,
FlagTods = 0x1,
FlagFromds = 0x2,
FlagMoreflag = 0x4,
FlagRetry = 0x8,
FlagPowerMgmt = 0x10,
FlagMoreData = 0x20,
FlagWep = 0x40,
FlagOrder = 0x80,
ProtoNone = 0,
ProtoLlc,
};
static Mux p_mux[] =
{
{ "llc", ProtoLlc },
{ 0 }
};
typedef struct Hdr Hdr;
struct Hdr
{
uchar vers;
uchar type;
uchar subtype;
uchar flags;
ushort dur;
uchar aid;
uchar ra[6];
uchar ta[6];
uchar bssid[6];
uchar sa[6];
uchar da[6];
ushort seq;
int proto;
int hdrlen;
};
static int
unpackhdr(uchar *p, uchar *ep, Hdr *h)
{
if(p+2 > ep)
return -1;
h->vers = p[0]&3;
if(h->vers != 0){
h->hdrlen = 2;
return -1;
}
h->type = (p[0]>>2)&3;
h->subtype = (p[0]>>4)&15;
h->flags = p[1];
h->hdrlen = 2;
if(h->vers != 0)
return 0;
switch(h->type){
case Tmgmt:
// fc dur da sa bssid seq
if(p+2+2+6+6+6+2 > ep)
return -1;
h->hdrlen = 24;
h->dur = LittleS(p+2);
memmove(h->da, p+4, 6);
memmove(h->sa, p+10, 6);
memmove(h->bssid, p+16, 6);
h->seq = LittleS(p+22);
break;
case Tctl:
switch(h->subtype){
case CtlPoll:
// fc aid bssid ta
if(p+2+2+6+6 > ep)
return -1;
h->hdrlen = 16;
h->aid = LittleS(p+2);
memmove(h->bssid, p+4, 6);
memmove(h->ta, p+10, 6);
break;
case CtlRts:
// fc dur ra ta
if(p+2+2+6+6 > ep)
return -1;
h->hdrlen = 16;
h->dur = LittleS(p+2);
memmove(h->ra, p+4, 6);
memmove(h->ta, p+10, 6);
break;
case CtlCts:
case CtlAck:
// fc dur ra
if(p+2+2+6 > ep)
return -1;
h->hdrlen = 10;
h->dur = LittleS(p+2);
memmove(h->ra, p+4, 6);
break;
case CtlCfEnd:
case CtlCfEndAck:
// fc dur ra bssid
if(p+2+2+6+6 > ep)
return -1;
h->hdrlen = 16;
h->dur = LittleS(p+2);
memmove(h->ra, p+4, 6);
memmove(h->bssid, p+10, 6);
break;
}
break;
case Tdata:
if(p+24 > ep)
return -1;
h->hdrlen = 24;
h->dur = LittleS(p+2); // ??? maybe
// Also, what is at p+22?
switch(h->flags&(FlagFromds|FlagTods)){
case 0:
memmove(h->da, p+4, 6);
memmove(h->sa, p+10, 6);
memmove(h->bssid, p+16, 6);
break;
case FlagFromds:
memmove(h->da, p+4, 6);
memmove(h->bssid, p+10, 6);
memmove(h->sa, p+16, 6);
break;
case FlagTods:
memmove(h->bssid, p+4, 6);
memmove(h->sa, p+10, 6);
memmove(h->da, p+16, 6);
break;
case FlagFromds|FlagTods:
if(p+30 > ep)
return -1;
h->hdrlen = 30;
memmove(h->ra, p+4, 6);
memmove(h->ta, p+10, 6);
memmove(h->da, p+16, 6);
memmove(h->sa, p+24, 6); // 24 sic
break;
}
p += h->hdrlen;
h->proto = ProtoNone;
if(!(h->flags&FlagWep))
h->proto = ProtoLlc;
break;
}
return 0;
}
enum
{
Os,
Od,
Ot,
Or,
Obssid,
Oa,
Opr,
};
static Field p_fields[] =
{
{ "s", Fether, Os, "source address" },
{ "d", Fether, Od, "destination address" },
{ "t", Fether, Ot, "transmit address" },
{ "r", Fether, Or, "receive address" },
{ "bssid", Fether, Obssid, "bssid address" },
{ "a", Fether, Oa, "any address" },
{ "sd", Fether, Oa, "source|destination address" },
{ 0 }
};
static void
p_compile(Filter *f)
{
Mux *m;
if(f->op == '='){
compile_cmp(p80211.name, f, p_fields);
return;
}
if(strcmp(f->s, "mgmt") == 0){
f->pr = &p80211;
f->ulv = Tmgmt;
f->subop = Ot;
return;
}
if(strcmp(f->s, "ctl") == 0){
f->pr = &p80211;
f->ulv = Tctl;
f->subop = Ot;
return;
}
if(strcmp(f->s, "data") == 0){
f->pr = &p80211;
f->ulv = Tdata;
f->subop = Ot;
return;
}
for(m = p_mux; m->name != nil; m++){
if(strcmp(f->s, m->name) == 0){
f->pr = m->pr;
f->ulv = m->val;
f->subop = Opr;
return;
}
}
sysfatal("unknown 802.11 field or protocol: %s", f->s);
}
static int
p_filter(Filter *f, Msg *m)
{
Hdr h;
memset(&h, 0, sizeof h);
if(unpackhdr(m->ps, m->pe, &h) < 0)
return 0;
m->ps += h.hdrlen;
switch(f->subop){
case Os:
return memcmp(h.sa, f->a, 6) == 0;
case Od:
return memcmp(h.da, f->a, 6) == 0;
case Ot:
return memcmp(h.ta, f->a, 6) == 0;
case Or:
return memcmp(h.ra, f->a, 6) == 0;
case Obssid:
return memcmp(h.bssid, f->a, 6) == 0;
case Oa:
return memcmp(h.sa, f->a, 6) == 0
|| memcmp(h.da, f->a, 6) == 0
|| memcmp(h.ta, f->a, 6) == 0
|| memcmp(h.ra, f->a, 6) == 0
|| memcmp(h.bssid, f->a, 6) == 0;
case Opr:
return h.proto == f->ulv;
}
return 0;
}
static int
p_seprint(Msg *m)
{
Hdr h;
memset(&h, 0, sizeof h);
if(unpackhdr(m->ps, m->pe, &h) < 0)
return -1;
m->pr = &dump;
m->p = seprint(m->p, m->e, "fc=%02x flags=%02x ", m->ps[0], m->ps[1]);
switch(h.type){
case Tmgmt:
m->p = seprint(m->p, m->e, "mgmt dur=%d d=%E s=%E bssid=%E seq=%d",
h.dur, h.da, h.sa, h.bssid, h.seq);
break;
case Tctl:
switch(h.subtype){
case CtlPoll:
m->p = seprint(m->p, m->e, "ctl poll aid=%d bssid=%E t=%E",
h.aid, h.bssid, h.ta);
break;
case CtlRts:
m->p = seprint(m->p, m->e, "ctl rts dur=%d r=%E t=%E",
h.dur, h.ra, h.ta);
break;
case CtlCts:
m->p = seprint(m->p, m->e, "ctl cts dur=%d r=%E",
h.dur, h.ra);
break;
case CtlAck:
m->p = seprint(m->p, m->e, "ctl ack dur=%d r=%E",
h.dur, h.ra);
break;
case CtlCfEnd:
m->p = seprint(m->p, m->e, "ctl cf end dur=%d r=%E bssid=%E",
h.dur, h.ra, h.bssid);
break;
case CtlCfEndAck:
m->p = seprint(m->p, m->e, "ctl cf end ack dur=%d r=%E bssid=%E",
h.dur, h.ra, h.bssid);
break;
default:
m->p = seprint(m->p, m->e, "ctl %.*H", m->ps, h.hdrlen);
break;
}
break;
case Tdata:
switch(h.flags&(FlagFromds|FlagTods)){
case 0:
m->p = seprint(m->p, m->e, "data d=%E s=%E bssid=%E",
h.da, h.sa, h.bssid);
break;
case FlagFromds:
m->p = seprint(m->p, m->e, "data fds d=%E bssid=%E s=%E",
h.da, h.bssid, h.sa);
break;
case FlagTods:
m->p = seprint(m->p, m->e, "data tds bssid=%E s=%E d=%E",
h.bssid, h.sa, h.da);
break;
case FlagFromds|FlagTods:
m->p = seprint(m->p, m->e, "data fds tds r=%E t=%E d=%E s=%E",
h.ra, h.ta, h.da, h.sa);
break;
}
if(!(h.flags&FlagWep))
m->pr = &llc;
break;
}
m->ps += h.hdrlen;
return 0;
}
Proto p80211 =
{
"802.11",
p_compile,
p_filter,
p_seprint,
p_mux,
nil,
nil,
defaultframer
};

View file

@ -29,5 +29,10 @@ Proto *protos[] =
&ppp_ipcp,
&pppoe_sess,
&pppoe_disc,
&dns,
&p80211,
&llc,
&radiotap,
&snap,
0
};

View file

@ -23,3 +23,8 @@ extern Proto ppp_chap;
extern Proto ppp_ipcp;
extern Proto pppoe_sess;
extern Proto pppoe_disc;
extern Proto dns;
extern Proto p80211;
extern Proto llc;
extern Proto radiotap;
extern Proto snap;

View file

@ -0,0 +1,117 @@
/*
* Radio tap as exported by BSD and Linux.
* The wireless ethernet devices return this format on Linux
* when running in monitor mode.
*
* TODO: Automatically determine whether the ethernet
* device is radio or ether, so that -h is not needed.
*/
#include <u.h>
#include <libc.h>
#include <ip.h>
#include "dat.h"
#include "protos.h"
static Mux p_mux[] =
{
{ "802.11", 0 },
{ 0 }
};
typedef struct Hdr Hdr;
struct Hdr
{
uchar vers;
uchar pad;
ushort len;
ulong present;
};
static int
unpackhdr(uchar *p, uchar *ep, Hdr *h)
{
if(p+sizeof(Hdr) > ep)
return -1;
h->vers = p[0];
h->pad = p[1];
h->len = LittleS(p+2);
h->present = LittleL(p+4);
// can be more present fields if 0x80000000 is set in each along the chain.
if(p+h->len > ep)
return -1;
return 0;
}
enum
{
Ot,
};
static Field p_fields[] =
{
{ 0 }
};
static void
p_compile(Filter *f)
{
Mux *m;
if(f->op == '='){
compile_cmp(radiotap.name, f, p_fields);
return;
}
for(m = p_mux; m->name != nil; m++){
if(strcmp(f->s, m->name) == 0){
f->pr = m->pr;
f->ulv = m->val;
f->subop = Ot;
return;
}
}
sysfatal("unknown radiotap field or protocol: %s", f->s);
}
static int
p_filter(Filter *f, Msg *m)
{
Hdr h;
memset(&h, 0, sizeof h);
if(unpackhdr(m->ps, m->pe, &h) < 0)
return 0;
m->ps += h.len;
switch(f->subop){
case Ot:
return 1;
}
return 0;
}
static int
p_seprint(Msg *m)
{
Hdr h;
memset(&h, 0, sizeof h);
if(unpackhdr(m->ps, m->pe, &h) < 0)
return -1;
m->p = seprint(m->p, m->e, "%.*H", h.len, m->ps);
m->ps += h.len;
m->pr = &p80211;
return 0;
}
Proto radiotap =
{
"radiotap",
p_compile,
p_filter,
p_seprint,
p_mux,
nil,
nil,
defaultframer
};

112
src/cmd/ip/snoopy/snap.c Normal file
View file

@ -0,0 +1,112 @@
/*
* SNAP.
*/
#include <u.h>
#include <libc.h>
#include <ip.h>
#include "dat.h"
#include "protos.h"
enum
{
Oorg,
Oet,
OuiEther = 0,
OuiCisco = 0xc,
OuiCisco90 = 0xf8,
OuiRfc2684 = 0x80c2,
OuiAppletalk = 0x80007,
};
static Mux p_mux[] =
{
{"ip", 0x0800, } ,
{"arp", 0x0806, } ,
{"rarp", 0x0806, } ,
{"ip6", 0x86dd, } ,
{"pppoe_disc", 0x8863, },
{"pppoe_sess", 0x8864, },
{"eapol", 0x888e, },
{ 0 }
};
typedef struct Hdr Hdr;
struct Hdr
{
uchar org[3];
uchar et[2];
};
static Field p_fields[] =
{
{ "org", Fnum, Oorg, "org" },
{ "et", Fnum, Oet, "et" },
{ 0 }
};
static void
p_compile(Filter *f)
{
Mux *m;
if(f->op == '='){
compile_cmp(snap.name, f, p_fields);
return;
}
for(m = p_mux; m->name != nil; m++){
if(strcmp(f->s, m->name) == 0){
f->pr = m->pr;
f->ulv = m->val;
f->subop = Oet;
return;
}
}
sysfatal("unknown snap field or protocol: %s", f->s);
}
static int
p_filter(Filter *f, Msg *m)
{
Hdr *h;
if(m->pe - m->ps < sizeof(Hdr))
return 0;
h = (Hdr*)m->ps;
m->ps += 5;
switch(f->subop){
case Oorg:
return f->ulv == Net3(h->org);
case Oet:
return f->ulv == NetS(h->et);
}
return 0;
}
static int
p_seprint(Msg *m)
{
Hdr *h;
if(m->pe - m->ps < sizeof(Hdr))
return 0;
h = (Hdr*)m->ps;
m->ps += 5;
demux(p_mux, NetS(h->et), NetS(h->et), m, &dump);
m->p = seprint(m->p, m->e, "org=%06x et=%04x", Net3(h->org), NetS(h->et));
return 0;
}
Proto snap =
{
"snap",
p_compile,
p_filter,
p_seprint,
p_mux,
nil,
nil,
defaultframer
};

View file

@ -41,6 +41,7 @@ static Mux p_mux[] =
{
{"bootp", 67, },
{"ninep", 6346, }, /* tvs */
{"dns", 53 },
{"rtp", ANYPORT, },
{"rtcp", ANYPORT, },
{0}