blob: 91d68279f751864d4947088140eb26b6b858222b [file] [log] [blame]
Junxiao Shi2222a612015-06-06 08:01:38 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Vince Lehman277ecf02016-02-10 16:37:48 -06003 * Copyright (c) 2014-2016, 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>
58
59#include <ndn-cxx/interest.hpp>
60#include <ndn-cxx/data.hpp>
Vince Lehman277ecf02016-02-10 16:37:48 -060061#include <ndn-cxx/lp/nack.hpp>
62#include <ndn-cxx/lp/packet.hpp>
Junxiao Shi2222a612015-06-06 08:01:38 -070063
64namespace ndn {
Junxiao Shi3cd47df2015-06-07 20:58:14 -070065namespace dump {
Junxiao Shi2222a612015-06-06 08:01:38 -070066
Junxiao Shi2222a612015-06-06 08:01:38 -070067const size_t MAX_SNAPLEN = 65535;
68
Junxiao Shic1c2b832016-07-24 20:45:36 +000069Ndndump::Ndndump()
70 : isVerbose(false)
71 , pcapProgram("(ether proto 0x8624) || (tcp port 6363) || (udp port 6363)")
72 // , isSuccinct(false)
73 // , isMatchInverted(false)
74 // , shouldPrintStructure(false)
75 // , isTcpOnly(false)
76 // , isUdpOnly(false)
77{
78}
79
Junxiao Shi2222a612015-06-06 08:01:38 -070080void
81Ndndump::run()
82{
83 if (inputFile.empty() && interface.empty()) {
84 char errbuf[PCAP_ERRBUF_SIZE];
85 const char* pcapDevice = pcap_lookupdev(errbuf);
86
Junxiao Shic1c2b832016-07-24 20:45:36 +000087 if (pcapDevice == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +000088 BOOST_THROW_EXCEPTION(Error(errbuf));
Junxiao Shi2222a612015-06-06 08:01:38 -070089 }
90
91 interface = pcapDevice;
92 }
93
94 if (isVerbose) {
95 if (!interface.empty()) {
96 std::cerr << "ndndump: listening on " << interface << std::endl;
97 }
98 else {
99 std::cerr << "ndndump: reading from " << inputFile << std::endl;
100 }
101
102 if (!nameFilter.empty()) {
103 std::cerr << "ndndump: using name filter " << nameFilter << std::endl;
104 }
105 }
106
107 if (!interface.empty()) {
108 char errbuf[PCAP_ERRBUF_SIZE];
109 m_pcap = pcap_open_live(interface.c_str(), MAX_SNAPLEN, 0, 1000, 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 interface " + interface + " (" + errbuf + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700112 }
113 }
114 else {
115 char errbuf[PCAP_ERRBUF_SIZE];
116 m_pcap = pcap_open_offline(inputFile.c_str(), errbuf);
Junxiao Shic1c2b832016-07-24 20:45:36 +0000117 if (m_pcap == nullptr) {
Junxiao Shic7599632016-07-24 20:46:24 +0000118 BOOST_THROW_EXCEPTION(Error("Cannot open file " + inputFile + " for reading (" + errbuf + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700119 }
120 }
121
122 if (!pcapProgram.empty()) {
123 if (isVerbose) {
124 std::cerr << "ndndump: pcap_filter = " << pcapProgram << std::endl;
125 }
126
127 bpf_program program;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000128 int res = pcap_compile(m_pcap, &program, pcapProgram.c_str(), 0, PCAP_NETMASK_UNKNOWN);
Junxiao Shi2222a612015-06-06 08:01:38 -0700129
Junxiao Shic1c2b832016-07-24 20:45:36 +0000130 if (res < 0) {
Junxiao Shic7599632016-07-24 20:46:24 +0000131 BOOST_THROW_EXCEPTION(Error("Cannot parse tcpdump expression '" + pcapProgram +
132 "' (" + pcap_geterr(m_pcap) + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700133 }
134
Junxiao Shic1c2b832016-07-24 20:45:36 +0000135 res = pcap_setfilter(m_pcap, &program);
Junxiao Shi2222a612015-06-06 08:01:38 -0700136 pcap_freecode(&program);
137
Junxiao Shic1c2b832016-07-24 20:45:36 +0000138 if (res < 0) {
Junxiao Shic7599632016-07-24 20:46:24 +0000139 BOOST_THROW_EXCEPTION(Error(std::string("pcap_setfilter failed (") + pcap_geterr(m_pcap) + ")"));
Junxiao Shi2222a612015-06-06 08:01:38 -0700140 }
141 }
142
143 m_dataLinkType = pcap_datalink(m_pcap);
Junxiao Shi022bddf2016-11-24 23:15:20 +0000144 if (m_dataLinkType != DLT_EN10MB && m_dataLinkType != DLT_PPP && m_dataLinkType != DLT_LINUX_SLL) {
Junxiao Shi1c71bcd2016-11-24 04:00:42 +0000145 BOOST_THROW_EXCEPTION(Error("Unsupported pcap format (" + to_string(m_dataLinkType) + ")"));
Junxiao Shic1c2b832016-07-24 20:45:36 +0000146 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700147
148 pcap_loop(m_pcap, -1, &Ndndump::onCapturedPacket, reinterpret_cast<uint8_t*>(this));
149}
150
151
152void
Junxiao Shic1c2b832016-07-24 20:45:36 +0000153Ndndump::onCapturedPacket(const pcap_pkthdr* header, const uint8_t* packet) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700154{
155 std::ostringstream os;
156 printInterceptTime(os, header);
157
158 const uint8_t* payload = packet;
159 ssize_t payloadSize = header->len;
160
161 int frameType = skipDataLinkHeaderAndGetFrameType(payload, payloadSize);
162 if (frameType < 0) {
163 std::cerr << "Unknown frame type" << std::endl;
164 return;
165 }
166
Junxiao Shic1c2b832016-07-24 20:45:36 +0000167 int res = skipAndProcessFrameHeader(frameType, payload, payloadSize, os);
168 if (res < 0) {
Junxiao Shi2222a612015-06-06 08:01:38 -0700169 return;
170 }
171
172 bool isOk = false;
173 Block block;
174 std::tie(isOk, block) = Block::fromBuffer(payload, payloadSize);
175 if (!isOk) {
Vince Lehman277ecf02016-02-10 16:37:48 -0600176 // if packet is incomplete, we will not be able to process it
177 if (payloadSize > 0) {
178 std::cout << os.str() << ", " << "INCOMPLETE-PACKET" << ", size: " << payloadSize << std::endl;
179 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700180 return;
181 }
182
Vince Lehman277ecf02016-02-10 16:37:48 -0600183 lp::Packet lpPacket;
184 Block netPacket;
185
186 if (block.type() == lp::tlv::LpPacket) {
187 lpPacket = lp::Packet(block);
188
189 Buffer::const_iterator begin, end;
190
191 if (lpPacket.has<lp::FragmentField>()) {
192 std::tie(begin, end) = lpPacket.get<lp::FragmentField>();
193 }
194 else {
195 std::cout << os.str() << ", " << "NDNLPv2-IDLE" << std::endl;
196 return;
197 }
198
199 bool isOk = false;
200 std::tie(isOk, netPacket) = Block::fromBuffer(&*begin, std::distance(begin, end));
201 if (!isOk) {
202 // if network packet is fragmented, we will not be able to process it
203 std::cout << os.str() << ", " << "NDNLPv2-FRAGMENT" << std::endl;
204 return;
205 }
206 }
207 else {
208 netPacket = block;
209 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700210
211 try {
Vince Lehman277ecf02016-02-10 16:37:48 -0600212 if (netPacket.type() == tlv::Interest) {
213 Interest interest(netPacket);
Junxiao Shi2222a612015-06-06 08:01:38 -0700214 if (matchesFilter(interest.getName())) {
Vince Lehman277ecf02016-02-10 16:37:48 -0600215
216 if (lpPacket.has<lp::NackField>()) {
217 lp::Nack nack(interest);
218 nack.setHeader(lpPacket.get<lp::NackField>());
219
220 std::cout << os.str() << ", " << "NACK: " << nack.getReason() << ", " << interest << std::endl;
221 }
222 else {
223 std::cout << os.str() << ", " << "INTEREST: " << interest << std::endl;
224 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700225 }
226 }
Vince Lehman277ecf02016-02-10 16:37:48 -0600227 else if (netPacket.type() == tlv::Data) {
228 Data data(netPacket);
Junxiao Shi2222a612015-06-06 08:01:38 -0700229 if (matchesFilter(data.getName())) {
230 std::cout << os.str() << ", " << "DATA: " << data.getName() << std::endl;
231 }
232 }
Vince Lehman277ecf02016-02-10 16:37:48 -0600233 else {
234 std::cout << os.str() << ", " << "UNKNOWN-NETWORK-PACKET" << std::endl;
235 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700236 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000237 catch (const tlv::Error& e) {
Junxiao Shi2222a612015-06-06 08:01:38 -0700238 std::cerr << e.what() << std::endl;
239 }
240}
241
242void
Junxiao Shic1c2b832016-07-24 20:45:36 +0000243Ndndump::printInterceptTime(std::ostream& os, const pcap_pkthdr* header) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700244{
245 os << header->ts.tv_sec
246 << "."
247 << std::setfill('0') << std::setw(6) << header->ts.tv_usec;
248
249 // struct tm* tm;
250 // if (flags.unit_time) {
251 // os << (int) header->ts.tv_sec
252 // << "."
253 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
254 // } else {
255 // tm = localtime(&(header->ts.tv_sec));
256 // os << (int)tm->tm_hour << ":"
257 // << setfill('0') << setw(2) << (int)tm->tm_min<< ":"
258 // << setfill('0') << setw(2) << (int)tm->tm_sec<< "."
259 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
260 // }
261 os << " ";
262}
263
264int
Junxiao Shic1c2b832016-07-24 20:45:36 +0000265Ndndump::skipDataLinkHeaderAndGetFrameType(const uint8_t*& payload, ssize_t& payloadSize) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700266{
267 int frameType = 0;
268
269 switch (m_dataLinkType) {
Junxiao Shic1c2b832016-07-24 20:45:36 +0000270 case DLT_EN10MB: { // Ethernet frames can have Ethernet or 802.3 encapsulation
Junxiao Shi2222a612015-06-06 08:01:38 -0700271 const ether_header* etherHeader = reinterpret_cast<const ether_header*>(payload);
272
273 if (payloadSize < 0) {
274 std::cerr << "Invalid pcap Ethernet frame" << std::endl;
275 return -1;
276 }
277
278 frameType = ntohs(etherHeader->ether_type);
279 payloadSize -= ETHER_HDRLEN;
280 payload += ETHER_HDRLEN;
281
282 break;
283 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000284 case DLT_PPP: {
Junxiao Shi2222a612015-06-06 08:01:38 -0700285 frameType = *payload;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000286 --payloadSize;
287 ++payload;
Junxiao Shi2222a612015-06-06 08:01:38 -0700288
289 if (!(frameType & 1)) {
290 frameType = (frameType << 8) | *payload;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000291 --payloadSize;
292 ++payload;
Junxiao Shi2222a612015-06-06 08:01:38 -0700293 }
294
295 if (payloadSize < 0) {
296 std::cerr << "Invalid PPP frame" << std::endl;
297 return -1;
298 }
299
300 break;
301 }
Junxiao Shi022bddf2016-11-24 23:15:20 +0000302 case DLT_LINUX_SLL: {
303 const sll_header* sllHeader = reinterpret_cast<const sll_header*>(payload);
304
305 if (payloadSize < SLL_HDR_LEN) {
306 std::cerr << "Invalid LINUX_SLL frame" << std::endl;
307 return -1;
308 }
309
310 frameType = ntohs(sllHeader->sll_protocol);
311 payloadSize -= SLL_HDR_LEN;
312 payload += SLL_HDR_LEN;
313
314 break;
315 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700316 }
317
318 return frameType;
319}
320
321int
322Ndndump::skipAndProcessFrameHeader(int frameType,
323 const uint8_t*& payload, ssize_t& payloadSize,
Junxiao Shic1c2b832016-07-24 20:45:36 +0000324 std::ostream& os) const
Junxiao Shi2222a612015-06-06 08:01:38 -0700325{
Junxiao Shic1c2b832016-07-24 20:45:36 +0000326 switch (frameType) {
327 case 0x0800: // ETHERTYPE_IP
328 case DLT_EN10MB: { // pcap encapsulation
329 const ip* ipHeader = reinterpret_cast<const ip*>(payload);
330 size_t ipHeaderSize = IP_HL(ipHeader) * 4;
331 if (ipHeaderSize < 20) {
332 std::cerr << "invalid IP header len " << ipHeaderSize << " bytes" << std::endl;
333 return -1;
334 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700335
Junxiao Shic1c2b832016-07-24 20:45:36 +0000336 os << "From: " << inet_ntoa(ipHeader->ip_src) << ", ";
337 os << "To: " << inet_ntoa(ipHeader->ip_dst);
Junxiao Shi2222a612015-06-06 08:01:38 -0700338
Junxiao Shic1c2b832016-07-24 20:45:36 +0000339 payloadSize -= ipHeaderSize;
340 payload += ipHeaderSize;
Junxiao Shi2222a612015-06-06 08:01:38 -0700341
Junxiao Shic1c2b832016-07-24 20:45:36 +0000342 if (payloadSize < 0) {
343 std::cerr << "Invalid pcap IP packet" << std::endl;
344 return -1;
345 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700346
Junxiao Shic1c2b832016-07-24 20:45:36 +0000347 switch (ipHeader->ip_p) {
348 case IPPROTO_UDP: {
349 // if (!flags.udp)
350 // return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700351
Junxiao Shic1c2b832016-07-24 20:45:36 +0000352 payloadSize -= sizeof(udphdr);
353 payload += sizeof(udphdr);
Junxiao Shi2222a612015-06-06 08:01:38 -0700354
Junxiao Shic1c2b832016-07-24 20:45:36 +0000355 if (payloadSize < 0) {
356 std::cerr << "Invalid pcap UDP/IP packet" << std::endl;
357 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700358 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700359
Junxiao Shic1c2b832016-07-24 20:45:36 +0000360 os << ", Tunnel Type: UDP";
361 break;
362 }
363 case IPPROTO_TCP: {
364 // if (!flags.tcp)
365 // return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700366
Junxiao Shic1c2b832016-07-24 20:45:36 +0000367 const tcphdr* tcpHeader = reinterpret_cast<const tcphdr*>(payload);
368 size_t tcpHeaderSize = TH_OFF(tcpHeader) * 4;
Junxiao Shi2222a612015-06-06 08:01:38 -0700369
Junxiao Shic1c2b832016-07-24 20:45:36 +0000370 if (tcpHeaderSize < 20) {
371 std::cerr << "Invalid TCP Header len: " << tcpHeaderSize <<" bytes" << std::endl;
372 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700373 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000374
375 payloadSize -= tcpHeaderSize;
376 payload += tcpHeaderSize;
377
378 if (payloadSize < 0) {
379 std::cerr << "Invalid pcap TCP/IP packet" << std::endl;
380 return -1;
381 }
382
383 os << ", Tunnel Type: TCP";
384 break;
385 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700386 default:
387 return -1;
Junxiao Shi2222a612015-06-06 08:01:38 -0700388 }
Junxiao Shic1c2b832016-07-24 20:45:36 +0000389
390 break;
391 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700392 case /*ETHERTYPE_NDN*/0x7777:
393 os << "Tunnel Type: EthernetFrame";
394 break;
395 case /*ETHERTYPE_NDNLP*/0x8624:
396 os << "Tunnel Type: EthernetFrame";
397 break;
398 case 0x0077: // pcap
399 os << "Tunnel Type: PPP";
400 payloadSize -= 2;
401 payload += 2;
402 break;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000403 default: // do nothing if it is not a recognized type of a packet
Junxiao Shi2222a612015-06-06 08:01:38 -0700404 return -1;
Junxiao Shic1c2b832016-07-24 20:45:36 +0000405 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700406
407 return 0;
408}
409
Junxiao Shic1c2b832016-07-24 20:45:36 +0000410bool
411Ndndump::matchesFilter(const Name& name) const
412{
413 if (nameFilter.empty())
414 return true;
415
416 /// \todo Switch to NDN regular expressions
417
418 return boost::regex_match(name.toUri(), nameFilter);
419}
420
Junxiao Shi3cd47df2015-06-07 20:58:14 -0700421} // namespace dump
Junxiao Shi2222a612015-06-06 08:01:38 -0700422} // namespace ndn