blob: e9a69122a7eb4e5a93132833b84b5e94a6885afe [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/*
3 * Copyright (c) 2014-2018, 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 */
Davide Pesavento60f8cc12018-05-10 22:05:21 -040019/*
Junxiao Shi2222a612015-06-06 08:01:38 -070020 * Copyright (c) 2011-2014, Regents of the University of California,
21 *
22 * This file is part of ndndump, the packet capture and analysis tool for Named Data
23 * Networking (NDN).
24 *
25 * ndndump is free software: you can redistribute it and/or modify it under the terms
26 * of the GNU General Public License as published by the Free Software Foundation,
27 * either version 3 of the License, or (at your option) any later version.
28 *
29 * ndndump is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
30 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
31 * PURPOSE. See the GNU General Public License for more details.
32 *
33 * You should have received a copy of the GNU General Public License along with
34 * ndndump, e.g., in COPYING file. If not, see <http://www.gnu.org/licenses/>.
35 **/
36
37#include "ndndump.hpp"
38
Davide Pesavento051db002018-07-22 18:56:16 -040039#include <net/ethernet.h>
40#include <netinet/ip.h>
41#include <netinet/tcp.h>
42#include <netinet/udp.h>
Junxiao Shi2222a612015-06-06 08:01:38 -070043
Junxiao Shi022bddf2016-11-24 23:15:20 +000044#include <pcap/sll.h>
45
Junxiao Shi2222a612015-06-06 08:01:38 -070046#include <iomanip>
Davide Pesaventoc0702702017-08-24 22:04:00 -040047#include <sstream>
Junxiao Shi2222a612015-06-06 08:01:38 -070048
Vince Lehman277ecf02016-02-10 16:37:48 -060049#include <ndn-cxx/lp/nack.hpp>
50#include <ndn-cxx/lp/packet.hpp>
Junxiao Shi2222a612015-06-06 08:01:38 -070051
52namespace ndn {
Junxiao Shi3cd47df2015-06-07 20:58:14 -070053namespace dump {
Junxiao Shi2222a612015-06-06 08:01:38 -070054
Junxiao Shi2222a612015-06-06 08:01:38 -070055const size_t MAX_SNAPLEN = 65535;
56
Junxiao Shic1c2b832016-07-24 20:45:36 +000057Ndndump::Ndndump()
58 : isVerbose(false)
59 , pcapProgram("(ether proto 0x8624) || (tcp port 6363) || (udp port 6363)")
60 // , isSuccinct(false)
61 // , isMatchInverted(false)
62 // , shouldPrintStructure(false)
63 // , isTcpOnly(false)
64 // , isUdpOnly(false)
65{
66}
67
Junxiao Shi2222a612015-06-06 08:01:38 -070068void
69Ndndump::run()
70{
71 if (inputFile.empty() && interface.empty()) {
72 char errbuf[PCAP_ERRBUF_SIZE];
73 const char* pcapDevice = pcap_lookupdev(errbuf);
74
Junxiao Shic1c2b832016-07-24 20:45:36 +000075 if (pcapDevice == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +000076 BOOST_THROW_EXCEPTION(Error(errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -070077 }
78
79 interface = pcapDevice;
80 }
81
82 if (isVerbose) {
83 if (!interface.empty()) {
84 std::cerr << "ndndump: listening on " << interface << std::endl;
85 }
86 else {
87 std::cerr << "ndndump: reading from " << inputFile << std::endl;
88 }
Junxiao Shi2222a612015-06-06 08:01:38 -070089 }
90
91 if (!interface.empty()) {
92 char errbuf[PCAP_ERRBUF_SIZE];
Davide Pesavento78de7352018-07-22 00:35:45 -040093 m_pcap = pcap_open_live(interface.data(), MAX_SNAPLEN, 0, 1000, errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +000094 if (m_pcap == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +000095 BOOST_THROW_EXCEPTION(Error("Cannot open interface " + interface + " (" + errbuf + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -070096 }
97 }
98 else {
99 char errbuf[PCAP_ERRBUF_SIZE];
Davide Pesavento78de7352018-07-22 00:35:45 -0400100 m_pcap = pcap_open_offline(inputFile.data(), errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000101 if (m_pcap == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +0000102 BOOST_THROW_EXCEPTION(Error("Cannot open file " + inputFile + " for reading (" + errbuf + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700103 }
104 }
105
106 if (!pcapProgram.empty()) {
107 if (isVerbose) {
108 std::cerr << "ndndump: pcap_filter = " << pcapProgram << std::endl;
109 }
110
111 bpf_program program;
Davide Pesavento78de7352018-07-22 00:35:45 -0400112 int res = pcap_compile(m_pcap, &program, pcapProgram.data(), 0, PCAP_NETMASK_UNKNOWN);
Junxiao Shi2222a612015-06-06 08:01:38 -0700113
Junxiao Shic1c2b832016-07-24 20:45:36 +0000114 if (res < 0) {
Junxiao Shic7599632016-07-24 20:46:24 +0000115 BOOST_THROW_EXCEPTION(Error("Cannot parse tcpdump expression '" + pcapProgram +
116 "' (" + pcap_geterr(m_pcap) + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700117 }
118
Junxiao Shic1c2b832016-07-24 20:45:36 +0000119 res = pcap_setfilter(m_pcap, &program);
Junxiao Shi2222a612015-06-06 08:01:38 -0700120 pcap_freecode(&program);
121
Junxiao Shic1c2b832016-07-24 20:45:36 +0000122 if (res < 0) {
Junxiao Shic7599632016-07-24 20:46:24 +0000123 BOOST_THROW_EXCEPTION(Error(std::string("pcap_setfilter failed (") + pcap_geterr(m_pcap) + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700124 }
125 }
126
127 m_dataLinkType = pcap_datalink(m_pcap);
Junxiao Shi022bddf2016-11-24 23:15:20 +0000128 if (m_dataLinkType != DLT_EN10MB && m_dataLinkType != DLT_PPP && m_dataLinkType != DLT_LINUX_SLL) {
Junxiao Shi1c71bcd2016-11-24 04:00:42 +0000129 BOOST_THROW_EXCEPTION(Error("Unsupported pcap format (" + to_string(m_dataLinkType) + ")"));
Junxiao Shic1c2b832016-07-24 20:45:36 +0000130 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700131
132 pcap_loop(m_pcap, -1, &Ndndump::onCapturedPacket, reinterpret_cast<uint8_t*>(this));
133}
134
Junxiao Shi2222a612015-06-06 08:01:38 -0700135void
Junxiao Shic1c2b832016-07-24 20:45:36 +0000136Ndndump::onCapturedPacket(const pcap_pkthdr* header, const uint8_t* packet) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700137{
138 std::ostringstream os;
139 printInterceptTime(os, header);
140
141 const uint8_t* payload = packet;
142 ssize_t payloadSize = header->len;
143
144 int frameType = skipDataLinkHeaderAndGetFrameType(payload, payloadSize);
145 if (frameType < 0) {
146 std::cerr << "Unknown frame type" << std::endl;
147 return;
148 }
149
Junxiao Shic1c2b832016-07-24 20:45:36 +0000150 int res = skipAndProcessFrameHeader(frameType, payload, payloadSize, os);
151 if (res < 0) {
Junxiao Shi2222a612015-06-06 08:01:38 -0700152 return;
153 }
154
155 bool isOk = false;
156 Block block;
157 std::tie(isOk, block) = Block::fromBuffer(payload, payloadSize);
158 if (!isOk) {
Vince Lehman277ecf02016-02-10 16:37:48 -0600159 // if packet is incomplete, we will not be able to process it
160 if (payloadSize > 0) {
161 std::cout << os.str() << ", " << "INCOMPLETE-PACKET" << ", size: " << payloadSize << std::endl;
162 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700163 return;
164 }
165
Vince Lehman277ecf02016-02-10 16:37:48 -0600166 lp::Packet lpPacket;
167 Block netPacket;
168
169 if (block.type() == lp::tlv::LpPacket) {
170 lpPacket = lp::Packet(block);
171
172 Buffer::const_iterator begin, end;
173
174 if (lpPacket.has<lp::FragmentField>()) {
175 std::tie(begin, end) = lpPacket.get<lp::FragmentField>();
176 }
177 else {
178 std::cout << os.str() << ", " << "NDNLPv2-IDLE" << std::endl;
179 return;
180 }
181
182 bool isOk = false;
183 std::tie(isOk, netPacket) = Block::fromBuffer(&*begin, std::distance(begin, end));
184 if (!isOk) {
185 // if network packet is fragmented, we will not be able to process it
186 std::cout << os.str() << ", " << "NDNLPv2-FRAGMENT" << std::endl;
187 return;
188 }
189 }
190 else {
191 netPacket = block;
192 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700193
194 try {
Vince Lehman277ecf02016-02-10 16:37:48 -0600195 if (netPacket.type() == tlv::Interest) {
196 Interest interest(netPacket);
Junxiao Shi2222a612015-06-06 08:01:38 -0700197 if (matchesFilter(interest.getName())) {
Vince Lehman277ecf02016-02-10 16:37:48 -0600198
199 if (lpPacket.has<lp::NackField>()) {
200 lp::Nack nack(interest);
201 nack.setHeader(lpPacket.get<lp::NackField>());
202
203 std::cout << os.str() << ", " << "NACK: " << nack.getReason() << ", " << interest << std::endl;
204 }
205 else {
206 std::cout << os.str() << ", " << "INTEREST: " << interest << std::endl;
207 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700208 }
209 }
Vince Lehman277ecf02016-02-10 16:37:48 -0600210 else if (netPacket.type() == tlv::Data) {
211 Data data(netPacket);
Junxiao Shi2222a612015-06-06 08:01:38 -0700212 if (matchesFilter(data.getName())) {
213 std::cout << os.str() << ", " << "DATA: " << data.getName() << std::endl;
214 }
215 }
Vince Lehman277ecf02016-02-10 16:37:48 -0600216 else {
217 std::cout << os.str() << ", " << "UNKNOWN-NETWORK-PACKET" << std::endl;
218 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700219 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000220 catch (const tlv::Error& e) {
Junxiao Shi2222a612015-06-06 08:01:38 -0700221 std::cerr << e.what() << std::endl;
222 }
223}
224
225void
Junxiao Shic1c2b832016-07-24 20:45:36 +0000226Ndndump::printInterceptTime(std::ostream& os, const pcap_pkthdr* header) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700227{
228 os << header->ts.tv_sec
229 << "."
230 << std::setfill('0') << std::setw(6) << header->ts.tv_usec;
231
232 // struct tm* tm;
233 // if (flags.unit_time) {
234 // os << (int) header->ts.tv_sec
235 // << "."
236 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
Davide Pesaventoc0702702017-08-24 22:04:00 -0400237 // }
238 // else {
Junxiao Shi2222a612015-06-06 08:01:38 -0700239 // tm = localtime(&(header->ts.tv_sec));
240 // os << (int)tm->tm_hour << ":"
241 // << setfill('0') << setw(2) << (int)tm->tm_min<< ":"
242 // << setfill('0') << setw(2) << (int)tm->tm_sec<< "."
243 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
244 // }
245 os << " ";
246}
247
248int
Junxiao Shic1c2b832016-07-24 20:45:36 +0000249Ndndump::skipDataLinkHeaderAndGetFrameType(const uint8_t*& payload, ssize_t& payloadSize) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700250{
251 int frameType = 0;
252
253 switch (m_dataLinkType) {
Junxiao Shic1c2b832016-07-24 20:45:36 +0000254 case DLT_EN10MB: { // Ethernet frames can have Ethernet or 802.3 encapsulation
Junxiao Shi2222a612015-06-06 08:01:38 -0700255 const ether_header* etherHeader = reinterpret_cast<const ether_header*>(payload);
256
257 if (payloadSize < 0) {
258 std::cerr << "Invalid pcap Ethernet frame" << std::endl;
259 return -1;
260 }
261
262 frameType = ntohs(etherHeader->ether_type);
Davide Pesavento051db002018-07-22 18:56:16 -0400263 payloadSize -= ETHER_HDR_LEN;
264 payload += ETHER_HDR_LEN;
Junxiao Shi2222a612015-06-06 08:01:38 -0700265
266 break;
267 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000268 case DLT_PPP: {
Junxiao Shi2222a612015-06-06 08:01:38 -0700269 frameType = *payload;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000270 --payloadSize;
271 ++payload;
Junxiao Shi2222a612015-06-06 08:01:38 -0700272
273 if (!(frameType & 1)) {
274 frameType = (frameType << 8) | *payload;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000275 --payloadSize;
276 ++payload;
Junxiao Shi2222a612015-06-06 08:01:38 -0700277 }
278
279 if (payloadSize < 0) {
280 std::cerr << "Invalid PPP frame" << std::endl;
281 return -1;
282 }
283
284 break;
285 }
Junxiao Shi022bddf2016-11-24 23:15:20 +0000286 case DLT_LINUX_SLL: {
287 const sll_header* sllHeader = reinterpret_cast<const sll_header*>(payload);
288
289 if (payloadSize < SLL_HDR_LEN) {
290 std::cerr << "Invalid LINUX_SLL frame" << std::endl;
291 return -1;
292 }
293
294 frameType = ntohs(sllHeader->sll_protocol);
295 payloadSize -= SLL_HDR_LEN;
296 payload += SLL_HDR_LEN;
297
298 break;
299 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700300 }
301
302 return frameType;
303}
304
305int
306Ndndump::skipAndProcessFrameHeader(int frameType,
307 const uint8_t*& payload, ssize_t& payloadSize,
Junxiao Shic1c2b832016-07-24 20:45:36 +0000308 std::ostream& os) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700309{
Junxiao Shic1c2b832016-07-24 20:45:36 +0000310 switch (frameType) {
311 case 0x0800: // ETHERTYPE_IP
312 case DLT_EN10MB: { // pcap encapsulation
313 const ip* ipHeader = reinterpret_cast<const ip*>(payload);
Davide Pesavento051db002018-07-22 18:56:16 -0400314 size_t ipHeaderSize = ipHeader->ip_hl * 4;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000315 if (ipHeaderSize < 20) {
316 std::cerr << "invalid IP header len " << ipHeaderSize << " bytes" << std::endl;
317 return -1;
318 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700319
Junxiao Shic1c2b832016-07-24 20:45:36 +0000320 os << "From: " << inet_ntoa(ipHeader->ip_src) << ", ";
321 os << "To: " << inet_ntoa(ipHeader->ip_dst);
Junxiao Shi2222a612015-06-06 08:01:38 -0700322
Junxiao Shic1c2b832016-07-24 20:45:36 +0000323 payloadSize -= ipHeaderSize;
324 payload += ipHeaderSize;
Junxiao Shi2222a612015-06-06 08:01:38 -0700325
Junxiao Shic1c2b832016-07-24 20:45:36 +0000326 if (payloadSize < 0) {
327 std::cerr << "Invalid pcap IP packet" << std::endl;
328 return -1;
329 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700330
Junxiao Shic1c2b832016-07-24 20:45:36 +0000331 switch (ipHeader->ip_p) {
332 case IPPROTO_UDP: {
333 // if (!flags.udp)
334 // return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700335
Junxiao Shic1c2b832016-07-24 20:45:36 +0000336 payloadSize -= sizeof(udphdr);
337 payload += sizeof(udphdr);
Junxiao Shi2222a612015-06-06 08:01:38 -0700338
Junxiao Shic1c2b832016-07-24 20:45:36 +0000339 if (payloadSize < 0) {
340 std::cerr << "Invalid pcap UDP/IP packet" << std::endl;
341 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700342 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700343
Junxiao Shic1c2b832016-07-24 20:45:36 +0000344 os << ", Tunnel Type: UDP";
345 break;
346 }
347 case IPPROTO_TCP: {
348 // if (!flags.tcp)
349 // return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700350
Junxiao Shic1c2b832016-07-24 20:45:36 +0000351 const tcphdr* tcpHeader = reinterpret_cast<const tcphdr*>(payload);
Davide Pesavento051db002018-07-22 18:56:16 -0400352 size_t tcpHeaderSize = tcpHeader->th_off * 4;
Junxiao Shi2222a612015-06-06 08:01:38 -0700353
Junxiao Shic1c2b832016-07-24 20:45:36 +0000354 if (tcpHeaderSize < 20) {
355 std::cerr << "Invalid TCP Header len: " << tcpHeaderSize <<" bytes" << std::endl;
356 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700357 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000358
359 payloadSize -= tcpHeaderSize;
360 payload += tcpHeaderSize;
361
362 if (payloadSize < 0) {
363 std::cerr << "Invalid pcap TCP/IP packet" << std::endl;
364 return -1;
365 }
366
367 os << ", Tunnel Type: TCP";
368 break;
369 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700370 default:
371 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700372 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000373
374 break;
375 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700376 case /*ETHERTYPE_NDN*/0x7777:
377 os << "Tunnel Type: EthernetFrame";
378 break;
379 case /*ETHERTYPE_NDNLP*/0x8624:
380 os << "Tunnel Type: EthernetFrame";
381 break;
382 case 0x0077: // pcap
383 os << "Tunnel Type: PPP";
384 payloadSize -= 2;
385 payload += 2;
386 break;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000387 default: // do nothing if it is not a recognized type of a packet
Junxiao Shi2222a612015-06-06 08:01:38 -0700388 return -1;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000389 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700390
391 return 0;
392}
393
Junxiao Shic1c2b832016-07-24 20:45:36 +0000394bool
395Ndndump::matchesFilter(const Name& name) const
396{
Davide Pesavento78de7352018-07-22 00:35:45 -0400397 if (!nameFilter)
Junxiao Shic1c2b832016-07-24 20:45:36 +0000398 return true;
399
400 /// \todo Switch to NDN regular expressions
Davide Pesavento78de7352018-07-22 00:35:45 -0400401 return std::regex_match(name.toUri(), *nameFilter);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000402}
403
Junxiao Shi3cd47df2015-06-07 20:58:14 -0700404} // namespace dump
Junxiao Shi2222a612015-06-06 08:01:38 -0700405} // namespace ndn