diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2017-04-07 17:45:02 +0200 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2017-04-07 17:45:02 +0200 |
commit | 84939234eb66fe7957eaf39956f18224e3108c25 (patch) | |
tree | 97a6d77c92ad373199cd3aecb2cbcb6488357480 | |
parent | bed4c80f31051ec47463caa3e9511374062c6536 (diff) | |
download | wifi-triangulator-84939234eb66fe7957eaf39956f18224e3108c25.tar.gz wifi-triangulator-84939234eb66fe7957eaf39956f18224e3108c25.tar.bz2 wifi-triangulator-84939234eb66fe7957eaf39956f18224e3108c25.tar.xz wifi-triangulator-84939234eb66fe7957eaf39956f18224e3108c25.zip |
o Refactoring into two parts, sender and receiver that sends protobuf messages over UDP.
-rw-r--r-- | CMakeLists.txt | 41 | ||||
-rw-r--r-- | capture.cpp | 108 | ||||
-rw-r--r-- | core.cpp (renamed from main.cpp) | 154 | ||||
-rw-r--r-- | include/wifi-triangulator/core.h | 65 | ||||
-rw-r--r-- | misc.cpp | 35 | ||||
-rw-r--r-- | receiver.cpp | 80 | ||||
m--------- | third-party/radiotap-library | 0 | ||||
-rw-r--r-- | wifi-triangulator.proto | 29 |
8 files changed, 417 insertions, 95 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ba802f4..6fa065d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,21 +1,42 @@ cmake_minimum_required(VERSION 3.6) +project(wifi-triangulator) + include(CheckIncludeFiles) include(CheckLibraryExists) -project(wifi-triangulator) +include(FindProtobuf) set(CMAKE_CXX_STANDARD 14) +###################################################### +# Check dependencies check_include_files("pcap.h" HAVE_PCAP_H) -if(NOT HAVE_PCAP_H) +if (NOT HAVE_PCAP_H) message(FATAL_ERROR "Missing pcap.h. You should probably install libpcap-dev or similar.") -endif() +endif () + +find_package(Protobuf REQUIRED) +include_directories(${Protobuf_INCLUDE_DIRS} ${Protobuf_INCLUDE_DIR}) + +# lib wifi-triangulator +protobuf_generate_cpp(PROTO_SRC PROTO_HEADER wifi-triangulator.proto) + +add_library(wifi-triangulator + core.cpp misc.cpp + ${PROTO_SRC} + ${PROTO_HEADER} + include/wifi-triangulator/core.h + third-party/radiotap-library/radiotap.c + third-party/radiotap-library/radiotap.h + third-party/radiotap-library/radiotap_iter.h + third-party/radiotap-library/platform.h) +target_include_directories(wifi-triangulator PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(wifi-triangulator PUBLIC pcap ${Protobuf_LIBRARIES}) -set(SOURCE_FILES main.cpp - third-party/radiotap-library/radiotap.c - third-party/radiotap-library/radiotap.h - third-party/radiotap-library/radiotap_iter.h - third-party/radiotap-library/platform.h) -add_executable(wifi-triangulator ${SOURCE_FILES}) +# capture +add_executable(capture capture.cpp) +target_link_libraries(capture PUBLIC wifi-triangulator) -target_link_libraries(wifi-triangulator PUBLIC pcap) +# receiver +add_executable(receiver receiver.cpp) +target_link_libraries(receiver PUBLIC wifi-triangulator) diff --git a/capture.cpp b/capture.cpp new file mode 100644 index 0000000..7e51fcd --- /dev/null +++ b/capture.cpp @@ -0,0 +1,108 @@ +#include <iostream> +#include "wifi-triangulator/core.h" +#include "wifi-triangulator.pb.h" + +#include <sstream> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <exception> +#include <cstring> + +using namespace std; +using namespace wifi_triangulator; + +class output { +public: + virtual void handle(const data &) = 0; +}; + +class human_output : public output { +public: + void handle(const data &data) { + printf("timestamp=%lu, rssi=%d, src=%s, dst=%s, type=%s\n", ((data.sec * 1000 * 1000) + data.usec) * 1000, + data.rssi, data.src.to_string().c_str(), data.dst.to_string().c_str(), to_string(data.type).c_str()); + fflush(stdout); + }; +}; + +class protobuf_output : public output { + int s; + struct sockaddr_in addr_si; + struct sockaddr *addr; + socklen_t addr_len; + +public: + protobuf_output(string host, uint16_t port) { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + throw std::runtime_error("socket"); + + + memset((char *) &addr_si, 0, sizeof(addr_si)); + addr_si.sin_family = AF_INET; + addr_si.sin_port = htons(port); + if (inet_aton(host.c_str(), &addr_si.sin_addr) == 0) { + throw std::runtime_error("inet_aton() failed"); + } + + addr = reinterpret_cast<struct sockaddr *>(&addr_si); + addr_len = sizeof(addr_si); + } + + ~protobuf_output() { + google::protobuf::ShutdownProtobufLibrary(); + } + + void handle(const data &data) override { + pb::envelope envelope; + envelope.set_type(data.type); + + if (data.type == pb::packet_type::probe_request) { + pb::probe *probe = envelope.mutable_probe(); + probe->set_src(data.src); + probe->set_dst(static_cast<uint64_t>(data.dst)); + probe->set_rssi(data.rssi); + } + + std::string str; + envelope.SerializeToString(&str); + + if (sendto(s, str.c_str(), str.length(), 0, addr, addr_len) == -1) { + throw std::runtime_error("sendto failed"); + } + cout << flush; + + static int count = 0; + cerr << "count=" << count << ", len=" << str.length() << "!\r" << flush; + count++; + } +}; + +bool human = false; + +int main(int argc, char *argv[]) { + if (argc != 3) { + fprintf(stderr, "usage: %s [interface] [host]\n", argv[0]); + return EXIT_FAILURE; + } + + string dev = argv[1]; + string host = argv[2]; + + output *output; + if (human) { + output = new human_output; + } else { + cerr << "Sending to stdout" << flush << endl; + output = new protobuf_output(host, 3333); + } + + return launch_capture(dev, [&](const data &data) { + output->handle(data); + }); +} @@ -5,34 +5,12 @@ #include <sstream> #include <iomanip> #include "third-party/radiotap-library/radiotap.h" +#include "wifi-triangulator/core.h" -struct eth_mac { - uint8_t a, b, c, d, e, f; - - const std::string to_string() { - std::stringstream s; - s << std::hex << std::setw(2) << std::setfill('0') - << (int) a << ":" - << std::setw(2) << std::setfill('0') << (int) b << ":" - << std::setw(2) << std::setfill('0') << (int) c << ":" - << std::setw(2) << std::setfill('0') << (int) d << ":" - << std::setw(2) << std::setfill('0') << (int) e << ":" - << std::setw(2) << std::setfill('0') << (int) f; - return s.str(); - } -}; - -class data { -public: - time_t sec; - suseconds_t usec; +using std::string; +using namespace wifi_triangulator::pb; - int rssi; - struct eth_mac src, dst; - - data() : sec(0), usec(0), src(), rssi(), dst() { - } -}; +namespace wifi_triangulator { struct ieee802_11_header { uint8_t frame_1; @@ -93,14 +71,15 @@ struct radio_tap_it { } }; +struct capture_context { + std::function<void( const data &)> data_consumer; +}; + void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) { // fprintf(stdout, "%lu.%lu, caplen=%d, len=%d\n", header->ts.tv_sec, header->ts.tv_usec, header->caplen, header->len); - struct ieee80211_radiotap_header *rtaphdr = (struct ieee80211_radiotap_header *) packet; - - class data data; - data.sec = header->ts.tv_sec; - data.usec = header->ts.tv_usec; + auto *ctx = reinterpret_cast<capture_context *>(args); + auto *rtaphdr = reinterpret_cast<const struct ieee80211_radiotap_header *>(packet); if (rtaphdr->it_version != 0) { return; @@ -108,7 +87,7 @@ void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *pa int present_count = 0; { - uint32_t *present_ptr = &rtaphdr->it_present; + auto *present_ptr = &rtaphdr->it_present; do { present_count++; } while ((*present_ptr++) & 1 << IEEE80211_RADIOTAP_EXT); @@ -116,8 +95,9 @@ void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *pa radio_tap_it it(packet + 4 + 4 * present_count); - uint32_t *present_ptr = &rtaphdr->it_present; + auto *present_ptr = &rtaphdr->it_present; bool is_radiotap = true; + int rssi = 0; do { uint32_t present = *present_ptr; bool next_is_radiotap = (present & 1 << IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE) != 0; @@ -164,7 +144,7 @@ void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *pa if (present & 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) { int8_t antsignal_dbm = it.read8s(); - data.rssi = antsignal_dbm; + rssi = antsignal_dbm; snprintf(buf, 100, "IEEE80211_RADIOTAP_DBM_ANTSIGNAL: dbm=%d\n", antsignal_dbm); present &= ~(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL); } @@ -255,126 +235,130 @@ void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *pa uint8_t type = ieee802_11_header->frame_1; - std::string type_str; + packet_type t; switch (type) { case 0x40: - type_str = "probe request"; + t = packet_type::probe_request; break; case 0x80: - type_str = "beacon"; + t = packet_type::beacon; break; case 0x50: - type_str = "probe response"; + t = packet_type::probe_response; break; case 0x48: - type_str = "null"; + t = packet_type::null; break; case 0xd4: - type_str = "ack"; + t = packet_type::ack; break; case 0x08: - type_str = "data"; + t = packet_type::data; break; case 0xc4: - type_str = "cts"; + t = packet_type::cts; break; case 0xb4: - type_str = "rts"; + t = packet_type::rts; break; case 0x1e: - type_str = "cf-end"; + t = packet_type::cf_end; break; case 0x1f: - type_str = "cf-end+cf-ack"; + t = packet_type::cf_end_cf_ack; break; case 0x1a: - type_str = "ps-poll"; + t = packet_type::ps_poll; break; - default: { - std::stringstream s; - s << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int) type; - type_str = s.str(); - } + default: + t = packet_type::unknown; } // printf("ieee802_11_header->frame_ctl=%02x, %s\n", type, type_str ? type_str : "???"); + eth_mac src, dst; + if (!ieee802_11_header->is_from_ds()) { if (!ieee802_11_header->is_to_ds()) { - data.dst = ieee802_11_header->addr1; - data.src = ieee802_11_header->addr2; + dst = ieee802_11_header->addr1; + src = ieee802_11_header->addr2; } else { - data.dst = ieee802_11_header->addr1; - data.src = ieee802_11_header->addr3; + dst = ieee802_11_header->addr1; + src = ieee802_11_header->addr3; } } else { if (!ieee802_11_header->is_to_ds()) { - data.dst = ieee802_11_header->addr3; - data.src = ieee802_11_header->addr2; + dst = ieee802_11_header->addr3; + src = ieee802_11_header->addr2; } else { - data.dst = ieee802_11_header->addr3; - data.src = ieee802_11_header->addr4; + dst = ieee802_11_header->addr3; + src = ieee802_11_header->addr4; } } - auto src = data.src.to_string(); - auto dst = data.dst.to_string(); - printf("timestamp=%lu, rssi=%d, src=%s, dst=%s, type=%s\n", ((data.sec * 1000 * 1000) + data.usec) * 1000, - data.rssi, src.c_str(), dst.c_str(), type_str.c_str()); - /// printf("\036{\"timestamp_us\": %lu, \"rssi\": %d, \"src\": \"%s\", \"dst\": \"%s\"}\n", data.sec * 1000 * 1000 + data.usec, data.rssi, src.c_str(), dst.c_str()); - - fflush(stdout); + ctx->data_consumer(data{ + t, + header->ts.tv_sec, header->ts.tv_usec, + rssi, src, dst}); } -int main(int argc, char *argv[]) { +int launch_capture(string dev, std::function<void(const class data &)> data_consumer) { char errbuf[1000]; - if (argc != 2) { - fprintf(stderr, "usage: %s [interface]\n", argv[0]); - return EXIT_FAILURE; - } - - char *dev = argv[1]; - pcap_t *handle; - handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf); + handle = pcap_open_live(dev.c_str(), BUFSIZ, 1, 1000, errbuf); // handle = pcap_open_offline(dev, errbuf); if (!handle) { - fprintf(stderr, "Could not open %s: %s\n", dev, errbuf); + fprintf(stderr, + "Could not open %s: %s\n", dev. + + c_str(), errbuf + + ); return EXIT_FAILURE; } int i = pcap_datalink(handle); if (i != DLT_IEEE802_11_RADIO) { - fprintf(stderr, "Device %s doesn't provide IEEE 802.11 radio headers\n", dev); - return (2); + fprintf(stderr, + "Device %s doesn't provide IEEE 802.11 radio headers\n", dev. + + c_str() + + ); + return EXIT_FAILURE; } - /* Compile and apply the filter */ +/* Compile and apply the filter */ // if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) { // fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle)); -// return(2); +// return EXIT_FAILURE; // } // if (pcap_setfilter(handle, &fp) == -1) { // fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle)); -// return(2); +// return EXIT_FAILURE; // } + capture_context ctx{data_consumer}; while (true) { - auto ret = pcap_loop(handle, 1000, got_packet, nullptr); + auto ret = pcap_loop(handle, 1000, got_packet, reinterpret_cast<u_char *>(&ctx)); if (ret == -1) { - printf("pcap failed: %s\n", pcap_geterr(handle)); + printf("pcap failed: %s\n", + pcap_geterr(handle) + ); break; } else if (ret == -2) { - // someone called break loop +// someone called break loop break; } } pcap_close(handle); - return (0); + return EXIT_SUCCESS; } + +} // namespace wifi_triangulator diff --git a/include/wifi-triangulator/core.h b/include/wifi-triangulator/core.h new file mode 100644 index 0000000..042490a --- /dev/null +++ b/include/wifi-triangulator/core.h @@ -0,0 +1,65 @@ +#pragma once + +#include <cstdint> +#include <sstream> +#include <iomanip> +#include <functional> + +#include "wifi-triangulator.pb.h" + +namespace wifi_triangulator { + +struct eth_mac { + uint8_t a, b, c, d, e, f; + + eth_mac() {} + + eth_mac(uint64_t value) : a(uint8_t(value >> 40 & 0xff)), + b(uint8_t(value >> 32 & 0xff)), + c(uint8_t(value >> 24 & 0xff)), + d(uint8_t(value >> 16 & 0xff)), + e(uint8_t(value >> 9 & 0xff)), + f(uint8_t(value & 0xff)) {} + + std::string to_string() const { + std::stringstream s; + s << std::hex << std::setw(2) << std::setfill('0') + << (int) a << ":" + << std::setw(2) << std::setfill('0') << (int) b << ":" + << std::setw(2) << std::setfill('0') << (int) c << ":" + << std::setw(2) << std::setfill('0') << (int) d << ":" + << std::setw(2) << std::setfill('0') << (int) e << ":" + << std::setw(2) << std::setfill('0') << (int) f; + return s.str(); + } + + operator uint64_t() const { + return uint64_t(a) << 40 | + uint64_t(b) << 32 | + uint64_t(c) << 24 | + uint64_t(d) << 16 | + uint64_t(e) << 8 | + uint64_t(f); + } +} __attribute__((packed)); + +class data { +public: + pb::packet_type type; + long sec; + long usec; + + int rssi; + eth_mac src, dst; + + data(pb::packet_type type, long sec, long usec, int rssi, eth_mac src, eth_mac dst) : + type(type), sec(sec), usec(usec), src(src), rssi(rssi), dst(dst) {} +}; + +int launch_capture(std::string dev, std::function<void(const class data &)>); + +} // namespace wifi_triangulator + +namespace std { +string to_string(wifi_triangulator::pb::packet_type t); +} // namespace std diff --git a/misc.cpp b/misc.cpp new file mode 100644 index 0000000..ffe174f --- /dev/null +++ b/misc.cpp @@ -0,0 +1,35 @@ +#include "wifi-triangulator/core.h" + +namespace std { +string to_string(wifi_triangulator::pb::packet_type t) { + using wifi_triangulator::pb::packet_type; + + switch (t) { + case packet_type::probe_request: + return "probe request"; + case packet_type::beacon: + return "beacon"; + case packet_type::probe_response: + return "probe response"; + case packet_type::null: + return "null"; + case packet_type::ack: + return "ack"; + case packet_type::data: + return "data"; + case packet_type::cts: + return "cts"; + case packet_type::rts: + return "rts"; + case packet_type::cf_end: + return "cf-end"; + case packet_type::cf_end_cf_ack: + return "cf-end+cf-ack"; + case packet_type::ps_poll: + return "ps-poll"; + case packet_type::unknown: + default: + return "unknown"; + } +} +} // namespace std diff --git a/receiver.cpp b/receiver.cpp new file mode 100644 index 0000000..c6c8f72 --- /dev/null +++ b/receiver.cpp @@ -0,0 +1,80 @@ +#include "wifi-triangulator/core.h" +#include "wifi-triangulator.pb.h" + +#include <iostream> +#include <unistd.h> + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <exception> +#include <cstring> + +using namespace std; +using namespace wifi_triangulator; + +void on_probe_request(const pb::probe &p) { + cerr << "PROBE REQUEST" + << ", src=" << eth_mac(p.src()).to_string() + << ", dst=" << eth_mac(p.dst()).to_string() + << ", rssi=" << p.rssi() + << endl; +} + +int main(int argc, char *argv[]) { + if (argc != 1) { + fprintf(stderr, "usage: %s\n", argv[0]); + return EXIT_FAILURE; + } + + GOOGLE_PROTOBUF_VERIFY_VERSION; + + uint16_t port = 3333; + int s; + struct sockaddr_in si_me; + if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + throw std::runtime_error("socket"); + + memset((char *) &si_me, 0, sizeof(si_me)); + si_me.sin_family = AF_INET; + si_me.sin_port = htons(port); + si_me.sin_addr.s_addr = htonl(INADDR_ANY); + if (::bind(s, reinterpret_cast<struct sockaddr *>(&si_me), sizeof(si_me)) == -1) { + throw std::runtime_error("bind"); + } + + sockaddr *me = reinterpret_cast<sockaddr *>(&si_me); + + pb::envelope envelope; + while (true) { + size_t len = 64 * 1024; + uint8_t bytes[len]; + ssize_t n_read; + + struct sockaddr_in si_other; + socklen_t addr_len; + if ((n_read = ::recvfrom(s, bytes, len, 0, reinterpret_cast<struct sockaddr *>(&si_other), &addr_len)) == -1) { + throw std::runtime_error("recvfrom"); + } + + string str; + for (socklen_t i = 0; i < n_read; i++) { + str += bytes[i]; + } + + istringstream ss{str}; + bool ok = envelope.ParseFromIstream(&ss); + if (ok) { + if (envelope.type() == pb::probe_request) { + on_probe_request(envelope.probe()); + } + } else { + cerr << "FAIL n_read=" << n_read << flush << endl; + } + } + + google::protobuf::ShutdownProtobufLibrary(); +} diff --git a/third-party/radiotap-library b/third-party/radiotap-library -Subproject 9fafde1c85e2705488b7fffdf6275b3889ec513 +Subproject b6fbcaf506080a96bfdda7b44309ad4a3d06556 diff --git a/wifi-triangulator.proto b/wifi-triangulator.proto new file mode 100644 index 0000000..7daf9bf --- /dev/null +++ b/wifi-triangulator.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package wifi_triangulator.pb; + +enum packet_type { + unknown = 0; + probe_request = 1; + beacon = 2; + probe_response = 3; + null = 4; + ack = 5; + data = 6; + cts = 7; + rts = 8; + cf_end = 9; + cf_end_cf_ack = 10; + ps_poll = 11; +}; + +message probe { + sint32 rssi = 2; + fixed64 src = 3; + fixed64 dst = 4; +} + +message envelope { + packet_type type = 1; + probe probe = 2; +} |