blob: cf8f8396eb1ab5c03ec8ca306ad3c93d105aeb73 [file] [log] [blame]
Junxiao Shi2222a612015-06-06 08:01:38 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Junxiao Shi3cd47df2015-06-07 20:58:14 -07003 * Copyright (c) 2014-2015, Regents of the University of California.
4 *
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 Shi2222a612015-06-06 08:01:38 -070050} // namespace ndn
Junxiao Shi3cd47df2015-06-07 20:58:14 -070051} // namespace dump
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>
59
60namespace ndn {
Junxiao Shi3cd47df2015-06-07 20:58:14 -070061namespace dump {
Junxiao Shi2222a612015-06-06 08:01:38 -070062
63// const uint8_t NDNLP_HEADER[] = {'N', 'd', 'n', 'l', 'p'};
64
65const 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
142Ndndump::onCapturedPacket(const struct pcap_pkthdr* header, const uint8_t* packet)
143{
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) {
165 // if packet is fragmented, we will not be able to process it
166 return;
167 }
168
169 /// \todo Detect various header (LocalControlHeader, NDNLP, etc.)
170
171 try {
172 if (block.type() == tlv::Interest) {
173 Interest interest(block);
174 if (matchesFilter(interest.getName())) {
175 std::cout << os.str() << ", " << "INTEREST: " << interest << std::endl;
176 }
177 }
178 else if (block.type() == tlv::Data) {
179 Data data(block);
180 if (matchesFilter(data.getName())) {
181 std::cout << os.str() << ", " << "DATA: " << data.getName() << std::endl;
182 }
183 }
184 }
185 catch (tlv::Error& e) {
186 std::cerr << e.what() << std::endl;
187 }
188}
189
190void
191Ndndump::printInterceptTime(std::ostream& os, const struct pcap_pkthdr* header)
192{
193 os << header->ts.tv_sec
194 << "."
195 << std::setfill('0') << std::setw(6) << header->ts.tv_usec;
196
197 // struct tm* tm;
198 // if (flags.unit_time) {
199 // os << (int) header->ts.tv_sec
200 // << "."
201 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
202 // } else {
203 // tm = localtime(&(header->ts.tv_sec));
204 // os << (int)tm->tm_hour << ":"
205 // << setfill('0') << setw(2) << (int)tm->tm_min<< ":"
206 // << setfill('0') << setw(2) << (int)tm->tm_sec<< "."
207 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
208 // }
209 os << " ";
210}
211
212int
213Ndndump::skipDataLinkHeaderAndGetFrameType(const uint8_t*& payload, ssize_t& payloadSize)
214{
215 int frameType = 0;
216
217 switch (m_dataLinkType) {
218 case DLT_EN10MB: // Ethernet frames can have Ethernet or 802.3 encapsulation
219 {
220 const ether_header* etherHeader = reinterpret_cast<const ether_header*>(payload);
221
222 if (payloadSize < 0) {
223 std::cerr << "Invalid pcap Ethernet frame" << std::endl;
224 return -1;
225 }
226
227 frameType = ntohs(etherHeader->ether_type);
228 payloadSize -= ETHER_HDRLEN;
229 payload += ETHER_HDRLEN;
230
231 break;
232 }
233 case DLT_PPP:
234 {
235 frameType = *payload;
236 payloadSize--;
237 payload++;
238
239 if (!(frameType & 1)) {
240 frameType = (frameType << 8) | *payload;
241 payloadSize--;
242 payload++;
243 }
244
245 if (payloadSize < 0) {
246 std::cerr << "Invalid PPP frame" << std::endl;
247 return -1;
248 }
249
250 break;
251 }
252 }
253
254 return frameType;
255}
256
257int
258Ndndump::skipAndProcessFrameHeader(int frameType,
259 const uint8_t*& payload, ssize_t& payloadSize,
260 std::ostream& os)
261{
262 switch (frameType)
263 {
264 case /*ETHERTYPE_IP*/0x0800:
265 case DLT_EN10MB: // pcap encapsulation
266 {
267 const ip* ipHeader = reinterpret_cast<const ip*>(payload);
268 size_t ipHeaderSize = IP_HL(ipHeader) * 4;
269 if (ipHeaderSize < 20) {
270 std::cerr << "invalid IP header len " << ipHeaderSize << " bytes" << std::endl;
271 return -1;
272 }
273
274 os << "From: " << inet_ntoa(ipHeader->ip_src) << ", ";
275 os << "To: " << inet_ntoa(ipHeader->ip_dst);
276
277 payloadSize -= ipHeaderSize;
278 payload += ipHeaderSize;
279
280 if (payloadSize < 0) {
281 std::cerr << "Invalid pcap IP packet" << std::endl;
282 return -1;
283 }
284
285 switch (ipHeader->ip_p) {
286 case IPPROTO_UDP:
287 {
288 // if (!flags.udp)
289 // return -1;
290
291 payloadSize -= sizeof(udphdr);
292 payload += sizeof(udphdr);
293
294 if (payloadSize < 0) {
295 std::cerr << "Invalid pcap UDP/IP packet" << std::endl;
296 return -1;
297 }
298
299 os << ", Tunnel Type: UDP";
300 break;
301 }
302 case IPPROTO_TCP:
303 {
304 // if (!flags.tcp)
305 // return -1;
306
307 const tcphdr* tcpHeader = reinterpret_cast<const tcphdr*>(payload);
308 size_t tcpHeaderSize = TH_OFF(tcpHeader) * 4;
309
310 if (tcpHeaderSize < 20) {
311 std::cerr << "Invalid TCP Header len: "<< tcpHeaderSize <<" bytes" << std::endl;
312 return -1;
313 }
314
315 payloadSize -= tcpHeaderSize;
316 payload += tcpHeaderSize;
317
318 if (payloadSize < 0) {
319 std::cerr << "Invalid pcap TCP/IP packet" << std::endl;
320 return -1;
321 }
322
323 os << ", Tunnel Type: TCP";
324 break;
325 }
326 default:
327 return -1;
328 }
329
330 break;
331 }
332 case /*ETHERTYPE_NDN*/0x7777:
333 os << "Tunnel Type: EthernetFrame";
334 break;
335 case /*ETHERTYPE_NDNLP*/0x8624:
336 os << "Tunnel Type: EthernetFrame";
337 break;
338 case 0x0077: // pcap
339 os << "Tunnel Type: PPP";
340 payloadSize -= 2;
341 payload += 2;
342 break;
343 default:
344 return -1;
345 break; // do nothing if it is not a recognized type of a packet
346 }
347
348 return 0;
349}
350
Junxiao Shi3cd47df2015-06-07 20:58:14 -0700351} // namespace dump
Junxiao Shi2222a612015-06-06 08:01:38 -0700352} // namespace ndn