From a245550432954c7f1d3eebf64cf691435cc5f2f2 Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 26 Jul 2006 03:15:16 +0000 Subject: add support for 802.11 packet injection via bpf Together with: Andrea Bittau Reviewed by: arch@ MFC after: 1 month --- sys/net/bpf.c | 31 ++++++++++ sys/net80211/ieee80211.c | 5 ++ sys/net80211/ieee80211_freebsd.h | 33 +++++++++++ sys/net80211/ieee80211_output.c | 124 +++++++++++++++++++++++++++++++++++++++ sys/net80211/ieee80211_proto.c | 1 + sys/net80211/ieee80211_proto.h | 5 ++ sys/net80211/ieee80211_var.h | 3 + sys/sys/socket.h | 3 +- 8 files changed, 204 insertions(+), 1 deletion(-) diff --git a/sys/net/bpf.c b/sys/net/bpf.c index f06ef76..9a2f104 100644 --- a/sys/net/bpf.c +++ b/sys/net/bpf.c @@ -75,6 +75,8 @@ #include #include +#include + static MALLOC_DEFINE(M_BPF, "BPF", "BPF data"); #if defined(DEV_BPF) || defined(NETGRAPH_BPF) @@ -159,6 +161,7 @@ static int bpf_movein(struct uio *uio, int linktype, int mtu, struct mbuf **mp, struct sockaddr *sockp, struct bpf_insn *wfilter) { + const struct ieee80211_bpf_params *p; struct mbuf *m; int error; int len; @@ -221,6 +224,17 @@ bpf_movein(struct uio *uio, int linktype, int mtu, struct mbuf **mp, hlen = 4; /* This should match PPP_HDRLEN */ break; + case DLT_IEEE802_11: /* IEEE 802.11 wireless */ + sockp->sa_family = AF_IEEE80211; + hlen = 0; + break; + + case DLT_IEEE802_11_RADIO: /* IEEE 802.11 wireless w/ phy params */ + sockp->sa_family = AF_IEEE80211; + sockp->sa_len = 12; /* XXX != 0 */ + hlen = sizeof(struct ieee80211_bpf_params); + break; + default: return (EIO); } @@ -263,6 +277,23 @@ bpf_movein(struct uio *uio, int linktype, int mtu, struct mbuf **mp, * Make room for link header, and copy it to sockaddr */ if (hlen != 0) { + if (sockp->sa_family == AF_IEEE80211) { + /* + * Collect true length from the parameter header + * NB: sockp is known to be zero'd so if we do a + * short copy unspecified parameters will be + * zero. + * NB: packet may not be aligned after stripping + * bpf params + * XXX check ibp_vers + */ + p = mtod(m, const struct ieee80211_bpf_params *); + hlen = p->ibp_len; + if (hlen > sizeof(sockp->sa_data)) { + error = EINVAL; + goto bad; + } + } bcopy(m->m_data, sockp->sa_data, hlen); m->m_pkthdr.len -= hlen; m->m_len -= hlen; diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index 33cea93..ebce2c6 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -126,6 +126,8 @@ ieee80211_ifattach(struct ieee80211com *ic) int i; ether_ifattach(ifp, ic->ic_myaddr); + ifp->if_output = ieee80211_output; + bpfattach2(ifp, DLT_IEEE802_11, sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); @@ -209,6 +211,9 @@ ieee80211_ifattach(struct ieee80211com *ic) */ if (ic->ic_reset == NULL) ic->ic_reset = ieee80211_default_reset; + + KASSERT(ifp->if_spare2 == NULL, ("oops, hosed")); + ifp->if_spare2 = ic; /* XXX temp backpointer */ } void diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h index 70a40c1..9fc23e5 100644 --- a/sys/net80211/ieee80211_freebsd.h +++ b/sys/net80211/ieee80211_freebsd.h @@ -225,4 +225,37 @@ struct ieee80211_michael_event { #define RTM_IEEE80211_MICHAEL 107 /* Michael MIC failure detected */ #define RTM_IEEE80211_REJOIN 108 /* station re-associate (ap mode) */ +/* + * Structure prepended to raw packets sent through the bpf + * interface when set to DLT_IEEE802_11_RADIO. This allows + * user applications to specify pretty much everything in + * an Atheros tx descriptor. XXX need to generalize. + * + * XXX cannot be more than 14 bytes as it is copied to a sockaddr's + * XXX sa_data area. + */ +struct ieee80211_bpf_params { + uint8_t ibp_vers; /* version */ +#define IEEE80211_BPF_VERSION 0 + uint8_t ibp_len; /* header length in bytes */ + uint8_t ibp_flags; +#define IEEE80211_BPF_SHORTPRE 0x01 /* tx with short preamble */ +#define IEEE80211_BPF_NOACK 0x02 /* tx with no ack */ +#define IEEE80211_BPF_CRYPTO 0x04 /* tx with h/w encryption */ +#define IEEE80211_BPF_FCS 0x10 /* frame incldues FCS */ +#define IEEE80211_BPF_DATAPAD 0x20 /* frame includes data padding */ +#define IEEE80211_BPF_RTS 0x40 /* tx with RTS/CTS */ +#define IEEE80211_BPF_CTS 0x80 /* tx with CTS only */ + uint8_t ibp_pri; /* WME/WMM AC+tx antenna */ + uint8_t ibp_try0; /* series 1 try count */ + uint8_t ibp_rate0; /* series 1 IEEE tx rate */ + uint8_t ibp_power; /* tx power (device units) */ + uint8_t ibp_ctsrate; /* IEEE tx rate for CTS */ + uint8_t ibp_try1; /* series 2 try count */ + uint8_t ibp_rate1; /* series 2 IEEE tx rate */ + uint8_t ibp_try2; /* series 3 try count */ + uint8_t ibp_rate2; /* series 3 IEEE tx rate */ + uint8_t ibp_try3; /* series 4 try count */ + uint8_t ibp_rate3; /* series 4 IEEE tx rate */ +}; #endif /* _NET80211_IEEE80211_FREEBSD_H_ */ diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 9dfbb8d..c91e389 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -204,6 +204,130 @@ ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, } /* + * Raw packet transmit stub for legacy drivers. + * Send the packet through the mgt q so we bypass + * the normal encapsulation work. + */ +int +ieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + + m->m_pkthdr.rcvif = (void *) ni; + IF_ENQUEUE(&ic->ic_mgtq, m); + if_start(ifp); + ifp->if_opackets++; + + return 0; +} + +/* + * 802.11 output routine. This is (currently) used only to + * connect bpf write calls to the 802.11 layer for injecting + * raw 802.11 frames. Note we locate the ieee80211com from + * the ifnet using a spare field setup at attach time. This + * will go away when the virtual ap support comes in. + */ +int +ieee80211_output(struct ifnet *ifp, struct mbuf *m, + struct sockaddr *dst, struct rtentry *rt0) +{ +#define senderr(e) do { error = (e); goto bad;} while (0) + struct ieee80211com *ic = ifp->if_spare2; /* XXX */ + struct ieee80211_node *ni = NULL; + struct ieee80211_frame *wh; + int error; + + /* + * Hand to the 802.3 code if not tagged as + * a raw 802.11 frame. + */ + if (dst->sa_family != AF_IEEE80211) + return ether_output(ifp, m, dst, rt0); +#ifdef MAC + error = mac_check_ifnet_transmit(ifp, m); + if (error) + senderr(error); +#endif + if (ifp->if_flags & IFF_MONITOR) + senderr(ENETDOWN); + if ((ifp->if_flags & IFF_UP) == 0) + senderr(ENETDOWN); + + /* XXX bypass bridge, pfil, carp, etc. */ + + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) + senderr(EIO); /* XXX */ + wh = mtod(m, struct ieee80211_frame *); + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != + IEEE80211_FC0_VERSION_0) + senderr(EIO); /* XXX */ + + /* locate destination node */ + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + case IEEE80211_FC1_DIR_FROMDS: + ni = ieee80211_find_txnode(ic, wh->i_addr1); + break; + case IEEE80211_FC1_DIR_TODS: + case IEEE80211_FC1_DIR_DSTODS: + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) + senderr(EIO); /* XXX */ + ni = ieee80211_find_txnode(ic, wh->i_addr3); + break; + default: + senderr(EIO); /* XXX */ + } + if (ni == NULL) { + /* + * Permit packets w/ bpf params through regardless + * (see below about sa_len). + */ + if (dst->sa_len == 0) + senderr(EHOSTUNREACH); + ni = ieee80211_ref_node(ic->ic_bss); + } + + /* XXX ctrl frames should go through */ + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + (m->m_flags & M_PWR_SAV) == 0) { + /* + * Station in power save mode; pass the frame + * to the 802.11 layer and continue. We'll get + * the frame back when the time is right. + */ + ieee80211_pwrsave(ic, ni, m); + error = 0; + goto reclaim; + } + + /* calculate priority so drivers can find the tx queue */ + /* XXX assumes an 802.3 frame */ + if (ieee80211_classify(ic, m, ni)) + senderr(EIO); /* XXX */ + + BPF_MTAP(ifp, m); + /* + * NB: DLT_IEEE802_11_RADIO identifies the parameters are + * present by setting the sa_len field of the sockaddr (yes, + * this is a hack). + * NB: we assume sa_data is suitably aligned to cast. + */ + return ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *) + (dst->sa_len ? dst->sa_data : NULL)); +bad: + if (m != NULL) + m_freem(m); +reclaim: + if (ni != NULL) + ieee80211_free_node(ni); + return error; +#undef senderr +} + +/* * Send a null data frame to the specified node. * * NB: the caller is assumed to have setup a node reference diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 16d22d3..e7441ef 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -114,6 +114,7 @@ ieee80211_proto_attach(struct ieee80211com *ic) /* initialize management frame handlers */ ic->ic_recv_mgmt = ieee80211_recv_mgmt; ic->ic_send_mgmt = ieee80211_send_mgmt; + ic->ic_raw_xmit = ieee80211_raw_xmit; } void diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index 4b8f6b4..397da44 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -64,6 +64,11 @@ int ieee80211_setup_rates(struct ieee80211_node *ni, void ieee80211_saveie(u_int8_t **, const u_int8_t *); void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, int, u_int32_t); +struct ieee80211_bpf_params; +int ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +int ieee80211_output(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); int ieee80211_send_nulldata(struct ieee80211_node *); int ieee80211_send_probereq(struct ieee80211_node *ni, const u_int8_t sa[IEEE80211_ADDR_LEN], diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 927d143..40f2fde 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -103,6 +103,9 @@ struct ieee80211com { void (*ic_newassoc)(struct ieee80211_node *, int); void (*ic_updateslot)(struct ifnet *); void (*ic_set_tim)(struct ieee80211_node *, int); + int (*ic_raw_xmit)(struct ieee80211_node *, + struct mbuf *, + const struct ieee80211_bpf_params *); u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; diff --git a/sys/sys/socket.h b/sys/sys/socket.h index bb00346..598008f 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -208,7 +208,8 @@ struct accept_filter_arg { #define AF_SCLUSTER 34 /* Sitara cluster protocol */ #define AF_ARP 35 #define AF_BLUETOOTH 36 /* Bluetooth sockets */ -#define AF_MAX 37 +#define AF_IEEE80211 37 /* IEEE 802.11 protocol */ +#define AF_MAX 38 #endif /* -- cgit v1.1