blob: 00f24224250e4cd799ad721e5af0026c6757c63f [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/*
Davide Pesavento7e9d7e42023-11-11 15:00:03 -05003 * Copyright (c) 2011-2023, 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
Davide Pesavento7e9d7e42023-11-11 15:00:03 -050034#include <ndn-cxx/lp/fields.hpp>
Vince Lehman277ecf02016-02-10 16:37:48 -060035#include <ndn-cxx/lp/nack.hpp>
36#include <ndn-cxx/lp/packet.hpp>
Davide Pesaventoecd44802018-07-23 23:48:10 -040037#include <ndn-cxx/net/ethernet.hpp>
Junxiao Shied0017b2022-02-04 02:16:39 +000038#include <ndn-cxx/util/scope.hpp>
Davide Pesaventob6b176c2018-07-28 22:05:16 -040039#include <ndn-cxx/util/string-helper.hpp>
40
41#include <boost/endian/conversion.hpp>
Junxiao Shi2222a612015-06-06 08:01:38 -070042
Davide Pesaventob3570c62022-02-19 19:19:00 -050043namespace ndn::dump {
Junxiao Shi2222a612015-06-06 08:01:38 -070044
Davide Pesaventob6b176c2018-07-28 22:05:16 -040045namespace endian = boost::endian;
46
47class OutputFormatter : noncopyable
48{
49public:
50 OutputFormatter(std::ostream& os, std::string d)
51 : m_os(os)
52 , m_delim(std::move(d))
53 {
54 }
55
56 OutputFormatter&
57 addDelimiter()
58 {
59 if (!m_isEmpty) {
60 m_wantDelim = true;
61 }
62 return *this;
63 }
64
65private:
66 std::ostream& m_os;
67 std::string m_delim;
68 bool m_isEmpty = true;
69 bool m_wantDelim = false;
70
71 template<typename T>
72 friend OutputFormatter& operator<<(OutputFormatter&, const T&);
73};
74
75template<typename T>
76OutputFormatter&
77operator<<(OutputFormatter& out, const T& val)
78{
79 if (out.m_wantDelim) {
80 out.m_os << out.m_delim;
81 out.m_wantDelim = false;
82 }
83 out.m_os << val;
84 out.m_isEmpty = false;
85 return out;
86}
87
Davide Pesaventoecd44802018-07-23 23:48:10 -040088NdnDump::~NdnDump()
Junxiao Shic1c2b832016-07-24 20:45:36 +000089{
Davide Pesaventoecd44802018-07-23 23:48:10 -040090 if (m_pcap)
91 pcap_close(m_pcap);
92}
93
Junxiao Shi2222a612015-06-06 08:01:38 -070094void
Davide Pesaventoecd44802018-07-23 23:48:10 -040095NdnDump::run()
Junxiao Shi2222a612015-06-06 08:01:38 -070096{
Davide Pesaventof9126532018-08-04 18:31:06 -040097 char errbuf[PCAP_ERRBUF_SIZE] = {};
Junxiao Shi2222a612015-06-06 08:01:38 -070098
Davide Pesaventoecd44802018-07-23 23:48:10 -040099 if (inputFile.empty() && interface.empty()) {
Junxiao Shied0017b2022-02-04 02:16:39 +0000100 pcap_if_t* allDevs = nullptr;
101 int res = pcap_findalldevs(&allDevs, errbuf);
102 auto freealldevs = ndn::make_scope_exit([=] { pcap_freealldevs(allDevs); });
Davide Pesaventoecd44802018-07-23 23:48:10 -0400103
Junxiao Shied0017b2022-02-04 02:16:39 +0000104 if (res != 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500105 NDN_THROW(Error(errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -0700106 }
Junxiao Shied0017b2022-02-04 02:16:39 +0000107 if (allDevs == nullptr) {
108 NDN_THROW(Error("No network interface found"));
109 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700110
Junxiao Shied0017b2022-02-04 02:16:39 +0000111 interface = allDevs->name;
Junxiao Shi2222a612015-06-06 08:01:38 -0700112 }
113
Davide Pesaventoecd44802018-07-23 23:48:10 -0400114 std::string action;
Junxiao Shi2222a612015-06-06 08:01:38 -0700115 if (!interface.empty()) {
Davide Pesavento24c08612018-07-26 13:33:24 -0400116 m_pcap = pcap_open_live(interface.data(), 65535, wantPromisc, 1000, errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000117 if (m_pcap == nullptr) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500118 NDN_THROW(Error("Cannot open interface " + interface + ": " + errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -0700119 }
Davide Pesaventoecd44802018-07-23 23:48:10 -0400120 action = "listening on " + interface;
Junxiao Shi2222a612015-06-06 08:01:38 -0700121 }
122 else {
Davide Pesavento78de7352018-07-22 00:35:45 -0400123 m_pcap = pcap_open_offline(inputFile.data(), errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000124 if (m_pcap == nullptr) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500125 NDN_THROW(Error("Cannot open file '" + inputFile + "' for reading: " + errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -0700126 }
Davide Pesaventoecd44802018-07-23 23:48:10 -0400127 action = "reading from file " + inputFile;
Junxiao Shi2222a612015-06-06 08:01:38 -0700128 }
129
Davide Pesaventoecd44802018-07-23 23:48:10 -0400130 m_dataLinkType = pcap_datalink(m_pcap);
131 const char* dltName = pcap_datalink_val_to_name(m_dataLinkType);
132 const char* dltDesc = pcap_datalink_val_to_description(m_dataLinkType);
133 std::string formattedDlt = dltName ? dltName : to_string(m_dataLinkType);
134 if (dltDesc) {
135 formattedDlt += " ("s + dltDesc + ")";
136 }
137
138 std::cerr << "ndndump: " << action << ", link-type " << formattedDlt << std::endl;
139
140 switch (m_dataLinkType) {
141 case DLT_EN10MB:
142 case DLT_LINUX_SLL:
143 case DLT_PPP:
144 // we know how to handle these
145 break;
146 default:
Davide Pesavento80baddf2019-02-23 15:51:59 -0500147 NDN_THROW(Error("Unsupported link-layer header type " + formattedDlt));
Davide Pesaventoecd44802018-07-23 23:48:10 -0400148 }
149
150 if (!pcapFilter.empty()) {
Davide Pesavento24c08612018-07-26 13:33:24 -0400151 if (wantVerbose) {
Davide Pesaventoecd44802018-07-23 23:48:10 -0400152 std::cerr << "ndndump: using pcap filter: " << pcapFilter << std::endl;
Junxiao Shi2222a612015-06-06 08:01:38 -0700153 }
154
155 bpf_program program;
Davide Pesaventofb42a3d2018-07-26 12:33:14 -0400156 int res = pcap_compile(m_pcap, &program, pcapFilter.data(), 1, PCAP_NETMASK_UNKNOWN);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000157 if (res < 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500158 NDN_THROW(Error("Cannot compile pcap filter '" + pcapFilter + "': " + pcap_geterr(m_pcap)));
Junxiao Shi2222a612015-06-06 08:01:38 -0700159 }
Junxiao Shied0017b2022-02-04 02:16:39 +0000160 auto freecode = ndn::make_scope_exit([&] { pcap_freecode(&program); });
Junxiao Shi2222a612015-06-06 08:01:38 -0700161
Junxiao Shic1c2b832016-07-24 20:45:36 +0000162 res = pcap_setfilter(m_pcap, &program);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000163 if (res < 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500164 NDN_THROW(Error("Cannot set pcap filter: "s + pcap_geterr(m_pcap)));
Junxiao Shi2222a612015-06-06 08:01:38 -0700165 }
166 }
167
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400168 auto callback = [] (uint8_t* user, const pcap_pkthdr* pkthdr, const uint8_t* payload) {
169 reinterpret_cast<const NdnDump*>(user)->printPacket(pkthdr, payload);
170 };
171
172 if (pcap_loop(m_pcap, -1, callback, reinterpret_cast<uint8_t*>(this)) < 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500173 NDN_THROW(Error("pcap_loop: "s + pcap_geterr(m_pcap)));
Junxiao Shic1c2b832016-07-24 20:45:36 +0000174 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700175}
176
Junxiao Shi2222a612015-06-06 08:01:38 -0700177void
Davide Pesaventoecd44802018-07-23 23:48:10 -0400178NdnDump::printPacket(const pcap_pkthdr* pkthdr, const uint8_t* payload) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700179{
Davide Pesaventoecd44802018-07-23 23:48:10 -0400180 // sanity checks
181 if (pkthdr->caplen == 0) {
182 std::cout << "[Invalid header: caplen=0]" << std::endl;
183 return;
184 }
185 if (pkthdr->len == 0) {
186 std::cout << "[Invalid header: len=0]" << std::endl;
187 return;
188 }
189 else if (pkthdr->len < pkthdr->caplen) {
190 std::cout << "[Invalid header: len(" << pkthdr->len
191 << ") < caplen(" << pkthdr->caplen << ")]" << std::endl;
192 return;
193 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700194
Davide Pesaventoecd44802018-07-23 23:48:10 -0400195 std::ostringstream os;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400196 OutputFormatter out(os, ", ");
Davide Pesaventoecd44802018-07-23 23:48:10 -0400197
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400198 bool shouldPrint = false;
199 switch (m_dataLinkType) {
200 case DLT_EN10MB:
201 shouldPrint = printEther(out, payload, pkthdr->len);
202 break;
203 case DLT_LINUX_SLL:
204 shouldPrint = printLinuxSll(out, payload, pkthdr->len);
205 break;
206 case DLT_PPP:
207 shouldPrint = printPpp(out, payload, pkthdr->len);
208 break;
209 default:
Davide Pesavento59984282022-02-16 22:41:03 -0500210 NDN_CXX_UNREACHABLE;
Junxiao Shi2222a612015-06-06 08:01:38 -0700211 }
212
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400213 if (shouldPrint) {
214 if (wantTimestamp) {
215 printTimestamp(std::cout, pkthdr->ts);
Vince Lehman277ecf02016-02-10 16:37:48 -0600216 }
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400217 std::cout << os.str() << std::endl;
Junxiao Shi2222a612015-06-06 08:01:38 -0700218 }
219}
220
221void
Davide Pesavento59984282022-02-16 22:41:03 -0500222NdnDump::printTimestamp(std::ostream& os, const timeval& tv)
Junxiao Shi2222a612015-06-06 08:01:38 -0700223{
Davide Pesavento59984282022-02-16 22:41:03 -0500224 // TODO: Add more timestamp formats (time since previous packet, time since first packet, ...)
Davide Pesaventoecd44802018-07-23 23:48:10 -0400225 os << tv.tv_sec
Junxiao Shi2222a612015-06-06 08:01:38 -0700226 << "."
Davide Pesaventoecd44802018-07-23 23:48:10 -0400227 << std::setfill('0') << std::setw(6) << tv.tv_usec
228 << " ";
Junxiao Shi2222a612015-06-06 08:01:38 -0700229}
230
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400231bool
232NdnDump::dispatchByEtherType(OutputFormatter& out, const uint8_t* pkt, size_t len, uint16_t etherType) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700233{
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400234 out.addDelimiter();
Junxiao Shi2222a612015-06-06 08:01:38 -0700235
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400236 switch (etherType) {
237 case ETHERTYPE_IP:
238 return printIp4(out, pkt, len);
Davide Pesavento0ca7e692018-08-05 02:21:06 -0400239 case ETHERTYPE_IPV6:
240 return printIp6(out, pkt, len);
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400241 case ethernet::ETHERTYPE_NDN:
242 case 0x7777: // NDN ethertype used in ndnSIM
Davide Pesaventof9126532018-08-04 18:31:06 -0400243 out << "Ethernet";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400244 return printNdn(out, pkt, len);
245 default:
Davide Pesaventof9126532018-08-04 18:31:06 -0400246 out << "[Unsupported ethertype " << AsHex{etherType} << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400247 return true;
Junxiao Shi2222a612015-06-06 08:01:38 -0700248 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700249}
250
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400251bool
252NdnDump::dispatchByIpProto(OutputFormatter& out, const uint8_t* pkt, size_t len, uint8_t ipProto) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700253{
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400254 out.addDelimiter();
Junxiao Shi2222a612015-06-06 08:01:38 -0700255
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400256 switch (ipProto) {
Davide Pesavento0ca7e692018-08-05 02:21:06 -0400257 case IPPROTO_NONE:
258 out << "[No next header]";
259 return true;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400260 case IPPROTO_TCP:
261 return printTcp(out, pkt, len);
262 case IPPROTO_UDP:
263 return printUdp(out, pkt, len);
264 default:
Davide Pesaventof9126532018-08-04 18:31:06 -0400265 out << "[Unsupported IP proto " << static_cast<int>(ipProto) << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400266 return true;
267 }
268}
Junxiao Shi2222a612015-06-06 08:01:38 -0700269
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400270bool
271NdnDump::printEther(OutputFormatter& out, const uint8_t* pkt, size_t len) const
272{
273 // IEEE 802.3 Ethernet
Junxiao Shi2222a612015-06-06 08:01:38 -0700274
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400275 if (len < ethernet::HDR_LEN) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400276 out << "Truncated Ethernet frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400277 return true;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000278 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700279
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400280 auto ether = reinterpret_cast<const ether_header*>(pkt);
281 pkt += ethernet::HDR_LEN;
282 len -= ethernet::HDR_LEN;
283
284 return dispatchByEtherType(out, pkt, len, endian::big_to_native(ether->ether_type));
285}
286
287bool
288NdnDump::printLinuxSll(OutputFormatter& out, const uint8_t* pkt, size_t len) const
289{
290 // Linux "cooked" capture encapsulation
291 // https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
292
293 if (len < SLL_HDR_LEN) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400294 out << "Truncated LINUX_SLL frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400295 return true;
296 }
297
298 auto sll = reinterpret_cast<const sll_header*>(pkt);
299 pkt += SLL_HDR_LEN;
300 len -= SLL_HDR_LEN;
301
302 return dispatchByEtherType(out, pkt, len, endian::big_to_native(sll->sll_protocol));
303}
304
305bool
306NdnDump::printPpp(OutputFormatter& out, const uint8_t* pkt, size_t len) const
307{
308 // PPP, as per RFC 1661 and RFC 1662; if the first 2 bytes are 0xff and 0x03,
309 // it's PPP in HDLC-like framing, with the PPP header following those two bytes,
310 // otherwise it's PPP without framing, and the packet begins with the PPP header.
311 // The data in the frame is not octet-stuffed or bit-stuffed.
312
313 if (len < 2) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400314 out << "Truncated PPP frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400315 return true;
316 }
317
318 if (pkt[0] == 0xff && pkt[1] == 0x03) {
319 // PPP in HDLC-like framing, skip the Address and Control fields
320 if (len < 4) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400321 out << "Truncated PPP frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400322 return true;
323 }
324 pkt += 2;
325 len -= 2;
326 }
327
328 unsigned int proto = pkt[0];
329 if (proto & 0x1) {
330 // Protocol field is compressed in 1 byte
331 pkt += 1;
332 len -= 1;
333 }
334 else {
335 // Protocol field is 2 bytes, in network byte order
336 proto = (proto << 8) | pkt[1];
337 pkt += 2;
338 len -= 2;
339 }
340
341 switch (proto) {
342 case 0x0077: // NDN in PPP frame, used by ndnSIM pcap traces
343 // For some reason, ndnSIM produces PPP frames with 2 extra bytes
344 // between the protocol field and the beginning of the NDN packet
345 if (len < 2) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400346 out << "Truncated NDN/PPP frame";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400347 return true;
348 }
349 pkt += 2;
350 len -= 2;
Davide Pesaventof9126532018-08-04 18:31:06 -0400351 out << "PPP";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400352 return printNdn(out, pkt, len);
353 default:
Davide Pesaventof9126532018-08-04 18:31:06 -0400354 out << "[Unsupported PPP proto " << AsHex{proto} << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400355 return true;
356 }
357}
358
Davide Pesaventof9126532018-08-04 18:31:06 -0400359static void
360printIpAddress(OutputFormatter& out, int af, const void* addr)
361{
362 char addrStr[1 + std::max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)] = {};
363 if (inet_ntop(af, addr, addrStr, sizeof(addrStr))) {
364 out << addrStr;
365 }
366 else {
367 out << "???";
368 }
369}
370
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400371bool
372NdnDump::printIp4(OutputFormatter& out, const uint8_t* pkt, size_t len) const
373{
Davide Pesaventof9126532018-08-04 18:31:06 -0400374 out.addDelimiter() << "IP ";
375
376 if (len < sizeof(ip)) {
377 out << "truncated header, length " << len;
378 return true;
379 }
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400380
381 auto ih = reinterpret_cast<const ip*>(pkt);
Davide Pesaventof9126532018-08-04 18:31:06 -0400382 if (ih->ip_v != 4) {
383 // huh? link layer said this was an IPv4 packet but IP header says otherwise
384 out << "bad version " << ih->ip_v;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400385 return true;
386 }
387
Davide Pesaventof9126532018-08-04 18:31:06 -0400388 size_t ipHdrLen = ih->ip_hl * 4;
389 if (ipHdrLen < sizeof(ip)) {
390 out << "bad header length " << ipHdrLen;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400391 return true;
392 }
393
Davide Pesaventof9126532018-08-04 18:31:06 -0400394 size_t ipLen = endian::big_to_native(ih->ip_len);
395 if (ipLen < ipHdrLen) {
396 out << "bad length " << ipLen;
397 return true;
398 }
399 if (ipLen > len) {
400 out << "truncated packet, " << ipLen - len << " bytes missing";
401 return true;
402 }
403 // if we reached this point, the following is true:
404 // sizeof(ip) <= ipHdrLen <= ipLen <= len
405
406 printIpAddress(out, AF_INET, &ih->ip_src);
407 out << " > ";
408 printIpAddress(out, AF_INET, &ih->ip_dst);
409
410 pkt += ipHdrLen;
411 len -= ipHdrLen;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400412
413 return dispatchByIpProto(out, pkt, len, ih->ip_p);
414}
415
416bool
Davide Pesavento0ca7e692018-08-05 02:21:06 -0400417NdnDump::printIp6(OutputFormatter& out, const uint8_t* pkt, size_t len) const
418{
419 out.addDelimiter() << "IP6 ";
420
421 if (len < sizeof(ip6_hdr)) {
422 out << "truncated header, length " << len;
423 return true;
424 }
425
426 auto ip6 = reinterpret_cast<const ip6_hdr*>(pkt);
427 unsigned int ipVer = (ip6->ip6_vfc & 0xf0) >> 4;
428 if (ipVer != 6) {
429 // huh? link layer said this was an IPv6 packet but IP header says otherwise
430 out << "bad version " << ipVer;
431 return true;
432 }
433
434 pkt += sizeof(ip6_hdr);
435 len -= sizeof(ip6_hdr);
436
437 size_t payloadLen = endian::big_to_native(ip6->ip6_plen);
438 if (len < payloadLen) {
439 out << "truncated payload, " << payloadLen - len << " bytes missing";
440 return true;
441 }
442
443 printIpAddress(out, AF_INET6, &ip6->ip6_src);
444 out << " > ";
445 printIpAddress(out, AF_INET6, &ip6->ip6_dst);
446
447 // we assume no extension headers are present
448 return dispatchByIpProto(out, pkt, len, ip6->ip6_nxt);
449}
450
451bool
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400452NdnDump::printTcp(OutputFormatter& out, const uint8_t* pkt, size_t len) const
453{
Davide Pesaventof9126532018-08-04 18:31:06 -0400454 out.addDelimiter() << "TCP";
455
456 if (len < sizeof(tcphdr)) {
457 out << " truncated header, length " << len;
458 return true;
459 }
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400460
461 auto th = reinterpret_cast<const tcphdr*>(pkt);
Davide Pesavento7b9837b2019-02-23 19:07:50 -0500462 size_t tcpHdrLen = th->TH_OFF * 4;
Davide Pesaventof9126532018-08-04 18:31:06 -0400463 if (tcpHdrLen < sizeof(tcphdr)) {
464 out << " bad header length " << tcpHdrLen;
465 return true;
466 }
467 if (tcpHdrLen > len) {
468 out << " truncated header, " << tcpHdrLen - len << " bytes missing";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400469 return true;
470 }
471
Davide Pesaventof9126532018-08-04 18:31:06 -0400472 pkt += tcpHdrLen;
473 len -= tcpHdrLen;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400474
Davide Pesaventof9126532018-08-04 18:31:06 -0400475 out.addDelimiter() << "length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400476
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400477 return printNdn(out, pkt, len);
478}
479
480bool
481NdnDump::printUdp(OutputFormatter& out, const uint8_t* pkt, size_t len) const
482{
Davide Pesaventof9126532018-08-04 18:31:06 -0400483 out.addDelimiter() << "UDP";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400484
485 if (len < sizeof(udphdr)) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400486 out << " truncated header, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400487 return true;
488 }
489
Davide Pesaventof9126532018-08-04 18:31:06 -0400490 auto uh = reinterpret_cast<const udphdr*>(pkt);
Davide Pesavento7b9837b2019-02-23 19:07:50 -0500491 size_t udpLen = endian::big_to_native(uh->UH_LEN);
Davide Pesaventof9126532018-08-04 18:31:06 -0400492 if (udpLen < sizeof(udphdr)) {
493 out << " bad length " << udpLen;
494 return true;
495 }
496 if (udpLen > len) {
497 out << " truncated packet, " << udpLen - len << " bytes missing";
498 return true;
499 }
500 else {
501 len = udpLen;
502 }
503
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400504 pkt += sizeof(udphdr);
505 len -= sizeof(udphdr);
506
Davide Pesaventof9126532018-08-04 18:31:06 -0400507 out.addDelimiter() << "length " << len;
508
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400509 return printNdn(out, pkt, len);
510}
511
512bool
513NdnDump::printNdn(OutputFormatter& out, const uint8_t* pkt, size_t len) const
514{
515 if (len == 0) {
516 return false;
517 }
518 out.addDelimiter();
519
Davide Pesaventob3570c62022-02-19 19:19:00 -0500520 auto [isOk, block] = Block::fromBuffer({pkt, len});
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400521 if (!isOk) {
522 // if packet is incomplete, we will not be able to process it
Davide Pesaventof9126532018-08-04 18:31:06 -0400523 out << "NDN truncated packet, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400524 return true;
525 }
526
527 lp::Packet lpPacket;
528 Block netPacket;
529
530 if (block.type() == lp::tlv::LpPacket) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400531 out << "NDNLPv2";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400532 try {
533 lpPacket.wireDecode(block);
534 }
535 catch (const tlv::Error& e) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400536 out << " invalid packet: " << e.what();
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400537 return true;
538 }
539
Davide Pesaventob3570c62022-02-19 19:19:00 -0500540 if (!lpPacket.has<lp::FragmentField>()) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400541 out << " idle";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400542 return true;
543 }
544
Davide Pesaventob3570c62022-02-19 19:19:00 -0500545 auto [begin, end] = lpPacket.get<lp::FragmentField>();
Davide Pesavento59984282022-02-16 22:41:03 -0500546 std::tie(isOk, netPacket) = Block::fromBuffer({begin, end});
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400547 if (!isOk) {
548 // if network packet is fragmented, we will not be able to process it
Davide Pesaventof9126532018-08-04 18:31:06 -0400549 out << " fragment";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400550 return true;
551 }
552 }
553 else {
554 netPacket = std::move(block);
555 }
Davide Pesaventof9126532018-08-04 18:31:06 -0400556 out.addDelimiter();
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400557
558 try {
559 switch (netPacket.type()) {
560 case tlv::Interest: {
561 Interest interest(netPacket);
562 if (!matchesFilter(interest.getName())) {
563 return false;
564 }
565
566 if (lpPacket.has<lp::NackField>()) {
567 lp::Nack nack(interest);
568 nack.setHeader(lpPacket.get<lp::NackField>());
Davide Pesaventof9126532018-08-04 18:31:06 -0400569 out << "NACK (" << nack.getReason() << "): " << interest;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400570 }
571 else {
572 out << "INTEREST: " << interest;
573 }
574 break;
575 }
576 case tlv::Data: {
577 Data data(netPacket);
578 if (!matchesFilter(data.getName())) {
579 return false;
580 }
581
582 out << "DATA: " << data.getName();
583 break;
584 }
585 default: {
Davide Pesaventof9126532018-08-04 18:31:06 -0400586 out << "[Unsupported NDN packet type " << netPacket.type() << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400587 break;
588 }
589 }
590 }
591 catch (const tlv::Error& e) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400592 out << "invalid network packet: " << e.what();
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400593 }
594
595 return true;
Junxiao Shi2222a612015-06-06 08:01:38 -0700596}
597
Junxiao Shic1c2b832016-07-24 20:45:36 +0000598bool
Davide Pesaventoecd44802018-07-23 23:48:10 -0400599NdnDump::matchesFilter(const Name& name) const
Junxiao Shic1c2b832016-07-24 20:45:36 +0000600{
Davide Pesavento78de7352018-07-22 00:35:45 -0400601 if (!nameFilter)
Junxiao Shic1c2b832016-07-24 20:45:36 +0000602 return true;
603
604 /// \todo Switch to NDN regular expressions
Davide Pesavento78de7352018-07-22 00:35:45 -0400605 return std::regex_match(name.toUri(), *nameFilter);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000606}
607
Davide Pesaventob3570c62022-02-19 19:19:00 -0500608} // namespace ndn::dump