blob: e018cd69e6bf6b71ac4d673878eb1885a486c395 [file] [log] [blame]
Junxiao Shi2222a612015-06-06 08:01:38 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2011-2014, Regents of the University of California,
4 *
5 * This file is part of ndndump, the packet capture and analysis tool for Named Data
6 * Networking (NDN).
7 *
8 * ndndump 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 * ndndump 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 * ndndump, e.g., in COPYING file. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
20#include "ndndump.hpp"
21
22#include "config.hpp"
23
24#include "tcpdump/tcpdump-stdinc.h"
25
26namespace ndn {
27namespace tools {
28// namespace is necessary for now to prevent clashing with system includes
29// this will not be needed after ndn-cxx/common.hpp will stop including <boost/asio.hpp>
30
31#include "tcpdump/ether.h"
32#include "tcpdump/ip.h"
33#include "tcpdump/udp.h"
34#include "tcpdump/tcp.h"
35
36} // namespace tools
37} // namespace ndn
38
39#include <boost/lexical_cast.hpp>
40
41#include <iomanip>
42
43#include <ndn-cxx/interest.hpp>
44#include <ndn-cxx/data.hpp>
45
46namespace ndn {
47namespace tools {
48
49// const uint8_t NDNLP_HEADER[] = {'N', 'd', 'n', 'l', 'p'};
50
51const size_t MAX_SNAPLEN = 65535;
52
53void
54Ndndump::run()
55{
56 if (inputFile.empty() && interface.empty()) {
57 char errbuf[PCAP_ERRBUF_SIZE];
58 const char* pcapDevice = pcap_lookupdev(errbuf);
59
60 if (pcapDevice == 0) {
61 throw Error(errbuf);
62 }
63
64 interface = pcapDevice;
65 }
66
67 if (isVerbose) {
68 if (!interface.empty()) {
69 std::cerr << "ndndump: listening on " << interface << std::endl;
70 }
71 else {
72 std::cerr << "ndndump: reading from " << inputFile << std::endl;
73 }
74
75 if (!nameFilter.empty()) {
76 std::cerr << "ndndump: using name filter " << nameFilter << std::endl;
77 }
78 }
79
80 if (!interface.empty()) {
81 char errbuf[PCAP_ERRBUF_SIZE];
82 m_pcap = pcap_open_live(interface.c_str(), MAX_SNAPLEN, 0, 1000, errbuf);
83 if (m_pcap == 0) {
84 throw Error("Cannot open interface " + interface + "(" + errbuf + ")");
85 }
86 }
87 else {
88 char errbuf[PCAP_ERRBUF_SIZE];
89 m_pcap = pcap_open_offline(inputFile.c_str(), errbuf);
90 if (m_pcap == 0) {
91 throw Error("Cannot file " + inputFile + " for reading (" + errbuf + ")");
92 }
93 }
94
95 if (!pcapProgram.empty()) {
96 if (isVerbose) {
97 std::cerr << "ndndump: pcap_filter = " << pcapProgram << std::endl;
98 }
99
100 bpf_program program;
101 int returnValue = pcap_compile(m_pcap, &program, pcapProgram.c_str(), 0, PCAP_NETMASK_UNKNOWN);
102
103 if (returnValue < 0) {
104 throw Error("Cannot parse tcpdump expression '" + pcapProgram +
105 "' (" + pcap_geterr(m_pcap) + ")");
106 }
107
108 returnValue = pcap_setfilter(m_pcap, &program);
109 pcap_freecode(&program);
110
111 if (returnValue < 0) {
112 throw Error(std::string("pcap_setfilter failed (") + pcap_geterr(m_pcap) + ")");
113 }
114 }
115
116 m_dataLinkType = pcap_datalink(m_pcap);
117 if (m_dataLinkType != DLT_EN10MB &&
118 m_dataLinkType != DLT_PPP)
119 {
120 throw Error("Unsupported pcap format (" + boost::lexical_cast<std::string>(m_dataLinkType));
121 }
122
123 pcap_loop(m_pcap, -1, &Ndndump::onCapturedPacket, reinterpret_cast<uint8_t*>(this));
124}
125
126
127void
128Ndndump::onCapturedPacket(const struct pcap_pkthdr* header, const uint8_t* packet)
129{
130 std::ostringstream os;
131 printInterceptTime(os, header);
132
133 const uint8_t* payload = packet;
134 ssize_t payloadSize = header->len;
135
136 int frameType = skipDataLinkHeaderAndGetFrameType(payload, payloadSize);
137 if (frameType < 0) {
138 std::cerr << "Unknown frame type" << std::endl;
139 return;
140 }
141
142 int returnValue = skipAndProcessFrameHeader(frameType, payload, payloadSize, os);
143 if (returnValue < 0) {
144 return;
145 }
146
147 bool isOk = false;
148 Block block;
149 std::tie(isOk, block) = Block::fromBuffer(payload, payloadSize);
150 if (!isOk) {
151 // if packet is fragmented, we will not be able to process it
152 return;
153 }
154
155 /// \todo Detect various header (LocalControlHeader, NDNLP, etc.)
156
157 try {
158 if (block.type() == tlv::Interest) {
159 Interest interest(block);
160 if (matchesFilter(interest.getName())) {
161 std::cout << os.str() << ", " << "INTEREST: " << interest << std::endl;
162 }
163 }
164 else if (block.type() == tlv::Data) {
165 Data data(block);
166 if (matchesFilter(data.getName())) {
167 std::cout << os.str() << ", " << "DATA: " << data.getName() << std::endl;
168 }
169 }
170 }
171 catch (tlv::Error& e) {
172 std::cerr << e.what() << std::endl;
173 }
174}
175
176void
177Ndndump::printInterceptTime(std::ostream& os, const struct pcap_pkthdr* header)
178{
179 os << header->ts.tv_sec
180 << "."
181 << std::setfill('0') << std::setw(6) << header->ts.tv_usec;
182
183 // struct tm* tm;
184 // if (flags.unit_time) {
185 // os << (int) header->ts.tv_sec
186 // << "."
187 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
188 // } else {
189 // tm = localtime(&(header->ts.tv_sec));
190 // os << (int)tm->tm_hour << ":"
191 // << setfill('0') << setw(2) << (int)tm->tm_min<< ":"
192 // << setfill('0') << setw(2) << (int)tm->tm_sec<< "."
193 // << setfill('0') << setw(6) << (int)header->ts.tv_usec;
194 // }
195 os << " ";
196}
197
198int
199Ndndump::skipDataLinkHeaderAndGetFrameType(const uint8_t*& payload, ssize_t& payloadSize)
200{
201 int frameType = 0;
202
203 switch (m_dataLinkType) {
204 case DLT_EN10MB: // Ethernet frames can have Ethernet or 802.3 encapsulation
205 {
206 const ether_header* etherHeader = reinterpret_cast<const ether_header*>(payload);
207
208 if (payloadSize < 0) {
209 std::cerr << "Invalid pcap Ethernet frame" << std::endl;
210 return -1;
211 }
212
213 frameType = ntohs(etherHeader->ether_type);
214 payloadSize -= ETHER_HDRLEN;
215 payload += ETHER_HDRLEN;
216
217 break;
218 }
219 case DLT_PPP:
220 {
221 frameType = *payload;
222 payloadSize--;
223 payload++;
224
225 if (!(frameType & 1)) {
226 frameType = (frameType << 8) | *payload;
227 payloadSize--;
228 payload++;
229 }
230
231 if (payloadSize < 0) {
232 std::cerr << "Invalid PPP frame" << std::endl;
233 return -1;
234 }
235
236 break;
237 }
238 }
239
240 return frameType;
241}
242
243int
244Ndndump::skipAndProcessFrameHeader(int frameType,
245 const uint8_t*& payload, ssize_t& payloadSize,
246 std::ostream& os)
247{
248 switch (frameType)
249 {
250 case /*ETHERTYPE_IP*/0x0800:
251 case DLT_EN10MB: // pcap encapsulation
252 {
253 const ip* ipHeader = reinterpret_cast<const ip*>(payload);
254 size_t ipHeaderSize = IP_HL(ipHeader) * 4;
255 if (ipHeaderSize < 20) {
256 std::cerr << "invalid IP header len " << ipHeaderSize << " bytes" << std::endl;
257 return -1;
258 }
259
260 os << "From: " << inet_ntoa(ipHeader->ip_src) << ", ";
261 os << "To: " << inet_ntoa(ipHeader->ip_dst);
262
263 payloadSize -= ipHeaderSize;
264 payload += ipHeaderSize;
265
266 if (payloadSize < 0) {
267 std::cerr << "Invalid pcap IP packet" << std::endl;
268 return -1;
269 }
270
271 switch (ipHeader->ip_p) {
272 case IPPROTO_UDP:
273 {
274 // if (!flags.udp)
275 // return -1;
276
277 payloadSize -= sizeof(udphdr);
278 payload += sizeof(udphdr);
279
280 if (payloadSize < 0) {
281 std::cerr << "Invalid pcap UDP/IP packet" << std::endl;
282 return -1;
283 }
284
285 os << ", Tunnel Type: UDP";
286 break;
287 }
288 case IPPROTO_TCP:
289 {
290 // if (!flags.tcp)
291 // return -1;
292
293 const tcphdr* tcpHeader = reinterpret_cast<const tcphdr*>(payload);
294 size_t tcpHeaderSize = TH_OFF(tcpHeader) * 4;
295
296 if (tcpHeaderSize < 20) {
297 std::cerr << "Invalid TCP Header len: "<< tcpHeaderSize <<" bytes" << std::endl;
298 return -1;
299 }
300
301 payloadSize -= tcpHeaderSize;
302 payload += tcpHeaderSize;
303
304 if (payloadSize < 0) {
305 std::cerr << "Invalid pcap TCP/IP packet" << std::endl;
306 return -1;
307 }
308
309 os << ", Tunnel Type: TCP";
310 break;
311 }
312 default:
313 return -1;
314 }
315
316 break;
317 }
318 case /*ETHERTYPE_NDN*/0x7777:
319 os << "Tunnel Type: EthernetFrame";
320 break;
321 case /*ETHERTYPE_NDNLP*/0x8624:
322 os << "Tunnel Type: EthernetFrame";
323 break;
324 case 0x0077: // pcap
325 os << "Tunnel Type: PPP";
326 payloadSize -= 2;
327 payload += 2;
328 break;
329 default:
330 return -1;
331 break; // do nothing if it is not a recognized type of a packet
332 }
333
334 return 0;
335}
336
337} // namespace tools
338} // namespace ndn