diff --git a/libraries/Netdump/README.md b/libraries/Netdump/README.md new file mode 100644 index 0000000000..7db9c725af --- /dev/null +++ b/libraries/Netdump/README.md @@ -0,0 +1,52 @@ + +esp8266/Arduino goodies +----------------------- + +* NetDump (lwip2) + Packet sniffer library to help study network issues, check example-sketches + Log examples on serial console: +``` +14:07:01.854 -> in 0 ARP who has 10.43.1.117 tell 10.43.1.254 +14:07:01.854 -> out 0 ARP 10.43.1.117 is at 5c:cf:7f:c3:ad:51 + +[...] hello-world, dumped in packets: +14:07:46.227 -> in 0 IPv4 10.43.1.254>10.43.1.117 TCP 54546>2[P.] seq:1945448681..1945448699 ack:6618 win:29200 len=18 +14:07:46.260 -> 5c cf 7f c3 ad 51 74 da 38 3a 1f 61 08 00 45 10 \..Qt.8:.a..E. +14:07:46.260 -> 00 3a b2 bc 40 00 40 06 70 29 0a 2b 01 fe 0a 2b .:..@.@.p).+...+ +14:07:46.260 -> 01 75 d5 12 00 02 73 f5 30 e9 00 00 19 da 50 18 .u....s.0.....P. +14:07:46.260 -> 72 10 f8 da 00 00 70 6c 20 68 65 6c 6c 6f 2d 77 r.....pl hello-w +14:07:46.260 -> 6f 72 6c 64 20 31 0d 0a orld 1.. +14:07:46.294 -> out 0 IPv4 10.43.1.117>10.43.1.254 TCP 2>54546[P.] seq:6618..6619 ack:1945448699 win:2126 len=1 +14:07:46.326 -> 00 20 00 00 00 00 aa aa 03 00 00 00 08 00 45 00 . ............E. +14:07:46.326 -> 00 29 00 0d 00 00 ff 06 a3 f9 0a 2b 01 75 0a 2b .).........+.u.+ +14:07:46.327 -> 01 fe 00 02 d5 12 00 00 19 da 73 f5 30 fb 50 18 ..........s.0.P. +14:07:46.327 -> 08 4e 93 d5 00 00 68 .N....h +14:07:46.327 -> in 0 IPv4 10.43.1.254>10.43.1.117 TCP 54546>2[.] seq:1945448699 ack:6619 win:29200 +14:07:46.327 -> 5c cf 7f c3 ad 51 74 da 38 3a 1f 61 08 00 45 10 \..Qt.8:.a..E. +14:07:46.360 -> 00 28 b2 bd 40 00 40 06 70 3a 0a 2b 01 fe 0a 2b .(..@.@.p:.+...+ +14:07:46.360 -> 01 75 d5 12 00 02 73 f5 30 fb 00 00 19 db 50 10 .u....s.0.....P. +14:07:46.360 -> 72 10 92 1b 00 00 r..... +14:07:46.360 -> out 0 IPv4 10.43.1.117>10.43.1.254 TCP 2>54546[P.] seq:6619..6630 ack:1945448699 win:2126 len=11 +14:07:46.360 -> 00 20 00 00 00 00 aa aa 03 00 00 00 08 00 45 00 . ............E. +14:07:46.360 -> 00 33 00 0e 00 00 ff 06 a3 ee 0a 2b 01 75 0a 2b .3.........+.u.+ +14:07:46.393 -> 01 fe 00 02 d5 12 00 00 19 db 73 f5 30 fb 50 18 ..........s.0.P. +14:07:46.393 -> 08 4e 16 a1 00 00 65 6c 6c 6f 2d 77 6f 72 6c 64 .N....ello-world +14:07:46.393 -> 0a . + +[...] help protocol decoding from inside the esp +14:08:11.715 -> in 0 IPv4 10.43.1.254>239.255.255.250 UDP 50315>1900 len=172 +14:08:11.716 -> 01 00 5e 7f ff fa 74 da 38 3a 1f 61 08 00 45 00 ....t.8:.a..E. +14:08:11.716 -> 00 c8 9b 40 40 00 01 11 e1 c1 0a 2b 01 fe ef ff ...@@......+.... +14:08:11.749 -> ff fa c4 8b 07 6c 00 b4 9c 28 4d 2d 53 45 41 52 .....l...(M-SEAR +14:08:11.749 -> 43 48 20 2a 20 48 54 54 50 2f 31 2e 31 0d 0a 48 CH * HTTP/1.1..H +14:08:11.749 -> 4f 53 54 3a 20 32 33 39 2e 32 35 35 2e 32 35 35 OST: 239.255.255 +14:08:11.749 -> 2e 32 35 30 3a 31 39 30 30 0d 0a 4d 41 4e 3a 20 .250:1900..MAN: +14:08:11.749 -> 22 73 73 64 70 3a 64 69 73 63 6f 76 65 72 22 0d "ssdp:discover". +14:08:11.749 -> 0a 4d 58 3a 20 31 0d 0a 53 54 3a 20 75 72 6e 3a .MX: 1..ST: urn: +14:08:11.782 -> 64 69 61 6c 2d 6d 75 6c 74 69 73 63 72 65 65 6e dial-multiscreen +14:08:11.782 -> 2d 6f 72 67 3a 73 65 72 76 69 63 65 3a 64 69 61 -org:service:dia +14:08:11.782 -> 6c 3a 31 0d 0a 55 53 45 52 2d 41 47 45 4e 54 3a l:1..USER-AGENT: +14:08:11.782 -> 20 47 6f 6f 67 6c 65 20 43 68 72 6f 6d 65 2f 36 Google Chrome/6 +14:08:11.782 -> 36 2e 30 2e 33 33 35 39 2e 31 31 37 20 4c 69 6e 6.0.3359.117 Lin +14:08:11.782 -> 75 78 0d 0a 0d 0a ux.... + diff --git a/libraries/Netdump/examples/Netdump/Netdump.ino b/libraries/Netdump/examples/Netdump/Netdump.ino new file mode 100644 index 0000000000..e1476a4028 --- /dev/null +++ b/libraries/Netdump/examples/Netdump/Netdump.ino @@ -0,0 +1,155 @@ +#include "Arduino.h" + +#include "Netdump.h" +#include +#include +#include +#include +#include + +using namespace NetCapture; + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif + +const char* ssid = STASSID; +const char* password = STAPSK; + +Netdump nd; + +//FS* filesystem = &SPIFFS; +FS* filesystem = &LittleFS; + +ESP8266WebServer webServer(80); // Used for sending commands +WiFiServer tcpServer(8000); // Used to show netcat option. +File tracefile; + +std::map packetCount; + +enum SerialOption { + AllFull, + LocalNone, + HTTPChar +}; + +void startSerial(int option) { + switch (option) { + case AllFull : //All Packets, show packet summary. + nd.printDump(Serial, Packet::PacketDetail::FULL); + break; + + case LocalNone : // Only local IP traffic, full details + nd.printDump(Serial, Packet::PacketDetail::NONE, + [](Packet n) { + return (n.hasIP(WiFi.localIP())); + } + ); + break; + case HTTPChar : // Only HTTP traffic, show packet content as chars + nd.printDump(Serial, Packet::PacketDetail::CHAR, + [](Packet n) { + return (n.isHTTP()); + } + ); + break; + default : + Serial.printf("No valid SerialOption provided\r\n"); + }; +} + +void startTracefile() { + // To file all traffic, format pcap file + tracefile = filesystem->open("/tr.pcap", "w"); + nd.fileDump(tracefile); +} + +void startTcpDump() { + // To tcpserver, all traffic. + tcpServer.begin(); + nd.tcpDump(tcpServer); +} + +void setup(void) { + Serial.begin(115200); + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("WiFi Failed"); + while (1) { + delay(1000); + } + } + + if (!MDNS.begin("netdumphost")) { + Serial.println("Error setting up MDNS responder!"); + } + + filesystem->begin(); + + webServer.on("/list", + []() { + Dir dir = filesystem->openDir("/"); + String d = "

File list

"; + while (dir.next()) { + d.concat("
  • " + dir.fileName() + "
  • "); + } + webServer.send(200, "text.html", d); + } + ); + + webServer.on("/req", + []() { + static int rq = 0; + String a = "

    You are connected, Number of requests = " + String(rq++) + "

    "; + webServer.send(200, "text/html", a); + } + ); + + webServer.on("/reset", + []() { + nd.reset(); + tracefile.close(); + tcpServer.close(); + webServer.send(200, "text.html", "

    Netdump session reset

    "); + } + ); + + webServer.serveStatic("/", *filesystem, "/"); + webServer.begin(); + + startSerial(AllFull); // Serial output examples, use enum SerialOption for selection + + // startTcpDump(); // tcpdump option + // startTracefile(); // output to SPIFFS or LittleFS + + // use a self provide callback, this count network packets + /* + nd.setCallback( + [](Packet p) + { + Serial.printf("PKT : %s : ",p.sourceIP().toString().c_str()); + for ( auto pp : p.allPacketTypes()) + { + Serial.printf("%s ",pp.toString().c_str()); + packetCount[pp]++; + } + Serial.printf("\r\n CNT "); + for (auto pc : packetCount) + { + Serial.printf("%s %d ", pc.first.toString().c_str(),pc.second); + } + Serial.printf("\r\n"); + } + ); + */ +} + +void loop(void) { + webServer.handleClient(); + MDNS.update(); +} + diff --git a/libraries/Netdump/keywords.txt b/libraries/Netdump/keywords.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/libraries/Netdump/keywords.txt @@ -0,0 +1 @@ + diff --git a/libraries/Netdump/library.properties b/libraries/Netdump/library.properties new file mode 100644 index 0000000000..2f6ad5e22e --- /dev/null +++ b/libraries/Netdump/library.properties @@ -0,0 +1,9 @@ +name=NetDump +version=2 +author=Herman Reintke +maintainer=Herman Reintke +sentence=tcpdump-like logger for esp8266/Arduino +paragraph=Dumps input / output packets on "Print"able type, or provide a TCP server for the real tcpdump. Check examples. Some other unrelated and independant tools are included. +category=Communication +url=https:// +architectures=esp8266 lwip diff --git a/libraries/Netdump/src/Netdump.cpp b/libraries/Netdump/src/Netdump.cpp new file mode 100644 index 0000000000..5614763524 --- /dev/null +++ b/libraries/Netdump/src/Netdump.cpp @@ -0,0 +1,317 @@ +/* + NetDump library - tcpdump-like packet logger facility + + Copyright (c) 2019 Herman Reintke. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "Netdump.h" +#include +#include "Schedule.h" +#include "sdk_structs.h" +#include "ieee80211_structs.h" +#include "string_utils.h" + +namespace NetCapture +{ + +CallBackList Netdump::lwipCallback; +CallBackList Netdump::wifiCallback; + +Netdump::Netdump(interface ifc) +{ + using namespace std::placeholders; + if (ifc == interface::LWIP) + { + phy_capture = lwipCapture; + lwipHandler = lwipCallback.add(std::bind(&Netdump::netdumpCapture, this, _1, _2, _3, _4, _5)); + } + else + { + wifi_set_opmode(STATION_MODE); + wifi_promiscuous_enable(0); + WiFi.disconnect(); + wifi_set_promiscuous_rx_cb(wifiCapture); + wifi_set_channel(6); + wifi_promiscuous_enable(1); + Serial.write("Prom mode\r\n"); + wifiHandler = wifiCallback.add(std::bind(&Netdump::wifidumpCapture,this, _1, _2)); + } +}; + +Netdump::~Netdump() +{ + reset(); + if (packetBuffer) + { + delete[] packetBuffer; + } +}; + +void Netdump::setCallback(const Callback nc) +{ + netDumpCallback = nc; +} + +void Netdump::setCallback(const Callback nc, const Filter nf) +{ + netDumpFilter = nf; + netDumpCallback = nc; +} + +void Netdump::setFilter(const Filter nf) +{ + netDumpFilter = nf; +} + +void Netdump::reset() +{ + setCallback(nullptr, nullptr); +} + +void Netdump::printDump(Print& out, Packet::PacketDetail ndd, const Filter nf) +{ + out.printf("netDump starting\r\n"); + setCallback([&out, ndd, this](const Packet & ndp) + { + printDumpProcess(out, ndd, ndp); + }, nf); +} + +void Netdump::fileDump(File& outfile, const Filter nf) +{ + + writePcapHeader(outfile); + setCallback([&outfile, this](const Packet & ndp) + { + fileDumpProcess(outfile, ndp); + }, nf); +} +void Netdump::tcpDump(WiFiServer &tcpDumpServer, const Filter nf) +{ + + if (!packetBuffer) + { + packetBuffer = new char[tcpBuffersize]; + } + bufferIndex = 0; + + schedule_function([&tcpDumpServer, this, nf]() + { + tcpDumpLoop(tcpDumpServer, nf); + }); +} + +void Netdump::lwipCapture(int netif_idx, const char* data, size_t len, int out, int success) +{ + if (lwipCallback.execute(netif_idx, data, len, out, success) == 0) + { + phy_capture = nullptr; // No active callback/netdump instances, will be set again by new object. + } +} + +void Netdump::netdumpCapture(int netif_idx, const char* data, size_t len, int out, int success) +{ + if (netDumpCallback) + { + Packet np(millis(), netif_idx, data, len, out, success); + if (netDumpFilter && !netDumpFilter(np)) + { + return; + } + netDumpCallback(np); + } +} + +void Netdump::wifiCapture(unsigned char* data, uint16_t len) +{ + wifiCallback.execute(reinterpret_cast(data),len); +} + +void Netdump::wifidumpCapture(const char* buff, uint16_t len) +{ +// netdumpCapture(0, data, len, 0, 1); + + // First layer: type cast the received buffer into our generic SDK structure + const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff; + // Second layer: define pointer to where the actual 802.11 packet is within the structure + const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload; + // Third layer: define pointers to the 802.11 packet header and payload + const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr; + const uint8_t *data = ipkt->payload; + + // Pointer to the frame control section within the packet header + const wifi_header_frame_control_t *frame_ctrl = (wifi_header_frame_control_t *)&hdr->frame_ctrl; + // Parse MAC addresses contained in packet header into human-readable strings + char addr1[] = "00:00:00:00:00:00\0"; + char addr2[] = "00:00:00:00:00:00\0"; + char addr3[] = "00:00:00:00:00:00\0"; + + mac2str(hdr->addr1, addr1); + mac2str(hdr->addr2, addr2); + mac2str(hdr->addr3, addr3); + + // Output info to serial +// if (!(frame_ctrl->type == WIFI_PKT_MGMT && frame_ctrl->subtype == BEACON)) + if ((frame_ctrl->type == WIFI_PKT_DATA)) + Serial.printf("\r\n%s | %s | %s | %u | %02d | %u | %u(%-2u) | %-28s | %u | %u | %u | %u | %u | %u | %u | %u | ", + addr1, + addr2, + addr3, + wifi_get_channel(), + ppkt->rx_ctrl.rssi, + frame_ctrl->protocol, + frame_ctrl->type, + frame_ctrl->subtype, + wifi_pkt_type2str((wifi_promiscuous_pkt_type_t)frame_ctrl->type, (wifi_mgmt_subtypes_t)frame_ctrl->subtype), + frame_ctrl->to_ds, + frame_ctrl->from_ds, + frame_ctrl->more_frag, + frame_ctrl->retry, + frame_ctrl->pwr_mgmt, + frame_ctrl->more_data, + frame_ctrl->wep, + frame_ctrl->strict); + if (frame_ctrl->type == WIFI_PKT_DATA) + { + Serial.printf("\r\nData %04x",frame_ctrl->subtype); + } + // Print ESSID if beacon + /* + if (frame_ctrl->type == WIFI_PKT_MGMT && frame_ctrl->subtype == BEACON) + { + const wifi_mgmt_beacon_t *beacon_frame = (wifi_mgmt_beacon_t*) ipkt->payload; + char ssid[32] = {0}; + + if (beacon_frame->tag_length >= 32) + { + strncpy(ssid, beacon_frame->ssid, 31); + } + else + { + strncpy(ssid, beacon_frame->ssid, beacon_frame->tag_length); + } + + Serial.printf("%s", ssid); + } + */ +} + +/* + uint32_t timestamp = now(); //current timestamp + uint32_t microseconds = (unsigned int)(micros() - millis() * 1000); //micro seconds offset (0 - 999) + pcap.newPacketSerial(timestamp, microseconds, len, buf); //write packet to file +*/ + +void Netdump::writePcapHeader(Stream& s) const +{ + uint32_t pcapHeader[6]; + pcapHeader[0] = 0xa1b2c3d4; // pcap magic number + pcapHeader[1] = 0x00040002; // pcap major/minor version + pcapHeader[2] = 0; // pcap UTC correction in seconds + pcapHeader[3] = 0; // pcap time stamp accuracy + pcapHeader[4] = maxPcapLength; // pcap max packet length per record + pcapHeader[5] = 1; // pacp data linkt type = ethernet + s.write(reinterpret_cast(pcapHeader), 24); +} + +void Netdump::printDumpProcess(Print& out, Packet::PacketDetail ndd, const Packet& np) const +{ + out.printf_P(PSTR("%8d %s"), np.getTime(), np.toString(ndd).c_str()); +} + +void Netdump::fileDumpProcess(File& outfile, const Packet& np) const +{ + size_t incl_len = np.getPacketSize() > maxPcapLength ? maxPcapLength : np.getPacketSize(); + uint32_t pcapHeader[4]; + + struct timeval tv; + gettimeofday(&tv, nullptr); + pcapHeader[0] = tv.tv_sec; + pcapHeader[1] = tv.tv_usec; + pcapHeader[2] = incl_len; + pcapHeader[3] = np.getPacketSize(); + outfile.write(reinterpret_cast(pcapHeader), 16); // pcap record header + + outfile.write(np.rawData(), incl_len); +} + +void Netdump::tcpDumpProcess(const Packet& np) +{ + if (np.isTCP() && np.hasPort(tcpDumpClient.localPort())) + { + // skip myself + return; + } + size_t incl_len = np.getPacketSize() > maxPcapLength ? maxPcapLength : np.getPacketSize(); + + if (bufferIndex + 16 + incl_len < tcpBuffersize) // only add if enough space available + { + struct timeval tv; + gettimeofday(&tv, nullptr); + uint32_t* pcapHeader = reinterpret_cast(&packetBuffer[bufferIndex]); + pcapHeader[0] = tv.tv_sec; // add pcap record header + pcapHeader[1] = tv.tv_usec; + pcapHeader[2] = incl_len; + pcapHeader[3] = np.getPacketSize(); + bufferIndex += 16; // pcap header size + memcpy(&packetBuffer[bufferIndex], np.rawData(), incl_len); + bufferIndex += incl_len; + } + + if (bufferIndex && tcpDumpClient && tcpDumpClient.availableForWrite() >= bufferIndex) + { + tcpDumpClient.write(packetBuffer, bufferIndex); + bufferIndex = 0; + } +} + +void Netdump::tcpDumpLoop(WiFiServer &tcpDumpServer, const Filter nf) +{ + if (tcpDumpServer.hasClient()) + { + tcpDumpClient = tcpDumpServer.available(); + tcpDumpClient.setNoDelay(true); + + bufferIndex = 0; + writePcapHeader(tcpDumpClient); + + setCallback([this](const Packet & ndp) + { + tcpDumpProcess(ndp); + }, nf); + } + if (!tcpDumpClient || !tcpDumpClient.connected()) + { + setCallback(nullptr); + } + if (bufferIndex && tcpDumpClient && tcpDumpClient.availableForWrite() >= bufferIndex) + { + tcpDumpClient.write(packetBuffer, bufferIndex); + bufferIndex = 0; + } + + if (tcpDumpServer.status() != CLOSED) + { + schedule_function([&tcpDumpServer, this, nf]() + { + tcpDumpLoop(tcpDumpServer, nf); + }); + } +} + +} // namespace NetCapture diff --git a/libraries/Netdump/src/Netdump.h b/libraries/Netdump/src/Netdump.h new file mode 100644 index 0000000000..dbce742dc0 --- /dev/null +++ b/libraries/Netdump/src/Netdump.h @@ -0,0 +1,99 @@ +/* + NetDump library - tcpdump-like packet logger facility + + Copyright (c) 2019 Herman Reintke. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __NETDUMP_H +#define __NETDUMP_H + +#include +#include +#include +#include +#include "NetdumpPacket.h" +#include +#include "CallBackList.h" + +namespace NetCapture +{ + +using namespace experimental::CBListImplentation; + +class Netdump +{ +public: + + using Filter = std::function; + using Callback = std::function; + using LwipCallback = std::function; + using WifiCallback = std::function; + + enum class interface + { + LWIP, + WIFI + }; + + Netdump(interface ifc = interface::WIFI); + ~Netdump(); + + void setCallback(const Callback nc); + void setCallback(const Callback nc, const Filter nf); + void setFilter(const Filter nf); + void reset(); + + void printDump(Print& out, Packet::PacketDetail ndd, const Filter nf = nullptr); + void fileDump(File& outfile, const Filter nf = nullptr); + void tcpDump(WiFiServer &tcpDumpServer, const Filter nf = nullptr); + + +private: + Callback netDumpCallback = nullptr; + Filter netDumpFilter = nullptr; + + static void lwipCapture(int netif_idx, const char* data, size_t len, int out, int success); + static void wifiCapture(unsigned char* data, uint16_t len); + static CallBackList lwipCallback; + CallBackList::CallBackHandler lwipHandler; + static CallBackList wifiCallback; + CallBackList::CallBackHandler wifiHandler; + + + void netdumpCapture(int netif_idx, const char* data, size_t len, int out, int success); + void wifidumpCapture(const char* data, uint16_t len); + + void printDumpProcess(Print& out, Packet::PacketDetail ndd, const Packet& np) const; + void fileDumpProcess(File& outfile, const Packet& np) const; + void tcpDumpProcess(const Packet& np); + void tcpDumpLoop(WiFiServer &tcpDumpServer, const Filter nf); + + void writePcapHeader(Stream& s) const; + + WiFiClient tcpDumpClient; + char* packetBuffer = nullptr; + size_t bufferIndex = 0; + + static constexpr int tcpBuffersize = 2048; + static constexpr int maxPcapLength = 1024; + static constexpr uint32_t pcapMagic = 0xa1b2c3d4; +}; + +} // namespace NetCapture + +#endif /* __NETDUMP_H */ diff --git a/libraries/Netdump/src/NetdumpIP.cpp b/libraries/Netdump/src/NetdumpIP.cpp new file mode 100644 index 0000000000..2a1c9212af --- /dev/null +++ b/libraries/Netdump/src/NetdumpIP.cpp @@ -0,0 +1,379 @@ +/* + NetDump library - tcpdump-like packet logger facility + + Copyright (c) 2019 Herman Reintke. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + +*/ +#include +#include + +namespace NetCapture +{ + +NetdumpIP::NetdumpIP() +{ +} + +NetdumpIP::~NetdumpIP() +{ +} + +NetdumpIP::NetdumpIP(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) +{ + setV4(); + (*this)[0] = first_octet; + (*this)[1] = second_octet; + (*this)[2] = third_octet; + (*this)[3] = fourth_octet; +} + +NetdumpIP::NetdumpIP(const uint8_t *address, bool v4) +{ + uint8_t cnt; + if (v4) + { + cnt = 4; + setV4(); + } + else + { + cnt = 16; + setV6(); + } + for (int i = 0; i < cnt; i++) + { + (*this)[i] = address[i]; + } +} + +NetdumpIP::NetdumpIP(const IPAddress& ip) +{ + if (!ip.isSet()) + { + setUnset(); + } + else if (ip.isV4()) + { + setV4(); + for (int i = 0; i < 4; i++) + { + rawip[i] = ip[i]; + } + } + else + { + setV6(); + for (int i = 0; i < 16; i++) + { + rawip[i] = ip[i]; + } + } +} + +NetdumpIP::NetdumpIP(const String& ip) +{ + if (!fromString(ip.c_str())) + { + setUnset(); + } +} + +bool NetdumpIP::fromString(const char *address) +{ + if (!fromString4(address)) + { + return fromString6(address); + } + return true; +} + +bool NetdumpIP::fromString4(const char *address) +{ + // TODO: (IPv4) add support for "a", "a.b", "a.b.c" formats + + uint16_t acc = 0; // Accumulator + uint8_t dots = 0; + + while (*address) + { + char c = *address++; + if (c >= '0' && c <= '9') + { + acc = acc * 10 + (c - '0'); + if (acc > 255) + { + // Value out of [0..255] range + return false; + } + } + else if (c == '.') + { + if (dots == 3) + { + // Too much dots (there must be 3 dots) + return false; + } + (*this)[dots++] = acc; + acc = 0; + } + else + { + // Invalid char + return false; + } + } + + if (dots != 3) + { + // Too few dots (there must be 3 dots) + return false; + } + (*this)[3] = acc; + + setV4(); + return true; +} + +bool NetdumpIP::fromString6(const char *address) +{ + // TODO: test test test + + uint32_t acc = 0; // Accumulator + int dots = 0, doubledots = -1; + + while (*address) + { + char c = tolower(*address++); + if (isalnum(c)) + { + if (c >= 'a') + { + c -= 'a' - '0' - 10; + } + acc = acc * 16 + (c - '0'); + if (acc > 0xffff) + // Value out of range + { + return false; + } + } + else if (c == ':') + { + if (*address == ':') + { + if (doubledots >= 0) + // :: allowed once + { + return false; + } + // remember location + doubledots = dots + !!acc; + address++; + } + if (dots == 7) + // too many separators + { + return false; + } + reinterpret_cast(rawip)[dots++] = PP_HTONS(acc); + acc = 0; + } + else + // Invalid char + { + return false; + } + } + + if (doubledots == -1 && dots != 7) + // Too few separators + { + return false; + } + reinterpret_cast(rawip)[dots++] = PP_HTONS(acc); + + if (doubledots != -1) + { + for (int i = dots - doubledots - 1; i >= 0; i--) + { + reinterpret_cast(rawip)[8 - dots + doubledots + i] = reinterpret_cast(rawip)[doubledots + i]; + } + for (int i = doubledots; i < 8 - dots + doubledots; i++) + { + reinterpret_cast(rawip)[i] = 0; + } + } + + setV6(); + return true; +} + +String NetdumpIP::toString() +{ + StreamString sstr; + if (isV6()) + { + sstr.reserve(40); // 8 shorts x 4 chars each + 7 colons + nullterm + + } + else + { + sstr.reserve(16); // 4 bytes with 3 chars max + 3 dots + nullterm, or '(IP unset)' + } + printTo(sstr); + return sstr; +} + +size_t NetdumpIP::printTo(Print& p) +{ + size_t n = 0; + + if (!isSet()) + { + return p.print(F("(IP unset)")); + } + + if (isV6()) + { + int count0 = 0; + for (int i = 0; i < 8; i++) + { + uint16_t bit = PP_NTOHS(reinterpret_cast(rawip)[i]); + if (bit || count0 < 0) + { + n += p.printf("%x", bit); + if (count0 > 0) + // no more hiding 0 + { + count0 = -8; + } + } + else + { + count0++; + } + if ((i != 7 && count0 < 2) || count0 == 7) + { + n += p.print(':'); + } + } + return n; + } + for (int i = 0; i < 4; i++) + { + n += p.print((*this)[i], DEC); + if (i != 3) + { + n += p.print('.'); + } + } + return n; +} + +bool NetdumpIP::compareRaw(IPversion v, const uint8_t* a, const uint8_t* b) const +{ + for (int i = 0; i < (v == IPversion::IPV4 ? 4 : 16); i++) + { + if (a[i] != b[i]) + { + return false; + } + } + return true; +} + +bool NetdumpIP::compareIP(const IPAddress& ip) const +{ + switch (ipv) + { + case IPversion::UNSET : + if (ip.isSet()) + { + return false; + } + else + { + return true; + } + break; + case IPversion::IPV4 : + if (ip.isV6() || !ip.isSet()) + { + return false; + } + else + { + return compareRaw(IPversion::IPV4, rawip, reinterpret_cast(&ip.v4())); + } + break; + case IPversion::IPV6 : + if (ip.isV4() || !ip.isSet()) + { + return false; + } + else + { + return compareRaw(IPversion::IPV6, rawip, reinterpret_cast(ip.raw6())); + } + break; + default : + return false; + break; + } +} + +bool NetdumpIP::compareIP(const NetdumpIP& nip) const +{ + switch (ipv) + { + case IPversion::UNSET : + if (nip.isSet()) + { + return false; + } + else + { + return true; + } + break; + case IPversion::IPV4 : + if (nip.isV6() || !nip.isSet()) + { + return false; + } + else + { + return compareRaw(IPversion::IPV4, rawip, nip.rawip); + } + break; + case IPversion::IPV6 : + if (nip.isV4() || !nip.isSet()) + { + return false; + } + else + { + return compareRaw(IPversion::IPV6, rawip, nip.rawip); + } + break; + default : + return false; + break; + } +} + +} // namespace NetCapture diff --git a/libraries/Netdump/src/NetdumpIP.h b/libraries/Netdump/src/NetdumpIP.h new file mode 100644 index 0000000000..df28b1c5ed --- /dev/null +++ b/libraries/Netdump/src/NetdumpIP.h @@ -0,0 +1,106 @@ +/* + NetdumpIP.h + + Created on: 18 mei 2019 + Author: Herman +*/ + +#ifndef LIBRARIES_ESPGOODIES_HR_SRC_NETDUMP_NETDUMPIP_H_ +#define LIBRARIES_ESPGOODIES_HR_SRC_NETDUMP_NETDUMPIP_H_ + +#include +#include +#include +#include + +namespace NetCapture +{ + +class NetdumpIP +{ +public: + NetdumpIP(); + ~NetdumpIP(); + + NetdumpIP(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); + NetdumpIP(const uint8_t *address, bool V4 = true); + NetdumpIP(const IPAddress& ip); + NetdumpIP(const String& ip); + + uint8_t& operator[](int index) + { + return rawip[index]; + } + + bool fromString(const char *address); + + String toString(); + +private: + enum class IPversion {UNSET, IPV4, IPV6}; + IPversion ipv = IPversion::UNSET; + + uint8_t rawip[16] = {0}; + + void setV4() + { + ipv = IPversion::IPV4; + }; + void setV6() + { + ipv = IPversion::IPV6; + }; + void setUnset() + { + ipv = IPversion::UNSET; + }; + + bool compareRaw(IPversion v, const uint8_t* a, const uint8_t* b) const; + bool compareIP(const IPAddress& ip) const; + bool compareIP(const NetdumpIP& nip) const; + + bool fromString4(const char *address); + bool fromString6(const char *address); + + size_t printTo(Print& p); +public: + + bool isV4() const + { + return (ipv == IPversion::IPV4); + }; + bool isV6() const + { + return (ipv == IPversion::IPV6); + }; + bool isUnset() const + { + return (ipv == IPversion::UNSET); + }; + bool isSet() const + { + return (ipv != IPversion::UNSET); + }; + + bool operator==(const IPAddress& addr) const + { + return compareIP(addr); + }; + bool operator!=(const IPAddress& addr) + { + return compareIP(addr); + }; + bool operator==(const NetdumpIP& addr) + { + return compareIP(addr); + }; + bool operator!=(const NetdumpIP& addr) + { + return !compareIP(addr); + }; + +}; + +} // namespace NetCapture + +#endif /* LIBRARIES_ESPGOODIES_HR_SRC_NETDUMP_NETDUMPIP_H_ */ diff --git a/libraries/Netdump/src/NetdumpPacket.cpp b/libraries/Netdump/src/NetdumpPacket.cpp new file mode 100644 index 0000000000..caba0d4364 --- /dev/null +++ b/libraries/Netdump/src/NetdumpPacket.cpp @@ -0,0 +1,433 @@ +/* + NetDump library - tcpdump-like packet logger facility + + Copyright (c) 2018 David Gauchard. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "Netdump.h" +#include + +namespace NetCapture +{ + +void Packet::printDetail(Print& out, const String& indent, const uint8_t* data, size_t size, PacketDetail pd) const +{ + if (pd == PacketDetail::NONE) + { + return; + } + + uint16_t charCount = (pd == PacketDetail::CHAR) ? 80 : 24; + + size_t start = 0; + while (start < size) + { + size_t end = start + charCount; + if (end > size) + { + end = size; + } + out.printf("%s", indent.c_str()); + if (pd != PacketDetail::CHAR) + { + for (size_t i = start; i < end; i++) + { + out.printf("%02x ", (unsigned char)data[i]); + } + for (size_t i = end; i < start + charCount; i++) + { + out.print(" "); + } + } + for (size_t i = start; i < end; i++) + { + out.printf("%c", data[i] >= 32 && data[i] < 128 ? data[i] : '.'); + } + out.println(); + + start += charCount; + } +} + +void Packet::setPacketType(PacketType pt) +{ + thisPacketType = pt; + thisAllPacketTypes.emplace_back(pt); +} + +void Packet::setPacketTypes() +{ + if (isARP()) + { + setPacketType(PacketType::ARP); + } + else if (isIP()) + { + setPacketType(PacketType::IP); + setPacketType(isIPv4() ? PacketType::IPv4 : PacketType::IPv6); + if (isUDP()) + { + setPacketType(PacketType::UDP); + if (isMDNS()) + { + setPacketType(PacketType::MDNS); + } + if (isDNS()) + { + setPacketType(PacketType::DNS); + } + if (isLLMNR()) + { + setPacketType(PacketType::LLMNR); + } + if (isSSDP()) + { + setPacketType(PacketType::SSDP); + } + if (isDHCP()) + { + setPacketType(PacketType::DHCP); + } + if (isWSDD()) + { + setPacketType(PacketType::WSDD); + } + if (isNETBIOS()) + { + setPacketType(PacketType::NETBIOS); + } + if (isSMB()) + { + setPacketType(PacketType::SMB); + } + if (isOTA()) + { + setPacketType(PacketType::OTA); + } + } + if (isTCP()) + { + setPacketType(PacketType::TCP); + if (isHTTP()) + { + setPacketType(PacketType::HTTP); + } + } + if (isICMP()) + { + setPacketType(PacketType::ICMP); + } + if (isIGMP()) + { + setPacketType(PacketType::IGMP); + } + } + else + { + setPacketType(PacketType::UKNW); + } +} + +const PacketType Packet::packetType() const +{ + return thisPacketType; +} + +const std::vector Packet::allPacketTypes() const +{ + return thisAllPacketTypes; +} + +void Packet::MACtoString(const uint8_t* mac, StreamString& sstr) const +{ + for (int i = 0; i < 6; i++) + { + sstr.printf_P(PSTR("%02x"), mac[i]); + if (i < 5) + { + sstr.print(':'); + } + } +} + +void Packet::ARPtoString(PacketDetail netdumpDetail, StreamString& sstr) const +{ + if (!arpPacket) + { + sstr.printf_P(PSTR("ARPtoString access error\r\n")); + return; + } + switch (arpPacket->opcode()) + { + case 1 : sstr.printf_P(PSTR("who has %s tell %s"), arpPacket->targetIP().toString().c_str(), arpPacket->senderIP().toString().c_str()); + break; + case 2 : sstr.printf_P(PSTR("%s is at "), arpPacket->senderIP().toString().c_str()); + MACtoString(arpPacket->hdr->senderHardwareAddress, sstr); + break; + } + sstr.printf("\r\n"); + printDetail(sstr, PSTR(" D "), arpPacket->raw, packetLength - ETH_HDR_LEN, netdumpDetail); +} + +void Packet::DNStoString(PacketDetail netdumpDetail, StreamString& sstr) const +{ + if (!dnsPacket || !udpPacket || !ipPacket) + { + sstr.printf_P(PSTR("DNStoString access error\r\n")); + return; + } + sstr.printf_P(PSTR("%s>%s "), ipPacket->sourceIP().toString().c_str(), ipPacket->destinationIP().toString().c_str()); + sstr.printf_P(PSTR("ID=0x%04x "), dnsPacket->id()); + sstr.printf_P(PSTR("F=0x%04x "), dnsPacket->flags()); + + if (uint16_t t = dnsPacket->qdcount()) + { + sstr.printf_P(PSTR("Q=%d "), t); + } + if (uint16_t t = dnsPacket->ancount()) + { + sstr.printf_P(PSTR("R=%d "), t); + } + if (uint16_t t = dnsPacket->nscount()) + { + sstr.printf_P(PSTR("TR=%d "), t); + } + if (uint16_t t = dnsPacket->arcount()) + { + sstr.printf_P(PSTR("DR=%d "), t); + } + sstr.printf_P(PSTR("\r\n")); + for (int i = 0;iqdcount();i++) + { + DNSPacket::DNSQuestion dq = dnsPacket->getQuestion(i); + sstr.printf(" Q : %s, type %d\r\n",dq.qname.c_str(),dq.qtype); + } + for (int i=0;iancount();i++) + { + DNSPacket::DNSAnswer da = dnsPacket->getAnswer(i); + sstr.printf(" R : %s TP : %d TTL : %d",da.name.c_str(),da.type,da.ttl); + if (da.getIP().isSet()) + { + sstr.printf(" IP : %s\r\n",da.getIP().toString().c_str()); + } + else + { + sstr.printf("\r\n"); + } + } + printDetail(sstr, PSTR(" H "), udpPacket->raw, udpPacket->hdrLength(), netdumpDetail); + printDetail(sstr, PSTR(" D "), udpPacket->hdr->payload, udpPacket->length(), netdumpDetail); +} + +void Packet::UDPtoString(PacketDetail netdumpDetail, StreamString& sstr) const +{ + sstr.printf_P(PSTR("%s>%s "), sourceIP().toString().c_str(), destIP().toString().c_str()); + sstr.printf_P(PSTR("%d:%d"), getSrcPort(), getDstPort()); + sstr.printf_P(PSTR("\r\n")); + printDetail(sstr, PSTR(" H "), udpPacket->raw, getUdpHdrLen(), netdumpDetail); + printDetail(sstr, PSTR(" D "), udpPacket->hdr->payload, getUdpLen(), netdumpDetail); +} + +void Packet::TCPtoString(PacketDetail netdumpDetail, StreamString& sstr) const +{ + if (!tcpPacket) + { + sstr.printf_P(PSTR("TCPtoString access error\r\n")); + return; + } + sstr.printf_P(PSTR("%s>%s "), sourceIP().toString().c_str(), destIP().toString().c_str()); + sstr.printf_P(PSTR("%d:%d "), getSrcPort(), getDstPort()); + uint16_t flags = getTcpFlags(); + sstr.print('['); + const char chars [] = "FSRPAUECN"; + for (uint8_t i = 0; i < sizeof chars; i++) + if (flags & (1 << i)) + { + sstr.print(chars[i]); + } + sstr.print(']'); + sstr.printf_P(PSTR(" len: %u seq: %u, ack: %u, wnd: %u "), getTcpLen(), getTcpSeq(), getTcpAck(), getTcpWindow()); + sstr.printf_P(PSTR("\r\n")); + printDetail(sstr, PSTR(" H "), tcpPacket->raw, getTcpHdrLen(), netdumpDetail); + printDetail(sstr, PSTR(" D "), tcpPacket->hdr->payload, getTcpLen(), netdumpDetail); +} + +void Packet::ICMPtoString(PacketDetail, StreamString& sstr) const +{ + if (!icmpPacket || !ipPacket) + { + sstr.printf_P(PSTR("ICMPtoString access error icmp\r\n")); + return; + } + if (!ipPacket) + { + sstr.printf_P(PSTR("ICMPtoString access error ip\r\n")); + return; + } + + sstr.printf_P(PSTR("%s>%s "), sourceIP().toString().c_str(), destIP().toString().c_str()); + if (isIPv4()) + { + switch (getIcmpType()) + { + case 0 : sstr.printf_P(PSTR("ping reply")); break; + case 3 : sstr.printf_P(PSTR("destination unreachable")); break; + case 5 : sstr.printf_P(PSTR("redirect")); break; + case 8 : sstr.printf_P(PSTR("ping request")); break; + + default: sstr.printf_P(PSTR("type(0x%02x)"), getIcmpType()); break; + } + } + if (isIPv6()) + { + switch (getIcmpType()) + { + case 129 : sstr.printf_P(PSTR("ping reply")); break; + case 128 : sstr.printf_P(PSTR("ping request")); break; + case 135 : sstr.printf_P(PSTR("Neighbour solicitation")); break; + case 136 : sstr.printf_P(PSTR("Neighbour advertisement")); break; + default: sstr.printf_P(PSTR("type(0x%02x)"), getIcmpType()); break; + } + } + sstr.printf("\r\n"); +} + +void Packet::IGMPtoString(PacketDetail, StreamString& sstr) const +{ + switch (getIgmpType()) + { + case 1 : sstr.printf_P(PSTR("Create Group Request")); break; + case 2 : sstr.printf_P(PSTR("Create Group Reply")); break; + case 3 : sstr.printf_P(PSTR("Join Group Request")); break; + case 4 : sstr.printf_P(PSTR("Join Group Reply")); break; + case 5 : sstr.printf_P(PSTR("Leave Group Request")); break; + case 6 : sstr.printf_P(PSTR("Leave Group Reply")); break; + case 7 : sstr.printf_P(PSTR("Confirm Group Request")); break; + case 8 : sstr.printf_P(PSTR("Confirm Group Reply")); break; + case 0x11 : sstr.printf_P(PSTR("Group Membership Query")); break; + case 0x12 : sstr.printf_P(PSTR("IGMPv1 Membership Report")); break; + case 0x22 : sstr.printf_P(PSTR("IGMPv3 Membership Report")); break; + default: sstr.printf_P(PSTR("type(0x%02x)"), getIgmpType()); break; + } + sstr.printf_P(PSTR("\r\n")); +} + +void Packet::IPtoString(PacketDetail netdumpDetail, StreamString& sstr) const +{ + sstr.printf_P(PSTR("%s>%s "), sourceIP().toString().c_str(), destIP().toString().c_str()); + sstr.printf_P(PSTR("Unknown IP type : %d\r\n"), ipType()); + printDetail(sstr, PSTR(" H "), ipPacket->raw(), getIpHdrLen(), netdumpDetail); + printDetail(sstr, PSTR(" D "), ipPacket->raw(), getIpTotalLen() - getIpHdrLen(), netdumpDetail); +} + +void Packet::UKNWtoString(PacketDetail, StreamString& sstr) const +{ + sstr.printf_P(PSTR("Unknown EtherType 0x%04x Src : "), ethType()); + MACtoString(rawData(), sstr); + sstr.printf_P(PSTR(" Dst : ")); + MACtoString(rawData()+6, sstr); + sstr.printf_P(PSTR("\r\n")); +} + +const String Packet::toString() const +{ + return toString(PacketDetail::NONE); +} + + +const String Packet::toString(PacketDetail netdumpDetail) const +{ + StreamString sstr; + sstr.reserve(128); + + sstr.printf_P(PSTR("%d %3s %-4s "), netif_idx, out ? "out" : "in ", packetType().toString().c_str()); + + if (netdumpDetail == PacketDetail::RAW) + { + sstr.printf_P(PSTR(" : ")); + for (auto at : thisAllPacketTypes) + { + sstr.printf_P(PSTR("%s "), at.toString().c_str()); + } + sstr.printf_P(PSTR("\r\n")); + printDetail(sstr, PSTR(" D "), rawData(), packetLength, netdumpDetail); + return sstr; + } + + switch (thisPacketType) + { + case PacketType::ARP : + { + ARPtoString(netdumpDetail, sstr); + break; + } + case PacketType::MDNS : + case PacketType::DNS : + case PacketType::LLMNR: + { + DNStoString(netdumpDetail, sstr); + break; + } + case PacketType::SSDP : + case PacketType::DHCP : + case PacketType::WSDD : + case PacketType::NETBIOS : + case PacketType::SMB : + case PacketType::OTA : + case PacketType::UDP : + { + UDPtoString(netdumpDetail, sstr); + break; + } + case PacketType::TCP : + case PacketType::HTTP : + { + TCPtoString(netdumpDetail, sstr); + break; + } + case PacketType::ICMP : + { + ICMPtoString(netdumpDetail, sstr); + break; + } + case PacketType::IGMP : + { + IGMPtoString(netdumpDetail, sstr); + break; + } + case PacketType::IPv4 : + case PacketType::IPv6 : + { + IPtoString(netdumpDetail, sstr); + break; + } + case PacketType::UKNW : + { + UKNWtoString(netdumpDetail, sstr); + break; + } + default : + { + sstr.printf_P(PSTR("Non identified packet\r\n")); + break; + } + } + return sstr; +} + +} // namespace NetCapture diff --git a/libraries/Netdump/src/NetdumpPacket.h b/libraries/Netdump/src/NetdumpPacket.h new file mode 100644 index 0000000000..9c06451771 --- /dev/null +++ b/libraries/Netdump/src/NetdumpPacket.h @@ -0,0 +1,392 @@ +/* + NetDump library - tcpdump-like packet logger facility + + Copyright (c) 2019 Herman Reintke. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __NETDUMP_PACKET_H +#define __NETDUMP_PACKET_H + +#include +#include +#include +#include "NetdumpIP.h" +#include "PacketType.h" +#include +#include "NetdumpUtils.h" +#include "structures.h" +#include +namespace NetCapture +{ + +class Packet +{ +public: + static int constexpr ETH_HDR_LEN = 14; + + Packet(unsigned long msec, int n, const char* d, size_t l, int o, int s) + : packetTime(msec), netif_idx(n), data(d), packetLength(l), out(o), success(s) + { + setPacketTypes(); +// ethernetFrame = new EthernetFrame(rawData()); + ethernetFrame.reset(new EthernetFrame(rawData())); +// ethernetFrame = std::unique_ptr(new EthernetFrame(rawData())); + switch (ethernetFrame->type()) + { + case 0x0800 : +// ipv4Packet = new IPv4Packet(ethernetFrame->hdr->payload); +// ipPacket = new IPPacket(ipv4Packet); + // ipv4Packet->reset(new IPv4Packet(ethernetFrame->hdr->payload)); + ipv4Packet.reset(new IPv4Packet(ethernetFrame->hdr->payload)); + ipPacket.reset(new IPPacket(ipv4Packet)); + break; + case 0x86dd : +// ipv6Packet = new IPv6Packet(ethernetFrame->hdr->payload); +// ipPacket = new IPPacket(ipv6Packet); + ipv6Packet.reset(new IPv6Packet(ethernetFrame->hdr->payload)); + ipPacket.reset(new IPPacket(ipv6Packet)); + break; + case 0x0806 : +// arpPacket = new ARPPacket(ethernetFrame->hdr->payload); + arpPacket.reset(new ARPPacket(ethernetFrame->hdr->payload)); + break; + default : + break; + } + + if (ipPacket) + { + switch (ipPacket->packetType()) + { + case 17 : //udpPacket = new UDPPacket(ipPacket->payload()); + udpPacket.reset(new UDPPacket(ipPacket->payload())); + break; + case 6 : //tcpPacket = new TCPPacket(ipPacket->payload()); + tcpPacket.reset(new TCPPacket(ipPacket->payload())); + break; + case 1 : + case 58 : + //icmpPacket = new ICMPPacket(ipPacket->payload()); + icmpPacket.reset(new ICMPPacket(ipPacket->payload())); + break; + default : break; + } + } + if (udpPacket) + { + if (((udpPacket->sourcePort() == 5353) || (udpPacket->destinationPort() == 5353)) + || ((udpPacket->sourcePort() == 53) || (udpPacket->destinationPort() == 53)) + || ((udpPacket->sourcePort() == 5355) || (udpPacket->destinationPort() == 5355))) + { +// dnsPacket = new DNSPacket(udpPacket->hdr->payload); + dnsPacket.reset(new DNSPacket(udpPacket->hdr->payload)); + } + } + }; + + Packet() {}; + + enum class PacketDetail + { + NONE, + FULL, + CHAR, + RAW + }; + + std::unique_ptr ethernetFrame = nullptr; +// EthernetFrame* ethernetFrame = nullptr; + std::unique_ptr arpPacket = nullptr; + std::unique_ptr ipv4Packet = nullptr; + std::unique_ptr ipv6Packet = nullptr; + std::unique_ptr ipPacket = nullptr; + std::unique_ptr udpPacket = nullptr; + std::unique_ptr dnsPacket = nullptr; + std::unique_ptr tcpPacket = nullptr; + std::unique_ptr icmpPacket = nullptr; + + + const uint8_t* rawData() const + { + return reinterpret_cast(data); + } + int getInOut() const + { + return out; + } + time_t getTime() const + { + return packetTime; + } + uint32_t getPacketSize() const + { + return packetLength; + } + uint16_t ntoh16(uint16_t idx) const + { + return data[idx + 1] | (((uint16_t)data[idx]) << 8); + }; + uint32_t ntoh32(uint16_t idx) const + { + return ntoh16(idx + 2) | (((uint32_t)ntoh16(idx)) << 16); + }; + uint8_t byteData(uint16_t idx) const + { + return data[idx]; + } + const char* byteIdx(uint16_t idx) const + { + return &data[idx]; + }; + uint16_t ethType() const + { + return ntoh16(12); + }; + uint8_t ipType() const + { + return isIP() ? isIPv4() ? data[ETH_HDR_LEN + 9] : data[ETH_HDR_LEN + 6] : 0; + }; + uint16_t getIpHdrLen() const + { + return isIPv4() ? (((unsigned char)data[ETH_HDR_LEN]) & 0x0f) << 2 : 40 ; // IPv6 is fixed length + } + uint16_t getIpTotalLen() const + { + return isIP() ? isIPv4() ? ntoh16(ETH_HDR_LEN + 2) : (packetLength - ETH_HDR_LEN) : 0; + } + uint32_t getTcpSeq() const + { + return isTCP() ? ntoh32(ETH_HDR_LEN + getIpHdrLen() + 4) : 0; + } + uint32_t getTcpAck() const + { + return isTCP() ? ntoh32(ETH_HDR_LEN + getIpHdrLen() + 8) : 0; + } + uint16_t getTcpFlags() const + { + return isTCP() ? ntoh16(ETH_HDR_LEN + getIpHdrLen() + 12) : 0; + } + uint16_t getTcpWindow() const + { + return isTCP() ? ntoh16(ETH_HDR_LEN + getIpHdrLen() + 14) : 0; + } + uint8_t getTcpHdrLen() const + { + return isTCP() ? (data[ETH_HDR_LEN + getIpHdrLen() + 12] >> 4) * 4 : 0; + };//Header len is in multiple of 4 bytes + uint16_t getTcpLen() const + { + return isTCP() ? getIpTotalLen() - getIpHdrLen() - getTcpHdrLen() : 0 ; + }; + + uint8_t getIcmpType() const + { + return icmpPacket ? icmpPacket->hdr->type : 0; + } + uint8_t getIgmpType() const + { + return isIGMP() ? data[ETH_HDR_LEN + getIpHdrLen() + 0] : 0; + } + uint8_t getARPType() const + { + return isARP() ? data[ETH_HDR_LEN + 7] : 0; + } + bool is_ARP_who() const + { + return (getARPType() == 1); + } + bool is_ARP_is() const + { + return (getARPType() == 2); + } + + uint8_t getUdpHdrLen() const + { + return isUDP() ? 8 : 0; + }; + uint16_t getUdpLen() const + { + return isUDP() ? ntoh16(ETH_HDR_LEN + getIpHdrLen() + 4) : 0; + }; + bool isARP() const + { + return (ethType() == 0x0806); + }; + bool isIPv4() const + { + return (ethType() == 0x0800); + }; + bool isIPv6() const + { + return (ethType() == 0x86dd); + }; + bool isIP() const + { + return (isIPv4() || isIPv6()); + }; + bool isICMP() const + { + return (isIP() && ((ipType() == 1) || (ipType() == 58))); + }; + bool isIGMP() const + { + return (isIP() && (ipType() == 2)); + }; + bool isTCP() const + { + return (isIP() && (ipType() == 6)); + }; + bool isUDP() const + { + return (isIP() && ipType() == 17); + }; + bool isMDNS() const + { + return (isUDP() && hasPort(5353)); + }; + bool isDNS() const + { + return (isUDP() && hasPort(53)); + }; + bool isLLMNR() const + { + return (isUDP() && hasPort(5355)); + } + bool isSSDP() const + { + return (isUDP() && hasPort(1900)); + }; + bool isDHCP() const + { + return (isUDP() && ((hasPort(546) || hasPort(547) || hasPort(67) || hasPort(68)))); + }; + bool isWSDD() const + { + return (isUDP() && hasPort(3702)); + }; + bool isHTTP() const + { + return (isTCP() && hasPort(80)); + }; + bool isOTA() const + { + return (isUDP() && hasPort(8266)); + } + bool isNETBIOS() const + { + return (isUDP() && (hasPort(137) || hasPort(138) || hasPort(139))); + } + bool isSMB() const + { + return (isUDP() && hasPort(445)); + } + NetdumpIP getIP(uint16_t idx) const + { + return NetdumpIP(data[idx], data[idx + 1], data[idx + 2], data[idx + 3]); + }; + + NetdumpIP getIP6(uint16_t idx) const + { + return NetdumpIP((const uint8_t*)&data[idx], false); + }; + NetdumpIP sourceIP() const + { + return ipPacket ? ipPacket->sourceIP() : NetdumpIP(); + NetdumpIP ip; + if (isIPv4()) + { + ip = getIP(ETH_HDR_LEN + 12); + } + else if (isIPv6()) + { + ip = getIP6(ETH_HDR_LEN + 8); + } + return ip; + }; + + bool hasIP(NetdumpIP ip) const + { + return (isIP() && ((ip == sourceIP()) || (ip == destIP()))); + } + + NetdumpIP destIP() const + { + return ipPacket ? ipPacket->destinationIP() : NetdumpIP(); + NetdumpIP ip; + if (isIPv4()) + { + ip = getIP(ETH_HDR_LEN + 16); + } + else if (isIPv6()) + { + ip = getIP6(ETH_HDR_LEN + 24); + } + return ip; + }; + uint16_t getSrcPort() const + { + // return tcpPacket ? tcpPacket->sourcePort() : (udpPacket ? udpPacket->sourcePort() : 0); + return isIP() ? ntoh16(ETH_HDR_LEN + getIpHdrLen() + 0) : 0; + } + uint16_t getDstPort() const + { + // return tcpPacket ? tcpPacket->destinationPort() : (udpPacket ? udpPacket->destinationPort() : 0); + return isIP() ? ntoh16(ETH_HDR_LEN + getIpHdrLen() + 2) : 0; + } + bool hasPort(uint16_t p) const + { + return (isIP() && ((getSrcPort() == p) || (getDstPort() == p))); + } + + const String toString() const; + const String toString(PacketDetail netdumpDetail) const; + void printDetail(Print& out, const String& indent, const uint8_t* data, size_t size, PacketDetail pd) const; + + const PacketType packetType() const; + const std::vector allPacketTypes() const; + + +private: + + void setPacketType(PacketType); + void setPacketTypes(); + + void MACtoString(const uint8_t* mac, StreamString& sstr) const; + void ARPtoString(PacketDetail netdumpDetail, StreamString& sstr) const; + void DNStoString(PacketDetail netdumpDetail, StreamString& sstr) const; + void UDPtoString(PacketDetail netdumpDetail, StreamString& sstr) const; + void TCPtoString(PacketDetail netdumpDetail, StreamString& sstr) const; + void ICMPtoString(PacketDetail netdumpDetail, StreamString& sstr) const; + void IGMPtoString(PacketDetail netdumpDetail, StreamString& sstr) const; + void IPtoString(PacketDetail netdumpDetail, StreamString& sstr) const; + void UKNWtoString(PacketDetail netdumpDetail, StreamString& sstr) const; + + + time_t packetTime; + int netif_idx; + const char* data; + size_t packetLength; + int out; + int success; + PacketType thisPacketType; + std::vector thisAllPacketTypes; +}; + +} // namespace NetCapture + +#endif /* __NETDUMP_PACKET_H */ diff --git a/libraries/Netdump/src/NetdumpUtils.cpp b/libraries/Netdump/src/NetdumpUtils.cpp new file mode 100644 index 0000000000..ba813420ae --- /dev/null +++ b/libraries/Netdump/src/NetdumpUtils.cpp @@ -0,0 +1,20 @@ +/* + * NetdumpUtils.cpp + * + * Created on: 5 mei 2020 + * Author: Herman + */ + +#include + +NetdumpUtils::NetdumpUtils() +{ + // TODO Auto-generated constructor stub + +} + +NetdumpUtils::~NetdumpUtils() +{ + // TODO Auto-generated destructor stub +} + diff --git a/libraries/Netdump/src/NetdumpUtils.h b/libraries/Netdump/src/NetdumpUtils.h new file mode 100644 index 0000000000..739ce311a5 --- /dev/null +++ b/libraries/Netdump/src/NetdumpUtils.h @@ -0,0 +1,28 @@ +/* + * NetdumpUtils.h + * + * Created on: 5 mei 2020 + * Author: Herman + */ + +#ifndef LIBRARIES_NETDUMP_SRC_NETDUMPUTILS_H_ +#define LIBRARIES_NETDUMP_SRC_NETDUMPUTILS_H_ + +#include "Arduino.h" + +class NetdumpUtils +{ +public: + NetdumpUtils(); + ~NetdumpUtils(); + static uint16_t ntoh16(const uint8_t data[2]) + { + return data[1] | (((uint16_t)data[0]) << 8); + }; + static uint32_t ntoh32(const uint8_t data[4]) + { + return ntoh16(&data[2]) | (((uint32_t)ntoh16(&data[0])) << 16); + }; +}; + +#endif /* LIBRARIES_NETDUMP_SRC_NETDUMPUTILS_H_ */ diff --git a/libraries/Netdump/src/PacketType.cpp b/libraries/Netdump/src/PacketType.cpp new file mode 100644 index 0000000000..bc56bfaad1 --- /dev/null +++ b/libraries/Netdump/src/PacketType.cpp @@ -0,0 +1,48 @@ +/* + PacketType.cpp + + Created on: 19 nov. 2019 + Author: Herman +*/ + +#include + +namespace NetCapture +{ + +PacketType::PacketType() +{ +} + +PacketType::~PacketType() +{ +} + +String PacketType::toString() const +{ + switch (ptype) + { + case PType::ARP : return PSTR("ARP"); + case PType::IP : return PSTR("IP"); + case PType::UDP : return PSTR("UDP"); + case PType::MDNS : return PSTR("MDNS"); + case PType::DNS : return PSTR("DNS"); + case PType::SSDP : return PSTR("SSDP"); + case PType::DHCP : return PSTR("DHCP"); + case PType::WSDD : return PSTR("WSDD"); + case PType::NETBIOS: return PSTR("NBIO"); + case PType::SMB : return PSTR("SMB"); + case PType::OTA : return PSTR("OTA"); + case PType::TCP : return PSTR("TCP"); + case PType::HTTP : return PSTR("HTTP"); + case PType::ICMP : return PSTR("ICMP"); + case PType::IGMP : return PSTR("IGMP"); + case PType::IPv4: return PSTR("IPv4"); + case PType::IPv6: return PSTR("IPv6"); + case PType::LLMNR: return PSTR("LLMR"); + case PType::UKNW : return PSTR("UKNW"); + default : return PSTR("ERR"); + }; +} + +} /* namespace NetCapture */ diff --git a/libraries/Netdump/src/PacketType.h b/libraries/Netdump/src/PacketType.h new file mode 100644 index 0000000000..d6a10340ae --- /dev/null +++ b/libraries/Netdump/src/PacketType.h @@ -0,0 +1,64 @@ +/* + PacketType.h + + Created on: 19 nov. 2019 + Author: Herman +*/ + +#ifndef LIBRARIES_NETDUMP_SRC_PACKETTYPE_H_ +#define LIBRARIES_NETDUMP_SRC_PACKETTYPE_H_ +#include "Arduino.h" + +namespace NetCapture +{ + +class PacketType +{ +public: + + enum PType : int + { + ARP, + IP, + UDP, + MDNS, + DNS, + SSDP, + DHCP, + WSDD, + NETBIOS, + SMB, + OTA, + TCP, + HTTP, + ICMP, + IGMP, + IPv4, + IPv6, + LLMNR, + UKNW, + }; + + PacketType(); + PacketType(PType pt) : ptype(pt) {}; + + ~PacketType(); + + operator PType() const + { + return ptype; + }; + bool operator==(const PacketType& p) + { + return ptype == p.ptype; + }; + + String toString() const; + +private: + PType ptype; +}; + +} /* namespace NetCapture */ + +#endif /* LIBRARIES_NETDUMP_SRC_PACKETTYPE_H_ */ diff --git a/libraries/Netdump/src/ieee80211_structs.h b/libraries/Netdump/src/ieee80211_structs.h new file mode 100644 index 0000000000..485e0443cf --- /dev/null +++ b/libraries/Netdump/src/ieee80211_structs.h @@ -0,0 +1,80 @@ +#ifndef _IEE80211_STRUCTS_H_ +#define _IEE80211_STRUCTS_H_ + +#include + +// IEEE802.11 data structures --------------------- + +typedef enum +{ + WIFI_PKT_MGMT, + WIFI_PKT_CTRL, + WIFI_PKT_DATA, + WIFI_PKT_MISC, +} wifi_promiscuous_pkt_type_t; + +typedef enum +{ + ASSOCIATION_REQ, + ASSOCIATION_RES, + REASSOCIATION_REQ, + REASSOCIATION_RES, + PROBE_REQ, + PROBE_RES, + NU1, /* ......................*/ + NU2, /* 0110, 0111 not used */ + BEACON, + ATIM, + DISASSOCIATION, + AUTHENTICATION, + DEAUTHENTICATION, + ACTION, + ACTION_NACK, +} wifi_mgmt_subtypes_t; + +typedef struct +{ + unsigned interval:16; + unsigned capability:16; + unsigned tag_number:8; + unsigned tag_length:8; + char ssid[0]; + uint8 rates[1]; +} wifi_mgmt_beacon_t; + +typedef struct +{ + unsigned protocol:2; + unsigned type:2; + unsigned subtype:4; + unsigned to_ds:1; + unsigned from_ds:1; + unsigned more_frag:1; + unsigned retry:1; + unsigned pwr_mgmt:1; + unsigned more_data:1; + unsigned wep:1; + unsigned strict:1; +} wifi_header_frame_control_t; + +/** + * Ref: https://github.com/lpodkalicki/blog/blob/master/esp32/016_wifi_sniffer/main/main.c + */ +typedef struct +{ + wifi_header_frame_control_t frame_ctrl; + //unsigned duration_id:16; /* !!!! ugly hack */ + uint8_t addr1[6]; /* receiver address */ + uint8_t addr2[6]; /* sender address */ + uint8_t addr3[6]; /* filtering address */ + unsigned sequence_ctrl:16; + uint8_t addr4[6]; /* optional */ +} wifi_ieee80211_mac_hdr_t; + +typedef struct +{ + wifi_ieee80211_mac_hdr_t hdr; + uint8_t payload[2]; /* network data ended with 4 bytes csum (CRC32) */ +} wifi_ieee80211_packet_t; + +#endif diff --git a/libraries/Netdump/src/sdk_structs.h b/libraries/Netdump/src/sdk_structs.h new file mode 100644 index 0000000000..d5af8f6a58 --- /dev/null +++ b/libraries/Netdump/src/sdk_structs.h @@ -0,0 +1,62 @@ +#ifndef _SDK_STRUCTS_H_ +#define _SDK_STRUCTS_H_ + +#include + +// SDK structures ----------------------------------- + +typedef struct +{ + signed rssi:8; /**< signal intensity of packet */ + unsigned rate:4; /**< data rate */ + unsigned is_group:1; + unsigned :1; /**< reserve */ + unsigned sig_mode:2; /**< 0:is not 11n packet; 1:is 11n packet */ + unsigned legacy_length:12; + unsigned damatch0:1; + unsigned damatch1:1; + unsigned bssidmatch0:1; + unsigned bssidmatch1:1; + unsigned mcs:7; /**< if is 11n packet, shows the modulation(range from 0 to 76) */ + unsigned cwb:1; /**< if is 11n packet, shows if is HT40 packet or not */ + unsigned HT_length:16; /**< reserve */ + unsigned smoothing:1; /**< reserve */ + unsigned not_sounding:1; /**< reserve */ + unsigned :1; /**< reserve */ + unsigned aggregation:1; /**< Aggregation */ + unsigned stbc:2; /**< STBC */ + unsigned fec_coding:1; /**< Flag is set for 11n packets which are LDPC */ + unsigned sgi:1; /**< SGI */ + unsigned rxend_state:8; + unsigned ampdu_cnt:8; /**< ampdu cnt */ + unsigned channel:4; /**< which channel this packet in */ + unsigned :4; /**< reserve */ + signed noise_floor:8; +} wifi_pkt_rx_ctrl_t; + +typedef struct { + wifi_pkt_rx_ctrl_t rx_ctrl; + uint8 buf[112]; + uint16 cnt; + uint16 len; //length of packet +} wifi_pkt_mgmt_t; + +typedef struct { + uint16 length; + uint16 seq; + uint8 address3[6]; +} wifi_pkt_lenseq_t; + +typedef struct { + wifi_pkt_rx_ctrl_t rx_ctrl; + uint8_t buf[36]; + uint16_t cnt; + wifi_pkt_lenseq_t lenseq[1]; +} wifi_pkt_data_t; + +typedef struct { + wifi_pkt_rx_ctrl_t rx_ctrl; /**< metadata header */ + uint8_t payload[0]; /**< Data or management payload. Length of payload is described by rx_ctrl.sig_len. Type of content determined by packet type argument of callback. */ +} wifi_promiscuous_pkt_t; + +#endif diff --git a/libraries/Netdump/src/string_utils.cpp b/libraries/Netdump/src/string_utils.cpp new file mode 100644 index 0000000000..c53871a123 --- /dev/null +++ b/libraries/Netdump/src/string_utils.cpp @@ -0,0 +1,66 @@ +#include "sdk_structs.h" +#include "ieee80211_structs.h" + +// Uncomment to enable MAC address masking +#define MASKED + +//Returns a human-readable string from a binary MAC address. +//If MASKED is defined, it masks the output with XX +void mac2str(const uint8_t* ptr, char* string) +{ +// #ifdef MASKED +// sprintf(string, "XX:XX:XX:%02x:%02x:XX", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); +// #else + sprintf(string, "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); +// #endif + return; +} + +//Parses 802.11 packet type-subtype pair into a human-readable string +const char* wifi_pkt_type2str(wifi_promiscuous_pkt_type_t type, wifi_mgmt_subtypes_t subtype) +{ + switch(type) + { + case WIFI_PKT_MGMT: + switch(subtype) + { + case ASSOCIATION_REQ: + return "Mgmt: Association request"; + case ASSOCIATION_RES: + return "Mgmt: Association response"; + case REASSOCIATION_REQ: + return "Mgmt: Reassociation request"; + case REASSOCIATION_RES: + return "Mgmt: Reassociation response"; + case PROBE_REQ: + return "Mgmt: Probe request"; + case PROBE_RES: + return "Mgmt: Probe response"; + case BEACON: + return "Mgmt: Beacon frame"; + case ATIM: + return "Mgmt: ATIM"; + case DISASSOCIATION: + return "Mgmt: Dissasociation"; + case AUTHENTICATION: + return "Mgmt: Authentication"; + case DEAUTHENTICATION: + return "Mgmt: Deauthentication"; + case ACTION: + return "Mgmt: Action"; + case ACTION_NACK: + return "Mgmt: Action no ack"; + default: + return "Mgmt: Unsupported/error"; + } + + case WIFI_PKT_CTRL: + return "Control"; + + case WIFI_PKT_DATA: + return "Data"; + + default: + return "Unsupported/error"; + } +} diff --git a/libraries/Netdump/src/string_utils.h b/libraries/Netdump/src/string_utils.h new file mode 100644 index 0000000000..7d88b92556 --- /dev/null +++ b/libraries/Netdump/src/string_utils.h @@ -0,0 +1,10 @@ +#ifndef _STRING_UTILS_H_ +#define _STRING_UTILS_H_ + +#include "sdk_structs.h" +#include "ieee80211_structs.h" + +void mac2str(const uint8_t* ptr, char* string); +const char* wifi_pkt_type2str(wifi_promiscuous_pkt_type_t type, wifi_mgmt_subtypes_t subtype); + +#endif diff --git a/libraries/Netdump/src/structures.h b/libraries/Netdump/src/structures.h new file mode 100644 index 0000000000..175cbf3610 --- /dev/null +++ b/libraries/Netdump/src/structures.h @@ -0,0 +1,550 @@ +/* + * structures.h + * + * Created on: 5 mei 2020 + * Author: Herman + */ + +#ifndef LIBRARIES_NETDUMP_SRC_STRUCTURES_H_ +#define LIBRARIES_NETDUMP_SRC_STRUCTURES_H_ + +#include "Arduino.h" +#include "NetdumpUtils.h" +#include "NetdumpIP.h" + +namespace NetCapture +{ + +class EthernetFrame +{ +public: + + struct EthernetHeader + { + uint8_t destinationMAC[6]; + uint8_t sourceMAC[6]; + uint8_t type[2]; + uint8_t payload[]; + }; + + EthernetFrame(const uint8_t* frame) + { + hdr = reinterpret_cast(frame); + raw = frame; + }; + ~EthernetFrame(){}; + + const EthernetHeader* hdr; + const uint8_t* raw; + + String destinationMAC() {return "";} + const uint8_t* payload() { return hdr->payload;} + uint16_t type() {return NetdumpUtils::ntoh16(hdr->type);} +}; + +class ARPPacket +{ +public: + struct ARPHeader + { + uint8_t hardwareType[2]; + uint8_t protocolType[2]; + uint8_t hardwareAddressLength; + uint8_t protocolAddressLength; + uint8_t opcode[2]; + uint8_t senderHardwareAddress[6]; + uint8_t senderProtocolAddress[4]; + uint8_t targetHardwareAddress[6]; + uint8_t targetProtocolAddress[4]; + }; + + ARPPacket(const uint8_t* packet) + { + hdr = reinterpret_cast(packet); + raw = packet; + } + + const ARPHeader* hdr; + const uint8_t* raw; + + uint16_t opcode() + { + return NetdumpUtils::ntoh16(hdr->opcode); + } + NetdumpIP targetIP() + { + return NetdumpIP(hdr->targetProtocolAddress); + } + NetdumpIP senderIP() + { + return NetdumpIP(hdr->senderProtocolAddress); + } +}; + +class IPv4Packet +{ +public: + struct IPv4Header + { +// uint8_t version:4; +// uint8_t headerLength:4; + uint8_t version_header; +// uint8_t serviceType:6; +// uint8_t ecn:2; + uint8_t type_ecn; + uint8_t totalLength[2]; + uint8_t identification[2]; +// uint8_t flags:3; +// uint16_t fragmentOffset:13; + uint8_t flags_fragment[2]; + uint8_t ttl; + uint8_t protocol; + uint8_t headerChecksum[2]; + uint8_t sourceIP[4]; + uint8_t destinationIP[4]; + uint8_t payload[]; + }; + IPv4Packet(const uint8_t* packet) + { + hdr = reinterpret_cast(packet); + raw = packet; + } + + const IPv4Header* hdr; + const uint8_t* raw; + + uint16_t headerLength() + { + return (hdr->version_header & 0x0f) << 2; + } + uint8_t ipVersion() + { + return (hdr->version_header >> 4) & 0x0f; + } + uint8_t serviceType() + { + return (hdr->type_ecn >> 2) & 0x3f; + } + uint8_t flags() + { + return (NetdumpUtils::ntoh16(hdr->flags_fragment) >> 13) & 0x07; + } + uint16_t fragmentationOffset() + { + return (NetdumpUtils::ntoh16(hdr->flags_fragment) & 0x1fff); + } + NetdumpIP sourceIP() + { + return NetdumpIP(hdr->sourceIP); + } + NetdumpIP destinationIP() + { + return NetdumpIP(hdr->destinationIP); + } +}; + +class IPv6Packet +{ +public: + + struct IPv6Header + { + uint8_t version_traffic_flow[4]; + uint8_t payloadLength[2]; + uint8_t nextHeader; + uint8_t hopLimit; + uint8_t sourceAddress[16]; + uint8_t destinationAddress[16]; + uint8_t payload[]; + }; + + IPv6Packet(const uint8_t* data) + : raw(data), + hdr(reinterpret_cast(data)) + {}; + + const uint8_t* raw; + const IPv6Header* hdr; + + uint8_t type() + { + return hdr->nextHeader; + } + NetdumpIP sourceIP() + { + return NetdumpIP(hdr->sourceAddress,false); + } + NetdumpIP destinationIP() + { + return NetdumpIP(hdr->destinationAddress,false); + } +}; + +class IPPacket +{ +public: + IPPacket(std::unique_ptr& i4) : ip4(true) + { + ipv4.reset(new IPv4Packet(i4->raw)); + }; + IPPacket(std::unique_ptr& i6) : ip4(false) + { + ipv6.reset(new IPv6Packet(i6->raw)); + }; + + union ipv4v6 + { + IPv4Packet::IPv4Header* ipv4h; + IPv6Packet::IPv6Header* ipv6h; + }; + + ipv4v6 ht; + + bool ip4 = false; + std::unique_ptr ipv4 = nullptr; + std::unique_ptr ipv6 = nullptr; + + uint8_t packetType() + { + return ip4 ? ipv4->hdr->protocol : ipv6->type(); + } + NetdumpIP sourceIP() + { + return ip4 ? ipv4->sourceIP() : ipv6->sourceIP(); + } + NetdumpIP destinationIP() + { + return ip4 ? ipv4->destinationIP() : ipv6->destinationIP(); + } + const uint8_t* payload() + { + return ip4 ? ipv4->hdr->payload : ipv6->hdr->payload; + } + const uint8_t* raw() + { + return ip4 ? ipv4->raw : ipv6->raw; + } +}; + +class TCPPacket +{ +public: + struct TCPHeader + { + uint8_t sourcePort[2]; + uint8_t destinationPort[2]; + uint8_t sequenceNumber[4]; + uint8_t acknowledgementNumber[4]; + uint8_t dataOffset:4; + uint8_t reserved:6; + uint8_t controlFlags:6; + uint8_t window[2]; + uint8_t checksum[2]; + uint8_t urgentPointer[2]; + uint8_t payload[]; + }; + + TCPPacket(const uint8_t* packet) + { + hdr = reinterpret_cast(packet); + raw = packet; + } + + const TCPHeader* hdr; + const uint8_t* raw; + + uint16_t sourcePort() const + { + return NetdumpUtils::ntoh16(hdr->sourcePort); + } + uint16_t destinationPort() const + { + return NetdumpUtils::ntoh16(hdr->destinationPort); + } + uint32_t sequenceNumber() const + { + return NetdumpUtils::ntoh32(hdr->sequenceNumber); + } +}; + +class UDPPacket +{ +public: + struct UDPHeader + { + uint8_t sourcePort[2]; + uint8_t destinationPort[2]; + uint8_t length[2]; + uint8_t checksum[2]; + uint8_t payload[]; + }; + + UDPPacket(const uint8_t* packet) + { + hdr = reinterpret_cast(packet); + raw = packet; + } + + const UDPHeader* hdr; + const uint8_t* raw; + + uint16_t sourcePort() + { + return NetdumpUtils::ntoh16(hdr->sourcePort); + } + uint16_t destinationPort() + { + return NetdumpUtils::ntoh16(hdr->destinationPort); + } + uint16_t length() + { + return NetdumpUtils::ntoh16(hdr->length); + } + uint16_t checksum() + { + return NetdumpUtils::ntoh16(hdr->checksum); + } + uint16_t hdrLength() + { + return 8; + } +}; + +class DNSPacket +{ +public: + + struct DNSHeaderFlags + { + uint8_t opcode:4; + uint8_t aa:1; + uint8_t tc:1; + uint8_t rd:1; + uint8_t z:1; + uint8_t ad:1; + uint8_t cd:1; + uint8_t rcode:4; + }; + + struct DNSHeader + { + uint8_t id[2]; + uint8_t flags[2]; + uint8_t qdcount[2]; + uint8_t ancount[2]; + uint8_t nscount[2]; + uint8_t arcount[2]; + uint8_t questions[]; + uint8_t details[]; + }; + + struct DNSQuestion + { + String qname = ""; + uint16_t qtype = 0; + uint16_t qclass = 0; + }; + + struct DNSAnswer + { + String name; + uint16_t type; + uint16_t aclass; + uint32_t ttl; + uint16_t rdlength; + const uint8_t* rdata; + + NetCapture::NetdumpIP getIP() + { + if (type == 1) + { + return NetdumpIP(rdata[0],rdata[1],rdata[2],rdata[3]); + } + else if (type == 28) + { + return NetdumpIP(rdata, false); + } + else + { + return NetdumpIP(); + } + } + + }; + + DNSPacket(const uint8_t* packet) + : raw(packet), + hdr(reinterpret_cast(packet)) + {}; + + const uint8_t* raw; + const DNSHeader* hdr; + + uint16_t id() + { + return (NetdumpUtils::ntoh16(hdr->id)); + } + uint16_t qdcount() + { + return (NetdumpUtils::ntoh16(hdr->qdcount)); + } + uint16_t ancount() + { + return (NetdumpUtils::ntoh16(hdr->ancount)); + } + uint16_t nscount() + { + return (NetdumpUtils::ntoh16(hdr->nscount)); + } + uint16_t arcount() + { + return (NetdumpUtils::ntoh16(hdr->arcount)); + } + uint16_t flags() + { + return (NetdumpUtils::ntoh16(hdr->flags)); + } + + using questionResult = std::tuple; + + DNSQuestion getQuestion(uint8_t questionIdx) + { + if (questionIdx >= qdcount()) + { + Serial.printf("Question index too high %d\r\n",questionIdx); + return DNSQuestion(); + } + + const uint8_t* qPtr = hdr->details; + DNSQuestion dq; + + for (uint8_t idx=0;idx<=questionIdx;idx++) + { + std::tie(dq,qPtr) = getQuestion1(qPtr); + } + return dq; + } + + using qNameResult = std::tuple; + + questionResult getQuestion1(const uint8_t* questionPtr) + { + DNSQuestion dq; + + String n; + const uint8_t* p; + std::tie(n,p) = qName(questionPtr); // c++17 : auto [n,p] = qName(questionPtr); + + dq.qname = n; + dq.qtype = NetdumpUtils::ntoh16(p); + dq.qclass = NetdumpUtils::ntoh16(&p[2]); + return std::make_tuple(dq,p+4); + } + + qNameResult qName(const uint8_t* qname) + { + String qn; + const uint8_t* returnvalue = nullptr; + while (*qname != 0x00) + { + if (((*qname) & 0xc0) == 0) + { + if (qn.length() != 0) + { + qn += String('.'); + } + uint8_t l = *qname; + qname++; + for(int idx=0;idx; + + DNSAnswer getAnswer(uint8_t answerIdx) + { + if (answerIdx >= ancount()) + { + Serial.printf("answer index too high %d\r\n",answerIdx); + return DNSAnswer(); + } + + const uint8_t* qPtr = hdr->details; + DNSQuestion dq; + + for (uint8_t idx=0;idx(packet); + raw = packet; + } + + const ICMPHeader* hdr; + const uint8_t* raw; + +}; + +} // namespace NetCapture + +#endif /* LIBRARIES_NETDUMP_SRC_STRUCTURES_H_ */