20 #include <arpa/inet.h>
23 namespace eth = satcat5::eth;
24 namespace log = satcat5::log;
25 namespace spcap = satcat5::pcap;
28 static const unsigned DEBUG_VERBOSE = 0;
31 static const unsigned MAX_ETH_FRAME = 1536;
34 static pcap_if_t* g_pcap_alldevs = 0;
39 explicit Device(
const char* ifname, u16 filter);
41 const char* name()
const {
return m_descr->name;}
42 const char* desc()
const {
return m_descr->description;}
47 struct bpf_program m_filter;
54 _TCHAR npcap_dir[512];
56 len = GetSystemDirectory(npcap_dir, 480);
58 log::Log(log::ERROR,
"GetSystemDirectory").
write((u32)GetLastError());
62 _tcscat_s(npcap_dir, 512, _T(
"\\Npcap"));
63 if (SetDllDirectory(npcap_dir) == 0) {
64 log::Log(log::ERROR,
"SetDllDirectory").
write((u32)GetLastError());
70 char errbuf[PCAP_ERRBUF_SIZE];
71 if (pcap_findalldevs(&g_pcap_alldevs, errbuf) < 0) {
72 log::Log(log::ERROR,
"pcap_findalldevs", errbuf);
81 bool is_ethernet_device(
const char* ifname) {
83 char errbuf[PCAP_ERRBUF_SIZE];
84 pcap_t* dev = pcap_open_live(ifname, 0, 0, 0, errbuf);
88 int type = pcap_datalink(dev);
90 return (type == DLT_EN10MB);
91 }
else if (DEBUG_VERBOSE > 0) {
97 spcap::Descriptor::Descriptor(
const char* n,
const char* d)
98 : name(n), desc(d ? d : n)
104 spcap::DescriptorList list;
107 if (!g_pcap_alldevs) pcap_init();
110 for (pcap_if_t* dev = g_pcap_alldevs ; dev ; dev = dev->next) {
111 if (is_ethernet_device(dev->name)) {
121 if (!g_pcap_alldevs) pcap_init();
124 for (pcap_if_t* dev = g_pcap_alldevs ; dev ; dev = dev->next) {
125 if (strstr(ifname, dev->name))
return true;
134 if (devs.size() == 1)
return devs[0].name;
135 if (devs.size() == 0) {
136 std::cerr <<
"No valid PCAP devices." << std::endl;
141 std::cout <<
"Please select a device from the list:" << std::endl;
142 for (
unsigned a = 0 ; a < devs.size() ; ++a) {
143 std::cout <<
" " << a <<
":\t" << devs[a].desc << std::endl;
145 std::cout <<
" (Any other number to cancel)" << std::endl;
150 if (sel < 0 || sel >= (
int)devs.size())
153 return devs[sel].name;
156 spcap::Device::Device(
const char* ifname, u16 filter)
162 if (!g_pcap_alldevs) pcap_init();
165 for (m_descr = g_pcap_alldevs ; m_descr ; m_descr = m_descr->next) {
166 if (strstr(ifname, m_descr->name))
break;
170 log::Log(log::ERROR, ifname,
"No matching Ethernet device.");
175 char errbuf[PCAP_ERRBUF_SIZE];
176 m_device = pcap_open_live(
184 log::Log(log::ERROR, ifname,
"Could not open: ").
write(errbuf);
189 if (pcap_setnonblock(m_device, 1, errbuf) == PCAP_ERROR) {
190 log::Log(log::ERROR, ifname,
"Could not set mode: ").
write(errbuf);
196 char filter_str[128];
197 snprintf(filter_str,
sizeof(filter_str),
"ether proto 0x%04X", filter);
198 if (pcap_compile(m_device, &m_filter, filter_str, 1, PCAP_NETMASK_UNKNOWN) < 0)
return;
199 if (pcap_setfilter(m_device, &m_filter) < 0)
return;
207 : satcat5::io::BufferedIO(
208 new u8[bsize], bsize, bsize/64,
209 new u8[bsize], bsize, bsize/64)
210 , m_device(new spcap::
Device(ifname, filter.value))
215 spcap::Socket::~Socket() {
217 if (m_device) pcap_close(m_device->m_device);
220 delete[] m_tx.get_buff_dtor();
221 delete[] m_rx.get_buff_dtor();
226 return (m_device) && (m_device->m_ok);
230 return m_device ? m_device->name() :
"";
234 return m_device ? m_device->desc() :
"";
239 unsigned nread = m_tx.get_read_ready();
240 if (nread > MAX_ETH_FRAME) {
241 log::Log(log::ERROR, m_device->name(),
"Tx frame too long.").
write(nread);
242 }
else if (nread && ok()) {
244 u8 temp[MAX_ETH_FRAME];
245 m_tx.read_bytes(nread, temp);
247 int result = pcap_sendpacket(m_device->m_device, temp, nread);
249 log::Log(log::WARNING, m_device->name(),
"Tx failed:\n")
250 .
write(pcap_geterr(m_device->m_device));
254 m_tx.read_finalize();
258 struct pcap_pkthdr* pkt_header;
259 const u_char *pkt_data;
265 int result = pcap_next_ex(m_device->m_device, &pkt_header, &pkt_data);
267 m_rx.write_bytes(pkt_header->len, pkt_data);
268 m_rx.write_finalize();
269 }
else if (result < 0 && DEBUG_VERBOSE > 0) {
270 log::Log(log::WARNING, m_device->name(),
"Rx error:\n")
271 .
write(pcap_geterr(m_device->m_device));
Abstract API for reading byte-streams and packets.
The Log class creates and formats one log message.
Log & write(const char *str)
Formatting methods for various data types.
const char * name() const
Unique interface ID.
Socket(const char *ifname, unsigned bsize=65536, satcat5::eth::MacType filter=satcat5::eth::ETYPE_NONE)
Open the specified interface by name.
void poll_always() override
Child class MUST override this method.
void data_rcvd(satcat5::io::Readable *src) override
The data_rcvd() callback is polled whenever data is available.
bool ok() const
Is the socket in a usable state?
const char * desc() const
Human-readable name.
Wrapper for PCAP / NPCAP socket library and supporting functions.
bool is_device(const char *ifname)
Check if a given name is on the list from list_all_devices.
std::string prompt_for_ifname()
Print a list of Ethernet devices, and select by index.
satcat5::pcap::DescriptorList list_all_devices()
Fetch a list of Ethernet device descriptors.
Diagnostic logging to UART and/or Ethernet ports.
EtherType field (uint16) is used a protocol-ID [1536..65535].
Structure for holding a device-name and user-readable description.