diff options
-rw-r--r-- | sys/net80211/ieee80211_input.c | 95 | ||||
-rw-r--r-- | sys/net80211/ieee80211_var.h | 16 |
2 files changed, 109 insertions, 2 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 5140ccdf636..415ed166b33 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_input.c,v 1.108 2009/01/28 18:55:18 damien Exp $ */ +/* $OpenBSD: ieee80211_input.c,v 1.109 2009/02/08 15:34:39 damien Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe @@ -62,6 +62,8 @@ #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_priv.h> +struct mbuf *ieee80211_defrag(struct ieee80211com *, struct mbuf *, int); +void ieee80211_defrag_timeout(void *); #ifndef IEEE80211_NO_HT void ieee80211_input_ba(struct ifnet *, struct mbuf *, struct ieee80211_node *, int, struct ieee80211_rxinfo *); @@ -550,6 +552,97 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, } } +/* + * Handle defragmentation (see 9.5 and Annex C). We support the concurrent + * reception of fragments of three fragmented MSDUs or MMPDUs. + */ +struct mbuf * +ieee80211_defrag(struct ieee80211com *ic, struct mbuf *m, int hdrlen) +{ + const struct ieee80211_frame *owh, *wh; + struct ieee80211_defrag *df; + u_int16_t rxseq, seq; + u_int8_t frag; + int i; + + wh = mtod(m, struct ieee80211_frame *); + rxseq = letoh16(*(const u_int16_t *)wh->i_seq); + seq = rxseq >> IEEE80211_SEQ_SEQ_SHIFT; + frag = rxseq & IEEE80211_SEQ_FRAG_MASK; + + if (frag == 0 && !(wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)) + return m; /* not fragmented */ + + if (frag == 0) { + /* first fragment, setup entry in the fragment cache */ + if (++ic->ic_defrag_cur == IEEE80211_DEFRAG_SIZE) + ic->ic_defrag_cur = 0; + df = &ic->ic_defrag[ic->ic_defrag_cur]; + if (df->df_m != NULL) + m_freem(df->df_m); /* discard old entry */ + df->df_seq = seq; + df->df_frag = 0; + df->df_m = m; + /* start receive MSDU timer of aMaxReceiveLifetime */ + timeout_add_sec(&df->df_to, 1); + return NULL; /* MSDU or MMPDU not yet complete */ + } + + /* find matching entry in the fragment cache */ + for (i = 0; i < IEEE80211_DEFRAG_SIZE; i++) { + df = &ic->ic_defrag[i]; + if (df->df_m == NULL) + continue; + if (df->df_seq != seq || df->df_frag + 1 != frag) + continue; + owh = mtod(df->df_m, struct ieee80211_frame *); + /* frame type, source and destination must match */ + if (((wh->i_fc[0] ^ owh->i_fc[0]) & IEEE80211_FC0_TYPE_MASK) || + !IEEE80211_ADDR_EQ(wh->i_addr1, owh->i_addr1) || + !IEEE80211_ADDR_EQ(wh->i_addr2, owh->i_addr2)) + continue; + /* matching entry found */ + break; + } + if (i == IEEE80211_DEFRAG_SIZE) { + /* no matching entry found, discard fragment */ + ic->ic_if.if_ierrors++; + m_freem(m); + return NULL; + } + + df->df_frag = frag; + /* strip 802.11 header and concatenate fragment */ + m_adj(m, hdrlen); + m_cat(df->df_m, m); + df->df_m->m_pkthdr.len += m->m_pkthdr.len; + + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) + return NULL; /* MSDU or MMPDU not yet complete */ + + /* MSDU or MMPDU complete */ + timeout_del(&df->df_to); + m = df->df_m; + df->df_m = NULL; + return m; +} + +/* + * Receive MSDU defragmentation timer exceeds aMaxReceiveLifetime. + */ +void +ieee80211_defrag_timeout(void *arg) +{ + struct ieee80211_defrag *df = arg; + int s = splnet(); + + /* discard all received fragments */ + m_freem(df->df_m); + df->df_m = NULL; + + splx(s); +} + #ifndef IEEE80211_NO_HT /* * Process a received data MPDU related to a specific HT-immediate Block Ack diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 2860ffef826..5e8e917b976 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_var.h,v 1.57 2009/01/28 18:55:18 damien Exp $ */ +/* $OpenBSD: ieee80211_var.h,v 1.58 2009/02/08 15:34:39 damien Exp $ */ /* $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */ /*- @@ -168,6 +168,17 @@ struct ieee80211_edca_ac_params { u_int8_t ac_acm; }; +#define IEEE80211_DEFRAG_SIZE 3 /* must be >= 3 according to spec */ +/* + * Entry in the fragment cache. + */ +struct ieee80211_defrag { + struct timeout df_to; + struct mbuf *df_m; + u_int16_t df_seq; + u_int8_t df_frag; +}; + #define IEEE80211_PROTO_NONE 0 #define IEEE80211_PROTO_RSN (1 << 0) #define IEEE80211_PROTO_WPA (1 << 1) @@ -288,6 +299,9 @@ struct ieee80211com { enum ieee80211_cipher ic_rsngroupcipher; enum ieee80211_cipher ic_rsngroupmgmtcipher; + struct ieee80211_defrag ic_defrag[IEEE80211_DEFRAG_SIZE]; + int ic_defrag_cur; + u_int8_t *ic_tim_bitmap; u_int ic_tim_len; u_int ic_tim_mcast_pending; |