blob: 4b28c631071993d404b3cd94a4b4f3a20804966c [file] [log] [blame]
Junxiao Shi2222a612015-06-06 08:01:38 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesavento60f8cc12018-05-10 22:05:21 -04002/*
Junxiao Shied0017b2022-02-04 02:16:39 +00003 * Copyright (c) 2011-2022, Regents of the University of California.
Junxiao Shi3cd47df2015-06-07 20:58:14 -07004 *
5 * This file is part of ndn-tools (Named Data Networking Essential Tools).
6 * See AUTHORS.md for complete list of ndn-tools authors and contributors.
7 *
8 * ndn-tools is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Foundation,
10 * either version 3 of the License, or (at your option) any later version.
11 *
12 * ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 */
Junxiao Shi2222a612015-06-06 08:01:38 -070019
20#include "ndndump.hpp"
21
Davide Pesaventof9126532018-08-04 18:31:06 -040022#include <arpa/inet.h>
Davide Pesavento051db002018-07-22 18:56:16 -040023#include <net/ethernet.h>
24#include <netinet/ip.h>
Davide Pesavento0ca7e692018-08-05 02:21:06 -040025#include <netinet/ip6.h>
Davide Pesavento051db002018-07-22 18:56:16 -040026#include <netinet/tcp.h>
27#include <netinet/udp.h>
Junxiao Shi2222a612015-06-06 08:01:38 -070028
Junxiao Shi022bddf2016-11-24 23:15:20 +000029#include <pcap/sll.h>
30
Junxiao Shi2222a612015-06-06 08:01:38 -070031#include <iomanip>
Davide Pesaventoc0702702017-08-24 22:04:00 -040032#include <sstream>
Junxiao Shi2222a612015-06-06 08:01:38 -070033
Vince Lehman277ecf02016-02-10 16:37:48 -060034#include <ndn-cxx/lp/nack.hpp>
35#include <ndn-cxx/lp/packet.hpp>
Davide Pesaventoecd44802018-07-23 23:48:10 -040036#include <ndn-cxx/net/ethernet.hpp>
Junxiao Shied0017b2022-02-04 02:16:39 +000037#include <ndn-cxx/util/scope.hpp>
Davide Pesaventob6b176c2018-07-28 22:05:16 -040038#include <ndn-cxx/util/string-helper.hpp>
39
40#include <boost/endian/conversion.hpp>
Junxiao Shi2222a612015-06-06 08:01:38 -070041
Davide Pesaventob3570c62022-02-19 19:19:00 -050042namespace ndn::dump {
Junxiao Shi2222a612015-06-06 08:01:38 -070043
Davide Pesaventob6b176c2018-07-28 22:05:16 -040044namespace endian = boost::endian;
45
46class OutputFormatter : noncopyable
47{
48public:
49 OutputFormatter(std::ostream& os, std::string d)
50 : m_os(os)
51 , m_delim(std::move(d))
52 {
53 }
54
55 OutputFormatter&
56 addDelimiter()
57 {
58 if (!m_isEmpty) {
59 m_wantDelim = true;
60 }
61 return *this;
62 }
63
64private:
65 std::ostream& m_os;
66 std::string m_delim;
67 bool m_isEmpty = true;
68 bool m_wantDelim = false;
69
70 template<typename T>
71 friend OutputFormatter& operator<<(OutputFormatter&, const T&);
72};
73
74template<typename T>
75OutputFormatter&
76operator<<(OutputFormatter& out, const T& val)
77{
78 if (out.m_wantDelim) {
79 out.m_os << out.m_delim;
80 out.m_wantDelim = false;
81 }
82 out.m_os << val;
83 out.m_isEmpty = false;
84 return out;
85}
86
Davide Pesaventoecd44802018-07-23 23:48:10 -040087NdnDump::~NdnDump()
Junxiao Shic1c2b832016-07-24 20:45:36 +000088{
Davide Pesaventoecd44802018-07-23 23:48:10 -040089 if (m_pcap)
90 pcap_close(m_pcap);
91}
92
Junxiao Shi2222a612015-06-06 08:01:38 -070093void
Davide Pesaventoecd44802018-07-23 23:48:10 -040094NdnDump::run()
Junxiao Shi2222a612015-06-06 08:01:38 -070095{
Davide Pesaventof9126532018-08-04 18:31:06 -040096 char errbuf[PCAP_ERRBUF_SIZE] = {};
Junxiao Shi2222a612015-06-06 08:01:38 -070097
Davide Pesaventoecd44802018-07-23 23:48:10 -040098 if (inputFile.empty() && interface.empty()) {
Junxiao Shied0017b2022-02-04 02:16:39 +000099 pcap_if_t* allDevs = nullptr;
100 int res = pcap_findalldevs(&allDevs, errbuf);
101 auto freealldevs = ndn::make_scope_exit([=] { pcap_freealldevs(allDevs); });
Davide Pesaventoecd44802018-07-23 23:48:10 -0400102
Junxiao Shied0017b2022-02-04 02:16:39 +0000103 if (res != 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500104 NDN_THROW(Error(errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -0700105 }
Junxiao Shied0017b2022-02-04 02:16:39 +0000106 if (allDevs == nullptr) {
107 NDN_THROW(Error("No network interface found"));
108 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700109
Junxiao Shied0017b2022-02-04 02:16:39 +0000110 interface = allDevs->name;
Junxiao Shi2222a612015-06-06 08:01:38 -0700111 }
112
Davide Pesaventoecd44802018-07-23 23:48:10 -0400113 std::string action;
Junxiao Shi2222a612015-06-06 08:01:38 -0700114 if (!interface.empty()) {
Davide Pesavento24c08612018-07-26 13:33:24 -0400115 m_pcap = pcap_open_live(interface.data(), 65535, wantPromisc, 1000, errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000116 if (m_pcap == nullptr) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500117 NDN_THROW(Error("Cannot open interface " + interface + ": " + errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -0700118 }
Davide Pesaventoecd44802018-07-23 23:48:10 -0400119 action = "listening on " + interface;
Junxiao Shi2222a612015-06-06 08:01:38 -0700120 }
121 else {
Davide Pesavento78de7352018-07-22 00:35:45 -0400122 m_pcap = pcap_open_offline(inputFile.data(), errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000123 if (m_pcap == nullptr) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500124 NDN_THROW(Error("Cannot open file '" + inputFile + "' for reading: " + errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -0700125 }
Davide Pesaventoecd44802018-07-23 23:48:10 -0400126 action = "reading from file " + inputFile;
Junxiao Shi2222a612015-06-06 08:01:38 -0700127 }
128
Davide Pesaventoecd44802018-07-23 23:48:10 -0400129 m_dataLinkType = pcap_datalink(m_pcap);
130 const char* dltName = pcap_datalink_val_to_name(m_dataLinkType);
131 const char* dltDesc = pcap_datalink_val_to_description(m_dataLinkType);
132 std::string formattedDlt = dltName ? dltName : to_string(m_dataLinkType);
133 if (dltDesc) {
134 formattedDlt += " ("s + dltDesc + ")";
135 }
136
137 std::cerr << "ndndump: " << action << ", link-type " << formattedDlt << std::endl;
138
139 switch (m_dataLinkType) {
140 case DLT_EN10MB:
141 case DLT_LINUX_SLL:
142 case DLT_PPP:
143 // we know how to handle these
144 break;
145 default:
Davide Pesavento80baddf2019-02-23 15:51:59 -0500146 NDN_THROW(Error("Unsupported link-layer header type " + formattedDlt));
Davide Pesaventoecd44802018-07-23 23:48:10 -0400147 }
148
149 if (!pcapFilter.empty()) {
Davide Pesavento24c08612018-07-26 13:33:24 -0400150 if (wantVerbose) {
Davide Pesaventoecd44802018-07-23 23:48:10 -0400151 std::cerr << "ndndump: using pcap filter: " << pcapFilter << std::endl;
Junxiao Shi2222a612015-06-06 08:01:38 -0700152 }
153
154 bpf_program program;
Davide Pesaventofb42a3d2018-07-26 12:33:14 -0400155 int res = pcap_compile(m_pcap, &program, pcapFilter.data(), 1, PCAP_NETMASK_UNKNOWN);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000156 if (res < 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500157 NDN_THROW(Error("Cannot compile pcap filter '" + pcapFilter + "': " + pcap_geterr(m_pcap)));
Junxiao Shi2222a612015-06-06 08:01:38 -0700158 }
Junxiao Shied0017b2022-02-04 02:16:39 +0000159 auto freecode = ndn::make_scope_exit([&] { pcap_freecode(&program); });
Junxiao Shi2222a612015-06-06 08:01:38 -0700160
Junxiao Shic1c2b832016-07-24 20:45:36 +0000161 res = pcap_setfilter(m_pcap, &program);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000162 if (res < 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500163 NDN_THROW(Error("Cannot set pcap filter: "s + pcap_geterr(m_pcap)));
Junxiao Shi2222a612015-06-06 08:01:38 -0700164 }
165 }
166
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400167 auto callback = [] (uint8_t* user, const pcap_pkthdr* pkthdr, const uint8_t* payload) {
168 reinterpret_cast<const NdnDump*>(user)->printPacket(pkthdr, payload);
169 };
170
171 if (pcap_loop(m_pcap, -1, callback, reinterpret_cast<uint8_t*>(this)) < 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500172 NDN_THROW(Error("pcap_loop: "s + pcap_geterr(m_pcap)));
Junxiao Shic1c2b832016-07-24 20:45:36 +0000173 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700174}
175
Junxiao Shi2222a612015-06-06 08:01:38 -0700176void
Davide Pesaventoecd44802018-07-23 23:48:10 -0400177NdnDump::printPacket(const pcap_pkthdr* pkthdr, const uint8_t* payload) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700178{
Davide Pesaventoecd44802018-07-23 23:48:10 -0400179 // sanity checks
180 if (pkthdr->caplen == 0) {
181 std::cout << "[Invalid header: caplen=0]" << std::endl;
182 return;
183 }
184 if (pkthdr->len == 0) {
185 std::cout << "[Invalid header: len=0]" << std::endl;
186 return;
187 }
188 else if (pkthdr->len < pkthdr->caplen) {
189 std::cout << "[Invalid header: len(" << pkthdr->len
190 << ") < caplen(" << pkthdr->caplen << ")]" << std::endl;
191 return;
192 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700193
Davide Pesaventoecd44802018-07-23 23:48:10 -0400194 std::ostringstream os;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400195 OutputFormatter out(os, ", ");
Davide Pesaventoecd44802018-07-23 23:48:10 -0400196
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400197 bool shouldPrint = false;
198 switch (m_dataLinkType) {
199 case DLT_EN10MB:
200 shouldPrint = printEther(out, payload, pkthdr->len);
201 break;
202 case DLT_LINUX_SLL:
203 shouldPrint = printLinuxSll(out, payload, pkthdr->len);
204 break;
205 case DLT_PPP:
206 shouldPrint = printPpp(out, payload, pkthdr->len);
207 break;
208 default:
Davide Pesavento59984282022-02-16 22:41:03 -0500209 NDN_CXX_UNREACHABLE;
Junxiao Shi2222a612015-06-06 08:01:38 -0700210 }
211
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400212 if (shouldPrint) {
213 if (wantTimestamp) {
214 printTimestamp(std::cout, pkthdr->ts);
Vince Lehman277ecf02016-02-10 16:37:48 -0600215 }
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400216 std::cout << os.str() << std::endl;
Junxiao Shi2222a612015-06-06 08:01:38 -0700217 }
218}
219
220void
Davide Pesavento59984282022-02-16 22:41:03 -0500221NdnDump::printTimestamp(std::ostream& os, const timeval& tv)
Junxiao Shi2222a612015-06-06 08:01:38 -0700222{
Davide Pesavento59984282022-02-16 22:41:03 -0500223 // TODO: Add more timestamp formats (time since previous packet, time since first packet, ...)
Davide Pesaventoecd44802018-07-23 23:48:10 -0400224 os << tv.tv_sec
Junxiao Shi2222a612015-06-06 08:01:38 -0700225 << "."
Davide Pesaventoecd44802018-07-23 23:48:10 -0400226 << std::setfill('0') << std::setw(6) << tv.tv_usec
227 << " ";
Junxiao Shi2222a612015-06-06 08:01:38 -0700228}
229
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400230bool
231NdnDump::dispatchByEtherType(OutputFormatter& out, const uint8_t* pkt, size_t len, uint16_t etherType) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700232{
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400233 out.addDelimiter();
Junxiao Shi2222a612015-06-06 08:01:38 -0700234
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400235 switch (etherType) {
236 case ETHERTYPE_IP:
237 return printIp4(out, pkt, len);
Davide Pesavento0ca7e692018-08-05 02:21:06 -0400238 case ETHERTYPE_IPV6:
239 return printIp6(out, pkt, len);
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400240 case ethernet::ETHERTYPE_NDN:
241 case 0x7777: // NDN ethertype used in ndnSIM
Davide Pesaventof9126532018-08-04 18:31:06 -0400242 out << "Ethernet";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400243 return printNdn(out, pkt, len);
244 default:
Davide Pesaventof9126532018-08-04 18:31:06 -0400245 out << "[Unsupported ethertype " << AsHex{etherType} << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400246 return true;
Junxiao Shi2222a612015-06-06 08:01:38 -0700247 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700248}
249
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400250bool
251NdnDump::dispatchByIpProto(OutputFormatter& out, const uint8_t* pkt, size_t len, uint8_t ipProto) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700252{
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400253 out.addDelimiter();
Junxiao Shi2222a612015-06-06 08:01:38 -0700254
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400255 switch (ipProto) {
Davide Pesavento0ca7e692018-08-05 02:21:06 -0400256 case IPPROTO_NONE:
257 out << "[No next header]";
258 return true;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400259 case IPPROTO_TCP:
260 return printTcp(out, pkt, len);
261 case IPPROTO_UDP:
262 return printUdp(out, pkt, len);
263 default:
Davide Pesaventof9126532018-08-04 18:31:06 -0400264 out << "[Unsupported IP proto " << static_cast<int>(ipProto) << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400265 return true;
266 }
267}
Junxiao Shi2222a612015-06-06 08:01:38 -0700268
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400269bool
270NdnDump::printEther(OutputFormatter& out, const uint8_t* pkt, size_t len) const
271{
272 // IEEE 802.3 Ethernet
Junxiao Shi2222a612015-06-06 08:01:38 -0700273
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400274 if (len < ethernet::HDR_LEN) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400275 out << "Truncated Ethernet frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400276 return true;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000277 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700278
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400279 auto ether = reinterpret_cast<const ether_header*>(pkt);
280 pkt += ethernet::HDR_LEN;
281 len -= ethernet::HDR_LEN;
282
283 return dispatchByEtherType(out, pkt, len, endian::big_to_native(ether->ether_type));
284}
285
286bool
287NdnDump::printLinuxSll(OutputFormatter& out, const uint8_t* pkt, size_t len) const
288{
289 // Linux "cooked" capture encapsulation
290 // https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
291
292 if (len < SLL_HDR_LEN) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400293 out << "Truncated LINUX_SLL frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400294 return true;
295 }
296
297 auto sll = reinterpret_cast<const sll_header*>(pkt);
298 pkt += SLL_HDR_LEN;
299 len -= SLL_HDR_LEN;
300
301 return dispatchByEtherType(out, pkt, len, endian::big_to_native(sll->sll_protocol));
302}
303
304bool
305NdnDump::printPpp(OutputFormatter& out, const uint8_t* pkt, size_t len) const
306{
307 // PPP, as per RFC 1661 and RFC 1662; if the first 2 bytes are 0xff and 0x03,
308 // it's PPP in HDLC-like framing, with the PPP header following those two bytes,
309 // otherwise it's PPP without framing, and the packet begins with the PPP header.
310 // The data in the frame is not octet-stuffed or bit-stuffed.
311
312 if (len < 2) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400313 out << "Truncated PPP frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400314 return true;
315 }
316
317 if (pkt[0] == 0xff && pkt[1] == 0x03) {
318 // PPP in HDLC-like framing, skip the Address and Control fields
319 if (len < 4) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400320 out << "Truncated PPP frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400321 return true;
322 }
323 pkt += 2;
324 len -= 2;
325 }
326
327 unsigned int proto = pkt[0];
328 if (proto & 0x1) {
329 // Protocol field is compressed in 1 byte
330 pkt += 1;
331 len -= 1;
332 }
333 else {
334 // Protocol field is 2 bytes, in network byte order
335 proto = (proto << 8) | pkt[1];
336 pkt += 2;
337 len -= 2;
338 }
339
340 switch (proto) {
341 case 0x0077: // NDN in PPP frame, used by ndnSIM pcap traces
342 // For some reason, ndnSIM produces PPP frames with 2 extra bytes
343 // between the protocol field and the beginning of the NDN packet
344 if (len < 2) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400345 out << "Truncated NDN/PPP frame";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400346 return true;
347 }
348 pkt += 2;
349 len -= 2;
Davide Pesaventof9126532018-08-04 18:31:06 -0400350 out << "PPP";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400351 return printNdn(out, pkt, len);
352 default:
Davide Pesaventof9126532018-08-04 18:31:06 -0400353 out << "[Unsupported PPP proto " << AsHex{proto} << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400354 return true;
355 }
356}
357
Davide Pesaventof9126532018-08-04 18:31:06 -0400358static void
359printIpAddress(OutputFormatter& out, int af, const void* addr)
360{
361 char addrStr[1 + std::max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)] = {};
362 if (inet_ntop(af, addr, addrStr, sizeof(addrStr))) {
363 out << addrStr;
364 }
365 else {
366 out << "???";
367 }
368}
369
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400370bool
371NdnDump::printIp4(OutputFormatter& out, const uint8_t* pkt, size_t len) const
372{
Davide Pesaventof9126532018-08-04 18:31:06 -0400373 out.addDelimiter() << "IP ";
374
375 if (len < sizeof(ip)) {
376 out << "truncated header, length " << len;
377 return true;
378 }
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400379
380 auto ih = reinterpret_cast<const ip*>(pkt);
Davide Pesaventof9126532018-08-04 18:31:06 -0400381 if (ih->ip_v != 4) {
382 // huh? link layer said this was an IPv4 packet but IP header says otherwise
383 out << "bad version " << ih->ip_v;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400384 return true;
385 }
386
Davide Pesaventof9126532018-08-04 18:31:06 -0400387 size_t ipHdrLen = ih->ip_hl * 4;
388 if (ipHdrLen < sizeof(ip)) {
389 out << "bad header length " << ipHdrLen;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400390 return true;
391 }
392
Davide Pesaventof9126532018-08-04 18:31:06 -0400393 size_t ipLen = endian::big_to_native(ih->ip_len);
394 if (ipLen < ipHdrLen) {
395 out << "bad length " << ipLen;
396 return true;
397 }
398 if (ipLen > len) {
399 out << "truncated packet, " << ipLen - len << " bytes missing";
400 return true;
401 }
402 // if we reached this point, the following is true:
403 // sizeof(ip) <= ipHdrLen <= ipLen <= len
404
405 printIpAddress(out, AF_INET, &ih->ip_src);
406 out << " > ";
407 printIpAddress(out, AF_INET, &ih->ip_dst);
408
409 pkt += ipHdrLen;
410 len -= ipHdrLen;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400411
412 return dispatchByIpProto(out, pkt, len, ih->ip_p);
413}
414
415bool
Davide Pesavento0ca7e692018-08-05 02:21:06 -0400416NdnDump::printIp6(OutputFormatter& out, const uint8_t* pkt, size_t len) const
417{
418 out.addDelimiter() << "IP6 ";
419
420 if (len < sizeof(ip6_hdr)) {
421 out << "truncated header, length " << len;
422 return true;
423 }
424
425 auto ip6 = reinterpret_cast<const ip6_hdr*>(pkt);
426 unsigned int ipVer = (ip6->ip6_vfc & 0xf0) >> 4;
427 if (ipVer != 6) {
428 // huh? link layer said this was an IPv6 packet but IP header says otherwise
429 out << "bad version " << ipVer;
430 return true;
431 }
432
433 pkt += sizeof(ip6_hdr);
434 len -= sizeof(ip6_hdr);
435
436 size_t payloadLen = endian::big_to_native(ip6->ip6_plen);
437 if (len < payloadLen) {
438 out << "truncated payload, " << payloadLen - len << " bytes missing";
439 return true;
440 }
441
442 printIpAddress(out, AF_INET6, &ip6->ip6_src);
443 out << " > ";
444 printIpAddress(out, AF_INET6, &ip6->ip6_dst);
445
446 // we assume no extension headers are present
447 return dispatchByIpProto(out, pkt, len, ip6->ip6_nxt);
448}
449
450bool
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400451NdnDump::printTcp(OutputFormatter& out, const uint8_t* pkt, size_t len) const
452{
Davide Pesaventof9126532018-08-04 18:31:06 -0400453 out.addDelimiter() << "TCP";
454
455 if (len < sizeof(tcphdr)) {
456 out << " truncated header, length " << len;
457 return true;
458 }
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400459
460 auto th = reinterpret_cast<const tcphdr*>(pkt);
Davide Pesavento7b9837b2019-02-23 19:07:50 -0500461 size_t tcpHdrLen = th->TH_OFF * 4;
Davide Pesaventof9126532018-08-04 18:31:06 -0400462 if (tcpHdrLen < sizeof(tcphdr)) {
463 out << " bad header length " << tcpHdrLen;
464 return true;
465 }
466 if (tcpHdrLen > len) {
467 out << " truncated header, " << tcpHdrLen - len << " bytes missing";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400468 return true;
469 }
470
Davide Pesaventof9126532018-08-04 18:31:06 -0400471 pkt += tcpHdrLen;
472 len -= tcpHdrLen;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400473
Davide Pesaventof9126532018-08-04 18:31:06 -0400474 out.addDelimiter() << "length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400475
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400476 return printNdn(out, pkt, len);
477}
478
479bool
480NdnDump::printUdp(OutputFormatter& out, const uint8_t* pkt, size_t len) const
481{
Davide Pesaventof9126532018-08-04 18:31:06 -0400482 out.addDelimiter() << "UDP";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400483
484 if (len < sizeof(udphdr)) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400485 out << " truncated header, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400486 return true;
487 }
488
Davide Pesaventof9126532018-08-04 18:31:06 -0400489 auto uh = reinterpret_cast<const udphdr*>(pkt);
Davide Pesavento7b9837b2019-02-23 19:07:50 -0500490 size_t udpLen = endian::big_to_native(uh->UH_LEN);
Davide Pesaventof9126532018-08-04 18:31:06 -0400491 if (udpLen < sizeof(udphdr)) {
492 out << " bad length " << udpLen;
493 return true;
494 }
495 if (udpLen > len) {
496 out << " truncated packet, " << udpLen - len << " bytes missing";
497 return true;
498 }
499 else {
500 len = udpLen;
501 }
502
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400503 pkt += sizeof(udphdr);
504 len -= sizeof(udphdr);
505
Davide Pesaventof9126532018-08-04 18:31:06 -0400506 out.addDelimiter() << "length " << len;
507
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400508 return printNdn(out, pkt, len);
509}
510
511bool
512NdnDump::printNdn(OutputFormatter& out, const uint8_t* pkt, size_t len) const
513{
514 if (len == 0) {
515 return false;
516 }
517 out.addDelimiter();
518
Davide Pesaventob3570c62022-02-19 19:19:00 -0500519 auto [isOk, block] = Block::fromBuffer({pkt, len});
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400520 if (!isOk) {
521 // if packet is incomplete, we will not be able to process it
Davide Pesaventof9126532018-08-04 18:31:06 -0400522 out << "NDN truncated packet, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400523 return true;
524 }
525
526 lp::Packet lpPacket;
527 Block netPacket;
528
529 if (block.type() == lp::tlv::LpPacket) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400530 out << "NDNLPv2";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400531 try {
532 lpPacket.wireDecode(block);
533 }
534 catch (const tlv::Error& e) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400535 out << " invalid packet: " << e.what();
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400536 return true;
537 }
538
Davide Pesaventob3570c62022-02-19 19:19:00 -0500539 if (!lpPacket.has<lp::FragmentField>()) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400540 out << " idle";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400541 return true;
542 }
543
Davide Pesaventob3570c62022-02-19 19:19:00 -0500544 auto [begin, end] = lpPacket.get<lp::FragmentField>();
Davide Pesavento59984282022-02-16 22:41:03 -0500545 std::tie(isOk, netPacket) = Block::fromBuffer({begin, end});
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400546 if (!isOk) {
547 // if network packet is fragmented, we will not be able to process it
Davide Pesaventof9126532018-08-04 18:31:06 -0400548 out << " fragment";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400549 return true;
550 }
551 }
552 else {
553 netPacket = std::move(block);
554 }
Davide Pesaventof9126532018-08-04 18:31:06 -0400555 out.addDelimiter();
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400556
557 try {
558 switch (netPacket.type()) {
559 case tlv::Interest: {
560 Interest interest(netPacket);
561 if (!matchesFilter(interest.getName())) {
562 return false;
563 }
564
565 if (lpPacket.has<lp::NackField>()) {
566 lp::Nack nack(interest);
567 nack.setHeader(lpPacket.get<lp::NackField>());
Davide Pesaventof9126532018-08-04 18:31:06 -0400568 out << "NACK (" << nack.getReason() << "): " << interest;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400569 }
570 else {
571 out << "INTEREST: " << interest;
572 }
573 break;
574 }
575 case tlv::Data: {
576 Data data(netPacket);
577 if (!matchesFilter(data.getName())) {
578 return false;
579 }
580
581 out << "DATA: " << data.getName();
582 break;
583 }
584 default: {
Davide Pesaventof9126532018-08-04 18:31:06 -0400585 out << "[Unsupported NDN packet type " << netPacket.type() << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400586 break;
587 }
588 }
589 }
590 catch (const tlv::Error& e) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400591 out << "invalid network packet: " << e.what();
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400592 }
593
594 return true;
Junxiao Shi2222a612015-06-06 08:01:38 -0700595}
596
Junxiao Shic1c2b832016-07-24 20:45:36 +0000597bool
Davide Pesaventoecd44802018-07-23 23:48:10 -0400598NdnDump::matchesFilter(const Name& name) const
Junxiao Shic1c2b832016-07-24 20:45:36 +0000599{
Davide Pesavento78de7352018-07-22 00:35:45 -0400600 if (!nameFilter)
Junxiao Shic1c2b832016-07-24 20:45:36 +0000601 return true;
602
603 /// \todo Switch to NDN regular expressions
Davide Pesavento78de7352018-07-22 00:35:45 -0400604 return std::regex_match(name.toUri(), *nameFilter);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000605}
606
Davide Pesaventob3570c62022-02-19 19:19:00 -0500607} // namespace ndn::dump