diff --git a/src/tx.cpp b/src/tx.cpp index 7c54ff3..9d0aa61 100644 --- a/src/tx.cpp +++ b/src/tx.cpp @@ -43,10 +43,11 @@ extern "C" #include "fec.h" } +using namespace std; + #include "wifibroadcast.hpp" #include "tx.hpp" -using namespace std; Transmitter::Transmitter(int k, int n, const string &keypair, uint64_t epoch, uint32_t channel_id) : \ fec_k(k), fec_n(n), block_idx(0), @@ -124,11 +125,15 @@ void Transmitter::make_session_key(void) } } -RawSocketTransmitter::RawSocketTransmitter(int k, int n, const string &keypair, uint64_t epoch, uint32_t channel_id, const vector &wlans) : \ +RawSocketTransmitter::RawSocketTransmitter(int k, int n, const string &keypair, uint64_t epoch, uint32_t channel_id, const vector &wlans, + shared_ptr radiotap_header, size_t radiotap_header_len, uint8_t frame_type) : \ Transmitter(k, n, keypair, epoch, channel_id), channel_id(channel_id), current_output(0), - ieee80211_seq(0) + ieee80211_seq(0), + radiotap_header(radiotap_header), + radiotap_header_len(radiotap_header_len), + frame_type(frame_type) { for(auto it=wlans.begin(); it!=wlans.end(); it++) { @@ -174,10 +179,14 @@ RawSocketTransmitter::RawSocketTransmitter(int k, int n, const string &keypair, void RawSocketTransmitter::inject_packet(const uint8_t *buf, size_t size) { assert(size <= MAX_FORWARDER_PACKET_SIZE); - uint8_t ieee_hdr[sizeof(ieee80211_header)]; + + // fill default values memcpy(ieee_hdr, ieee80211_header, sizeof(ieee80211_header)); + // frame_type + ieee_hdr[0] = frame_type; + // channel_id uint32_t channel_id_be = htobe32(channel_id); memcpy(ieee_hdr + SRC_MAC_THIRD_BYTE, &channel_id_be, sizeof(uint32_t)); @@ -191,8 +200,8 @@ void RawSocketTransmitter::inject_packet(const uint8_t *buf, size_t size) struct iovec iov[3] = \ { // radiotap header - { .iov_base = (void*)radiotap_header, - .iov_len = sizeof(radiotap_header) + { .iov_base = (void*)radiotap_header.get(), + .iov_len = radiotap_header_len }, // ieee80211 header { .iov_base = (void*)ieee_hdr, @@ -553,13 +562,18 @@ int main(int argc, char * const *argv) int stbc = 0; int ldpc = 0; int mcs_index = 1; + int vht_nss = 1; int debug_port = 0; int fec_timeout = 0; int rcv_buf = 0; bool mirror = false; + bool vht_mode = false; string keypair = "tx.key"; + shared_ptr radiotap_header = NULL; + size_t radiotap_header_len = 0; + uint8_t frame_type = FRAME_TYPE_DATA; - while ((opt = getopt(argc, argv, "K:k:n:u:p:l:B:G:S:L:M:D:T:i:e:R:f:m")) != -1) { + while ((opt = getopt(argc, argv, "K:k:n:u:p:l:B:G:S:L:M:N:D:T:i:e:R:f:m")) != -1) { switch (opt) { case 'K': keypair = optarg; @@ -594,6 +608,9 @@ int main(int argc, char * const *argv) case 'M': mcs_index = atoi(optarg); break; + case 'N': + vht_nss = atoi(optarg); + break; case 'D': debug_port = atoi(optarg); break; @@ -616,12 +633,12 @@ int main(int argc, char * const *argv) if (strcmp(optarg, "data") == 0) { fprintf(stderr, "Using data frames\n"); - ieee80211_header[0] = FRAME_TYPE_DATA; + frame_type = FRAME_TYPE_DATA; } else if (strcmp(optarg, "rts") == 0) { fprintf(stderr, "Using rts frames\n"); - ieee80211_header[0] = FRAME_TYPE_RTS; + frame_type = FRAME_TYPE_RTS; } else { @@ -631,10 +648,10 @@ int main(int argc, char * const *argv) break; default: /* '?' */ show_usage: - fprintf(stderr, "Usage: %s [-K tx_key] [-k RS_K] [-n RS_N] [-u udp_port] [-R rcv_buf] [-p radio_port] [-B bandwidth] [-G guard_interval] [-S stbc] [-L ldpc] [-M mcs_index] [-T fec_timeout] [-l log_interval] [-e epoch] [-i link_id] [-f { data | rts }] [ -m ] interface1 [interface2] ...\n", + fprintf(stderr, "Usage: %s [-K tx_key] [-k RS_K] [-n RS_N] [-u udp_port] [-R rcv_buf] [-p radio_port] [-B bandwidth] [-G guard_interval] [-S stbc] [-L ldpc] [-M mcs_index] [-N VHT_NSS] [-T fec_timeout] [-l log_interval] [-e epoch] [-i link_id] [-f { data | rts }] [ -m ] interface1 [interface2] ...\n", argv[0]); - fprintf(stderr, "Default: K='%s', k=%d, n=%d, udp_port=%d, link_id=0x%06x, radio_port=%u, epoch=%" PRIu64 ", bandwidth=%d guard_interval=%s stbc=%d ldpc=%d mcs_index=%d, fec_timeout=%d, log_interval=%d, rcv_buf=system_default, frame_type=data, mirror=false\n", - keypair.c_str(), k, n, udp_port, link_id, radio_port, epoch, bandwidth, short_gi ? "short" : "long", stbc, ldpc, mcs_index, fec_timeout, log_interval); + fprintf(stderr, "Default: K='%s', k=%d, n=%d, udp_port=%d, link_id=0x%06x, radio_port=%u, epoch=%" PRIu64 ", bandwidth=%d guard_interval=%s stbc=%d ldpc=%d mcs_index=%d vht_nss=%d, fec_timeout=%d, log_interval=%d, rcv_buf=system_default, frame_type=data, mirror=false\n", + keypair.c_str(), k, n, udp_port, link_id, radio_port, epoch, bandwidth, short_gi ? "short" : "long", stbc, ldpc, mcs_index, vht_nss, fec_timeout, log_interval); fprintf(stderr, "Radio MTU: %lu\n", (unsigned long)MAX_PAYLOAD_SIZE); fprintf(stderr, "WFB-ng version " WFB_VERSION "\n"); fprintf(stderr, "WFB-ng home page: \n"); @@ -646,9 +663,16 @@ int main(int argc, char * const *argv) goto show_usage; } - // Set flags in radiotap header + // Only use VHT when BW set to 80 or higher + if (bandwidth >= 80) { + vht_mode = true; + } + + if (!vht_mode) { + // Set flags in HT radiotap header uint8_t flags = 0; + switch(bandwidth) { case 20: flags |= IEEE80211_RADIOTAP_MCS_BW_20; @@ -688,9 +712,52 @@ int main(int argc, char * const *argv) flags |= IEEE80211_RADIOTAP_MCS_FEC_LDPC; } + radiotap_header_len = sizeof(radiotap_header_ht); + radiotap_header = shared_ptr(new uint8_t[radiotap_header_len]); + memcpy(radiotap_header.get(), radiotap_header_ht, radiotap_header_len); + radiotap_header[MCS_FLAGS_OFF] = flags; radiotap_header[MCS_IDX_OFF] = mcs_index; } + else + { + // Set flags in VHT radiotap header + uint8_t flags = 0; + radiotap_header_len = sizeof(radiotap_header_vht); + radiotap_header = shared_ptr(new uint8_t[radiotap_header_len]); + memcpy(radiotap_header.get(), radiotap_header_vht, radiotap_header_len); + + if (short_gi) + { + flags |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; + } + + if (stbc) + { + flags |= IEEE80211_RADIOTAP_VHT_FLAG_STBC; + } + + switch(bandwidth) { + case 80: + radiotap_header[VHT_BW_OFF] = IEEE80211_RADIOTAP_VHT_BW_80M; + break; + case 160: + radiotap_header[VHT_BW_OFF] = IEEE80211_RADIOTAP_VHT_BW_160M; + break; + default: + fprintf(stderr, "Unsupported bandwidth: %d\n", bandwidth); + exit(1); + } + + if (ldpc) + { + radiotap_header[VHT_CODING_OFF] = IEEE80211_RADIOTAP_VHT_CODING_LDPC_USER0; + } + + radiotap_header[VHT_FLAGS_OFF] = flags; + radiotap_header[VHT_MCSNSS0_OFF] |= ((mcs_index<(new UdpTransmitter(k, n, keypair, "127.0.0.1", debug_port, epoch, channel_id)); } else { - t = shared_ptr(new RawSocketTransmitter(k, n, keypair, epoch, channel_id, wlans)); + t = shared_ptr(new RawSocketTransmitter(k, n, keypair, epoch, channel_id, wlans, + radiotap_header, radiotap_header_len, frame_type)); } data_source(t, rx_fd, fec_timeout, mirror, log_interval); diff --git a/src/tx.hpp b/src/tx.hpp index 9ed9baa..1c4401d 100644 --- a/src/tx.hpp +++ b/src/tx.hpp @@ -96,7 +96,7 @@ typedef std::unordered_map tx_antenna_stat_t; class RawSocketTransmitter : public Transmitter { public: - RawSocketTransmitter(int k, int m, const std::string &keypair, uint64_t epoch, uint32_t channel_id, const std::vector &wlans); + RawSocketTransmitter(int k, int m, const std::string &keypair, uint64_t epoch, uint32_t channel_id, const std::vector &wlans, shared_ptr radiotap_header, size_t radiotap_header_len, uint8_t frame_type); virtual ~RawSocketTransmitter(); virtual void select_output(int idx) { current_output = idx; } virtual void dump_stats(FILE *fp, uint64_t ts, uint32_t &injected, uint32_t &dropped); @@ -107,6 +107,9 @@ private: uint16_t ieee80211_seq; std::vector sockfds; tx_antenna_stat_t antenna_stat; + shared_ptr radiotap_header; + size_t radiotap_header_len; + uint8_t frame_type; }; diff --git a/src/wifibroadcast.hpp b/src/wifibroadcast.hpp index 63a3323..235f05d 100644 --- a/src/wifibroadcast.hpp +++ b/src/wifibroadcast.hpp @@ -62,9 +62,20 @@ extern std::string string_format(const char *format, ...); #define IEEE80211_RADIOTAP_MCS_STBC_3 3 #define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5 +#define IEEE80211_RADIOTAP_VHT_FLAG_STBC 0x01 +#define IEEE80211_RADIOTAP_VHT_FLAG_SGI 0x04 +#define IEEE80211_RADIOTAP_VHT_MCS_MASK 0xF0 +#define IEEE80211_RADIOTAP_VHT_NSS_MASK 0x0F +#define IEEE80211_RADIOTAP_VHT_MCS_SHIFT 4 +#define IEEE80211_RADIOTAP_VHT_NSS_SHIFT 0 +#define IEEE80211_RADIOTAP_VHT_BW_80M 0x04 +#define IEEE80211_RADIOTAP_VHT_BW_160M 0x0B +#define IEEE80211_RADIOTAP_VHT_CODING_LDPC_USER0 0x01 + + #define MCS_KNOWN (IEEE80211_RADIOTAP_MCS_HAVE_MCS | IEEE80211_RADIOTAP_MCS_HAVE_BW | IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_STBC | IEEE80211_RADIOTAP_MCS_HAVE_FEC) -static uint8_t radiotap_header[] __attribute__((unused)) = { +static const uint8_t radiotap_header_ht[] __attribute__((unused)) = { 0x00, 0x00, // <-- radiotap version 0x0d, 0x00, // <- radiotap header length 0x00, 0x80, 0x08, 0x00, // <-- radiotap present flags: RADIOTAP_TX_FLAGS + RADIOTAP_MCS @@ -72,6 +83,19 @@ static uint8_t radiotap_header[] __attribute__((unused)) = { MCS_KNOWN , 0x00, 0x00 // bitmap, flags, mcs_index }; +static const uint8_t radiotap_header_vht[] __attribute__((unused)) = { + 0x00, 0x00, // <-- radiotap version + 0x14, 0x00, // <- radiotap header length + 0x00, 0x00, 0x20, 0x00, // <-- radiotap present flags: VHT Information + 0x45, 0x00, // Known VHT information: 0000 0000 0100 0101, BW, GI, STBC + 0x00, // Flags, BIT(0)=STBC, BIT(2)=GI + 0x04, // BW, 0:20M, 1:40M, 4:80, 11:160 + 0x00, 0x00, 0x00, 0x00, // MCS_NSS[0:3] + 0x00, // Coding[3:0], BCC/LDPC + 0x00, // Group ID, not used + 0x00, 0x00 // Partial AID, not used +}; + #define WIFI_MTU 1500 // WiFi interface mtu. You can increase it if your card allow larger packets, // but this can lead to interoperability issues and/or kernel crashes. // If you use non-default MTU then you need to configure proper MTU on WiFi cards manually. @@ -81,13 +105,18 @@ static uint8_t radiotap_header[] __attribute__((unused)) = { #define PACKET_INJECTION_TIMEOUT_MS 5 // Radiotap header will be discarded after injection so we can ingnore it in MTU calculations -#define MAX_PACKET_SIZE (WIFI_MTU + sizeof(radiotap_header)) #define MAX_RX_INTERFACES 8 // offset of MCS_FLAGS and MCS index #define MCS_FLAGS_OFF 11 #define MCS_IDX_OFF 12 +// offset of VHT information +#define VHT_FLAGS_OFF 10 +#define VHT_BW_OFF 11 +#define VHT_MCSNSS0_OFF 12 +#define VHT_CODING_OFF 16 + //the last four bytes used for channel_id #define SRC_MAC_THIRD_BYTE 12 #define DST_MAC_THIRD_BYTE 18 @@ -102,7 +131,7 @@ static uint8_t radiotap_header[] __attribute__((unused)) = { // First address byte 'W'(0x57) has two lower bits set that means that address is multicast and locally administred // See https://en.wikipedia.org/wiki/MAC_address for reference -static uint8_t ieee80211_header[] __attribute__((unused)) = { +static const uint8_t ieee80211_header[] __attribute__((unused)) = { 0x08, 0x01, 0x00, 0x00, // data frame, not protected, from STA to DS via an AP, duration not set 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // receiver is broadcast 0x57, 0x42, 0xaa, 0xbb, 0xcc, 0xdd, // last four bytes will be replaced by channel_id @@ -186,9 +215,9 @@ typedef struct { uint16_t packet_size; // big endian } __attribute__ ((packed)) wpacket_hdr_t; -#define MAX_PAYLOAD_SIZE (MAX_PACKET_SIZE - sizeof(radiotap_header) - sizeof(ieee80211_header) - sizeof(wblock_hdr_t) - crypto_aead_chacha20poly1305_ABYTES - sizeof(wpacket_hdr_t)) -#define MAX_FEC_PAYLOAD (MAX_PACKET_SIZE - sizeof(radiotap_header) - sizeof(ieee80211_header) - sizeof(wblock_hdr_t) - crypto_aead_chacha20poly1305_ABYTES) -#define MAX_FORWARDER_PACKET_SIZE (MAX_PACKET_SIZE - sizeof(radiotap_header) - sizeof(ieee80211_header)) +#define MAX_PAYLOAD_SIZE (WIFI_MTU - sizeof(ieee80211_header) - sizeof(wblock_hdr_t) - crypto_aead_chacha20poly1305_ABYTES - sizeof(wpacket_hdr_t)) +#define MAX_FEC_PAYLOAD (WIFI_MTU - sizeof(ieee80211_header) - sizeof(wblock_hdr_t) - crypto_aead_chacha20poly1305_ABYTES) +#define MAX_FORWARDER_PACKET_SIZE (WIFI_MTU - sizeof(ieee80211_header)) int open_udp_socket_for_rx(int port, int rcv_buf_size); uint64_t get_time_ms(void); diff --git a/wfb_ng/server.py b/wfb_ng/server.py index ee8d556..72d2bf6 100644 --- a/wfb_ng/server.py +++ b/wfb_ng/server.py @@ -420,6 +420,10 @@ def init_wlans(max_bw, wlans): ht_mode = 'HT20' elif max_bw == 40: ht_mode = 'HT40+' + elif max_bw == 80: + ht_mode = '80MHz' + elif max_bw == 160: + ht_mode = '160MHz' else: raise Exception('Unsupported bandwith %d MHz' % (max_bw,))