blob: 9e0d5d49a372ed6ae9cde34e3474f3d235933b21 [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
Junxiao Shi2222a612015-06-06 08:01:38 -070039#include "tcpdump/tcpdump-stdinc.h"
40
41namespace ndn {
Junxiao Shi3cd47df2015-06-07 20:58:14 -070042namespace dump {
43// namespace is necessary to prevent clashing with system includes
Junxiao Shi2222a612015-06-06 08:01:38 -070044
45#include "tcpdump/ether.h"
46#include "tcpdump/ip.h"
47#include "tcpdump/udp.h"
48#include "tcpdump/tcp.h"
49
Junxiao Shi3cd47df2015-06-07 20:58:14 -070050} // namespace dump
Vince Lehman277ecf02016-02-10 16:37:48 -060051} // namespace ndn
Junxiao Shi2222a612015-06-06 08:01:38 -070052
Junxiao Shi022bddf2016-11-24 23:15:20 +000053#include <pcap/sll.h>
54
Junxiao Shi2222a612015-06-06 08:01:38 -070055#include <iomanip>
Davide Pesaventoc0702702017-08-24 22:04:00 -040056#include <sstream>
Junxiao Shi2222a612015-06-06 08:01:38 -070057
Vince Lehman277ecf02016-02-10 16:37:48 -060058#include <ndn-cxx/lp/nack.hpp>
59#include <ndn-cxx/lp/packet.hpp>
Junxiao Shi2222a612015-06-06 08:01:38 -070060
61namespace ndn {
Junxiao Shi3cd47df2015-06-07 20:58:14 -070062namespace dump {
Junxiao Shi2222a612015-06-06 08:01:38 -070063
Junxiao Shi2222a612015-06-06 08:01:38 -070064const size_t MAX_SNAPLEN = 65535;
65
Junxiao Shic1c2b832016-07-24 20:45:36 +000066Ndndump::Ndndump()
67 : isVerbose(false)
68 , pcapProgram("(ether proto 0x8624) || (tcp port 6363) || (udp port 6363)")
69 // , isSuccinct(false)
70 // , isMatchInverted(false)
71 // , shouldPrintStructure(false)
72 // , isTcpOnly(false)
73 // , isUdpOnly(false)
74{
75}
76
Junxiao Shi2222a612015-06-06 08:01:38 -070077void
78Ndndump::run()
79{
80 if (inputFile.empty() && interface.empty()) {
81 char errbuf[PCAP_ERRBUF_SIZE];
82 const char* pcapDevice = pcap_lookupdev(errbuf);
83
Junxiao Shic1c2b832016-07-24 20:45:36 +000084 if (pcapDevice == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +000085 BOOST_THROW_EXCEPTION(Error(errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -070086 }
87
88 interface = pcapDevice;
89 }
90
91 if (isVerbose) {
92 if (!interface.empty()) {
93 std::cerr << "ndndump: listening on " << interface << std::endl;
94 }
95 else {
96 std::cerr << "ndndump: reading from " << inputFile << std::endl;
97 }
Junxiao Shi2222a612015-06-06 08:01:38 -070098 }
99
100 if (!interface.empty()) {
101 char errbuf[PCAP_ERRBUF_SIZE];
Davide Pesavento78de7352018-07-22 00:35:45 -0400102 m_pcap = pcap_open_live(interface.data(), MAX_SNAPLEN, 0, 1000, errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000103 if (m_pcap == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +0000104 BOOST_THROW_EXCEPTION(Error("Cannot open interface " + interface + " (" + errbuf + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700105 }
106 }
107 else {
108 char errbuf[PCAP_ERRBUF_SIZE];
Davide Pesavento78de7352018-07-22 00:35:45 -0400109 m_pcap = pcap_open_offline(inputFile.data(), errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000110 if (m_pcap == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +0000111 BOOST_THROW_EXCEPTION(Error("Cannot open file " + inputFile + " for reading (" + errbuf + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700112 }
113 }
114
115 if (!pcapProgram.empty()) {
116 if (isVerbose) {
117 std::cerr << "ndndump: pcap_filter = " << pcapProgram << std::endl;
118 }
119
120 bpf_program program;
Davide Pesavento78de7352018-07-22 00:35:45 -0400121 int res = pcap_compile(m_pcap, &program, pcapProgram.data(), 0, PCAP_NETMASK_UNKNOWN);
Junxiao Shi2222a612015-06-06 08:01:38 -0700122
Junxiao Shic1c2b832016-07-24 20:45:36 +0000123 if (res < 0) {
Junxiao Shic7599632016-07-24 20:46:24 +0000124 BOOST_THROW_EXCEPTION(Error("Cannot parse tcpdump expression '" + pcapProgram +
125 "' (" + pcap_geterr(m_pcap) + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700126 }
127
Junxiao Shic1c2b832016-07-24 20:45:36 +0000128 res = pcap_setfilter(m_pcap, &program);
Junxiao Shi2222a612015-06-06 08:01:38 -0700129 pcap_freecode(&program);
130
Junxiao Shic1c2b832016-07-24 20:45:36 +0000131 if (res < 0) {
Junxiao Shic7599632016-07-24 20:46:24 +0000132 BOOST_THROW_EXCEPTION(Error(std::string("pcap_setfilter failed (") + pcap_geterr(m_pcap) + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700133 }
134 }
135
136 m_dataLinkType = pcap_datalink(m_pcap);
Junxiao Shi022bddf2016-11-24 23:15:20 +0000137 if (m_dataLinkType != DLT_EN10MB && m_dataLinkType != DLT_PPP && m_dataLinkType != DLT_LINUX_SLL) {
Junxiao Shi1c71bcd2016-11-24 04:00:42 +0000138 BOOST_THROW_EXCEPTION(Error("Unsupported pcap format (" + to_string(m_dataLinkType) + ")"));
Junxiao Shic1c2b832016-07-24 20:45:36 +0000139 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700140
141 pcap_loop(m_pcap, -1, &Ndndump::onCapturedPacket, reinterpret_cast<uint8_t*>(this));
142}
143
Junxiao Shi2222a612015-06-06 08:01:38 -0700144void
Junxiao Shic1c2b832016-07-24 20:45:36 +0000145Ndndump::onCapturedPacket(const pcap_pkthdr* header, const uint8_t* packet) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700146{
147 std::ostringstream os;
148 printInterceptTime(os, header);
149
150 const uint8_t* payload = packet;
151 ssize_t payloadSize = header->len;
152
153 int frameType = skipDataLinkHeaderAndGetFrameType(payload, payloadSize);
154 if (frameType < 0) {
155 std::cerr << "Unknown frame type" << std::endl;
156 return;
157 }
158
Junxiao Shic1c2b832016-07-24 20:45:36 +0000159 int res = skipAndProcessFrameHeader(frameType, payload, payloadSize, os);
160 if (res < 0) {
Junxiao Shi2222a612015-06-06 08:01:38 -0700161 return;
162 }
163
164 bool isOk = false;
165 Block block;
166 std::tie(isOk, block) = Block::fromBuffer(payload, payloadSize);
167 if (!isOk) {
Vince Lehman277ecf02016-02-10 16:37:48 -0600168 // if packet is incomplete, we will not be able to process it
169 if (payloadSize > 0) {
170 std::cout << os.str() << ", " << "INCOMPLETE-PACKET" << ", size: " << payloadSize << std::endl;
171 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700172 return;
173 }
174
Vince Lehman277ecf02016-02-10 16:37:48 -0600175 lp::Packet lpPacket;
176 Block netPacket;
177
178 if (block.type() == lp::tlv::LpPacket) {
179 lpPacket = lp::Packet(block);
180
181 Buffer::const_iterator begin, end;
182
183 if (lpPacket.has<lp::FragmentField>()) {
184 std::tie(begin, end) = lpPacket.get<lp::FragmentField>();
185 }
186 else {
187 std::cout << os.str() << ", " << "NDNLPv2-IDLE" << std::endl;
188 return;
189 }
190
191 bool isOk = false;
192 std::tie(isOk, netPacket) = Block::fromBuffer(&*begin, std::distance(begin, end));
193 if (!isOk) {
194 // if network packet is fragmented, we will not be able to process it
195 std::cout << os.str() << ", " << "NDNLPv2-FRAGMENT" << std::endl;
196 return;
197 }
198 }
199 else {
200 netPacket = block;
201 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700202
203 try {
Vince Lehman277ecf02016-02-10 16:37:48 -0600204 if (netPacket.type() == tlv::Interest) {
205 Interest interest(netPacket);
Junxiao Shi2222a612015-06-06 08:01:38 -0700206 if (matchesFilter(interest.getName())) {
Vince Lehman277ecf02016-02-10 16:37:48 -0600207
208 if (lpPacket.has<lp::NackField>()) {
209 lp::Nack nack(interest);
210 nack.setHeader(lpPacket.get<lp::NackField>());
211
212 std::cout << os.str() << ", " << "NACK: " << nack.getReason() << ", " << interest << std::endl;
213 }
214 else {
215 std::cout << os.str() << ", " << "INTEREST: " << interest << std::endl;
216 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700217 }
218 }
Vince Lehman277ecf02016-02-10 16:37:48 -0600219 else if (netPacket.type() == tlv::Data) {
220 Data data(netPacket);
Junxiao Shi2222a612015-06-06 08:01:38 -0700221 if (matchesFilter(data.getName())) {
222 std::cout << os.str() << ", " << "DATA: " << data.getName() << std::endl;
223 }
224 }
Vince Lehman277ecf02016-02-10 16:37:48 -0600225 else {
226 std::cout << os.str() << ", " << "UNKNOWN-NETWORK-PACKET" << std::endl;
227 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700228 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000229 catch (const tlv::Error& e) {
Junxiao Shi2222a612015-06-06 08:01:38 -0700230 std::cerr << e.what() << std::endl;
231 }
232}
233
234void
Junxiao Shic1c2b832016-07-24 20:45:36 +0000235Ndndump::printInterceptTime(std::ostream& os, const pcap_pkthdr* header) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700236{
237 os << header->ts.tv_sec
238 << "."
239 << std::setfill('0') << std::setw(6) << header->ts.tv_usec;
240
241 // struct tm* tm;
242 // if (flags.unit_time) {
243 // os << (int) header->ts.tv_sec
244 // << "."
245 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
Davide Pesaventoc0702702017-08-24 22:04:00 -0400246 // }
247 // else {
Junxiao Shi2222a612015-06-06 08:01:38 -0700248 // tm = localtime(&(header->ts.tv_sec));
249 // os << (int)tm->tm_hour << ":"
250 // << setfill('0') << setw(2) << (int)tm->tm_min<< ":"
251 // << setfill('0') << setw(2) << (int)tm->tm_sec<< "."
252 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
253 // }
254 os << " ";
255}
256
257int
Junxiao Shic1c2b832016-07-24 20:45:36 +0000258Ndndump::skipDataLinkHeaderAndGetFrameType(const uint8_t*& payload, ssize_t& payloadSize) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700259{
260 int frameType = 0;
261
262 switch (m_dataLinkType) {
Junxiao Shic1c2b832016-07-24 20:45:36 +0000263 case DLT_EN10MB: { // Ethernet frames can have Ethernet or 802.3 encapsulation
Junxiao Shi2222a612015-06-06 08:01:38 -0700264 const ether_header* etherHeader = reinterpret_cast<const ether_header*>(payload);
265
266 if (payloadSize < 0) {
267 std::cerr << "Invalid pcap Ethernet frame" << std::endl;
268 return -1;
269 }
270
271 frameType = ntohs(etherHeader->ether_type);
272 payloadSize -= ETHER_HDRLEN;
273 payload += ETHER_HDRLEN;
274
275 break;
276 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000277 case DLT_PPP: {
Junxiao Shi2222a612015-06-06 08:01:38 -0700278 frameType = *payload;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000279 --payloadSize;
280 ++payload;
Junxiao Shi2222a612015-06-06 08:01:38 -0700281
282 if (!(frameType & 1)) {
283 frameType = (frameType << 8) | *payload;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000284 --payloadSize;
285 ++payload;
Junxiao Shi2222a612015-06-06 08:01:38 -0700286 }
287
288 if (payloadSize < 0) {
289 std::cerr << "Invalid PPP frame" << std::endl;
290 return -1;
291 }
292
293 break;
294 }
Junxiao Shi022bddf2016-11-24 23:15:20 +0000295 case DLT_LINUX_SLL: {
296 const sll_header* sllHeader = reinterpret_cast<const sll_header*>(payload);
297
298 if (payloadSize < SLL_HDR_LEN) {
299 std::cerr << "Invalid LINUX_SLL frame" << std::endl;
300 return -1;
301 }
302
303 frameType = ntohs(sllHeader->sll_protocol);
304 payloadSize -= SLL_HDR_LEN;
305 payload += SLL_HDR_LEN;
306
307 break;
308 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700309 }
310
311 return frameType;
312}
313
314int
315Ndndump::skipAndProcessFrameHeader(int frameType,
316 const uint8_t*& payload, ssize_t& payloadSize,
Junxiao Shic1c2b832016-07-24 20:45:36 +0000317 std::ostream& os) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700318{
Junxiao Shic1c2b832016-07-24 20:45:36 +0000319 switch (frameType) {
320 case 0x0800: // ETHERTYPE_IP
321 case DLT_EN10MB: { // pcap encapsulation
322 const ip* ipHeader = reinterpret_cast<const ip*>(payload);
323 size_t ipHeaderSize = IP_HL(ipHeader) * 4;
324 if (ipHeaderSize < 20) {
325 std::cerr << "invalid IP header len " << ipHeaderSize << " bytes" << std::endl;
326 return -1;
327 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700328
Junxiao Shic1c2b832016-07-24 20:45:36 +0000329 os << "From: " << inet_ntoa(ipHeader->ip_src) << ", ";
330 os << "To: " << inet_ntoa(ipHeader->ip_dst);
Junxiao Shi2222a612015-06-06 08:01:38 -0700331
Junxiao Shic1c2b832016-07-24 20:45:36 +0000332 payloadSize -= ipHeaderSize;
333 payload += ipHeaderSize;
Junxiao Shi2222a612015-06-06 08:01:38 -0700334
Junxiao Shic1c2b832016-07-24 20:45:36 +0000335 if (payloadSize < 0) {
336 std::cerr << "Invalid pcap IP packet" << std::endl;
337 return -1;
338 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700339
Junxiao Shic1c2b832016-07-24 20:45:36 +0000340 switch (ipHeader->ip_p) {
341 case IPPROTO_UDP: {
342 // if (!flags.udp)
343 // return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700344
Junxiao Shic1c2b832016-07-24 20:45:36 +0000345 payloadSize -= sizeof(udphdr);
346 payload += sizeof(udphdr);
Junxiao Shi2222a612015-06-06 08:01:38 -0700347
Junxiao Shic1c2b832016-07-24 20:45:36 +0000348 if (payloadSize < 0) {
349 std::cerr << "Invalid pcap UDP/IP packet" << std::endl;
350 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700351 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700352
Junxiao Shic1c2b832016-07-24 20:45:36 +0000353 os << ", Tunnel Type: UDP";
354 break;
355 }
356 case IPPROTO_TCP: {
357 // if (!flags.tcp)
358 // return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700359
Junxiao Shic1c2b832016-07-24 20:45:36 +0000360 const tcphdr* tcpHeader = reinterpret_cast<const tcphdr*>(payload);
361 size_t tcpHeaderSize = TH_OFF(tcpHeader) * 4;
Junxiao Shi2222a612015-06-06 08:01:38 -0700362
Junxiao Shic1c2b832016-07-24 20:45:36 +0000363 if (tcpHeaderSize < 20) {
364 std::cerr << "Invalid TCP Header len: " << tcpHeaderSize <<" bytes" << std::endl;
365 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700366 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000367
368 payloadSize -= tcpHeaderSize;
369 payload += tcpHeaderSize;
370
371 if (payloadSize < 0) {
372 std::cerr << "Invalid pcap TCP/IP packet" << std::endl;
373 return -1;
374 }
375
376 os << ", Tunnel Type: TCP";
377 break;
378 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700379 default:
380 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700381 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000382
383 break;
384 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700385 case /*ETHERTYPE_NDN*/0x7777:
386 os << "Tunnel Type: EthernetFrame";
387 break;
388 case /*ETHERTYPE_NDNLP*/0x8624:
389 os << "Tunnel Type: EthernetFrame";
390 break;
391 case 0x0077: // pcap
392 os << "Tunnel Type: PPP";
393 payloadSize -= 2;
394 payload += 2;
395 break;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000396 default: // do nothing if it is not a recognized type of a packet
Junxiao Shi2222a612015-06-06 08:01:38 -0700397 return -1;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000398 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700399
400 return 0;
401}
402
Junxiao Shic1c2b832016-07-24 20:45:36 +0000403bool
404Ndndump::matchesFilter(const Name& name) const
405{
Davide Pesavento78de7352018-07-22 00:35:45 -0400406 if (!nameFilter)
Junxiao Shic1c2b832016-07-24 20:45:36 +0000407 return true;
408
409 /// \todo Switch to NDN regular expressions
Davide Pesavento78de7352018-07-22 00:35:45 -0400410 return std::regex_match(name.toUri(), *nameFilter);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000411}
412
Junxiao Shi3cd47df2015-06-07 20:58:14 -0700413} // namespace dump
Junxiao Shi2222a612015-06-06 08:01:38 -0700414} // namespace ndn