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_ */ pFad - Phonifier reborn

    Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

    Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


    Alternative Proxies:

    Alternative Proxy

    pFad Proxy

    pFad v3 Proxy

    pFad v4 Proxy