blob: e6cfc6a415993dd4d27bfd51ff6110b6b3f26da3 [file] [log] [blame]
Junxiao Shi2222a612015-06-06 08:01:38 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Davide Pesaventoc0702702017-08-24 22:04:00 -04003 * Copyright (c) 2014-2017, 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 */
19/**
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 <boost/lexical_cast.hpp>
56
57#include <iomanip>
Davide Pesaventoc0702702017-08-24 22:04:00 -040058#include <sstream>
Junxiao Shi2222a612015-06-06 08:01:38 -070059
60#include <ndn-cxx/interest.hpp>
61#include <ndn-cxx/data.hpp>
Vince Lehman277ecf02016-02-10 16:37:48 -060062#include <ndn-cxx/lp/nack.hpp>
63#include <ndn-cxx/lp/packet.hpp>
Junxiao Shi2222a612015-06-06 08:01:38 -070064
65namespace ndn {
Junxiao Shi3cd47df2015-06-07 20:58:14 -070066namespace dump {
Junxiao Shi2222a612015-06-06 08:01:38 -070067
Junxiao Shi2222a612015-06-06 08:01:38 -070068const size_t MAX_SNAPLEN = 65535;
69
Junxiao Shic1c2b832016-07-24 20:45:36 +000070Ndndump::Ndndump()
71 : isVerbose(false)
72 , pcapProgram("(ether proto 0x8624) || (tcp port 6363) || (udp port 6363)")
73 // , isSuccinct(false)
74 // , isMatchInverted(false)
75 // , shouldPrintStructure(false)
76 // , isTcpOnly(false)
77 // , isUdpOnly(false)
78{
79}
80
Junxiao Shi2222a612015-06-06 08:01:38 -070081void
82Ndndump::run()
83{
84 if (inputFile.empty() && interface.empty()) {
85 char errbuf[PCAP_ERRBUF_SIZE];
86 const char* pcapDevice = pcap_lookupdev(errbuf);
87
Junxiao Shic1c2b832016-07-24 20:45:36 +000088 if (pcapDevice == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +000089 BOOST_THROW_EXCEPTION(Error(errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -070090 }
91
92 interface = pcapDevice;
93 }
94
95 if (isVerbose) {
96 if (!interface.empty()) {
97 std::cerr << "ndndump: listening on " << interface << std::endl;
98 }
99 else {
100 std::cerr << "ndndump: reading from " << inputFile << std::endl;
101 }
102
103 if (!nameFilter.empty()) {
104 std::cerr << "ndndump: using name filter " << nameFilter << std::endl;
105 }
106 }
107
108 if (!interface.empty()) {
109 char errbuf[PCAP_ERRBUF_SIZE];
110 m_pcap = pcap_open_live(interface.c_str(), MAX_SNAPLEN, 0, 1000, errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000111 if (m_pcap == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +0000112 BOOST_THROW_EXCEPTION(Error("Cannot open interface " + interface + " (" + errbuf + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700113 }
114 }
115 else {
116 char errbuf[PCAP_ERRBUF_SIZE];
117 m_pcap = pcap_open_offline(inputFile.c_str(), errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000118 if (m_pcap == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +0000119 BOOST_THROW_EXCEPTION(Error("Cannot open file " + inputFile + " for reading (" + errbuf + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700120 }
121 }
122
123 if (!pcapProgram.empty()) {
124 if (isVerbose) {
125 std::cerr << "ndndump: pcap_filter = " << pcapProgram << std::endl;
126 }
127
128 bpf_program program;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000129 int res = pcap_compile(m_pcap, &program, pcapProgram.c_str(), 0, PCAP_NETMASK_UNKNOWN);
Junxiao Shi2222a612015-06-06 08:01:38 -0700130
Junxiao Shic1c2b832016-07-24 20:45:36 +0000131 if (res < 0) {
Junxiao Shic7599632016-07-24 20:46:24 +0000132 BOOST_THROW_EXCEPTION(Error("Cannot parse tcpdump expression '" + pcapProgram +
133 "' (" + pcap_geterr(m_pcap) + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700134 }
135
Junxiao Shic1c2b832016-07-24 20:45:36 +0000136 res = pcap_setfilter(m_pcap, &program);
Junxiao Shi2222a612015-06-06 08:01:38 -0700137 pcap_freecode(&program);
138
Junxiao Shic1c2b832016-07-24 20:45:36 +0000139 if (res < 0) {
Junxiao Shic7599632016-07-24 20:46:24 +0000140 BOOST_THROW_EXCEPTION(Error(std::string("pcap_setfilter failed (") + pcap_geterr(m_pcap) + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700141 }
142 }
143
144 m_dataLinkType = pcap_datalink(m_pcap);
Junxiao Shi022bddf2016-11-24 23:15:20 +0000145 if (m_dataLinkType != DLT_EN10MB && m_dataLinkType != DLT_PPP && m_dataLinkType != DLT_LINUX_SLL) {
Junxiao Shi1c71bcd2016-11-24 04:00:42 +0000146 BOOST_THROW_EXCEPTION(Error("Unsupported pcap format (" + to_string(m_dataLinkType) + ")"));
Junxiao Shic1c2b832016-07-24 20:45:36 +0000147 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700148
149 pcap_loop(m_pcap, -1, &Ndndump::onCapturedPacket, reinterpret_cast<uint8_t*>(this));
150}
151
152
153void
Junxiao Shic1c2b832016-07-24 20:45:36 +0000154Ndndump::onCapturedPacket(const pcap_pkthdr* header, const uint8_t* packet) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700155{
156 std::ostringstream os;
157 printInterceptTime(os, header);
158
159 const uint8_t* payload = packet;
160 ssize_t payloadSize = header->len;
161
162 int frameType = skipDataLinkHeaderAndGetFrameType(payload, payloadSize);
163 if (frameType < 0) {
164 std::cerr << "Unknown frame type" << std::endl;
165 return;
166 }
167
Junxiao Shic1c2b832016-07-24 20:45:36 +0000168 int res = skipAndProcessFrameHeader(frameType, payload, payloadSize, os);
169 if (res < 0) {
Junxiao Shi2222a612015-06-06 08:01:38 -0700170 return;
171 }
172
173 bool isOk = false;
174 Block block;
175 std::tie(isOk, block) = Block::fromBuffer(payload, payloadSize);
176 if (!isOk) {
Vince Lehman277ecf02016-02-10 16:37:48 -0600177 // if packet is incomplete, we will not be able to process it
178 if (payloadSize > 0) {
179 std::cout << os.str() << ", " << "INCOMPLETE-PACKET" << ", size: " << payloadSize << std::endl;
180 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700181 return;
182 }
183
Vince Lehman277ecf02016-02-10 16:37:48 -0600184 lp::Packet lpPacket;
185 Block netPacket;
186
187 if (block.type() == lp::tlv::LpPacket) {
188 lpPacket = lp::Packet(block);
189
190 Buffer::const_iterator begin, end;
191
192 if (lpPacket.has<lp::FragmentField>()) {
193 std::tie(begin, end) = lpPacket.get<lp::FragmentField>();
194 }
195 else {
196 std::cout << os.str() << ", " << "NDNLPv2-IDLE" << std::endl;
197 return;
198 }
199
200 bool isOk = false;
201 std::tie(isOk, netPacket) = Block::fromBuffer(&*begin, std::distance(begin, end));
202 if (!isOk) {
203 // if network packet is fragmented, we will not be able to process it
204 std::cout << os.str() << ", " << "NDNLPv2-FRAGMENT" << std::endl;
205 return;
206 }
207 }
208 else {
209 netPacket = block;
210 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700211
212 try {
Vince Lehman277ecf02016-02-10 16:37:48 -0600213 if (netPacket.type() == tlv::Interest) {
214 Interest interest(netPacket);
Junxiao Shi2222a612015-06-06 08:01:38 -0700215 if (matchesFilter(interest.getName())) {
Vince Lehman277ecf02016-02-10 16:37:48 -0600216
217 if (lpPacket.has<lp::NackField>()) {
218 lp::Nack nack(interest);
219 nack.setHeader(lpPacket.get<lp::NackField>());
220
221 std::cout << os.str() << ", " << "NACK: " << nack.getReason() << ", " << interest << std::endl;
222 }
223 else {
224 std::cout << os.str() << ", " << "INTEREST: " << interest << std::endl;
225 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700226 }
227 }
Vince Lehman277ecf02016-02-10 16:37:48 -0600228 else if (netPacket.type() == tlv::Data) {
229 Data data(netPacket);
Junxiao Shi2222a612015-06-06 08:01:38 -0700230 if (matchesFilter(data.getName())) {
231 std::cout << os.str() << ", " << "DATA: " << data.getName() << std::endl;
232 }
233 }
Vince Lehman277ecf02016-02-10 16:37:48 -0600234 else {
235 std::cout << os.str() << ", " << "UNKNOWN-NETWORK-PACKET" << std::endl;
236 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700237 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000238 catch (const tlv::Error& e) {
Junxiao Shi2222a612015-06-06 08:01:38 -0700239 std::cerr << e.what() << std::endl;
240 }
241}
242
243void
Junxiao Shic1c2b832016-07-24 20:45:36 +0000244Ndndump::printInterceptTime(std::ostream& os, const pcap_pkthdr* header) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700245{
246 os << header->ts.tv_sec
247 << "."
248 << std::setfill('0') << std::setw(6) << header->ts.tv_usec;
249
250 // struct tm* tm;
251 // if (flags.unit_time) {
252 // os << (int) header->ts.tv_sec
253 // << "."
254 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
Davide Pesaventoc0702702017-08-24 22:04:00 -0400255 // }
256 // else {
Junxiao Shi2222a612015-06-06 08:01:38 -0700257 // tm = localtime(&(header->ts.tv_sec));
258 // os << (int)tm->tm_hour << ":"
259 // << setfill('0') << setw(2) << (int)tm->tm_min<< ":"
260 // << setfill('0') << setw(2) << (int)tm->tm_sec<< "."
261 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
262 // }
263 os << " ";
264}
265
266int
Junxiao Shic1c2b832016-07-24 20:45:36 +0000267Ndndump::skipDataLinkHeaderAndGetFrameType(const uint8_t*& payload, ssize_t& payloadSize) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700268{
269 int frameType = 0;
270
271 switch (m_dataLinkType) {
Junxiao Shic1c2b832016-07-24 20:45:36 +0000272 case DLT_EN10MB: { // Ethernet frames can have Ethernet or 802.3 encapsulation
Junxiao Shi2222a612015-06-06 08:01:38 -0700273 const ether_header* etherHeader = reinterpret_cast<const ether_header*>(payload);
274
275 if (payloadSize < 0) {
276 std::cerr << "Invalid pcap Ethernet frame" << std::endl;
277 return -1;
278 }
279
280 frameType = ntohs(etherHeader->ether_type);
281 payloadSize -= ETHER_HDRLEN;
282 payload += ETHER_HDRLEN;
283
284 break;
285 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000286 case DLT_PPP: {
Junxiao Shi2222a612015-06-06 08:01:38 -0700287 frameType = *payload;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000288 --payloadSize;
289 ++payload;
Junxiao Shi2222a612015-06-06 08:01:38 -0700290
291 if (!(frameType & 1)) {
292 frameType = (frameType << 8) | *payload;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000293 --payloadSize;
294 ++payload;
Junxiao Shi2222a612015-06-06 08:01:38 -0700295 }
296
297 if (payloadSize < 0) {
298 std::cerr << "Invalid PPP frame" << std::endl;
299 return -1;
300 }
301
302 break;
303 }
Junxiao Shi022bddf2016-11-24 23:15:20 +0000304 case DLT_LINUX_SLL: {
305 const sll_header* sllHeader = reinterpret_cast<const sll_header*>(payload);
306
307 if (payloadSize < SLL_HDR_LEN) {
308 std::cerr << "Invalid LINUX_SLL frame" << std::endl;
309 return -1;
310 }
311
312 frameType = ntohs(sllHeader->sll_protocol);
313 payloadSize -= SLL_HDR_LEN;
314 payload += SLL_HDR_LEN;
315
316 break;
317 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700318 }
319
320 return frameType;
321}
322
323int
324Ndndump::skipAndProcessFrameHeader(int frameType,
325 const uint8_t*& payload, ssize_t& payloadSize,
Junxiao Shic1c2b832016-07-24 20:45:36 +0000326 std::ostream& os) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700327{
Junxiao Shic1c2b832016-07-24 20:45:36 +0000328 switch (frameType) {
329 case 0x0800: // ETHERTYPE_IP
330 case DLT_EN10MB: { // pcap encapsulation
331 const ip* ipHeader = reinterpret_cast<const ip*>(payload);
332 size_t ipHeaderSize = IP_HL(ipHeader) * 4;
333 if (ipHeaderSize < 20) {
334 std::cerr << "invalid IP header len " << ipHeaderSize << " bytes" << std::endl;
335 return -1;
336 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700337
Junxiao Shic1c2b832016-07-24 20:45:36 +0000338 os << "From: " << inet_ntoa(ipHeader->ip_src) << ", ";
339 os << "To: " << inet_ntoa(ipHeader->ip_dst);
Junxiao Shi2222a612015-06-06 08:01:38 -0700340
Junxiao Shic1c2b832016-07-24 20:45:36 +0000341 payloadSize -= ipHeaderSize;
342 payload += ipHeaderSize;
Junxiao Shi2222a612015-06-06 08:01:38 -0700343
Junxiao Shic1c2b832016-07-24 20:45:36 +0000344 if (payloadSize < 0) {
345 std::cerr << "Invalid pcap IP packet" << std::endl;
346 return -1;
347 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700348
Junxiao Shic1c2b832016-07-24 20:45:36 +0000349 switch (ipHeader->ip_p) {
350 case IPPROTO_UDP: {
351 // if (!flags.udp)
352 // return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700353
Junxiao Shic1c2b832016-07-24 20:45:36 +0000354 payloadSize -= sizeof(udphdr);
355 payload += sizeof(udphdr);
Junxiao Shi2222a612015-06-06 08:01:38 -0700356
Junxiao Shic1c2b832016-07-24 20:45:36 +0000357 if (payloadSize < 0) {
358 std::cerr << "Invalid pcap UDP/IP packet" << std::endl;
359 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700360 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700361
Junxiao Shic1c2b832016-07-24 20:45:36 +0000362 os << ", Tunnel Type: UDP";
363 break;
364 }
365 case IPPROTO_TCP: {
366 // if (!flags.tcp)
367 // return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700368
Junxiao Shic1c2b832016-07-24 20:45:36 +0000369 const tcphdr* tcpHeader = reinterpret_cast<const tcphdr*>(payload);
370 size_t tcpHeaderSize = TH_OFF(tcpHeader) * 4;
Junxiao Shi2222a612015-06-06 08:01:38 -0700371
Junxiao Shic1c2b832016-07-24 20:45:36 +0000372 if (tcpHeaderSize < 20) {
373 std::cerr << "Invalid TCP Header len: " << tcpHeaderSize <<" bytes" << std::endl;
374 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700375 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000376
377 payloadSize -= tcpHeaderSize;
378 payload += tcpHeaderSize;
379
380 if (payloadSize < 0) {
381 std::cerr << "Invalid pcap TCP/IP packet" << std::endl;
382 return -1;
383 }
384
385 os << ", Tunnel Type: TCP";
386 break;
387 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700388 default:
389 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700390 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000391
392 break;
393 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700394 case /*ETHERTYPE_NDN*/0x7777:
395 os << "Tunnel Type: EthernetFrame";
396 break;
397 case /*ETHERTYPE_NDNLP*/0x8624:
398 os << "Tunnel Type: EthernetFrame";
399 break;
400 case 0x0077: // pcap
401 os << "Tunnel Type: PPP";
402 payloadSize -= 2;
403 payload += 2;
404 break;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000405 default: // do nothing if it is not a recognized type of a packet
Junxiao Shi2222a612015-06-06 08:01:38 -0700406 return -1;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000407 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700408
409 return 0;
410}
411
Junxiao Shic1c2b832016-07-24 20:45:36 +0000412bool
413Ndndump::matchesFilter(const Name& name) const
414{
415 if (nameFilter.empty())
416 return true;
417
418 /// \todo Switch to NDN regular expressions
419
420 return boost::regex_match(name.toUri(), nameFilter);
421}
422
Junxiao Shi3cd47df2015-06-07 20:58:14 -0700423} // namespace dump
Junxiao Shi2222a612015-06-06 08:01:38 -0700424} // namespace ndn