diff --git a/astyle_goodies.conf b/astyle_goodies.conf new file mode 100644 index 0000000..b1c6ed3 --- /dev/null +++ b/astyle_goodies.conf @@ -0,0 +1,32 @@ +# Code formatting rules for Arduino examples, taken from: +# +# https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf +# + +mode=c +lineend=linux +style=allman + +# 4 spaces indentation +indent=spaces=4 + +# also indent macros +#indent-preprocessor + +# indent classes, switches (and cases), comments starting at column 1 +indent-col1-comments + +# put a space around operators +pad-oper + +# put a space after if/for/while +pad-header + +# if you like one-liners, keep them +keep-one-line-statements + +attach-closing-while +unpad-paren +pad-oper +remove-comment-prefix +add-braces \ No newline at end of file diff --git a/examples/NetDumpTest.cpp b/examples/NetDumpTest.cpp new file mode 100644 index 0000000..d239ab9 --- /dev/null +++ b/examples/NetDumpTest.cpp @@ -0,0 +1,75 @@ +// Do not remove the include below +#include "NetDumpTest.h" +#include +#include "LocalDefines.h" +#include +#include +#include + +/* + dump network packets on serial console + released to the public domain +*/ + + +Netdump nd; +ESP8266WebServer server(80); +WiFiServer ws(8000); + + +void handleRoot() { + static int rq = 0; + String a = "

You are connected, rq = "+String(rq++)+"

