diff options
author | Felix Fietkau <nbd@openwrt.org> | 2009-01-27 20:05:46 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2009-01-27 20:05:46 +0000 |
commit | 9bb8734d2ef37d72dd817a855a04e40dadce76b5 (patch) | |
tree | 0502d0763034070a4b370b9133befb4fc530d009 | |
parent | fe48f4cac0351f01aa7cea32fb84706021b30f38 (diff) | |
download | master-31e0f0ae-9bb8734d2ef37d72dd817a855a04e40dadce76b5.tar.gz master-31e0f0ae-9bb8734d2ef37d72dd817a855a04e40dadce76b5.tar.bz2 master-31e0f0ae-9bb8734d2ef37d72dd817a855a04e40dadce76b5.zip |
hostapd: add support for socket filtering to get rid of some of the massive cpu overhead with mac80211 drivers (backport from hostapd git repository)
SVN-Revision: 14225
-rw-r--r-- | package/hostapd/patches/110-nl80211_socketfilter.patch | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/package/hostapd/patches/110-nl80211_socketfilter.patch b/package/hostapd/patches/110-nl80211_socketfilter.patch new file mode 100644 index 0000000000..4427d00615 --- /dev/null +++ b/package/hostapd/patches/110-nl80211_socketfilter.patch @@ -0,0 +1,184 @@ +--- a/hostapd/driver_nl80211.c ++++ b/hostapd/driver_nl80211.c +@@ -27,6 +27,7 @@ + #include <net/if.h> + #include <netpacket/packet.h> + #include "wireless_copy.h" ++#include <linux/filter.h> + #include <net/if_arp.h> + + #include "hostapd.h" +@@ -1728,6 +1729,9 @@ static void handle_frame(struct hostapd_ + case WLAN_FC_TODS: + bssid = hdr->addr1; + break; ++ case WLAN_FC_FROMDS: ++ bssid = hdr->addr2; ++ break; + default: + /* discard */ + return; +@@ -1908,6 +1912,150 @@ static void handle_monitor_read(int sock + } + + ++/* ++ * we post-process the filter code later and rewrite ++ * this to the offset to the last instruction ++ */ ++#define PASS 0xFF ++#define FAIL 0xFE ++ ++static struct sock_filter msock_filter_insns[] = { ++ /* ++ * do a little-endian load of the radiotap length field ++ */ ++ /* load lower byte into A */ ++ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), ++ /* put it into X (== index register) */ ++ BPF_STMT(BPF_MISC| BPF_TAX, 0), ++ /* load upper byte into A */ ++ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3), ++ /* left-shift it by 8 */ ++ BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8), ++ /* or with X */ ++ BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0), ++ /* put result into X */ ++ BPF_STMT(BPF_MISC| BPF_TAX, 0), ++ ++ /* ++ * Allow management frames through, this also gives us those ++ * management frames that we sent ourselves with status ++ */ ++ /* load the lower byte of the IEEE 802.11 frame control field */ ++ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), ++ /* mask off frame type and version */ ++ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF), ++ /* accept frame if it's both 0, fall through otherwise */ ++ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0), ++ ++ /* ++ * TODO: add a bit to radiotap RX flags that indicates ++ * that the sending station is not associated, then ++ * add a filter here that filters on our DA and that flag ++ * to allow us to deauth frames to that bad station. ++ * ++ * Not a regression -- we didn't do it before either. ++ */ ++ ++#if 0 ++ /* ++ * drop non-data frames, WDS frames ++ */ ++ /* load the lower byte of the frame control field */ ++ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), ++ /* mask off QoS bit */ ++ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c), ++ /* drop non-data frames */ ++ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), ++ /* load the upper byte of the frame control field */ ++ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), ++ /* mask off toDS/fromDS */ ++ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), ++ /* drop WDS frames */ ++ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, FAIL, 0), ++#endif ++ ++ /* ++ * add header length to index ++ */ ++ /* load the lower byte of the frame control field */ ++ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), ++ /* mask off QoS bit */ ++ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x80), ++ /* right shift it by 6 to give 0 or 2 */ ++ BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 6), ++ /* add data frame header length */ ++ BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 24), ++ /* add index, was start of 802.11 header */ ++ BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), ++ /* move to index, now start of LL header */ ++ BPF_STMT(BPF_MISC | BPF_TAX, 0), ++ ++ /* ++ * Accept empty data frames, we use those for ++ * polling activity. ++ */ ++ BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), ++ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0), ++ ++ /* ++ * Accept EAPOL frames ++ */ ++ BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0), ++ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL), ++ BPF_STMT(BPF_LD | BPF_W | BPF_IND, 4), ++ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL), ++ ++ /* keep these last two statements or change the code below */ ++ /* return 0 == "DROP" */ ++ BPF_STMT(BPF_RET | BPF_K, 0), ++ /* return ~0 == "keep all" */ ++ BPF_STMT(BPF_RET | BPF_K, ~0), ++}; ++ ++static struct sock_fprog msock_filter = { ++ .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]), ++ .filter = msock_filter_insns, ++}; ++ ++ ++static int add_monitor_filter(int s) ++{ ++ int idx; ++ ++ /* rewrite all PASS/FAIL jump offsets */ ++ for (idx = 0; idx < msock_filter.len; idx++) { ++ struct sock_filter *insn = &msock_filter_insns[idx]; ++ ++ if (BPF_CLASS(insn->code) == BPF_JMP) { ++ if (insn->code == (BPF_JMP|BPF_JA)) { ++ if (insn->k == PASS) ++ insn->k = msock_filter.len - idx - 2; ++ else if (insn->k == FAIL) ++ insn->k = msock_filter.len - idx - 3; ++ } ++ ++ if (insn->jt == PASS) ++ insn->jt = msock_filter.len - idx - 2; ++ else if (insn->jt == FAIL) ++ insn->jt = msock_filter.len - idx - 3; ++ ++ if (insn->jf == PASS) ++ insn->jf = msock_filter.len - idx - 2; ++ else if (insn->jf == FAIL) ++ insn->jf = msock_filter.len - idx - 3; ++ } ++ } ++ ++ if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, ++ &msock_filter, sizeof(msock_filter))) { ++ perror("SO_ATTACH_FILTER"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ + static int nl80211_create_monitor_interface(struct i802_driver_data *drv) + { + char buf[IFNAMSIZ]; +@@ -1936,6 +2084,12 @@ static int nl80211_create_monitor_interf + goto error; + } + ++ if (add_monitor_filter(drv->monitor_sock)) { ++ wpa_printf(MSG_INFO, "Failed to set socket filter for monitor " ++ "interface; do filtering in user space"); ++ /* This works, but will cost in performance. */ ++ } ++ + if (bind(drv->monitor_sock, (struct sockaddr *) &ll, + sizeof(ll)) < 0) { + perror("monitor socket bind"); |