/* For terms of usage/redistribution/modification see the LICENSE file */
/* For authors and contributors see the AUTHORS file */
#include "iptraf-ng-compat.h"
#include "options.h"
#include "promisc.h"
#include "error.h"
#include "ifaces.h"
#include "packet.h"
#include "timer.h"
#include "capt.h"
#include "capt-recvmsg.h"
#include "capt-recvmmsg.h"
#include "capt-mmap-v2.h"
#include "capt-mmap-v3.h"
int capt_get_socket(struct capt *capt) {
/* initialize socket first with some default protocol;
* the right protocol is then set with bind();
* this overcomes the problem with getting packets
* from other interfaces, because the socket was not
* properly initialized yet */
int fd = socket(PF_PACKET, SOCK_RAW, 0);
if (fd == -1)
return -1;
capt->fd = fd;
return 0;
}
void capt_put_socket(struct capt *capt) {
close(capt->fd);
capt->fd = -1;
}
static int capt_set_recv_timeout(int fd, unsigned int msec)
{
struct timeval timeout;
socklen_t len = sizeof(timeout);
timeout.tv_sec = msec / 1000;
timeout.tv_usec = (msec % 1000) * 1000;
if(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, len) != 0)
return -1;
else
return 0;
}
static int capt_setup_receive_function(struct capt *capt)
{
/* try packet mmap() TPACKET_V3 */
if (capt_setup_mmap_v3(capt) == 0)
return 0;
/* try packet mmap() TPACKET_V2 */
if (capt_setup_mmap_v2(capt) == 0)
return 0;
/* try packet recvmmsg() */
if (capt_setup_recvmmsg(capt) == 0)
return 0;
/* try packet recvmsg() */
if (capt_setup_recvmsg(capt) == 0)
return 0;
return -1;
}
int capt_init(struct capt *capt, char *ifname)
{
capt->have_packet = NULL;
capt->get_packet = NULL;
capt->put_packet = NULL;
capt->get_dropped = NULL;
capt->cleanup = NULL;
capt->fd = -1;
capt->dropped = 0UL;
INIT_LIST_HEAD(&capt->promisc);
/* try all available receive functions */
if (capt_setup_receive_function(capt) == -1)
goto out;
/* set socket receive timeout */
if (capt_set_recv_timeout(capt->fd, 250) == -1)
goto out;
if (options.promisc)
promisc_enable(capt->fd, &capt->promisc, ifname);
/* bind interface (and protocol) to socket
* (interface can be NULL -> any interface) */
if (dev_bind_ifname(capt->fd, ifname) == -1)
goto out;
return 0; /* all O.K. */
out:
capt_destroy(capt);
return -1;
}
void capt_destroy(struct capt *capt)
{
promisc_disable(capt->fd, &capt->promisc);
if (capt->cleanup)
capt->cleanup(capt);
capt_put_socket(capt);
}
static unsigned long capt_get_dropped_generic(struct capt *capt)
{
struct tpacket_stats stats;
socklen_t len = sizeof(stats);
memset(&stats, 0, len);
int err = getsockopt(capt->fd, SOL_PACKET, PACKET_STATISTICS, &stats, &len);
if (err < 0)
die_errno("%s(): getsockopt(PACKET_STATISTICS)", __func__);
capt->dropped += stats.tp_drops;
return capt->dropped;
}
unsigned long capt_get_dropped(struct capt *capt)
{
if (capt->get_dropped)
return capt->get_dropped(capt);
return capt_get_dropped_generic(capt);
}
int capt_get_packet(struct capt *capt, struct pkt_hdr *pkt, int *ch, WINDOW *win)
{
struct pollfd pfds[2];
nfds_t nfds = 0;
int pfd_packet = -1;
int pfd_key = -1;
int ss = 0;
bool have_packet = capt->have_packet(capt);
int timeout = ch ? DEFAULT_UPDATE_DELAY : -1;
static struct timespec next_kbd_check = { 0 };
/* no packet ready, so poll() for it */
if (!have_packet) {
pfds[nfds].fd = capt->fd;
pfds[nfds].events = POLLIN;
pfd_packet = nfds;
nfds++;
}
/* check for key press */
/* Monitor stdin only if in interactive, not daemon mode. */
if (ch && !daemonized) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
/* if we're going to poll() for packet, check the key
* press too */
if (!have_packet || time_after(&now, &next_kbd_check)) {
pfds[nfds].fd = 0;
pfds[nfds].events = POLLIN;
pfd_key = nfds;
nfds++;
if (have_packet)
timeout = 0;
next_kbd_check = now;
time_add_msecs(&next_kbd_check, 10);
}
}
if (nfds > 0)
do {
ss = poll(pfds, nfds, timeout);
} while ((ss == -1) && (errno == EINTR));
/* no packet ready yet */
pkt->pkt_len = 0;
if ((pfd_packet != -1) && (ss > 0)) {
if (pfds[pfd_packet].revents & POLLERR) {
/* some error occured, don't try to get packet */
have_packet = false;
/* ... and return error */
ss = -1;
} else if (pfds[pfd_packet].revents & POLLIN) {
/* packet ready */
have_packet = true;
}
}
if (have_packet) {
int ret = capt->get_packet(capt, pkt);
if (ret <= 0)
ss = ret;
}
if (ch) {
*ch = ERR; /* signalize we have no key ready */
if (!daemonized && (((pfd_key != -1) && ((ss > 0) && ((pfds[pfd_key].revents & POLLIN) != 0)))))
*ch = wgetch(win);
}
return ss;
}
int capt_put_packet(struct capt *capt, struct pkt_hdr *pkt)
{
if (capt->put_packet)
capt->put_packet(capt, pkt);
return 0;
}