"; + server.send(200, "text/html", a); +} + + +File outfile = SPIFFS.open("test", "w"); + +void setup(void) { + Serial.begin(115200); + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid,password); + SPIFFS.begin(); + + server.on("/", handleRoot); + server.begin(); + +/* +// To serial, include hex print only localIP traffic + nd.printDump(Serial, true, + [](NetdumpPacket n) + { + return ((n.ethType() != 0x8912) && + ((n.sourceIP()==WiFi.localIP()) || n.destIP()==WiFi.localIP()) ); + }); +*/ + +/* +// To serial, include hex print only localIP traffic + nd.printDump(Serial, false); // To serial all traffic, no hex dump +*/ + +/* + nd.printDump(outfile, false); // To file all traffic, no hex dump, format ascii +*/ + +/* + nd.fileDump(outfile, false); // To file all traffic, no hex dump, format pcap file +*/ + +/* +// To telnet, all traffic, use `n 10.0.0.212 8000 | tcpdump -r -` + ws.begin(); + nd.tcpDump(ws); + +*/ + + +} + +void loop(void) { + server.handleClient(); + +} diff --git a/src/NetDump.h b/src/NetDumpDAV.h similarity index 100% rename from src/NetDump.h rename to src/NetDumpDAV.h diff --git a/src/Netdump/NetDump.cpp b/src/Netdump/NetDump.cpp new file mode 100644 index 0000000..aa18698 --- /dev/null +++ b/src/Netdump/NetDump.cpp @@ -0,0 +1,198 @@ +/* + 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 + +Netdump* Netdump::self; + +void Netdump::setCallback(NetdumpCallback nc) +{ + netDumpCallback = nc; +} +void Netdump::setCallback(NetdumpCallback nc, NetdumpFilter nf) +{ + netDumpFilter = nf; + netDumpCallback = nc; +} +void Netdump::setFilter(NetdumpFilter nf) +{ + netDumpFilter = nf; +} +void Netdump::reset() +{ + setCallback(nullptr, nullptr); +} +void Netdump::printDump(Print& out, NetdumpPacket::PacketDetail ndd, NetdumpFilter nf) +{ + out.printf("netDump starting\r\n"); + setCallback(std::bind(&Netdump::printDumpProcess, this, std::ref(out), ndd, std::placeholders::_1), nf); + // setCallback([out,ndd](NetdumpPacket& ndp){printDumpProcess(out,ndd,ndp);}); + setCallback([&out, ndd, this](NetdumpPacket & ndp) + { + printDumpProcess(out, ndd, ndp); + }, nf); + + +} +void Netdump::fileDump(File outfile, NetdumpFilter nf) +{ + + char buf[24]; + + *(uint32_t*)&buf[0] = 0xa1b2c3d4; + *(uint32_t*)&buf[4] = 0x00040002; + *(uint32_t*)&buf[8] = 0; + *(uint32_t*)&buf[12] = 0; + *(uint32_t*)&buf[16] = 1024; + *(uint32_t*)&buf[20] = 1; + + outfile.write(buf, 24); + // setCallback( std::bind(&Netdump::fileDumpProcess, this, outfile, std::placeholders::_1)); + setCallback([outfile, this](NetdumpPacket & ndp) + { + fileDumpProcess(outfile, ndp); + }, nf); +} +void Netdump::tcpDump(WiFiServer &tcpDumpServer, NetdumpFilter nf) +{ + // Get initialize code from netdumpout.cpp + if (packetBuffer) + { + delete packetBuffer; + } + packetBuffer = new char[2048]; + bufferIndex = 0; + + // schedule_function(std::bind(&Netdump::tcpDumpLoop,this,std::ref(tcpDumpServer))); + schedule_function([&tcpDumpServer, this]() + { + tcpDumpLoop(tcpDumpServer); + }); + Serial.printf("scheduled\r\n"); +} + +void Netdump::capture(int netif_idx, const char* data, size_t len, int out, int success) +{ + NetdumpPacket np(netif_idx, data, len, out, success); + if (self->netDumpCallback) + { + if (self->netDumpFilter && !self->netDumpFilter(np)) + { + return; + } + self->netDumpCallback(np); + } +} + +void Netdump::printDumpProcess(Print& out, NetdumpPacket::PacketDetail ndd, NetdumpPacket np) +{ + out.printf("%8d %s", millis(), np.toString(ndd).c_str()); +} +void Netdump::fileDumpProcess(File outfile, NetdumpPacket np) +{ + size_t incl_len = np.len > 1024 ? 1024 : np.len; + char buf[16]; + + struct timeval tv; + gettimeofday(&tv, nullptr); + *(uint32_t*)&buf[0] = tv.tv_sec; + *(uint32_t*)&buf[4] = tv.tv_usec; + *(uint32_t*)&buf[8] = incl_len; + *(uint32_t*)&buf[12] = np.len; + outfile.write(buf, 16); + + outfile.write(np.data, incl_len); +} +void Netdump::tcpDumpProcess(NetdumpPacket np) +{ + // Get capture code from netdumpout.cpp + if (np.isIPv4() && np.isTCP() + && ((np.out && np.getSrcPort() == tcpDumpClient.localPort()) + || (!np.out && np.getDstPort() == tcpDumpClient.localPort()) + ) + ) + { + // skip myself + return; + } + size_t incl_len = np.len > 1024 ? 1024 : np.len; + + struct timeval tv; + gettimeofday(&tv, nullptr); + *(uint32_t*)&packetBuffer[bufferIndex] = tv.tv_sec; + *(uint32_t*)&packetBuffer[bufferIndex + 4] = tv.tv_usec; + *(uint32_t*)&packetBuffer[bufferIndex + 8] = incl_len; + *(uint32_t*)&packetBuffer[bufferIndex + 12] = np.len; + bufferIndex += 16; + memcpy(&packetBuffer[bufferIndex], np.data, incl_len); + bufferIndex += incl_len; + if (bufferIndex && tcpDumpClient && tcpDumpClient.availableForWrite() >= bufferIndex) + { + tcpDumpClient.write(packetBuffer, bufferIndex); + bufferIndex = 0; + } + + +} +void Netdump::tcpDumpLoop(WiFiServer &tcpDumpServer) +{ + + if (tcpDumpServer.hasClient()) + { + tcpDumpClient = tcpDumpServer.available(); + //if (fastsend) + tcpDumpClient.setNoDelay(true); + + + // pcap-savefile(5) capture preamble + *(uint32_t*)&packetBuffer[0] = 0xa1b2c3d4; + *(uint32_t*)&packetBuffer[4] = 0x00040002; + *(uint32_t*)&packetBuffer[8] = 0; + *(uint32_t*)&packetBuffer[12] = 0; + *(uint32_t*)&packetBuffer[16] = 1024; + *(uint32_t*)&packetBuffer[20] = 1; + tcpDumpClient.write(packetBuffer, 24); + bufferIndex = 0; + // setCallback(std::bind(&Netdump::tcpDumpProcess,this,std::placeholders::_1)); + setCallback([this](NetdumpPacket & ndp) + { + tcpDumpProcess(ndp); + }); + + Serial.printf("client started\r\n"); + } + if (!tcpDumpClient || !tcpDumpClient.connected()) + { + setCallback(nullptr); + } + if (bufferIndex && tcpDumpClient && tcpDumpClient.availableForWrite() >= bufferIndex) + { + Serial.printf("tcp write %d\r\n", bufferIndex); + tcpDumpClient.write(packetBuffer, bufferIndex); + bufferIndex = 0; + } + // schedule_function(std::bind(&Netdump::tcpDumpLoop,this,std::ref(tcpDumpServer))); + schedule_function([&tcpDumpServer, this]() + { + tcpDumpLoop(tcpDumpServer); + }); +} diff --git a/src/Netdump/NetDumpPacket.cpp b/src/Netdump/NetDumpPacket.cpp new file mode 100644 index 0000000..382005b --- /dev/null +++ b/src/Netdump/NetDumpPacket.cpp @@ -0,0 +1,228 @@ +/* + 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 + +void NetdumpPacket::printDetail(Print& out, String indent, const char* data, size_t size, PacketDetail pd) +{ + if (pd == PacketDetail::NONE) + { + return; + } + + uint16_t charCount = pd == PacketDetail::FULL ? 24 : 80; + + 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::FULL) + { + 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; + } +} + + +String NetdumpPacket::toString(PacketDetail netdumpDetail) +{ + StreamString sstr; + sstr.reserve(128); + + sstr.printf("%d %3s ", netif_idx, out ? "out" : "in "); + + if (isARP()) + { + sstr.printf("ARP "); + switch (getARPType()) + { + case 1 : sstr.printf("who has %s tell %s", getIP(ETH_HDR_LEN + 24).toString().c_str(), getIP(ETH_HDR_LEN + 14).toString().c_str()); + break; + case 2 : sstr.printf("%s is at ", getIP(ETH_HDR_LEN + 14).toString().c_str()); + for (int i = 0; i < 6; i++) + { + sstr.printf("%02x", (unsigned char)data[ETH_HDR_LEN + 8 + i]); + if (i < 5) + { + sstr.print(':'); + } + } + break; + } + sstr.printf("\r\n"); + return sstr; + } + + if (isIP()) + { + if (isUDP()) + { + if (isMDNS() || isDNS()) + { + sstr.printf("%s%s>%s ", isMDNS() ? "MDNS " : "DNS ", sourceIP().toString().c_str(), destIP().toString().c_str()); + sstr.printf("ID=0x%04x ", ntoh16(ETH_HDR_LEN + getIpHdrLen() + 8)); + sstr.printf("F=0x%04x ", ntoh16(ETH_HDR_LEN + getIpHdrLen() + 8 + 2)); + if (uint16_t t = ntoh16(ETH_HDR_LEN + getIpHdrLen() + 8 + 4)) + { + sstr.printf("Q=%d ", t); + } + if (uint16_t t = ntoh16(ETH_HDR_LEN + getIpHdrLen() + 8 + 6)) + { + sstr.printf("R=%d ", t); + } + if (uint16_t t = ntoh16(ETH_HDR_LEN + getIpHdrLen() + 8 + 8)) + { + sstr.printf("TR=%d ", t); + } + if (uint16_t t = ntoh16(ETH_HDR_LEN + getIpHdrLen() + 8 + 10)) + { + sstr.printf("DR=%d ", t); + } + sstr.printf("\r\n"); + } + else + { + String udpType; + + if (isSSDP()) + { + udpType = "SSDP"; + } + else if (isDHCP()) + { + udpType = "DHCP"; + } + else if (isWSDD()) + { + udpType = "WSDD"; + } + else + { + udpType = "UDP "; + } + sstr.printf("%s %s>%s ", udpType.c_str(), sourceIP().toString().c_str(), destIP().toString().c_str()); + sstr.printf("%d:%d", getSrcPort(), getDstPort()); + sstr.printf("\r\n"); + } + printDetail(sstr, " H ", &data[ETH_HDR_LEN + getIpHdrLen()], getUdpHdrLen(), netdumpDetail); + printDetail(sstr, " D ", &data[ETH_HDR_LEN + getIpHdrLen() + getUdpHdrLen()], getUdpLen(), netdumpDetail); + return sstr; + } + else if (isTCP()) + { + sstr.printf(isHTTP() ? "HTTP " : "TCP "); + sstr.printf("%s>%s ", sourceIP().toString().c_str(), destIP().toString().c_str()); + sstr.printf("%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(" len: %u seq: %u, ack: %u, wnd: %u ", getTcpLen(), getTcpSeq(), getTcpAck(), getTcpWindow()); + sstr.printf("\r\n"); + printDetail(sstr, " H ", &data[ETH_HDR_LEN + getIpHdrLen()], getTcpHdrLen(), netdumpDetail); + printDetail(sstr, " D ", &data[ETH_HDR_LEN + getIpHdrLen() + getTcpHdrLen()], getTcpLen(), netdumpDetail); + return sstr; + } + else if (isICMP()) + { + sstr.printf("ICMP "); + sstr.printf("%s>%s ", sourceIP().toString().c_str(), destIP().toString().c_str()); + if (isIPv4()) + { + switch (getIcmpType()) + { + case 0 : sstr.printf("ping reply"); break; + case 8 : sstr.printf("ping request"); break; + default: sstr.printf("type(0x%02x)", getIcmpType()); break; + } + } + if (isIPv6()) + { + switch (getIcmpType()) + { + case 129 : sstr.printf("ping reply"); break; + case 128 : sstr.printf("ping request"); break; + case 135 : sstr.printf("Neighbour solicitation"); break; + case 136 : sstr.printf("Neighbour advertisement"); break; + default: sstr.printf("type(0x%02x)", getIcmpType()); break; + } + } + sstr.printf("\r\n"); + return sstr; + } + else if (isIGMP()) + { + sstr.printf("IGMP "); + switch (getIgmpType()) + { + case 1 : sstr.printf("Create Group Request"); break; + case 2 : sstr.printf("Create Group Reply"); break; + case 3 : sstr.printf("Join Group Request"); break; + case 4 : sstr.printf("Join Group Reply"); break; + case 5 : sstr.printf("Leave Group Request"); break; + case 6 : sstr.printf("Leave Group Reply"); break; + case 7 : sstr.printf("Confirm Group Request"); break; + case 8 : sstr.printf("Confirm Group Reply"); break; + case 0x11 : sstr.printf("Group Membership Query"); break; + case 0x12 : sstr.printf("IGMPv1 Membership Report"); break; + case 0x22 : sstr.printf("IGMPv3 Membership Report"); break; + default: sstr.printf("type(0x%02x)", getIgmpType()); break; + } + sstr.printf("\r\n"); + return sstr; + } + else + { + sstr.printf("UKWN type = %d\r\n", ipType()); + return sstr; + } + } + + sstr.printf("Unknown packet, type = 0x%04x\r\n", ethType()); + return sstr; + +} + diff --git a/src/Netdump/Netdump.h b/src/Netdump/Netdump.h new file mode 100644 index 0000000..765d1f0 --- /dev/null +++ b/src/Netdump/Netdump.h @@ -0,0 +1,79 @@ +/* + 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 +*/ + +#ifndef __NETDUMP_H +#define __NETDUMP_H + +#include +#include +#include +#include +#include +#include +#include "NetdumpPacket.h" +#include +#include "Schedule.h" + + +using NetdumpFilter = std::function; +using NetdumpCallback = std::function; + +class Netdump +{ +public: + Netdump() + { + phy_capture = capture; + self = this; + }; + virtual ~Netdump() + { + phy_capture = nullptr; + }; + + void setCallback(NetdumpCallback nc); + void setCallback(NetdumpCallback nc, NetdumpFilter nf); + void setFilter(NetdumpFilter nf); + void reset(); + + void printDump(Print& out, NetdumpPacket::PacketDetail ndd, NetdumpFilter nf = nullptr); + void fileDump(File outfile, NetdumpFilter nf = nullptr); + void tcpDump(WiFiServer &tcpDumpServer, NetdumpFilter nf = nullptr); + + +private: + NetdumpCallback netDumpCallback = nullptr; + NetdumpFilter netDumpFilter = nullptr; + + static Netdump* self; + + static void capture(int netif_idx, const char* data, size_t len, int out, int success); + void printDumpProcess(Print& out, NetdumpPacket::PacketDetail ndd, NetdumpPacket np); + void fileDumpProcess(File outfile, NetdumpPacket np); + void tcpDumpProcess(NetdumpPacket np); + void tcpDumpLoop(WiFiServer &tcpDumpServer); + + WiFiClient tcpDumpClient; + char* packetBuffer; + size_t bufferIndex; +}; + +#endif /* LIBRARIES_ESPGOODIES_HR_SRC_NETDUMP_H_ */ diff --git a/src/Netdump/NetdumpIP.cpp b/src/Netdump/NetdumpIP.cpp new file mode 100644 index 0000000..e825ec5 --- /dev/null +++ b/src/Netdump/NetdumpIP.cpp @@ -0,0 +1,362 @@ +/* + NetdumpIP.cpp + + Created on: 18 mei 2019 + Author: Herman +*/ +#include +#include + +NetdumpIP::NetdumpIP() +{ + // TODO Auto-generated constructor stub +} + +NetdumpIP::~NetdumpIP() +{ + // TODO Auto-generated destructor stub +} + +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(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(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) +{ + for (int i = 0; i < (v == IPversion::IPV4 ? 4 : 16); i++) + { + if (a[i] != b[i]) + { + return false; + } + } + return true; +} + +bool NetdumpIP::compareIP(IPAddress ip) +{ + 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, &ip[0]); + } + break; + case IPversion::IPV6 : + if (ip.isV4() || !ip.isSet()) + { + return false; + } + else + { + return compareRaw(IPversion::IPV6, rawip, &ip[0]); + } + break; + default : + return false; + break; + } +} + +bool NetdumpIP::compareIP(NetdumpIP nip) +{ + 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; + } +} diff --git a/src/Netdump/NetdumpIP.h b/src/Netdump/NetdumpIP.h new file mode 100644 index 0000000..91eb4b4 --- /dev/null +++ b/src/Netdump/NetdumpIP.h @@ -0,0 +1,99 @@ +/* + 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 + +class NetdumpIP +{ +public: + NetdumpIP(); + virtual ~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(IPAddress ip); + NetdumpIP(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 isV4() + { + return (ipv == IPversion::IPV4); + }; + bool isV6() + { + return (ipv == IPversion::IPV6); + }; + bool isUnset() + { + return (ipv == IPversion::UNSET); + }; + bool isSet() + { + return (ipv != IPversion::UNSET); + }; + + bool compareRaw(IPversion v, const uint8_t* a, const uint8_t* b); + bool compareIP(IPAddress ip); + bool compareIP(NetdumpIP nip) ; + + bool fromString4(const char *address); + bool fromString6(const char *address); + + size_t printTo(Print& p) ; +public: + bool operator==(const IPAddress& addr) + { + 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); + }; + +}; + +#endif /* LIBRARIES_ESPGOODIES_HR_SRC_NETDUMP_NETDUMPIP_H_ */ diff --git a/src/Netdump/NetdumpPacket.h b/src/Netdump/NetdumpPacket.h new file mode 100644 index 0000000..ae92420 --- /dev/null +++ b/src/Netdump/NetdumpPacket.h @@ -0,0 +1,265 @@ +/* + 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 +*/ + +#ifndef __NETDUMP_PACKET_H +#define __NETDUMP_PACKET_H + +#include +#include +#include +#include "NetdumpIP.h" + +#define ETH_HDR_LEN 14 + +class NetdumpPacket +{ +public: + NetdumpPacket(int n, const char* d, size_t l, int o, int s) + : netif_idx(n), data(d), len(l), out(o), success(s) + {}; + + virtual ~NetdumpPacket() {}; + + enum class PacketDetail + { + NONE, + FULL, + CHARS + }; + + int netif_idx; + const char* data; + size_t len; + int out; + int success; + + uint16_t ntoh16(uint16_t idx) + { + return data[idx + 1] | (((uint16_t)data[idx]) << 8); + }; + uint32_t ntoh32(uint16_t idx) + { + return ntoh16(idx + 2) | (((uint32_t)ntoh16(idx)) << 16); + }; + uint8_t byteData(uint16_t idx) + { + return data[idx]; + } + const char* byteIdx(uint16_t idx) + { + return &data[idx]; + }; + uint16_t ethType() + { + return ntoh16(12); + }; + uint8_t ipType() + { + return isIPv4() ? data[ETH_HDR_LEN + 9] : data[ETH_HDR_LEN + 6]; + }; + + uint16_t getIpHdrLen() + { + return isIPv4() ? (((unsigned char)data[ETH_HDR_LEN]) & 0x0f) << 2 : 40 ; // IPv6 is fixed length + } + uint16_t getIpTotLen() + { + return ntoh16(ETH_HDR_LEN + 2); + } + // uint16_t getIpOptLen() { return getIpHdrLen() - 20; } + // uint16_t getIpUsrLen() { return getIpTotLen() - getIpHdrLen(); } + + uint32_t getTcpSeq() + { + return ntoh32(ETH_HDR_LEN + getIpHdrLen() + 4); + } + uint32_t getTcpAck() + { + return ntoh32(ETH_HDR_LEN + getIpHdrLen() + 8); + } + uint16_t getTcpFlags() + { + return ntoh16(ETH_HDR_LEN + getIpHdrLen() + 12); + } + uint16_t getTcpWindow() + { + return ntoh16(ETH_HDR_LEN + getIpHdrLen() + 14); + } + uint8_t getTcpHdrLen() + { + return (data[ETH_HDR_LEN + getIpHdrLen() + 12] >> 4) * 4; + };//Header len is in multiple of 4 bytes + uint16_t getTcpLen() + { + return getIpTotLen() - getIpHdrLen() - getTcpHdrLen() ; + }; + + uint8_t getIcmpType() + { + return data[ETH_HDR_LEN + getIpHdrLen() + 0]; + } + uint8_t getIgmpType() + { + return data[ETH_HDR_LEN + getIpHdrLen() + 0]; + } + + uint8_t getARPType() + { + return data[ETH_HDR_LEN + 7]; + } + bool is_ARP_who() + { + return getARPType() == 1; + } + bool is_ARP_is() + { + return getARPType() == 2; + } + + uint8_t getUdpHdrLen() + { + return 8; + }; + uint16_t getUdpLen() + { + return ntoh16(ETH_HDR_LEN + getIpHdrLen() + 4); + }; + + + bool isARP() + { + return (ethType() == 0x0806); + }; + bool isIPv4() + { + return (ethType() == 0x0800); + }; + bool isIPv6() + { + return (ethType() == 0x86dd); + }; + bool isIP() + { + return (isIPv4() || isIPv6()); + }; + bool isICMP() + { + return ((ipType() == 1) || ipType() == 58); + }; + bool isIGMP() + { + return ipType() == 2; + }; + bool isTCP() + { + return ipType() == 6; + }; + bool isUDP() + { + return ipType() == 17; + }; + bool isMDNS() + { + return hasPort(5353); + }; + bool isDNS() + { + return hasPort(53); + }; + bool isSSDP() + { + return hasPort(1900); + }; + bool isDHCP() + { + return (hasPort(546) || hasPort(547) || hasPort(67) || hasPort(68)); + }; + bool isWSDD() + { + return (hasPort(3702)); + }; + bool isHTTP() + { + return (hasPort(80)); + }; + + + NetdumpIP getIP(uint16_t idx) + { + return NetdumpIP(data[idx], + data[idx + 1], + data[idx + 2], + data[idx + 3]); + }; + + NetdumpIP getIP6(uint16_t idx) + { + return NetdumpIP((const uint8_t*)&data[idx], false); + }; + + + NetdumpIP sourceIP() + { + NetdumpIP ip; + if (isIPv4()) + { + ip = getIP(ETH_HDR_LEN + 12); + } + else if (isIPv6()) + { + ip = getIP6(ETH_HDR_LEN + 8); + } + return ip; + }; + + NetdumpIP destIP() + { + NetdumpIP ip; + if (isIPv4()) + { + ip = getIP(ETH_HDR_LEN + 16); + } + else if (isIPv6()) + { + ip = getIP6(ETH_HDR_LEN + 24); + } + return ip; + }; + uint16_t getSrcPort() + { + return ntoh16(ETH_HDR_LEN + getIpHdrLen() + 0); + } + uint16_t getDstPort() + { + return ntoh16(ETH_HDR_LEN + getIpHdrLen() + 2); + } + bool hasPort(uint16_t p) + { + return ((getSrcPort() == p) || (getDstPort() == p)); + } + + String toString(PacketDetail netdumpDetail = PacketDetail::NONE); + void printDetail(Print& out, String indent, const char* data, size_t size, PacketDetail pd); + +}; + + +#endif /* LIBRARIES_ESPGOODIES_HR_SRC_NETDUMP_PACKET_H_ */ diff --git a/src/utility/NetDump.cpp b/src/utility/NetDump.cpp deleted file mode 100644 index dc04019..0000000 --- a/src/utility/NetDump.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* - 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 -#include - -#if LWIP_VERSION_MAJOR != 1 - -static void snap (Print& out) -{ - out.println(F("(snap)")); -} - -void netDumpMac (Print& out, const char* mac) -{ - for (int i = 0; i < 6; i++) - { - out.printf("%02x", (unsigned char)mac[i]); - if (i < 5) - out.print(':'); - } -} - -void netDumpIPv4 (Print& out, const char* ethdata) -{ - for (int i = 0; i < 4; i++) - { - out.printf("%u", (unsigned char)ethdata[i]); - if (i < 3) - out.print('.'); - } -} - -static void netDumpARP (Print& out, const char* ethdata, size_t size) -{ - out.print(F(" ARP ")); - if (size < ETH_HDR_LEN + 28) - return; - char type = netDump_getARPType(ethdata); - if (type == 1) - { - out.print(F("who has ")); - netDumpIPv4(out, ethdata + ETH_HDR_LEN + 24); - out.print(F(" tell ")); - netDumpIPv4(out, ethdata + ETH_HDR_LEN + 14); - } - else if (type == 2) - { - netDumpIPv4(out, ethdata + ETH_HDR_LEN + 14); - out.print(F(" is at ")); - netDumpMac(out, ethdata + ETH_HDR_LEN + 8); - } - else - out.printf("(type=%d)", type); - out.println(); -} - -static void netDumpICMP (Print& out, const char* ethdata, size_t size) -{ - out.print(F(" ICMP ")); - if (size < 1) - return snap(out); - - switch (ethdata[ETH_HDR_LEN + 20 + 0]) - { - case 0: out.println(F("ping reply")); break; - case 8: out.println(F("ping request")); break; - default: out.printf("type(0x%02x)\r\n", ethdata[ETH_HDR_LEN + 20 + 0]); - } -} - -static void netDumpIGMP (Print& out, const char* ethdata, size_t size) -{ - out.println(F(" IGMP")); - if (size < 1) - return snap(out); - (void)ethdata; -} - -static void netDumpPort (Print& out, const char* ethdata) -{ - out.printf("%d>%d", netDump_getSrcPort(ethdata), netDump_getDstPort (ethdata)); -} - -void netDumpTCPFlags (Print& out, const char* ethdata) -{ - uint16_t flags = netDump_getTcpFlags(ethdata); - out.print('['); - const char chars [] = "FSRP.UECN"; - for (uint8_t i = 0; i < sizeof chars; i++) - if (flags & (1 << i)) - out.print(chars[i]); - out.print(']'); -} - -static void netDumpTCP (Print& out, const char* ethdata, size_t size) -{ - out.print(F(" TCP ")); - if (size < ETH_HDR_LEN + 20 + 16) - return snap(out); - netDumpPort(out, ethdata); - netDumpTCPFlags(out, ethdata); - - uint16_t tcplen = netDump_getIpUsrLen(ethdata) - netDump_getTcpHdrLen(ethdata); - uint32_t seq = netDump_getTcpSeq(ethdata); - out.printf(" seq:%u", seq); - if (tcplen) - out.printf("..%u", seq + tcplen); - out.printf(" ack:%u win:%d", - (unsigned)netDump_getTcpAck(ethdata), - netDump_getTcpWindow(ethdata)); - if (tcplen) - out.printf(" len=%u", tcplen); - - size_t options = ETH_HDR_LEN + netDump_getIpHdrLen(ethdata) + 20; - size_t options_len = netDump_getTcpHdrLen(ethdata) - 20; - for (size_t i = 0; i < options_len; ) - { - uint8_t opt = ethdata[options + i]; - uint8_t sz = opt >= 2? ethdata[options + i + 1]: 1; - switch (opt) - { - case 0: - case 1: break; - case 2: out.printf(" mss=%u", ntoh16(ethdata + options + i + 2)); break; - default: out.printf(" opt%u(%u)", opt, sz); - } - if (!opt) - // end of option - break; - i += sz; - } - - out.println(); -} - -static void netDumpUDP (Print& out, const char* ethdata, size_t size) -{ - out.print(F(" UDP ")); - if (size < ETH_HDR_LEN + 20 + 8) - return snap(out); - - netDumpPort(out, ethdata); - uint16_t udplen = netDump_getUdpUsrLen(ethdata); - uint16_t iplen = netDump_getIpUsrLen(ethdata) - 8/*udp hdr size*/; - if (udplen != iplen) - out.printf(" len=%d?", iplen); - out.printf(" len=%d\r\n", udplen); -} - -static void netDumpIPv4 (Print& out, const char* ethdata, size_t size) -{ - if (size < ETH_HDR_LEN + 20) - return snap(out); - - out.print(F(" IPv4 ")); - - netDumpIPv4(out, ethdata + ETH_HDR_LEN + 12); - out.print('>'); - netDumpIPv4(out, ethdata + ETH_HDR_LEN + 16); - //out.printf(" (iphdrlen=%d)", netDump_getIpHdrLen(ethdata)); - - if (netDump_is_ICMP(ethdata)) netDumpICMP(out, ethdata, size); - else if (netDump_is_IGMP(ethdata)) netDumpIGMP(out, ethdata, size); - else if (netDump_is_TCP(ethdata)) netDumpTCP (out, ethdata, size); - else if (netDump_is_UDP(ethdata)) netDumpUDP (out, ethdata, size); - else out.printf(" ip proto 0x%02x\r\n", netDump_getIpType(ethdata)); -} - -void netDump (Print& out, const char* ethdata, size_t size) -{ - if (size < ETH_HDR_LEN) - return snap(out); - - if (netDump_is_ARP(ethdata)) netDumpARP (out, ethdata, size); - else if (netDump_is_IPv4(ethdata)) netDumpIPv4(out, ethdata, size); - else if (netDump_is_IPv6(ethdata)) out.println(F(" IPv6")); - else out.printf(" eth proto 0x%04x\r\n", netDump_ethtype(ethdata)); -} - -#endif // !lwip-v1 diff --git a/src/utility/NetDumpHex.cpp b/src/utility/NetDumpHex.cpp index 2c68f62..4b9c36e 100644 --- a/src/utility/NetDumpHex.cpp +++ b/src/utility/NetDumpHex.cpp @@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include void netDumpHex (Print& out, const char* data, size_t size, bool show_hex, bool show_ascii, size_t per_line) { diff --git a/src/utility/NetDumpMac.cpp b/src/utility/NetDumpMac.cpp index 76a6320..d5b73ac 100644 --- a/src/utility/NetDumpMac.cpp +++ b/src/utility/NetDumpMac.cpp @@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include #if 0 #include diff --git a/src/utility/NetDumpOut.cpp b/src/utility/NetDumpOut.cpp index b13a8ba..42c920d 100644 --- a/src/utility/NetDumpOut.cpp +++ b/src/utility/NetDumpOut.cpp @@ -20,7 +20,7 @@ */ #include -#include +#include #include #include @@ -132,4 +132,4 @@ void tcpdump_loop () } } -#endif // !lwip-v1 \ No newline at end of file +#endif // !lwip-v1