diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2005-04-20 19:52:44 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2005-04-20 19:52:44 +0000 |
commit | 472569caed823134f3cdf4b7b1f19c50910c3879 (patch) | |
tree | 47f7fc8ecb225ce29c9fcd2defd292b2af4f2512 | |
parent | f89146cba13d07521aafe77f436a3255fb95e64c (diff) |
send raw 802.11 frames with bpf(4) using the IEEE802_11 or
IEEE802_11_RADIO data link types.
ok canacar@ damien@
-rw-r--r-- | sys/net/bpf.c | 18 | ||||
-rw-r--r-- | sys/net80211/ieee80211.c | 5 | ||||
-rw-r--r-- | sys/net80211/ieee80211_output.c | 111 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.h | 4 |
4 files changed, 134 insertions, 4 deletions
diff --git a/sys/net/bpf.c b/sys/net/bpf.c index 8588e507fa8..980066a9490 100644 --- a/sys/net/bpf.c +++ b/sys/net/bpf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bpf.c,v 1.57 2005/04/20 17:03:22 reyk Exp $ */ +/* $OpenBSD: bpf.c,v 1.58 2005/04/20 19:52:42 reyk Exp $ */ /* $NetBSD: bpf.c,v 1.33 1997/02/21 23:59:35 thorpej Exp $ */ /* @@ -107,6 +107,7 @@ bpf_movein(struct uio *uio, u_int linktype, struct mbuf **mp, struct sockaddr *sockp, struct bpf_insn *filter) { struct mbuf *m; + struct m_tag *mtag; int error; u_int hlen; u_int len; @@ -150,6 +151,12 @@ bpf_movein(struct uio *uio, u_int linktype, struct mbuf **mp, hlen = 24; break; + case DLT_IEEE802_11: + case DLT_IEEE802_11_RADIO: + sockp->sa_family = AF_UNSPEC; + hlen = 0; + break; + case DLT_RAW: case DLT_NULL: sockp->sa_family = AF_UNSPEC; @@ -211,6 +218,15 @@ bpf_movein(struct uio *uio, u_int linktype, struct mbuf **mp, m->m_data += hlen; /* XXX */ } + /* + * Prepend the data link type as a mbuf tag + */ + mtag = m_tag_get(PACKET_TAG_DLT, sizeof(u_int), M_NOWAIT); + if (mtag == NULL) + return (ENOMEM); + *(u_int *)(mtag + 1) = linktype; + m_tag_prepend(m, mtag); + return (0); bad: m_freem(m); diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index 20af2f2d2ea..ab2c5a3b9bd 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211.c,v 1.4 2005/02/17 18:28:05 reyk Exp $ */ +/* $OpenBSD: ieee80211.c,v 1.5 2005/04/20 19:52:43 reyk Exp $ */ /* $NetBSD: ieee80211.c,v 1.19 2004/06/06 05:45:29 dyoung Exp $ */ /*- @@ -155,6 +155,9 @@ ieee80211_ifattach(struct ifnet *ifp) #else ether_ifattach(ifp, ic->ic_myaddr); #endif + + ifp->if_output = ieee80211_output; + #if NBPFILTER > 0 BPF_ATTACH(ifp, DLT_IEEE802_11, sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf); diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 8818c0db755..c104fe472b0 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_output.c,v 1.8 2005/03/11 23:20:26 jsg Exp $ */ +/* $OpenBSD: ieee80211_output.c,v 1.9 2005/04/20 19:52:43 reyk Exp $ */ /* $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $ */ /*- @@ -92,6 +92,67 @@ __KERNEL_RCSID(0, "$NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung #include <net80211/ieee80211_compat.h> /* + * IEEE 802.11 output routine. Normally this will directly call the + * Ethernet output routine because 802.11 encapsulation is called + * later by the driver. This function could be used to send raw frames + * if the mbuf has been tagged with a 802.11 data link type. + */ +int +ieee80211_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt) +{ + u_int dlt = 0; + int s, error = 0; + struct m_tag *mtag; + + /* Interface has to be up and running */ + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != + (IFF_UP | IFF_RUNNING)) { + error = ENETDOWN; + goto bad; + } + + /* Try to get the DLT from a mbuf tag */ + if ((mtag = m_tag_find(m, PACKET_TAG_DLT, NULL)) != NULL) { + dlt = *(u_int *)(mtag + 1); + + /* Fallback to ethernet for non-802.11 linktypes */ + if (!(dlt == DLT_IEEE802_11 || dlt == DLT_IEEE802_11_RADIO)) + goto fallback; + + /* + * Queue message on interface without adding any + * further headers, and start output if interface not + * yet active. + */ + s = splimp(); + IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); + if (error) { + /* mbuf is already freed */ + splx(s); + if_printf(ifp, "failed to queue raw tx frame\n"); + return (error); + } + ifp->if_obytes += m->m_pkthdr.len; + if (m->m_flags & M_MCAST) + ifp->if_omcasts++; + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); + splx(s); + + return (error); + } + + fallback: + return (ether_output(ifp, m, dst, rt)); + + bad: + if (m) + m_freem(m); + return (error); +} + +/* * Send a management frame to the specified node. The node pointer * must have a reference as the pointer will be passed to the driver * and potentially held for a long time. If the frame is successfully @@ -185,7 +246,55 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni) struct ieee80211_frame *wh; struct ieee80211_node *ni = NULL; struct llc *llc; + struct m_tag *mtag; + u_int8_t *addr; + u_int dlt; + + /* Handle raw frames if mbuf is tagged as 802.11 */ + if ((mtag = m_tag_find(m, PACKET_TAG_DLT, NULL)) != NULL) { + dlt = *(u_int *)(mtag + 1); + + if (!(dlt == DLT_IEEE802_11 || dlt == DLT_IEEE802_11_RADIO)) + goto fallback; + + wh = mtod(m, struct ieee80211_frame *); + + if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) + goto bad; + + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != + IEEE80211_FC0_VERSION_0) + goto bad; + + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + case IEEE80211_FC1_DIR_FROMDS: + addr = wh->i_addr1; + break; + case IEEE80211_FC1_DIR_DSTODS: + case IEEE80211_FC1_DIR_TODS: + addr = wh->i_addr3; + break; + default: + goto bad; + } + + ni = ieee80211_find_txnode(ic, addr); + if (ni == NULL) + ni = ieee80211_ref_node(ic->ic_bss); + if (ni == NULL) { + if_printf(ifp, "no node for dst %s, " + "discard raw tx frame\n", ether_sprintf(addr)); + ic->ic_stats.is_tx_nonode++; + goto bad; + } + ni->ni_inact = 0; + + *pni = ni; + return (m); + } + fallback: if (m->m_len < sizeof(struct ether_header)) { m = m_pullup(m, sizeof(struct ether_header)); if (m == NULL) { diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index 0a59806594e..670d8019f38 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_proto.h,v 1.6 2005/03/03 14:36:41 damien Exp $ */ +/* $OpenBSD: ieee80211_proto.h,v 1.7 2005/04/20 19:52:43 reyk Exp $ */ /* $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $ */ /*- @@ -61,6 +61,8 @@ extern void ieee80211_proto_detach(struct ifnet *); struct ieee80211_node; extern void ieee80211_input(struct ifnet *, struct mbuf *, struct ieee80211_node *, int, u_int32_t); +extern int ieee80211_output(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, int, u_int32_t); extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *, |