blob: e964c5876fe5c6cdcbf41ae6442b7754199d577f [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
53#include <boost/lexical_cast.hpp>
54
55#include <iomanip>
56
57#include <ndn-cxx/interest.hpp>
58#include <ndn-cxx/data.hpp>
Vince Lehman277ecf02016-02-10 16:37:48 -060059#include <ndn-cxx/lp/nack.hpp>
60#include <ndn-cxx/lp/packet.hpp>
Junxiao Shi2222a612015-06-06 08:01:38 -070061
62namespace ndn {
Junxiao Shi3cd47df2015-06-07 20:58:14 -070063namespace dump {
Junxiao Shi2222a612015-06-06 08:01:38 -070064
Junxiao Shi2222a612015-06-06 08:01:38 -070065const size_t MAX_SNAPLEN = 65535;
66
67void
68Ndndump::run()
69{
70 if (inputFile.empty() && interface.empty()) {
71 char errbuf[PCAP_ERRBUF_SIZE];
72 const char* pcapDevice = pcap_lookupdev(errbuf);
73
74 if (pcapDevice == 0) {
75 throw Error(errbuf);
76 }
77
78 interface = pcapDevice;
79 }
80
81 if (isVerbose) {
82 if (!interface.empty()) {
83 std::cerr << "ndndump: listening on " << interface << std::endl;
84 }
85 else {
86 std::cerr << "ndndump: reading from " << inputFile << std::endl;
87 }
88
89 if (!nameFilter.empty()) {
90 std::cerr << "ndndump: using name filter " << nameFilter << std::endl;
91 }
92 }
93
94 if (!interface.empty()) {
95 char errbuf[PCAP_ERRBUF_SIZE];
96 m_pcap = pcap_open_live(interface.c_str(), MAX_SNAPLEN, 0, 1000, errbuf);
97 if (m_pcap == 0) {
98 throw Error("Cannot open interface " + interface + "(" + errbuf + ")");
99 }
100 }
101 else {
102 char errbuf[PCAP_ERRBUF_SIZE];
103 m_pcap = pcap_open_offline(inputFile.c_str(), errbuf);
104 if (m_pcap == 0) {
105 throw Error("Cannot file " + inputFile + " for reading (" + errbuf + ")");
106 }
107 }
108
109 if (!pcapProgram.empty()) {
110 if (isVerbose) {
111 std::cerr << "ndndump: pcap_filter = " << pcapProgram << std::endl;
112 }
113
114 bpf_program program;
115 int returnValue = pcap_compile(m_pcap, &program, pcapProgram.c_str(), 0, PCAP_NETMASK_UNKNOWN);
116
117 if (returnValue < 0) {
118 throw Error("Cannot parse tcpdump expression '" + pcapProgram +
119 "' (" + pcap_geterr(m_pcap) + ")");
120 }
121
122 returnValue = pcap_setfilter(m_pcap, &program);
123 pcap_freecode(&program);
124
125 if (returnValue < 0) {
126 throw Error(std::string("pcap_setfilter failed (") + pcap_geterr(m_pcap) + ")");
127 }
128 }
129
130 m_dataLinkType = pcap_datalink(m_pcap);
131 if (m_dataLinkType != DLT_EN10MB &&
132 m_dataLinkType != DLT_PPP)
133 {
134 throw Error("Unsupported pcap format (" + boost::lexical_cast<std::string>(m_dataLinkType));
135 }
136
137 pcap_loop(m_pcap, -1, &Ndndump::onCapturedPacket, reinterpret_cast<uint8_t*>(this));
138}
139
140
141void
Vince Lehman277ecf02016-02-10 16:37:48 -0600142Ndndump::onCapturedPacket(const pcap_pkthdr* header, const uint8_t* packet)
Junxiao Shi2222a612015-06-06 08:01:38 -0700143{
144 std::ostringstream os;
145 printInterceptTime(os, header);
146
147 const uint8_t* payload = packet;
148 ssize_t payloadSize = header->len;
149
150 int frameType = skipDataLinkHeaderAndGetFrameType(payload, payloadSize);
151 if (frameType < 0) {
152 std::cerr << "Unknown frame type" << std::endl;
153 return;
154 }
155
156 int returnValue = skipAndProcessFrameHeader(frameType, payload, payloadSize, os);
157 if (returnValue < 0) {
158 return;
159 }
160
161 bool isOk = false;
162 Block block;
163 std::tie(isOk, block) = Block::fromBuffer(payload, payloadSize);
164 if (!isOk) {
Vince Lehman277ecf02016-02-10 16:37:48 -0600165 // if packet is incomplete, we will not be able to process it
166 if (payloadSize > 0) {
167 std::cout << os.str() << ", " << "INCOMPLETE-PACKET" << ", size: " << payloadSize << std::endl;
168 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700169 return;
170 }
171
Vince Lehman277ecf02016-02-10 16:37:48 -0600172 lp::Packet lpPacket;
173 Block netPacket;
174
175 if (block.type() == lp::tlv::LpPacket) {
176 lpPacket = lp::Packet(block);
177
178 Buffer::const_iterator begin, end;
179
180 if (lpPacket.has<lp::FragmentField>()) {
181 std::tie(begin, end) = lpPacket.get<lp::FragmentField>();
182 }
183 else {
184 std::cout << os.str() << ", " << "NDNLPv2-IDLE" << std::endl;
185 return;
186 }
187
188 bool isOk = false;
189 std::tie(isOk, netPacket) = Block::fromBuffer(&*begin, std::distance(begin, end));
190 if (!isOk) {
191 // if network packet is fragmented, we will not be able to process it
192 std::cout << os.str() << ", " << "NDNLPv2-FRAGMENT" << std::endl;
193 return;
194 }
195 }
196 else {
197 netPacket = block;
198 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700199
200 try {
Vince Lehman277ecf02016-02-10 16:37:48 -0600201 if (netPacket.type() == tlv::Interest) {
202 Interest interest(netPacket);
Junxiao Shi2222a612015-06-06 08:01:38 -0700203 if (matchesFilter(interest.getName())) {
Vince Lehman277ecf02016-02-10 16:37:48 -0600204
205 if (lpPacket.has<lp::NackField>()) {
206 lp::Nack nack(interest);
207 nack.setHeader(lpPacket.get<lp::NackField>());
208
209 std::cout << os.str() << ", " << "NACK: " << nack.getReason() << ", " << interest << std::endl;
210 }
211 else {
212 std::cout << os.str() << ", " << "INTEREST: " << interest << std::endl;
213 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700214 }
215 }
Vince Lehman277ecf02016-02-10 16:37:48 -0600216 else if (netPacket.type() == tlv::Data) {
217 Data data(netPacket);
Junxiao Shi2222a612015-06-06 08:01:38 -0700218 if (matchesFilter(data.getName())) {
219 std::cout << os.str() << ", " << "DATA: " << data.getName() << std::endl;
220 }
221 }
Vince Lehman277ecf02016-02-10 16:37:48 -0600222 else {
223 std::cout << os.str() << ", " << "UNKNOWN-NETWORK-PACKET" << std::endl;
224 }
Junxiao Shi2222a612015-06-06 08:01:38 -0700225 }
226 catch (tlv::Error& e) {
227 std::cerr << e.what() << std::endl;
228 }
229}
230
231void
Vince Lehman277ecf02016-02-10 16:37:48 -0600232Ndndump::printInterceptTime(std::ostream& os, const pcap_pkthdr* header)
Junxiao Shi2222a612015-06-06 08:01:38 -0700233{
234 os << header->ts.tv_sec
235 << "."
236 << std::setfill('0') << std::setw(6) << header->ts.tv_usec;
237
238 // struct tm* tm;
239 // if (flags.unit_time) {
240 // os << (int) header->ts.tv_sec
241 // << "."
242 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
243 // } else {
244 // tm = localtime(&(header->ts.tv_sec));
245 // os << (int)tm->tm_hour << ":"
246 // << setfill('0') << setw(2) << (int)tm->tm_min<< ":"
247 // << setfill('0') << setw(2) << (int)tm->tm_sec<< "."
248 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
249 // }
250 os << " ";
251}
252
253int
254Ndndump::skipDataLinkHeaderAndGetFrameType(const uint8_t*& payload, ssize_t& payloadSize)
255{
256 int frameType = 0;
257
258 switch (m_dataLinkType) {
259 case DLT_EN10MB: // Ethernet frames can have Ethernet or 802.3 encapsulation
260 {
261 const ether_header* etherHeader = reinterpret_cast<const ether_header*>(payload);
262
263 if (payloadSize < 0) {
264 std::cerr << "Invalid pcap Ethernet frame" << std::endl;
265 return -1;
266 }
267
268 frameType = ntohs(etherHeader->ether_type);
269 payloadSize -= ETHER_HDRLEN;
270 payload += ETHER_HDRLEN;
271
272 break;
273 }
274 case DLT_PPP:
275 {
276 frameType = *payload;
277 payloadSize--;
278 payload++;
279
280 if (!(frameType & 1)) {
281 frameType = (frameType << 8) | *payload;
282 payloadSize--;
283 payload++;
284 }
285
286 if (payloadSize < 0) {
287 std::cerr << "Invalid PPP frame" << std::endl;
288 return -1;
289 }
290
291 break;
292 }
293 }
294
295 return frameType;
296}
297
298int
299Ndndump::skipAndProcessFrameHeader(int frameType,
300 const uint8_t*& payload, ssize_t& payloadSize,
301 std::ostream& os)
302{
303 switch (frameType)
304 {
305 case /*ETHERTYPE_IP*/0x0800:
306 case DLT_EN10MB: // pcap encapsulation
307 {
308 const ip* ipHeader = reinterpret_cast<const ip*>(payload);
309 size_t ipHeaderSize = IP_HL(ipHeader) * 4;
310 if (ipHeaderSize < 20) {
311 std::cerr << "invalid IP header len " << ipHeaderSize << " bytes" << std::endl;
312 return -1;
313 }
314
315 os << "From: " << inet_ntoa(ipHeader->ip_src) << ", ";
316 os << "To: " << inet_ntoa(ipHeader->ip_dst);
317
318 payloadSize -= ipHeaderSize;
319 payload += ipHeaderSize;
320
321 if (payloadSize < 0) {
322 std::cerr << "Invalid pcap IP packet" << std::endl;
323 return -1;
324 }
325
326 switch (ipHeader->ip_p) {
327 case IPPROTO_UDP:
328 {
329 // if (!flags.udp)
330 // return -1;
331
332 payloadSize -= sizeof(udphdr);
333 payload += sizeof(udphdr);
334
335 if (payloadSize < 0) {
336 std::cerr << "Invalid pcap UDP/IP packet" << std::endl;
337 return -1;
338 }
339
340 os << ", Tunnel Type: UDP";
341 break;
342 }
343 case IPPROTO_TCP:
344 {
345 // if (!flags.tcp)
346 // return -1;
347
348 const tcphdr* tcpHeader = reinterpret_cast<const tcphdr*>(payload);
349 size_t tcpHeaderSize = TH_OFF(tcpHeader) * 4;
350
351 if (tcpHeaderSize < 20) {
352 std::cerr << "Invalid TCP Header len: "<< tcpHeaderSize <<" bytes" << std::endl;
353 return -1;
354 }
355
356 payloadSize -= tcpHeaderSize;
357 payload += tcpHeaderSize;
358
359 if (payloadSize < 0) {
360 std::cerr << "Invalid pcap TCP/IP packet" << std::endl;
361 return -1;
362 }
363
364 os << ", Tunnel Type: TCP";
365 break;
366 }
367 default:
368 return -1;
369 }
370
371 break;
372 }
373 case /*ETHERTYPE_NDN*/0x7777:
374 os << "Tunnel Type: EthernetFrame";
375 break;
376 case /*ETHERTYPE_NDNLP*/0x8624:
377 os << "Tunnel Type: EthernetFrame";
378 break;
379 case 0x0077: // pcap
380 os << "Tunnel Type: PPP";
381 payloadSize -= 2;
382 payload += 2;
383 break;
384 default:
385 return -1;
386 break; // do nothing if it is not a recognized type of a packet
387 }
388
389 return 0;
390}
391
Junxiao Shi3cd47df2015-06-07 20:58:14 -0700392} // namespace dump
Junxiao Shi2222a612015-06-06 08:01:38 -0700393} // namespace ndn