/
pcap-helper.cpp
141 lines (120 loc) · 4.11 KB
/
pcap-helper.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pcap-helper.hpp"
#include <pcap/pcap.h>
#include <unistd.h>
#if !defined(PCAP_NETMASK_UNKNOWN)
#define PCAP_NETMASK_UNKNOWN 0xffffffff
#endif
namespace nfd {
namespace face {
PcapHelper::PcapHelper(const std::string& interfaceName)
: m_pcap(nullptr)
{
char errbuf[PCAP_ERRBUF_SIZE] = {};
m_pcap = pcap_create(interfaceName.data(), errbuf);
if (!m_pcap)
BOOST_THROW_EXCEPTION(Error("pcap_create: " + std::string(errbuf)));
#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
// Enable "immediate mode", effectively disabling any read buffering in the kernel.
// This corresponds to the BIOCIMMEDIATE ioctl on BSD-like systems (including macOS)
// where libpcap uses a BPF device. On Linux this forces libpcap not to use TPACKET_V3,
// even if the kernel supports it, thus preventing bug #1511.
if (pcap_set_immediate_mode(m_pcap, 1) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_set_immediate_mode failed"));
#endif
}
PcapHelper::~PcapHelper()
{
close();
}
void
PcapHelper::activate(int dlt)
{
int ret = pcap_activate(m_pcap);
if (ret < 0)
BOOST_THROW_EXCEPTION(Error("pcap_activate: " + std::string(pcap_statustostr(ret))));
if (pcap_set_datalink(m_pcap, dlt) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_set_datalink: " + getLastError()));
if (pcap_setdirection(m_pcap, PCAP_D_IN) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_setdirection: " + getLastError()));
}
void
PcapHelper::close()
{
if (m_pcap) {
pcap_close(m_pcap);
m_pcap = nullptr;
}
}
int
PcapHelper::getFd() const
{
int fd = pcap_get_selectable_fd(m_pcap);
if (fd < 0)
BOOST_THROW_EXCEPTION(Error("pcap_get_selectable_fd failed"));
// we need to duplicate the fd, otherwise both pcap_close() and the
// caller may attempt to close the same fd and one of them will fail
return ::dup(fd);
}
std::string
PcapHelper::getLastError() const
{
return pcap_geterr(m_pcap);
}
size_t
PcapHelper::getNDropped() const
{
pcap_stat ps{};
if (pcap_stats(m_pcap, &ps) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_stats: " + getLastError()));
return ps.ps_drop;
}
void
PcapHelper::setPacketFilter(const char* filter) const
{
bpf_program prog;
if (pcap_compile(m_pcap, &prog, filter, 1, PCAP_NETMASK_UNKNOWN) < 0)
BOOST_THROW_EXCEPTION(Error("pcap_compile: " + getLastError()));
int ret = pcap_setfilter(m_pcap, &prog);
pcap_freecode(&prog);
if (ret < 0)
BOOST_THROW_EXCEPTION(Error("pcap_setfilter: " + getLastError()));
}
std::tuple<const uint8_t*, size_t, std::string>
PcapHelper::readNextPacket() const
{
pcap_pkthdr* header;
const uint8_t* packet;
int ret = pcap_next_ex(m_pcap, &header, &packet);
if (ret < 0)
return std::make_tuple(nullptr, 0, getLastError());
else if (ret == 0)
return std::make_tuple(nullptr, 0, "timed out");
else
return std::make_tuple(packet, header->caplen, "");
}
} // namespace face
} // namespace nfd