blob: a3f5fd0fc382866158450baf1f981e7fd578597e [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 Pesavento11fc3eb2024-01-26 01:46:56 -05003 * Copyright (c) 2011-2024, 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 Pesavento5748e822024-01-26 18:40:22 -050032#include <iostream>
Davide Pesaventoc0702702017-08-24 22:04:00 -040033#include <sstream>
Junxiao Shi2222a612015-06-06 08:01:38 -070034
Davide Pesavento7e9d7e42023-11-11 15:00:03 -050035#include <ndn-cxx/lp/fields.hpp>
Vince Lehman277ecf02016-02-10 16:37:48 -060036#include <ndn-cxx/lp/nack.hpp>
37#include <ndn-cxx/lp/packet.hpp>
Davide Pesaventoecd44802018-07-23 23:48:10 -040038#include <ndn-cxx/net/ethernet.hpp>
Davide Pesavento5748e822024-01-26 18:40:22 -050039#include <ndn-cxx/util/backports.hpp>
40#include <ndn-cxx/util/exception.hpp>
Junxiao Shied0017b2022-02-04 02:16:39 +000041#include <ndn-cxx/util/scope.hpp>
Davide Pesaventob6b176c2018-07-28 22:05:16 -040042#include <ndn-cxx/util/string-helper.hpp>
43
44#include <boost/endian/conversion.hpp>
Junxiao Shi2222a612015-06-06 08:01:38 -070045
Davide Pesaventob3570c62022-02-19 19:19:00 -050046namespace ndn::dump {
Junxiao Shi2222a612015-06-06 08:01:38 -070047
Davide Pesaventob6b176c2018-07-28 22:05:16 -040048namespace endian = boost::endian;
49
50class OutputFormatter : noncopyable
51{
52public:
53 OutputFormatter(std::ostream& os, std::string d)
54 : m_os(os)
55 , m_delim(std::move(d))
56 {
57 }
58
59 OutputFormatter&
60 addDelimiter()
61 {
62 if (!m_isEmpty) {
63 m_wantDelim = true;
64 }
65 return *this;
66 }
67
68private:
69 std::ostream& m_os;
70 std::string m_delim;
71 bool m_isEmpty = true;
72 bool m_wantDelim = false;
73
74 template<typename T>
75 friend OutputFormatter& operator<<(OutputFormatter&, const T&);
76};
77
78template<typename T>
79OutputFormatter&
80operator<<(OutputFormatter& out, const T& val)
81{
82 if (out.m_wantDelim) {
83 out.m_os << out.m_delim;
84 out.m_wantDelim = false;
85 }
86 out.m_os << val;
87 out.m_isEmpty = false;
88 return out;
89}
90
Davide Pesaventoecd44802018-07-23 23:48:10 -040091NdnDump::~NdnDump()
Junxiao Shic1c2b832016-07-24 20:45:36 +000092{
Davide Pesaventoecd44802018-07-23 23:48:10 -040093 if (m_pcap)
94 pcap_close(m_pcap);
95}
96
Junxiao Shi2222a612015-06-06 08:01:38 -070097void
Davide Pesaventoecd44802018-07-23 23:48:10 -040098NdnDump::run()
Junxiao Shi2222a612015-06-06 08:01:38 -070099{
Davide Pesaventof9126532018-08-04 18:31:06 -0400100 char errbuf[PCAP_ERRBUF_SIZE] = {};
Junxiao Shi2222a612015-06-06 08:01:38 -0700101
Davide Pesaventoecd44802018-07-23 23:48:10 -0400102 if (inputFile.empty() && interface.empty()) {
Junxiao Shied0017b2022-02-04 02:16:39 +0000103 pcap_if_t* allDevs = nullptr;
104 int res = pcap_findalldevs(&allDevs, errbuf);
105 auto freealldevs = ndn::make_scope_exit([=] { pcap_freealldevs(allDevs); });
Davide Pesaventoecd44802018-07-23 23:48:10 -0400106
Junxiao Shied0017b2022-02-04 02:16:39 +0000107 if (res != 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500108 NDN_THROW(Error(errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -0700109 }
Junxiao Shied0017b2022-02-04 02:16:39 +0000110 if (allDevs == nullptr) {
111 NDN_THROW(Error("No network interface found"));
112 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700113
Junxiao Shied0017b2022-02-04 02:16:39 +0000114 interface = allDevs->name;
Junxiao Shi2222a612015-06-06 08:01:38 -0700115 }
116
Davide Pesaventoecd44802018-07-23 23:48:10 -0400117 std::string action;
Junxiao Shi2222a612015-06-06 08:01:38 -0700118 if (!interface.empty()) {
Davide Pesavento24c08612018-07-26 13:33:24 -0400119 m_pcap = pcap_open_live(interface.data(), 65535, wantPromisc, 1000, errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000120 if (m_pcap == nullptr) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500121 NDN_THROW(Error("Cannot open interface " + interface + ": " + errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -0700122 }
Davide Pesaventoecd44802018-07-23 23:48:10 -0400123 action = "listening on " + interface;
Junxiao Shi2222a612015-06-06 08:01:38 -0700124 }
125 else {
Davide Pesavento78de7352018-07-22 00:35:45 -0400126 m_pcap = pcap_open_offline(inputFile.data(), errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000127 if (m_pcap == nullptr) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500128 NDN_THROW(Error("Cannot open file '" + inputFile + "' for reading: " + errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -0700129 }
Davide Pesaventoecd44802018-07-23 23:48:10 -0400130 action = "reading from file " + inputFile;
Junxiao Shi2222a612015-06-06 08:01:38 -0700131 }
132
Davide Pesaventoecd44802018-07-23 23:48:10 -0400133 m_dataLinkType = pcap_datalink(m_pcap);
134 const char* dltName = pcap_datalink_val_to_name(m_dataLinkType);
135 const char* dltDesc = pcap_datalink_val_to_description(m_dataLinkType);
Davide Pesavento11fc3eb2024-01-26 01:46:56 -0500136 std::string formattedDlt = dltName ? dltName : std::to_string(m_dataLinkType);
Davide Pesaventoecd44802018-07-23 23:48:10 -0400137 if (dltDesc) {
138 formattedDlt += " ("s + dltDesc + ")";
139 }
140
141 std::cerr << "ndndump: " << action << ", link-type " << formattedDlt << std::endl;
142
143 switch (m_dataLinkType) {
144 case DLT_EN10MB:
145 case DLT_LINUX_SLL:
146 case DLT_PPP:
147 // we know how to handle these
148 break;
149 default:
Davide Pesavento80baddf2019-02-23 15:51:59 -0500150 NDN_THROW(Error("Unsupported link-layer header type " + formattedDlt));
Davide Pesaventoecd44802018-07-23 23:48:10 -0400151 }
152
153 if (!pcapFilter.empty()) {
Davide Pesavento24c08612018-07-26 13:33:24 -0400154 if (wantVerbose) {
Davide Pesaventoecd44802018-07-23 23:48:10 -0400155 std::cerr << "ndndump: using pcap filter: " << pcapFilter << std::endl;
Junxiao Shi2222a612015-06-06 08:01:38 -0700156 }
157
158 bpf_program program;
Davide Pesaventofb42a3d2018-07-26 12:33:14 -0400159 int res = pcap_compile(m_pcap, &program, pcapFilter.data(), 1, PCAP_NETMASK_UNKNOWN);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000160 if (res < 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500161 NDN_THROW(Error("Cannot compile pcap filter '" + pcapFilter + "': " + pcap_geterr(m_pcap)));
Junxiao Shi2222a612015-06-06 08:01:38 -0700162 }
Junxiao Shied0017b2022-02-04 02:16:39 +0000163 auto freecode = ndn::make_scope_exit([&] { pcap_freecode(&program); });
Junxiao Shi2222a612015-06-06 08:01:38 -0700164
Junxiao Shic1c2b832016-07-24 20:45:36 +0000165 res = pcap_setfilter(m_pcap, &program);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000166 if (res < 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500167 NDN_THROW(Error("Cannot set pcap filter: "s + pcap_geterr(m_pcap)));
Junxiao Shi2222a612015-06-06 08:01:38 -0700168 }
169 }
170
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400171 auto callback = [] (uint8_t* user, const pcap_pkthdr* pkthdr, const uint8_t* payload) {
172 reinterpret_cast<const NdnDump*>(user)->printPacket(pkthdr, payload);
173 };
174
175 if (pcap_loop(m_pcap, -1, callback, reinterpret_cast<uint8_t*>(this)) < 0) {
Davide Pesavento80baddf2019-02-23 15:51:59 -0500176 NDN_THROW(Error("pcap_loop: "s + pcap_geterr(m_pcap)));
Junxiao Shic1c2b832016-07-24 20:45:36 +0000177 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700178}
179
Junxiao Shi2222a612015-06-06 08:01:38 -0700180void
Davide Pesaventoecd44802018-07-23 23:48:10 -0400181NdnDump::printPacket(const pcap_pkthdr* pkthdr, const uint8_t* payload) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700182{
Davide Pesaventoecd44802018-07-23 23:48:10 -0400183 // sanity checks
184 if (pkthdr->caplen == 0) {
185 std::cout << "[Invalid header: caplen=0]" << std::endl;
186 return;
187 }
188 if (pkthdr->len == 0) {
189 std::cout << "[Invalid header: len=0]" << std::endl;
190 return;
191 }
192 else if (pkthdr->len < pkthdr->caplen) {
193 std::cout << "[Invalid header: len(" << pkthdr->len
194 << ") < caplen(" << pkthdr->caplen << ")]" << std::endl;
195 return;
196 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700197
Davide Pesaventoecd44802018-07-23 23:48:10 -0400198 std::ostringstream os;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400199 OutputFormatter out(os, ", ");
Davide Pesaventoecd44802018-07-23 23:48:10 -0400200
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400201 bool shouldPrint = false;
202 switch (m_dataLinkType) {
203 case DLT_EN10MB:
204 shouldPrint = printEther(out, payload, pkthdr->len);
205 break;
206 case DLT_LINUX_SLL:
207 shouldPrint = printLinuxSll(out, payload, pkthdr->len);
208 break;
209 case DLT_PPP:
210 shouldPrint = printPpp(out, payload, pkthdr->len);
211 break;
212 default:
Davide Pesavento59984282022-02-16 22:41:03 -0500213 NDN_CXX_UNREACHABLE;
Junxiao Shi2222a612015-06-06 08:01:38 -0700214 }
215
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400216 if (shouldPrint) {
217 if (wantTimestamp) {
218 printTimestamp(std::cout, pkthdr->ts);
Vince Lehman277ecf02016-02-10 16:37:48 -0600219 }
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400220 std::cout << os.str() << std::endl;
Junxiao Shi2222a612015-06-06 08:01:38 -0700221 }
222}
223
224void
Davide Pesavento59984282022-02-16 22:41:03 -0500225NdnDump::printTimestamp(std::ostream& os, const timeval& tv)
Junxiao Shi2222a612015-06-06 08:01:38 -0700226{
Davide Pesavento59984282022-02-16 22:41:03 -0500227 // TODO: Add more timestamp formats (time since previous packet, time since first packet, ...)
Davide Pesaventoecd44802018-07-23 23:48:10 -0400228 os << tv.tv_sec
Junxiao Shi2222a612015-06-06 08:01:38 -0700229 << "."
Davide Pesaventoecd44802018-07-23 23:48:10 -0400230 << std::setfill('0') << std::setw(6) << tv.tv_usec
231 << " ";
Junxiao Shi2222a612015-06-06 08:01:38 -0700232}
233
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400234bool
235NdnDump::dispatchByEtherType(OutputFormatter& out, const uint8_t* pkt, size_t len, uint16_t etherType) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700236{
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400237 out.addDelimiter();
Junxiao Shi2222a612015-06-06 08:01:38 -0700238
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400239 switch (etherType) {
240 case ETHERTYPE_IP:
241 return printIp4(out, pkt, len);
Davide Pesavento0ca7e692018-08-05 02:21:06 -0400242 case ETHERTYPE_IPV6:
243 return printIp6(out, pkt, len);
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400244 case ethernet::ETHERTYPE_NDN:
245 case 0x7777: // NDN ethertype used in ndnSIM
Davide Pesaventof9126532018-08-04 18:31:06 -0400246 out << "Ethernet";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400247 return printNdn(out, pkt, len);
248 default:
Davide Pesaventof9126532018-08-04 18:31:06 -0400249 out << "[Unsupported ethertype " << AsHex{etherType} << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400250 return true;
Junxiao Shi2222a612015-06-06 08:01:38 -0700251 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700252}
253
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400254bool
255NdnDump::dispatchByIpProto(OutputFormatter& out, const uint8_t* pkt, size_t len, uint8_t ipProto) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700256{
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400257 out.addDelimiter();
Junxiao Shi2222a612015-06-06 08:01:38 -0700258
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400259 switch (ipProto) {
Davide Pesavento0ca7e692018-08-05 02:21:06 -0400260 case IPPROTO_NONE:
261 out << "[No next header]";
262 return true;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400263 case IPPROTO_TCP:
264 return printTcp(out, pkt, len);
265 case IPPROTO_UDP:
266 return printUdp(out, pkt, len);
267 default:
Davide Pesaventof9126532018-08-04 18:31:06 -0400268 out << "[Unsupported IP proto " << static_cast<int>(ipProto) << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400269 return true;
270 }
271}
Junxiao Shi2222a612015-06-06 08:01:38 -0700272
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400273bool
274NdnDump::printEther(OutputFormatter& out, const uint8_t* pkt, size_t len) const
275{
276 // IEEE 802.3 Ethernet
Junxiao Shi2222a612015-06-06 08:01:38 -0700277
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400278 if (len < ethernet::HDR_LEN) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400279 out << "Truncated Ethernet frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400280 return true;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000281 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700282
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400283 auto ether = reinterpret_cast<const ether_header*>(pkt);
284 pkt += ethernet::HDR_LEN;
285 len -= ethernet::HDR_LEN;
286
287 return dispatchByEtherType(out, pkt, len, endian::big_to_native(ether->ether_type));
288}
289
290bool
291NdnDump::printLinuxSll(OutputFormatter& out, const uint8_t* pkt, size_t len) const
292{
293 // Linux "cooked" capture encapsulation
294 // https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
295
296 if (len < SLL_HDR_LEN) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400297 out << "Truncated LINUX_SLL frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400298 return true;
299 }
300
301 auto sll = reinterpret_cast<const sll_header*>(pkt);
302 pkt += SLL_HDR_LEN;
303 len -= SLL_HDR_LEN;
304
305 return dispatchByEtherType(out, pkt, len, endian::big_to_native(sll->sll_protocol));
306}
307
308bool
309NdnDump::printPpp(OutputFormatter& out, const uint8_t* pkt, size_t len) const
310{
311 // PPP, as per RFC 1661 and RFC 1662; if the first 2 bytes are 0xff and 0x03,
312 // it's PPP in HDLC-like framing, with the PPP header following those two bytes,
313 // otherwise it's PPP without framing, and the packet begins with the PPP header.
314 // The data in the frame is not octet-stuffed or bit-stuffed.
315
316 if (len < 2) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400317 out << "Truncated PPP frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400318 return true;
319 }
320
321 if (pkt[0] == 0xff && pkt[1] == 0x03) {
322 // PPP in HDLC-like framing, skip the Address and Control fields
323 if (len < 4) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400324 out << "Truncated PPP frame, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400325 return true;
326 }
327 pkt += 2;
328 len -= 2;
329 }
330
331 unsigned int proto = pkt[0];
332 if (proto & 0x1) {
333 // Protocol field is compressed in 1 byte
334 pkt += 1;
335 len -= 1;
336 }
337 else {
338 // Protocol field is 2 bytes, in network byte order
339 proto = (proto << 8) | pkt[1];
340 pkt += 2;
341 len -= 2;
342 }
343
344 switch (proto) {
345 case 0x0077: // NDN in PPP frame, used by ndnSIM pcap traces
346 // For some reason, ndnSIM produces PPP frames with 2 extra bytes
347 // between the protocol field and the beginning of the NDN packet
348 if (len < 2) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400349 out << "Truncated NDN/PPP frame";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400350 return true;
351 }
352 pkt += 2;
353 len -= 2;
Davide Pesaventof9126532018-08-04 18:31:06 -0400354 out << "PPP";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400355 return printNdn(out, pkt, len);
356 default:
Davide Pesaventof9126532018-08-04 18:31:06 -0400357 out << "[Unsupported PPP proto " << AsHex{proto} << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400358 return true;
359 }
360}
361
Davide Pesaventof9126532018-08-04 18:31:06 -0400362static void
363printIpAddress(OutputFormatter& out, int af, const void* addr)
364{
365 char addrStr[1 + std::max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)] = {};
366 if (inet_ntop(af, addr, addrStr, sizeof(addrStr))) {
367 out << addrStr;
368 }
369 else {
370 out << "???";
371 }
372}
373
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400374bool
375NdnDump::printIp4(OutputFormatter& out, const uint8_t* pkt, size_t len) const
376{
Davide Pesaventof9126532018-08-04 18:31:06 -0400377 out.addDelimiter() << "IP ";
378
379 if (len < sizeof(ip)) {
380 out << "truncated header, length " << len;
381 return true;
382 }
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400383
384 auto ih = reinterpret_cast<const ip*>(pkt);
Davide Pesaventof9126532018-08-04 18:31:06 -0400385 if (ih->ip_v != 4) {
386 // huh? link layer said this was an IPv4 packet but IP header says otherwise
387 out << "bad version " << ih->ip_v;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400388 return true;
389 }
390
Davide Pesaventof9126532018-08-04 18:31:06 -0400391 size_t ipHdrLen = ih->ip_hl * 4;
392 if (ipHdrLen < sizeof(ip)) {
393 out << "bad header length " << ipHdrLen;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400394 return true;
395 }
396
Davide Pesaventof9126532018-08-04 18:31:06 -0400397 size_t ipLen = endian::big_to_native(ih->ip_len);
398 if (ipLen < ipHdrLen) {
399 out << "bad length " << ipLen;
400 return true;
401 }
402 if (ipLen > len) {
403 out << "truncated packet, " << ipLen - len << " bytes missing";
404 return true;
405 }
406 // if we reached this point, the following is true:
407 // sizeof(ip) <= ipHdrLen <= ipLen <= len
408
409 printIpAddress(out, AF_INET, &ih->ip_src);
410 out << " > ";
411 printIpAddress(out, AF_INET, &ih->ip_dst);
412
413 pkt += ipHdrLen;
414 len -= ipHdrLen;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400415
416 return dispatchByIpProto(out, pkt, len, ih->ip_p);
417}
418
419bool
Davide Pesavento0ca7e692018-08-05 02:21:06 -0400420NdnDump::printIp6(OutputFormatter& out, const uint8_t* pkt, size_t len) const
421{
422 out.addDelimiter() << "IP6 ";
423
424 if (len < sizeof(ip6_hdr)) {
425 out << "truncated header, length " << len;
426 return true;
427 }
428
429 auto ip6 = reinterpret_cast<const ip6_hdr*>(pkt);
430 unsigned int ipVer = (ip6->ip6_vfc & 0xf0) >> 4;
431 if (ipVer != 6) {
432 // huh? link layer said this was an IPv6 packet but IP header says otherwise
433 out << "bad version " << ipVer;
434 return true;
435 }
436
437 pkt += sizeof(ip6_hdr);
438 len -= sizeof(ip6_hdr);
439
440 size_t payloadLen = endian::big_to_native(ip6->ip6_plen);
441 if (len < payloadLen) {
442 out << "truncated payload, " << payloadLen - len << " bytes missing";
443 return true;
444 }
445
446 printIpAddress(out, AF_INET6, &ip6->ip6_src);
447 out << " > ";
448 printIpAddress(out, AF_INET6, &ip6->ip6_dst);
449
450 // we assume no extension headers are present
451 return dispatchByIpProto(out, pkt, len, ip6->ip6_nxt);
452}
453
454bool
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400455NdnDump::printTcp(OutputFormatter& out, const uint8_t* pkt, size_t len) const
456{
Davide Pesaventof9126532018-08-04 18:31:06 -0400457 out.addDelimiter() << "TCP";
458
459 if (len < sizeof(tcphdr)) {
460 out << " truncated header, length " << len;
461 return true;
462 }
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400463
464 auto th = reinterpret_cast<const tcphdr*>(pkt);
Davide Pesavento7b9837b2019-02-23 19:07:50 -0500465 size_t tcpHdrLen = th->TH_OFF * 4;
Davide Pesaventof9126532018-08-04 18:31:06 -0400466 if (tcpHdrLen < sizeof(tcphdr)) {
467 out << " bad header length " << tcpHdrLen;
468 return true;
469 }
470 if (tcpHdrLen > len) {
471 out << " truncated header, " << tcpHdrLen - len << " bytes missing";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400472 return true;
473 }
474
Davide Pesaventof9126532018-08-04 18:31:06 -0400475 pkt += tcpHdrLen;
476 len -= tcpHdrLen;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400477
Davide Pesaventof9126532018-08-04 18:31:06 -0400478 out.addDelimiter() << "length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400479
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400480 return printNdn(out, pkt, len);
481}
482
483bool
484NdnDump::printUdp(OutputFormatter& out, const uint8_t* pkt, size_t len) const
485{
Davide Pesaventof9126532018-08-04 18:31:06 -0400486 out.addDelimiter() << "UDP";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400487
488 if (len < sizeof(udphdr)) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400489 out << " truncated header, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400490 return true;
491 }
492
Davide Pesaventof9126532018-08-04 18:31:06 -0400493 auto uh = reinterpret_cast<const udphdr*>(pkt);
Davide Pesavento7b9837b2019-02-23 19:07:50 -0500494 size_t udpLen = endian::big_to_native(uh->UH_LEN);
Davide Pesaventof9126532018-08-04 18:31:06 -0400495 if (udpLen < sizeof(udphdr)) {
496 out << " bad length " << udpLen;
497 return true;
498 }
499 if (udpLen > len) {
500 out << " truncated packet, " << udpLen - len << " bytes missing";
501 return true;
502 }
503 else {
504 len = udpLen;
505 }
506
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400507 pkt += sizeof(udphdr);
508 len -= sizeof(udphdr);
509
Davide Pesaventof9126532018-08-04 18:31:06 -0400510 out.addDelimiter() << "length " << len;
511
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400512 return printNdn(out, pkt, len);
513}
514
515bool
516NdnDump::printNdn(OutputFormatter& out, const uint8_t* pkt, size_t len) const
517{
518 if (len == 0) {
519 return false;
520 }
521 out.addDelimiter();
522
Davide Pesaventob3570c62022-02-19 19:19:00 -0500523 auto [isOk, block] = Block::fromBuffer({pkt, len});
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400524 if (!isOk) {
525 // if packet is incomplete, we will not be able to process it
Davide Pesaventof9126532018-08-04 18:31:06 -0400526 out << "NDN truncated packet, length " << len;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400527 return true;
528 }
529
530 lp::Packet lpPacket;
531 Block netPacket;
532
533 if (block.type() == lp::tlv::LpPacket) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400534 out << "NDNLPv2";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400535 try {
536 lpPacket.wireDecode(block);
537 }
538 catch (const tlv::Error& e) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400539 out << " invalid packet: " << e.what();
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400540 return true;
541 }
542
Davide Pesaventob3570c62022-02-19 19:19:00 -0500543 if (!lpPacket.has<lp::FragmentField>()) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400544 out << " idle";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400545 return true;
546 }
547
Davide Pesaventob3570c62022-02-19 19:19:00 -0500548 auto [begin, end] = lpPacket.get<lp::FragmentField>();
Davide Pesavento59984282022-02-16 22:41:03 -0500549 std::tie(isOk, netPacket) = Block::fromBuffer({begin, end});
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400550 if (!isOk) {
551 // if network packet is fragmented, we will not be able to process it
Davide Pesaventof9126532018-08-04 18:31:06 -0400552 out << " fragment";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400553 return true;
554 }
555 }
556 else {
557 netPacket = std::move(block);
558 }
Davide Pesaventof9126532018-08-04 18:31:06 -0400559 out.addDelimiter();
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400560
561 try {
562 switch (netPacket.type()) {
563 case tlv::Interest: {
564 Interest interest(netPacket);
565 if (!matchesFilter(interest.getName())) {
566 return false;
567 }
568
569 if (lpPacket.has<lp::NackField>()) {
570 lp::Nack nack(interest);
571 nack.setHeader(lpPacket.get<lp::NackField>());
Davide Pesaventof9126532018-08-04 18:31:06 -0400572 out << "NACK (" << nack.getReason() << "): " << interest;
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400573 }
574 else {
575 out << "INTEREST: " << interest;
576 }
577 break;
578 }
579 case tlv::Data: {
580 Data data(netPacket);
581 if (!matchesFilter(data.getName())) {
582 return false;
583 }
584
585 out << "DATA: " << data.getName();
586 break;
587 }
588 default: {
Davide Pesaventof9126532018-08-04 18:31:06 -0400589 out << "[Unsupported NDN packet type " << netPacket.type() << "]";
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400590 break;
591 }
592 }
593 }
594 catch (const tlv::Error& e) {
Davide Pesaventof9126532018-08-04 18:31:06 -0400595 out << "invalid network packet: " << e.what();
Davide Pesaventob6b176c2018-07-28 22:05:16 -0400596 }
597
598 return true;
Junxiao Shi2222a612015-06-06 08:01:38 -0700599}
600
Junxiao Shic1c2b832016-07-24 20:45:36 +0000601bool
Davide Pesaventoecd44802018-07-23 23:48:10 -0400602NdnDump::matchesFilter(const Name& name) const
Junxiao Shic1c2b832016-07-24 20:45:36 +0000603{
Davide Pesavento78de7352018-07-22 00:35:45 -0400604 if (!nameFilter)
Junxiao Shic1c2b832016-07-24 20:45:36 +0000605 return true;
606
607 /// \todo Switch to NDN regular expressions
Davide Pesavento78de7352018-07-22 00:35:45 -0400608 return std::regex_match(name.toUri(), *nameFilter);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000609}
610
Davide Pesaventob3570c62022-02-19 19:19:00 -0500611} // namespace ndn::dump