2025-02-08 11:57:11 +01:00
|
|
|
/*
|
|
|
|
* Author Jerry Lundström <jerry@dns-oarc.net>
|
2025-04-21 09:31:29 +02:00
|
|
|
* Copyright (c) 2016-2025 OARC, Inc.
|
2025-02-08 11:57:11 +01:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
*
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
*
|
|
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
|
|
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "pcap_thread.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/select.h>
|
|
|
|
|
|
|
|
#ifndef PCAP_THREAD_LAYER_TRACE
|
|
|
|
#define PCAP_THREAD_LAYER_TRACE 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Forward declares for layer callbacks
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void pcap_thread_callback(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* pkt, const char* name, int dlt);
|
|
|
|
static void pcap_thread_callback_linux_sll(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
2025-04-21 09:31:29 +02:00
|
|
|
static void pcap_thread_callback_linux_sll2(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
2025-02-08 11:57:11 +01:00
|
|
|
static void pcap_thread_callback_ether(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_null(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_loop(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_ieee802(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_gre(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_ip(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_ipv4(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_ipv6(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_icmp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_icmpv6(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_udp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
static void pcap_thread_callback_tcp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Version
|
|
|
|
*/
|
|
|
|
|
|
|
|
static const char* _version = PCAP_THREAD_VERSION_STR;
|
|
|
|
|
|
|
|
const char* pcap_thread_version_str(void)
|
|
|
|
{
|
|
|
|
return _version;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_version_major(void)
|
|
|
|
{
|
|
|
|
return PCAP_THREAD_VERSION_MAJOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_version_minor(void)
|
|
|
|
{
|
|
|
|
return PCAP_THREAD_VERSION_MINOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_version_patch(void)
|
|
|
|
{
|
|
|
|
return PCAP_THREAD_VERSION_PATCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create/Free
|
|
|
|
*/
|
|
|
|
|
|
|
|
static pcap_thread_t _pcap_thread_defaults = PCAP_THREAD_T_INIT;
|
|
|
|
|
|
|
|
pcap_thread_t* pcap_thread_create(void)
|
|
|
|
{
|
|
|
|
pcap_thread_t* pcap_thread = calloc(1, sizeof(pcap_thread_t));
|
|
|
|
if (pcap_thread) {
|
|
|
|
memcpy(pcap_thread, &_pcap_thread_defaults, sizeof(pcap_thread_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcap_thread_free(pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread_close(pcap_thread);
|
|
|
|
if (pcap_thread->filter) {
|
|
|
|
free(pcap_thread->filter);
|
|
|
|
}
|
|
|
|
free(pcap_thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get/Set
|
|
|
|
*/
|
|
|
|
|
|
|
|
int pcap_thread_use_threads(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->use_threads;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_use_threads(pcap_thread_t* pcap_thread, const int use_threads)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->use_threads = use_threads;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_use_layers(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->use_layers;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_use_layers(pcap_thread_t* pcap_thread, const int use_layers)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->use_layers = use_layers;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread_queue_mode_t pcap_thread_queue_mode(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->queue_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_queue_mode(pcap_thread_t* pcap_thread, const pcap_thread_queue_mode_t queue_mode)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (queue_mode) {
|
|
|
|
case PCAP_THREAD_QUEUE_MODE_COND:
|
|
|
|
case PCAP_THREAD_QUEUE_MODE_DIRECT:
|
|
|
|
break;
|
|
|
|
case PCAP_THREAD_QUEUE_MODE_YIELD:
|
|
|
|
case PCAP_THREAD_QUEUE_MODE_WAIT:
|
|
|
|
case PCAP_THREAD_QUEUE_MODE_DROP:
|
|
|
|
return PCAP_THREAD_EOBSOLETE;
|
|
|
|
default:
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->queue_mode = queue_mode;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timeval pcap_thread_queue_wait(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
static struct timeval tv = { 0, 0 };
|
|
|
|
return tv;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_queue_wait(pcap_thread_t* pcap_thread, const struct timeval queue_wait)
|
|
|
|
{
|
|
|
|
return PCAP_THREAD_EOBSOLETE;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread_queue_mode_t pcap_thread_callback_queue_mode(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
return PCAP_THREAD_EOBSOLETE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_queue_mode(pcap_thread_t* pcap_thread, const pcap_thread_queue_mode_t callback_queue_mode)
|
|
|
|
{
|
|
|
|
return PCAP_THREAD_EOBSOLETE;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timeval pcap_thread_callback_queue_wait(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
static struct timeval tv = { 0, 0 };
|
|
|
|
return tv;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_queue_wait(pcap_thread_t* pcap_thread, const struct timeval callback_queue_wait)
|
|
|
|
{
|
|
|
|
return PCAP_THREAD_EOBSOLETE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_snapshot(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->snapshot;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_snaplen(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->snaplen;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_snaplen(pcap_thread_t* pcap_thread, const int snaplen)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->snaplen = snaplen;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_promiscuous(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->promiscuous;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_promiscuous(pcap_thread_t* pcap_thread, const int promiscuous)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->promiscuous = promiscuous;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_monitor(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->monitor;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_monitor(pcap_thread_t* pcap_thread, const int monitor)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->monitor = monitor;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_timeout(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->timeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_timeout(pcap_thread_t* pcap_thread, const int timeout)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->timeout = timeout;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_buffer_size(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->buffer_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_buffer_size(pcap_thread_t* pcap_thread, const int buffer_size)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->buffer_size = buffer_size;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_timestamp_type(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->timestamp_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_timestamp_type(pcap_thread_t* pcap_thread, const int timestamp_type)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->have_timestamp_type = 1;
|
|
|
|
pcap_thread->timestamp_type = timestamp_type;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_timestamp_precision(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->timestamp_precision;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_timestamp_precision(pcap_thread_t* pcap_thread, const int timestamp_precision)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->have_timestamp_precision = 1;
|
|
|
|
pcap_thread->timestamp_precision = timestamp_precision;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_immediate_mode(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->immediate_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_immediate_mode(pcap_thread_t* pcap_thread, const int immediate_mode)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->immediate_mode = immediate_mode;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_direction_t pcap_thread_direction(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_PCAP_DIRECTION_T
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->direction;
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_direction(pcap_thread_t* pcap_thread, const pcap_direction_t direction)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_PCAP_DIRECTION_T
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->have_direction = 1;
|
|
|
|
pcap_thread->direction = direction;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
#else
|
|
|
|
return PCAP_THREAD_ENODIR;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* pcap_thread_filter(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_filter(pcap_thread_t* pcap_thread, const char* filter, const size_t filter_len)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!filter) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!filter_len) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->filter) {
|
|
|
|
free(pcap_thread->filter);
|
|
|
|
}
|
|
|
|
if (!(pcap_thread->filter = strndup(filter, filter_len))) {
|
|
|
|
return PCAP_THREAD_ENOMEM;
|
|
|
|
}
|
|
|
|
pcap_thread->filter_len = filter_len;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_clear_filter(pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->filter) {
|
|
|
|
free(pcap_thread->filter);
|
|
|
|
pcap_thread->filter = 0;
|
|
|
|
pcap_thread->filter_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_filter_errno(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->filter_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_filter_optimize(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->filter_optimize;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_filter_optimize(pcap_thread_t* pcap_thread, const int filter_optimize)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->filter_optimize = filter_optimize;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
bpf_u_int32 pcap_thread_filter_netmask(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->filter_netmask;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_filter_netmask(pcap_thread_t* pcap_thread, const bpf_u_int32 filter_netmask)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->filter_netmask = filter_netmask;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timeval pcap_thread_timedrun(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
static struct timeval tv = { 0, 0 };
|
|
|
|
return tv;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->timedrun;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_timedrun(pcap_thread_t* pcap_thread, const struct timeval timedrun)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->timedrun = timedrun;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timeval pcap_thread_timedrun_to(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
static struct timeval tv = { 0, 0 };
|
|
|
|
return tv;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->timedrun_to;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_timedrun_to(pcap_thread_t* pcap_thread, const struct timeval timedrun_to)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->timedrun_to = timedrun_to;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread_activate_mode_t pcap_thread_activate_mode(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_DEFAULT_ACTIVATE_MODE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->activate_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_activate_mode(pcap_thread_t* pcap_thread, const pcap_thread_activate_mode_t activate_mode)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->activate_mode = activate_mode;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_was_stopped(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->was_stopped;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Queue
|
|
|
|
*/
|
|
|
|
|
|
|
|
size_t pcap_thread_queue_size(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->queue_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_queue_size(pcap_thread_t* pcap_thread, const size_t queue_size)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!queue_size) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->queue_size = queue_size;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback(pcap_thread_t* pcap_thread, pcap_thread_callback_t callback)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback = callback;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_dropback(pcap_thread_t* pcap_thread, pcap_thread_callback_t dropback)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->dropback = dropback;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Layers
|
|
|
|
*/
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_linux_sll(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_linux_sll)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
2025-04-21 09:31:29 +02:00
|
|
|
if (pcap_thread->callback_linux_sll2
|
|
|
|
|| pcap_thread->callback_ether
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_linux_sll = callback_linux_sll;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
2025-04-21 09:31:29 +02:00
|
|
|
int pcap_thread_set_callback_linux_sll2(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_linux_sll2)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_linux_sll2 = callback_linux_sll2;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
2025-02-08 11:57:11 +01:00
|
|
|
int pcap_thread_set_callback_ether(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_ether)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_ether = callback_ether;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_null(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_null)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_null = callback_null;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_loop(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_loop)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_loop = callback_loop;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_ieee802(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_ieee802)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_ieee802 = callback_ieee802;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_gre(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_gre)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_gre = callback_gre;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_ip(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_ip)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_ip = callback_ip;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_ipv4(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_ipv4)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_ipv4 = callback_ipv4;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_ipv4_frag(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_frag_t callback_ipv4_frag)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!callback_ipv4_frag.new
|
|
|
|
|| !callback_ipv4_frag.free
|
|
|
|
|| !callback_ipv4_frag.reassemble
|
|
|
|
|| !callback_ipv4_frag.release) {
|
|
|
|
if (callback_ipv4_frag.new
|
|
|
|
|| callback_ipv4_frag.free
|
|
|
|
|| callback_ipv4_frag.reassemble
|
|
|
|
|| callback_ipv4_frag.release) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_ipv4_frag = callback_ipv4_frag;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_ipv6(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_ipv6)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_ipv6 = callback_ipv6;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_ipv6_frag(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_frag_t callback_ipv6_frag)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!callback_ipv6_frag.new
|
|
|
|
|| !callback_ipv6_frag.free
|
|
|
|
|| !callback_ipv6_frag.reassemble
|
|
|
|
|| !callback_ipv6_frag.release) {
|
|
|
|
if (callback_ipv6_frag.new
|
|
|
|
|| callback_ipv6_frag.free
|
|
|
|
|| callback_ipv6_frag.reassemble
|
|
|
|
|| callback_ipv6_frag.release) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_ipv6_frag = callback_ipv6_frag;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_icmp(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_icmp)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_icmp = callback_icmp;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_icmpv6(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_icmpv6)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_icmpv6 = callback_icmpv6;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_udp(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_udp)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_udp = callback_udp;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_tcp(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_tcp)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
2025-02-08 11:57:11 +01:00
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6) {
|
|
|
|
return PCAP_THREAD_ELAYERCB;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_tcp = callback_tcp;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_set_callback_invalid(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_invalid)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->callback_invalid = callback_invalid;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define need4x2(v1, v2, p, l) \
|
|
|
|
if (l < 1) { \
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
v1 = (*p) >> 4; \
|
|
|
|
v2 = (*p) & 0xf; \
|
|
|
|
p += 1; \
|
|
|
|
l -= 1
|
|
|
|
|
|
|
|
#define need8(v, p, l) \
|
|
|
|
if (l < 1) { \
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
v = *p; \
|
|
|
|
p += 1; \
|
|
|
|
l -= 1
|
|
|
|
|
|
|
|
#define need16(v, p, l) \
|
|
|
|
if (l < 2) { \
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
v = (*p << 8) + *(p + 1); \
|
|
|
|
p += 2; \
|
|
|
|
l -= 2
|
|
|
|
|
|
|
|
#define need32(v, p, l) \
|
|
|
|
if (l < 4) { \
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
v = (*p << 24) + (*(p + 1) << 16) + (*(p + 2) << 8) + *(p + 3); \
|
|
|
|
p += 4; \
|
|
|
|
l -= 4
|
|
|
|
|
|
|
|
#define needxb(b, x, p, l) \
|
|
|
|
if (l < x) { \
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
memcpy(b, p, x); \
|
|
|
|
p += x; \
|
|
|
|
l -= x
|
|
|
|
|
|
|
|
#define advancexb(x, p, l) \
|
|
|
|
if (l < x) { \
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
p += x; \
|
|
|
|
l -= x
|
|
|
|
|
|
|
|
#if PCAP_THREAD_LAYER_TRACE
|
|
|
|
#define layer_trace(msg) printf("LT %s:%d: " msg "\n", __FILE__, __LINE__)
|
|
|
|
#define layer_tracef(msg, args...) printf("LT %s:%d: " msg "\n", __FILE__, __LINE__, args)
|
|
|
|
#else
|
|
|
|
#define layer_trace(msg)
|
|
|
|
#define layer_tracef(msg, args...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void pcap_thread_callback(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* pkt, const char* name, int dlt)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
size_t length;
|
|
|
|
pcap_thread_packet_t packet;
|
|
|
|
const u_char* orig = pkt;
|
|
|
|
size_t origlength;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pkthdr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pkt) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!name) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&packet, 0, sizeof(packet));
|
|
|
|
packet.name = name;
|
|
|
|
packet.dlt = dlt;
|
|
|
|
packet.pkthdr = *pkthdr;
|
|
|
|
packet.have_pkthdr = 1;
|
|
|
|
length = pkthdr->caplen;
|
|
|
|
origlength = length;
|
|
|
|
|
|
|
|
layer_tracef("packet, length %lu", length);
|
|
|
|
|
|
|
|
switch (dlt) {
|
|
|
|
case DLT_NULL:
|
|
|
|
layer_trace("dlt_null");
|
|
|
|
{
|
|
|
|
uint8_t hdr[4];
|
|
|
|
|
|
|
|
packet.state = PCAP_THREAD_PACKET_INVALID_NULL;
|
|
|
|
need8(hdr[0], pkt, length);
|
|
|
|
need8(hdr[1], pkt, length);
|
|
|
|
need8(hdr[2], pkt, length);
|
|
|
|
need8(hdr[3], pkt, length);
|
|
|
|
packet.state = PCAP_THREAD_PACKET_OK;
|
|
|
|
|
|
|
|
/*
|
2025-04-21 09:31:29 +02:00
|
|
|
* The header for null is in host byte order but may not be
|
|
|
|
* in the same endian as host if coming from a savefile
|
|
|
|
*/
|
2025-02-08 11:57:11 +01:00
|
|
|
|
|
|
|
if (pcaplist->is_offline && pcap_is_swapped(pcaplist->pcap)) {
|
|
|
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
|
|
packet.nullhdr.family = hdr[3] + (hdr[2] << 8) + (hdr[1] << 16) + (hdr[0] << 24);
|
|
|
|
#elif __BYTE_ORDER == __BIG_ENDIAN
|
|
|
|
packet.nullhdr.family = hdr[0] + (hdr[1] << 8) + (hdr[2] << 16) + (hdr[3] << 24);
|
|
|
|
#else
|
|
|
|
#error "Please fix <endian.h>"
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
|
|
packet.nullhdr.family = hdr[0] + (hdr[1] << 8) + (hdr[2] << 16) + (hdr[3] << 24);
|
|
|
|
#elif __BYTE_ORDER == __BIG_ENDIAN
|
|
|
|
packet.nullhdr.family = hdr[3] + (hdr[2] << 8) + (hdr[1] << 16) + (hdr[0] << 24);
|
|
|
|
#else
|
|
|
|
#error "Please fix <endian.h>"
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
packet.have_nullhdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_null)
|
|
|
|
pcaplist->pcap_thread->callback_null(pcaplist->user, &packet, pkt, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_null((void*)pcaplist, &packet, pkt, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DLT_EN10MB:
|
|
|
|
layer_trace("dlt_en10mb");
|
|
|
|
packet.state = PCAP_THREAD_PACKET_INVALID_ETHER;
|
|
|
|
needxb(packet.ethhdr.ether_dhost, sizeof(packet.ethhdr.ether_dhost), pkt, length);
|
|
|
|
needxb(packet.ethhdr.ether_shost, sizeof(packet.ethhdr.ether_shost), pkt, length);
|
|
|
|
need16(packet.ethhdr.ether_type, pkt, length);
|
|
|
|
packet.state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet.have_ethhdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_ether)
|
|
|
|
pcaplist->pcap_thread->callback_ether(pcaplist->user, &packet, pkt, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ether((void*)pcaplist, &packet, pkt, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case DLT_LOOP:
|
|
|
|
layer_trace("dlt_loop");
|
|
|
|
packet.state = PCAP_THREAD_PACKET_INVALID_LOOP;
|
|
|
|
need32(packet.loophdr.family, pkt, length);
|
|
|
|
packet.state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet.have_loophdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_loop)
|
|
|
|
pcaplist->pcap_thread->callback_loop(pcaplist->user, &packet, pkt, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_loop((void*)pcaplist, &packet, pkt, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case DLT_RAW:
|
|
|
|
#ifdef DLT_IPV4
|
|
|
|
case DLT_IPV4:
|
|
|
|
#endif
|
|
|
|
#ifdef DLT_IPV6
|
|
|
|
case DLT_IPV6:
|
|
|
|
#endif
|
|
|
|
layer_trace("dlt_raw/ipv4/ipv6");
|
|
|
|
if (pcaplist->pcap_thread->callback_ip)
|
|
|
|
pcaplist->pcap_thread->callback_ip(pcaplist->user, &packet, pkt, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ip((void*)pcaplist, &packet, pkt, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case DLT_LINUX_SLL:
|
|
|
|
layer_trace("dlt_linux_sll");
|
|
|
|
packet.state = PCAP_THREAD_PACKET_INVALID_LINUX_SLL;
|
|
|
|
need16(packet.linux_sll.packet_type, pkt, length);
|
|
|
|
need16(packet.linux_sll.arp_hardware, pkt, length);
|
|
|
|
need16(packet.linux_sll.link_layer_address_length, pkt, length);
|
|
|
|
needxb(packet.linux_sll.link_layer_address, 8, pkt, length);
|
|
|
|
need16(packet.linux_sll.ether_type, pkt, length);
|
|
|
|
packet.state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet.have_linux_sll = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_linux_sll)
|
|
|
|
pcaplist->pcap_thread->callback_linux_sll(pcaplist->user, &packet, pkt, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_linux_sll((void*)pcaplist, &packet, pkt, length);
|
|
|
|
return;
|
|
|
|
|
2025-04-21 09:31:29 +02:00
|
|
|
case DLT_LINUX_SLL2:
|
|
|
|
layer_trace("dlt_linux_sll2");
|
|
|
|
packet.state = PCAP_THREAD_PACKET_INVALID_LINUX_SLL2;
|
|
|
|
need16(packet.linux_sll2.protocol_type, pkt, length);
|
|
|
|
need16(packet.linux_sll2.reserved, pkt, length);
|
|
|
|
need32(packet.linux_sll2.interface_index, pkt, length);
|
|
|
|
need16(packet.linux_sll2.arphrd_type, pkt, length);
|
|
|
|
need8(packet.linux_sll2.packet_type, pkt, length);
|
|
|
|
need8(packet.linux_sll2.link_layer_address_length, pkt, length);
|
|
|
|
needxb(packet.linux_sll2.link_layer_address, 8, pkt, length);
|
|
|
|
packet.state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet.have_linux_sll2 = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_linux_sll2)
|
|
|
|
pcaplist->pcap_thread->callback_linux_sll2(pcaplist->user, &packet, pkt, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_linux_sll2((void*)pcaplist, &packet, pkt, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* TODO: These might be interesting to implement
|
2025-02-08 11:57:11 +01:00
|
|
|
case DLT_IPNET:
|
|
|
|
case DLT_PKTAP:
|
|
|
|
*/
|
|
|
|
|
|
|
|
default:
|
|
|
|
packet.state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet.state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet.state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, &packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_linux_sll(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet->have_linux_sll) {
|
|
|
|
layer_trace("have_linux_sll");
|
|
|
|
switch (packet->linux_sll.ether_type) {
|
|
|
|
case 0x8100: /* 802.1q */
|
|
|
|
case 0x88a8: /* 802.1ad */
|
|
|
|
case 0x9100: /* 802.1 QinQ non-standard */
|
|
|
|
if (packet->have_ieee802hdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
{
|
|
|
|
uint16_t tci;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IEEE802;
|
|
|
|
need16(tci, payload, length);
|
|
|
|
packet->ieee802hdr.pcp = (tci & 0xe000) >> 13;
|
|
|
|
packet->ieee802hdr.dei = (tci & 0x1000) >> 12;
|
|
|
|
packet->ieee802hdr.vid = tci & 0x0fff;
|
|
|
|
need16(packet->ieee802hdr.ether_type, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_ieee802hdr = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_ieee802)
|
|
|
|
pcaplist->pcap_thread->callback_ieee802(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ieee802((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case ETHERTYPE_IP:
|
|
|
|
case ETHERTYPE_IPV6:
|
|
|
|
if (pcaplist->pcap_thread->callback_ip)
|
|
|
|
pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ip((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-04-21 09:31:29 +02:00
|
|
|
static void pcap_thread_callback_linux_sll2(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet->have_linux_sll2) {
|
|
|
|
layer_trace("have_linux_sll2");
|
|
|
|
switch (packet->linux_sll2.protocol_type) {
|
|
|
|
case 0x8100: /* 802.1q */
|
|
|
|
case 0x88a8: /* 802.1ad */
|
|
|
|
case 0x9100: /* 802.1 QinQ non-standard */
|
|
|
|
if (packet->have_ieee802hdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
{
|
|
|
|
uint16_t tci;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IEEE802;
|
|
|
|
need16(tci, payload, length);
|
|
|
|
packet->ieee802hdr.pcp = (tci & 0xe000) >> 13;
|
|
|
|
packet->ieee802hdr.dei = (tci & 0x1000) >> 12;
|
|
|
|
packet->ieee802hdr.vid = tci & 0x0fff;
|
|
|
|
need16(packet->ieee802hdr.ether_type, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_ieee802hdr = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_ieee802)
|
|
|
|
pcaplist->pcap_thread->callback_ieee802(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ieee802((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case ETHERTYPE_IP:
|
|
|
|
case ETHERTYPE_IPV6:
|
|
|
|
if (pcaplist->pcap_thread->callback_ip)
|
|
|
|
pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ip((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-08 11:57:11 +01:00
|
|
|
static void pcap_thread_callback_ether(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet->have_ethhdr) {
|
|
|
|
layer_trace("have_ethhdr");
|
|
|
|
switch (packet->ethhdr.ether_type) {
|
|
|
|
case 0x8100: /* 802.1q */
|
|
|
|
case 0x88a8: /* 802.1ad */
|
|
|
|
case 0x9100: /* 802.1 QinQ non-standard */
|
|
|
|
if (packet->have_ieee802hdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
{
|
|
|
|
uint16_t tci;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IEEE802;
|
|
|
|
need16(tci, payload, length);
|
|
|
|
packet->ieee802hdr.pcp = (tci & 0xe000) >> 13;
|
|
|
|
packet->ieee802hdr.dei = (tci & 0x1000) >> 12;
|
|
|
|
packet->ieee802hdr.vid = tci & 0x0fff;
|
|
|
|
need16(packet->ieee802hdr.ether_type, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_ieee802hdr = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_ieee802)
|
|
|
|
pcaplist->pcap_thread->callback_ieee802(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ieee802((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case ETHERTYPE_IP:
|
|
|
|
case ETHERTYPE_IPV6:
|
|
|
|
if (pcaplist->pcap_thread->callback_ip)
|
|
|
|
pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ip((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_null(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet->have_nullhdr) {
|
|
|
|
layer_trace("have_nullhdr");
|
|
|
|
|
|
|
|
/* From libpcap link types documentation:
|
|
|
|
* containing a value of 2 for IPv4 packets, a value of either 24, 28,
|
|
|
|
* or 30 for IPv6 packets, a value of 7 for OSI packets, or a value of 23
|
|
|
|
* for IPX packets. All of the IPv6 values correspond to IPv6 packets;
|
|
|
|
* code reading files should check for all of them.
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch (packet->nullhdr.family) {
|
|
|
|
case 2:
|
|
|
|
case 24:
|
|
|
|
case 28:
|
|
|
|
case 30:
|
|
|
|
if (pcaplist->pcap_thread->callback_ip)
|
|
|
|
pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ip((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_loop(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet->have_loophdr) {
|
|
|
|
layer_trace("have_loophdr");
|
|
|
|
|
|
|
|
/* From libpcap link types documentation:
|
|
|
|
* containing a value of 2 for IPv4 packets, a value of either 24, 28,
|
|
|
|
* or 30 for IPv6 packets, a value of 7 for OSI packets, or a value of 23
|
|
|
|
* for IPX packets. All of the IPv6 values correspond to IPv6 packets;
|
|
|
|
* code reading files should check for all of them.
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch (packet->loophdr.family) {
|
|
|
|
case 2:
|
|
|
|
case 24:
|
|
|
|
case 28:
|
|
|
|
case 30:
|
|
|
|
if (pcaplist->pcap_thread->callback_ip)
|
|
|
|
pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ip((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_ieee802(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet->have_ieee802hdr) {
|
|
|
|
layer_trace("have_ieee802hdr");
|
|
|
|
|
|
|
|
switch (packet->ieee802hdr.ether_type) {
|
|
|
|
case 0x88a8: /* 802.1ad */
|
|
|
|
case 0x9100: /* 802.1 QinQ non-standard */
|
|
|
|
{
|
|
|
|
pcap_thread_packet_t ieee802pkt;
|
|
|
|
uint16_t tci;
|
|
|
|
|
|
|
|
memset(&ieee802pkt, 0, sizeof(ieee802pkt));
|
|
|
|
ieee802pkt.prevpkt = packet;
|
|
|
|
ieee802pkt.have_prevpkt = 1;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IEEE802;
|
|
|
|
need16(tci, payload, length);
|
|
|
|
ieee802pkt.ieee802hdr.pcp = (tci & 0xe000) >> 13;
|
|
|
|
ieee802pkt.ieee802hdr.dei = (tci & 0x1000) >> 12;
|
|
|
|
ieee802pkt.ieee802hdr.vid = tci & 0x0fff;
|
|
|
|
need16(ieee802pkt.ieee802hdr.ether_type, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
ieee802pkt.have_ieee802hdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_ieee802)
|
|
|
|
pcaplist->pcap_thread->callback_ieee802(pcaplist->user, &ieee802pkt, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ieee802((void*)pcaplist, &ieee802pkt, payload, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case ETHERTYPE_IP:
|
|
|
|
case ETHERTYPE_IPV6:
|
|
|
|
if (pcaplist->pcap_thread->callback_ip)
|
|
|
|
pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ip((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_gre(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet->have_grehdr) {
|
|
|
|
pcap_thread_packet_t grepkt;
|
|
|
|
|
|
|
|
layer_trace("have_grehdr");
|
|
|
|
|
|
|
|
memset(&grepkt, 0, sizeof(grepkt));
|
|
|
|
grepkt.prevpkt = packet;
|
|
|
|
grepkt.have_prevpkt = 1;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_GRE;
|
|
|
|
if (packet->grehdr.gre_flags & 0x1) {
|
|
|
|
need16(packet->gre.checksum, payload, length);
|
|
|
|
}
|
|
|
|
if (packet->grehdr.gre_flags & 0x4) {
|
|
|
|
need16(packet->gre.key, payload, length);
|
|
|
|
}
|
|
|
|
if (packet->grehdr.gre_flags & 0x8) {
|
|
|
|
need16(packet->gre.sequence, payload, length);
|
|
|
|
}
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_gre = 1;
|
|
|
|
|
|
|
|
switch (packet->grehdr.ether_type) {
|
|
|
|
case ETHERTYPE_IP:
|
|
|
|
case ETHERTYPE_IPV6:
|
|
|
|
if (pcaplist->pcap_thread->callback_ip)
|
|
|
|
pcaplist->pcap_thread->callback_ip(pcaplist->user, &grepkt, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ip((void*)pcaplist, &grepkt, payload, length);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_ip(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!packet->have_iphdr && !packet->have_ip6hdr) {
|
|
|
|
layer_trace("checking for ip");
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IP;
|
|
|
|
need4x2(packet->iphdr.ip_v, packet->iphdr.ip_hl, payload, length);
|
|
|
|
if (packet->iphdr.ip_v == 4) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IPV4;
|
|
|
|
need8(packet->iphdr.ip_tos, payload, length);
|
|
|
|
need16(packet->iphdr.ip_len, payload, length);
|
|
|
|
need16(packet->iphdr.ip_id, payload, length);
|
|
|
|
need16(packet->iphdr.ip_off, payload, length);
|
|
|
|
need8(packet->iphdr.ip_ttl, payload, length);
|
|
|
|
need8(packet->iphdr.ip_p, payload, length);
|
|
|
|
need16(packet->iphdr.ip_sum, payload, length);
|
|
|
|
needxb(&(packet->iphdr.ip_src.s_addr), 4, payload, length);
|
|
|
|
needxb(&(packet->iphdr.ip_dst.s_addr), 4, payload, length);
|
|
|
|
|
|
|
|
/* TODO: IPv4 options */
|
|
|
|
|
|
|
|
if (packet->iphdr.ip_hl < 5)
|
|
|
|
break;
|
|
|
|
if (packet->iphdr.ip_hl > 5) {
|
|
|
|
advancexb((packet->iphdr.ip_hl - 5) * 4, payload, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_iphdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_ipv4)
|
|
|
|
pcaplist->pcap_thread->callback_ipv4(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ipv4((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
} else if (packet->iphdr.ip_v == 6) {
|
|
|
|
/*
|
|
|
|
* Clear IPv4 headers and reverse reading one byte
|
|
|
|
*/
|
|
|
|
packet->iphdr.ip_v = 0;
|
|
|
|
packet->iphdr.ip_hl = 0;
|
|
|
|
payload--;
|
|
|
|
length++;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IPV6;
|
|
|
|
need32(packet->ip6hdr.ip6_flow, payload, length);
|
|
|
|
need16(packet->ip6hdr.ip6_plen, payload, length);
|
|
|
|
need8(packet->ip6hdr.ip6_nxt, payload, length);
|
|
|
|
need8(packet->ip6hdr.ip6_hlim, payload, length);
|
|
|
|
needxb(&(packet->ip6hdr.ip6_src), 16, payload, length);
|
|
|
|
needxb(&(packet->ip6hdr.ip6_dst), 16, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_ip6hdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_ipv6)
|
|
|
|
pcaplist->pcap_thread->callback_ipv6(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_ipv6((void*)pcaplist, packet, payload, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_ipv4(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
int release_frag = 0;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet->have_iphdr) {
|
|
|
|
layer_trace("have_iphdr");
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
/* Check reported length for missing payload or padding */
|
|
|
|
if (packet->iphdr.ip_len < (packet->iphdr.ip_hl * 4)) {
|
|
|
|
layer_trace("ip_len < ip header");
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IPV4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (length < (packet->iphdr.ip_len - (packet->iphdr.ip_hl * 4))) {
|
|
|
|
layer_trace("length < (ip_len - ip header)");
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IPV4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (length > (packet->iphdr.ip_len - (packet->iphdr.ip_hl * 4))) {
|
|
|
|
layer_trace("have_ippadding");
|
|
|
|
packet->ippadding = length - (packet->iphdr.ip_len - (packet->iphdr.ip_hl * 4));
|
|
|
|
packet->have_ippadding = 1;
|
|
|
|
length -= packet->ippadding;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if packet wants more fragments or has an offset */
|
|
|
|
if (packet->iphdr.ip_off & 0x2000 || packet->iphdr.ip_off & 0x1fff) {
|
|
|
|
layer_trace("is_v4_frag");
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_ipv4_frag.reassemble) {
|
|
|
|
pcap_thread_packet_t* whole_packet = 0;
|
|
|
|
const u_char* whole_payload = 0;
|
|
|
|
size_t whole_length = 0;
|
|
|
|
|
|
|
|
packet->state = pcaplist->pcap_thread->callback_ipv4_frag.reassemble(pcaplist->ipv4_frag_ctx, packet, payload, length, &whole_packet, &whole_payload, &whole_length);
|
|
|
|
|
|
|
|
/* Defragmentation failed some how, we return packet as invalid */
|
|
|
|
if (packet->state != PCAP_THREAD_PACKET_OK) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No whole/defragmented packet return, need more */
|
|
|
|
if (!whole_packet || !whole_payload || !whole_length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
layer_tracef("v4_reasm %p %p %lu", whole_packet, whole_payload, whole_length);
|
|
|
|
|
|
|
|
packet = whole_packet;
|
|
|
|
payload = whole_payload;
|
|
|
|
length = whole_length;
|
|
|
|
release_frag = 1;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Mark packet as fragment and send it to the next user
|
|
|
|
* layer (if any) or return it as invalid.
|
|
|
|
*/
|
|
|
|
packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT;
|
|
|
|
|
|
|
|
switch (packet->iphdr.ip_p) {
|
|
|
|
case IPPROTO_GRE:
|
|
|
|
layer_trace("ipproto_gre frag");
|
|
|
|
|
|
|
|
if (!(packet->iphdr.ip_off & 0x1fff)) {
|
|
|
|
for (;;) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_FRAGMENTED_GREHDR;
|
|
|
|
need16(packet->grehdr.gre_flags, payload, length);
|
|
|
|
need16(packet->grehdr.ether_type, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT;
|
|
|
|
packet->have_grehdr = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_gre) {
|
|
|
|
pcaplist->pcap_thread->callback_gre(pcaplist->user, packet, payload, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPPROTO_ICMP:
|
|
|
|
layer_trace("ipproto_icmp frag");
|
|
|
|
|
|
|
|
if (!(packet->iphdr.ip_off & 0x1fff)) {
|
|
|
|
for (;;) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_FRAGMENTED_ICMPHDR;
|
|
|
|
need8(packet->icmphdr.type, payload, length);
|
|
|
|
need8(packet->icmphdr.code, payload, length);
|
|
|
|
need16(packet->icmphdr.checksum, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT;
|
|
|
|
packet->have_icmphdr = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_icmp) {
|
|
|
|
pcaplist->pcap_thread->callback_icmp(pcaplist->user, packet, payload, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
layer_trace("ipproto_udp frag");
|
|
|
|
|
|
|
|
if (!(packet->iphdr.ip_off & 0x1fff)) {
|
|
|
|
for (;;) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_FRAGMENTED_UDPHDR;
|
|
|
|
need16(packet->udphdr.uh_sport, payload, length);
|
|
|
|
need16(packet->udphdr.uh_dport, payload, length);
|
|
|
|
need16(packet->udphdr.uh_ulen, payload, length);
|
|
|
|
need16(packet->udphdr.uh_sum, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT;
|
|
|
|
packet->have_udphdr = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_udp) {
|
|
|
|
pcaplist->pcap_thread->callback_udp(pcaplist->user, packet, payload, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
layer_trace("ipproto_tcp frag");
|
|
|
|
|
|
|
|
if (!(packet->iphdr.ip_off & 0x1fff)) {
|
|
|
|
for (;;) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_FRAGMENTED_TCPHDR;
|
|
|
|
need16(packet->tcphdr.th_sport, payload, length);
|
|
|
|
need16(packet->tcphdr.th_dport, payload, length);
|
|
|
|
need32(packet->tcphdr.th_seq, payload, length);
|
|
|
|
need32(packet->tcphdr.th_ack, payload, length);
|
|
|
|
need4x2(packet->tcphdr.th_off, packet->tcphdr.th_x2, payload, length);
|
|
|
|
need8(packet->tcphdr.th_flags, payload, length);
|
|
|
|
need16(packet->tcphdr.th_win, payload, length);
|
|
|
|
need16(packet->tcphdr.th_sum, payload, length);
|
|
|
|
need16(packet->tcphdr.th_urp, payload, length);
|
|
|
|
if (packet->tcphdr.th_off > 5) {
|
|
|
|
packet->tcpopts_len = (packet->tcphdr.th_off - 5) * 4;
|
|
|
|
needxb(&(packet->tcpopts[0]), packet->tcpopts_len, payload, length);
|
|
|
|
packet->have_tcpopts = 1;
|
|
|
|
}
|
|
|
|
packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT;
|
|
|
|
packet->have_tcphdr = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_tcp) {
|
|
|
|
pcaplist->pcap_thread->callback_tcp(pcaplist->user, packet, payload, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (packet->iphdr.ip_p) {
|
|
|
|
case IPPROTO_GRE:
|
|
|
|
layer_trace("ipproto_gre");
|
|
|
|
|
|
|
|
if (packet->have_grehdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_GRE;
|
|
|
|
need16(packet->grehdr.gre_flags, payload, length);
|
|
|
|
need16(packet->grehdr.ether_type, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_grehdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_gre)
|
|
|
|
pcaplist->pcap_thread->callback_gre(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_gre((void*)pcaplist, packet, payload, length);
|
|
|
|
|
|
|
|
if (release_frag) {
|
|
|
|
pcaplist->pcap_thread->callback_ipv4_frag.release(pcaplist->ipv4_frag_ctx, packet, payload, length);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case IPPROTO_ICMP:
|
|
|
|
layer_trace("ipproto_icmp");
|
|
|
|
|
|
|
|
if (packet->have_icmphdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_ICMP;
|
|
|
|
need8(packet->icmphdr.type, payload, length);
|
|
|
|
need8(packet->icmphdr.code, payload, length);
|
|
|
|
need16(packet->icmphdr.checksum, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_icmphdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_icmp)
|
|
|
|
pcaplist->pcap_thread->callback_icmp(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_icmp((void*)pcaplist, packet, payload, length);
|
|
|
|
|
|
|
|
if (release_frag) {
|
|
|
|
pcaplist->pcap_thread->callback_ipv4_frag.release(pcaplist->ipv4_frag_ctx, packet, payload, length);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
layer_trace("ipproto_udp");
|
|
|
|
|
|
|
|
if (packet->have_udphdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_UDP;
|
|
|
|
need16(packet->udphdr.uh_sport, payload, length);
|
|
|
|
need16(packet->udphdr.uh_dport, payload, length);
|
|
|
|
need16(packet->udphdr.uh_ulen, payload, length);
|
|
|
|
need16(packet->udphdr.uh_sum, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_udphdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_udp)
|
|
|
|
pcaplist->pcap_thread->callback_udp(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_udp((void*)pcaplist, packet, payload, length);
|
|
|
|
|
|
|
|
if (release_frag) {
|
|
|
|
pcaplist->pcap_thread->callback_ipv4_frag.release(pcaplist->ipv4_frag_ctx, packet, payload, length);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
layer_trace("ipproto_tcp");
|
|
|
|
|
|
|
|
if (packet->have_tcphdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_TCP;
|
|
|
|
need16(packet->tcphdr.th_sport, payload, length);
|
|
|
|
need16(packet->tcphdr.th_dport, payload, length);
|
|
|
|
need32(packet->tcphdr.th_seq, payload, length);
|
|
|
|
need32(packet->tcphdr.th_ack, payload, length);
|
|
|
|
need4x2(packet->tcphdr.th_off, packet->tcphdr.th_x2, payload, length);
|
|
|
|
need8(packet->tcphdr.th_flags, payload, length);
|
|
|
|
need16(packet->tcphdr.th_win, payload, length);
|
|
|
|
need16(packet->tcphdr.th_sum, payload, length);
|
|
|
|
need16(packet->tcphdr.th_urp, payload, length);
|
|
|
|
if (packet->tcphdr.th_off > 5) {
|
|
|
|
packet->tcpopts_len = (packet->tcphdr.th_off - 5) * 4;
|
|
|
|
needxb(&(packet->tcpopts[0]), packet->tcpopts_len, payload, length);
|
|
|
|
packet->have_tcpopts = 1;
|
|
|
|
}
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_tcphdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_tcp)
|
|
|
|
pcaplist->pcap_thread->callback_tcp(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_tcp((void*)pcaplist, packet, payload, length);
|
|
|
|
|
|
|
|
if (release_frag) {
|
|
|
|
pcaplist->pcap_thread->callback_ipv4_frag.release(pcaplist->ipv4_frag_ctx, packet, payload, length);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
if (release_frag)
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (release_frag) {
|
|
|
|
pcaplist->pcap_thread->callback_ipv4_frag.release(pcaplist->ipv4_frag_ctx, packet, payload, length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_ipv6(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
int release_frag = 0;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet->have_ip6hdr) {
|
|
|
|
struct ip6_ext ext;
|
|
|
|
size_t already_advanced = 0;
|
|
|
|
|
|
|
|
layer_trace("have_ip6hdr");
|
|
|
|
|
|
|
|
/* Check reported length for missing payload or padding */
|
|
|
|
if (length < packet->ip6hdr.ip6_plen) {
|
|
|
|
layer_trace("length < ip6_plen");
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IPV6;
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (length > packet->ip6hdr.ip6_plen) {
|
|
|
|
layer_trace("have_ip6padding");
|
|
|
|
packet->ip6padding = length - packet->ip6hdr.ip6_plen;
|
|
|
|
packet->have_ip6padding = 1;
|
|
|
|
length -= packet->ip6padding;
|
|
|
|
}
|
|
|
|
|
|
|
|
ext.ip6e_nxt = packet->ip6hdr.ip6_nxt;
|
|
|
|
ext.ip6e_len = 0;
|
|
|
|
while (ext.ip6e_nxt != IPPROTO_NONE
|
|
|
|
&& ext.ip6e_nxt != IPPROTO_GRE
|
|
|
|
&& ext.ip6e_nxt != IPPROTO_ICMPV6
|
|
|
|
&& ext.ip6e_nxt != IPPROTO_UDP
|
|
|
|
&& ext.ip6e_nxt != IPPROTO_TCP) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_IPV6HDR;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Advance to the start of next header, this may not be needed
|
|
|
|
* if it's the first header or if the header is supported.
|
|
|
|
*/
|
|
|
|
if (ext.ip6e_len) {
|
|
|
|
if (ext.ip6e_len < already_advanced) {
|
|
|
|
/* Header length is invalid */
|
|
|
|
layer_trace("ip6hdr invalid");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Advance if not already there */
|
|
|
|
else if (ext.ip6e_len > already_advanced) {
|
|
|
|
advancexb((ext.ip6e_len - already_advanced) * 8, payload, length);
|
|
|
|
}
|
|
|
|
already_advanced = 0;
|
|
|
|
} else if (already_advanced) {
|
|
|
|
/* Already advanced but header has no length */
|
|
|
|
layer_trace("ip6hdr already advanced");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Store IPv6 headers? */
|
|
|
|
|
|
|
|
/* Handle supported headers */
|
|
|
|
if (ext.ip6e_nxt == IPPROTO_FRAGMENT) {
|
|
|
|
if (packet->have_ip6frag) {
|
|
|
|
layer_trace("dup ip6frag");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
layer_trace("ip6frag");
|
|
|
|
need8(ext.ip6e_nxt, payload, length);
|
|
|
|
need8(packet->ip6frag.ip6f_reserved, payload, length);
|
|
|
|
need16(packet->ip6frag.ip6f_offlg, payload, length);
|
|
|
|
need32(packet->ip6frag.ip6f_ident, payload, length);
|
|
|
|
packet->have_ip6frag = 1;
|
|
|
|
ext.ip6e_len = 1;
|
|
|
|
already_advanced = 1;
|
|
|
|
} else if (ext.ip6e_nxt == IPPROTO_ROUTING) {
|
|
|
|
struct ip6_rthdr rthdr;
|
|
|
|
struct in6_addr rt[255];
|
|
|
|
|
|
|
|
if (packet->have_ip6rtdst) {
|
|
|
|
layer_trace("dup ip6rtdst");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
need8(ext.ip6e_nxt, payload, length);
|
|
|
|
need8(ext.ip6e_len, payload, length);
|
|
|
|
need8(rthdr.ip6r_type, payload, length);
|
|
|
|
need8(rthdr.ip6r_segleft, payload, length);
|
|
|
|
if (!rthdr.ip6r_type) {
|
|
|
|
if (rthdr.ip6r_segleft > ext.ip6e_len)
|
|
|
|
break;
|
|
|
|
for (rthdr.ip6r_len = 0; rthdr.ip6r_len < ext.ip6e_len; rthdr.ip6r_len++, already_advanced += 2) {
|
|
|
|
needxb(&rt[rthdr.ip6r_len], 16, payload, length);
|
|
|
|
}
|
|
|
|
if (!rthdr.ip6r_len || rthdr.ip6r_len != ext.ip6e_len) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (rthdr.ip6r_segleft) {
|
|
|
|
packet->ip6rtdst = rt[rthdr.ip6r_segleft];
|
|
|
|
packet->have_ip6rtdst = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Nonsupported header */
|
|
|
|
layer_trace("ip6hdr?");
|
|
|
|
need8(ext.ip6e_nxt, payload, length);
|
|
|
|
need8(ext.ip6e_len, payload, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
|
|
|
|
if (!ext.ip6e_len)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; packet->state == PCAP_THREAD_PACKET_OK;) {
|
|
|
|
if (packet->have_ip6frag) {
|
|
|
|
packet->ip6frag_payload = ext.ip6e_nxt;
|
|
|
|
|
|
|
|
layer_trace("is_v6_frag");
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_ipv6_frag.reassemble) {
|
|
|
|
pcap_thread_packet_t* whole_packet = 0;
|
|
|
|
const u_char* whole_payload = 0;
|
|
|
|
size_t whole_length = 0;
|
|
|
|
|
|
|
|
packet->state = pcaplist->pcap_thread->callback_ipv6_frag.reassemble(pcaplist->ipv6_frag_ctx, packet, payload, length, &whole_packet, &whole_payload, &whole_length);
|
|
|
|
|
|
|
|
/* Defragmentation failed some how, we return packet as invalid */
|
|
|
|
if (packet->state != PCAP_THREAD_PACKET_OK) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No whole/defragmented packet return, need more */
|
|
|
|
if (!whole_packet || !whole_payload || !whole_length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
layer_tracef("v6_reasm %p %p %lu", whole_packet, whole_payload, whole_length);
|
|
|
|
|
|
|
|
packet = whole_packet;
|
|
|
|
payload = whole_payload;
|
|
|
|
length = whole_length;
|
|
|
|
release_frag = 1;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Mark packet as fragment and send it to the next user
|
|
|
|
* layer (if any) or return it as invalid.
|
|
|
|
*/
|
|
|
|
packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT;
|
|
|
|
|
|
|
|
switch (ext.ip6e_nxt) {
|
|
|
|
case IPPROTO_GRE:
|
|
|
|
layer_trace("ipproto_gre frag");
|
|
|
|
|
|
|
|
if (!packet->ip6frag.ip6f_offlg) {
|
|
|
|
for (;;) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_FRAGMENTED_GREHDR;
|
|
|
|
need16(packet->grehdr.gre_flags, payload, length);
|
|
|
|
need16(packet->grehdr.ether_type, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT;
|
|
|
|
packet->have_grehdr = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_gre) {
|
|
|
|
pcaplist->pcap_thread->callback_gre(pcaplist->user, packet, payload, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPPROTO_ICMPV6:
|
|
|
|
layer_trace("ipproto_icmpv6 frag");
|
|
|
|
|
|
|
|
if (!packet->ip6frag.ip6f_offlg) {
|
|
|
|
for (;;) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_FRAGMENTED_ICMPV6HDR;
|
|
|
|
need8(packet->icmpv6hdr.icmp6_type, payload, length);
|
|
|
|
need8(packet->icmpv6hdr.icmp6_code, payload, length);
|
|
|
|
need16(packet->icmpv6hdr.icmp6_cksum, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT;
|
|
|
|
packet->have_icmpv6hdr = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_icmpv6) {
|
|
|
|
pcaplist->pcap_thread->callback_icmpv6(pcaplist->user, packet, payload, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
layer_trace("ipproto_udp frag");
|
|
|
|
|
|
|
|
if (!packet->ip6frag.ip6f_offlg) {
|
|
|
|
for (;;) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_FRAGMENTED_UDPHDR;
|
|
|
|
need16(packet->udphdr.uh_sport, payload, length);
|
|
|
|
need16(packet->udphdr.uh_dport, payload, length);
|
|
|
|
need16(packet->udphdr.uh_ulen, payload, length);
|
|
|
|
need16(packet->udphdr.uh_sum, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT;
|
|
|
|
packet->have_udphdr = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_udp) {
|
|
|
|
pcaplist->pcap_thread->callback_udp(pcaplist->user, packet, payload, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
layer_trace("ipproto_tcp frag");
|
|
|
|
|
|
|
|
if (!packet->ip6frag.ip6f_offlg) {
|
|
|
|
for (;;) {
|
|
|
|
packet->state = PCAP_THREAD_PACKET_FRAGMENTED_TCPHDR;
|
|
|
|
need16(packet->tcphdr.th_sport, payload, length);
|
|
|
|
need16(packet->tcphdr.th_dport, payload, length);
|
|
|
|
need32(packet->tcphdr.th_seq, payload, length);
|
|
|
|
need32(packet->tcphdr.th_ack, payload, length);
|
|
|
|
need4x2(packet->tcphdr.th_off, packet->tcphdr.th_x2, payload, length);
|
|
|
|
need8(packet->tcphdr.th_flags, payload, length);
|
|
|
|
need16(packet->tcphdr.th_win, payload, length);
|
|
|
|
need16(packet->tcphdr.th_sum, payload, length);
|
|
|
|
need16(packet->tcphdr.th_urp, payload, length);
|
|
|
|
if (packet->tcphdr.th_off > 5) {
|
|
|
|
packet->tcpopts_len = (packet->tcphdr.th_off - 5) * 4;
|
|
|
|
needxb(&(packet->tcpopts[0]), packet->tcpopts_len, payload, length);
|
|
|
|
packet->have_tcpopts = 1;
|
|
|
|
}
|
|
|
|
packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT;
|
|
|
|
packet->have_tcphdr = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_tcp) {
|
|
|
|
pcaplist->pcap_thread->callback_tcp(pcaplist->user, packet, payload, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ext.ip6e_nxt) {
|
|
|
|
case IPPROTO_GRE:
|
|
|
|
if (packet->have_grehdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_GRE;
|
|
|
|
need16(packet->grehdr.gre_flags, payload, length);
|
|
|
|
need16(packet->grehdr.ether_type, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_grehdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_gre)
|
|
|
|
pcaplist->pcap_thread->callback_gre(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_gre((void*)pcaplist, packet, payload, length);
|
|
|
|
|
|
|
|
if (release_frag) {
|
|
|
|
pcaplist->pcap_thread->callback_ipv6_frag.release(pcaplist->ipv6_frag_ctx, packet, payload, length);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case IPPROTO_ICMPV6:
|
|
|
|
layer_trace("ipproto_icmpv6");
|
|
|
|
|
|
|
|
if (packet->have_icmpv6hdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_ICMPV6;
|
|
|
|
need8(packet->icmpv6hdr.icmp6_type, payload, length);
|
|
|
|
need8(packet->icmpv6hdr.icmp6_code, payload, length);
|
|
|
|
need16(packet->icmpv6hdr.icmp6_cksum, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_icmpv6hdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_icmpv6)
|
|
|
|
pcaplist->pcap_thread->callback_icmpv6(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_icmpv6((void*)pcaplist, packet, payload, length);
|
|
|
|
|
|
|
|
if (release_frag) {
|
|
|
|
pcaplist->pcap_thread->callback_ipv6_frag.release(pcaplist->ipv6_frag_ctx, packet, payload, length);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
if (packet->have_udphdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_UDP;
|
|
|
|
need16(packet->udphdr.uh_sport, payload, length);
|
|
|
|
need16(packet->udphdr.uh_dport, payload, length);
|
|
|
|
need16(packet->udphdr.uh_ulen, payload, length);
|
|
|
|
need16(packet->udphdr.uh_sum, payload, length);
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_udphdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_udp)
|
|
|
|
pcaplist->pcap_thread->callback_udp(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_udp((void*)pcaplist, packet, payload, length);
|
|
|
|
|
|
|
|
if (release_frag) {
|
|
|
|
pcaplist->pcap_thread->callback_ipv6_frag.release(pcaplist->ipv6_frag_ctx, packet, payload, length);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
if (packet->have_tcphdr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID_TCP;
|
|
|
|
need16(packet->tcphdr.th_sport, payload, length);
|
|
|
|
need16(packet->tcphdr.th_dport, payload, length);
|
|
|
|
need32(packet->tcphdr.th_seq, payload, length);
|
|
|
|
need32(packet->tcphdr.th_ack, payload, length);
|
|
|
|
need4x2(packet->tcphdr.th_off, packet->tcphdr.th_x2, payload, length);
|
|
|
|
need8(packet->tcphdr.th_flags, payload, length);
|
|
|
|
need16(packet->tcphdr.th_win, payload, length);
|
|
|
|
need16(packet->tcphdr.th_sum, payload, length);
|
|
|
|
need16(packet->tcphdr.th_urp, payload, length);
|
|
|
|
if (packet->tcphdr.th_off > 5) {
|
|
|
|
packet->tcpopts_len = (packet->tcphdr.th_off - 5) * 4;
|
|
|
|
needxb(&(packet->tcpopts[0]), packet->tcpopts_len, payload, length);
|
|
|
|
packet->have_tcpopts = 1;
|
|
|
|
}
|
|
|
|
packet->state = PCAP_THREAD_PACKET_OK;
|
|
|
|
packet->have_tcphdr = 1;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_tcp)
|
|
|
|
pcaplist->pcap_thread->callback_tcp(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcap_thread_callback_tcp((void*)pcaplist, packet, payload, length);
|
|
|
|
|
|
|
|
if (release_frag) {
|
|
|
|
pcaplist->pcap_thread->callback_ipv6_frag.release(pcaplist->ipv6_frag_ctx, packet, payload, length);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNSUPPORTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
if (release_frag)
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, payload, length);
|
|
|
|
else
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (release_frag) {
|
|
|
|
pcaplist->pcap_thread->callback_ipv6_frag.release(pcaplist->ipv6_frag_ctx, packet, payload, length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_icmp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Higher layer support? */
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNPROCESSED;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_icmpv6(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Higher layer support? */
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNPROCESSED;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_udp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Higher layer support? */
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNPROCESSED;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pcap_thread_callback_tcp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
const u_char* orig = payload;
|
|
|
|
size_t origlength = length;
|
|
|
|
|
|
|
|
if (!pcaplist) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!packet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!payload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Higher layer support? */
|
|
|
|
packet->state = PCAP_THREAD_PACKET_UNPROCESSED;
|
|
|
|
|
|
|
|
if (pcaplist->pcap_thread->callback_invalid) {
|
|
|
|
if (packet->state == PCAP_THREAD_PACKET_OK)
|
|
|
|
packet->state = PCAP_THREAD_PACKET_INVALID;
|
|
|
|
pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open/Close
|
|
|
|
*/
|
|
|
|
|
|
|
|
static pcap_thread_pcaplist_t _pcaplist_defaults = PCAP_THREAD_PCAPLIST_T_INIT;
|
|
|
|
|
|
|
|
int pcap_thread_open(pcap_thread_t* pcap_thread, const char* device, void* user)
|
|
|
|
{
|
|
|
|
pcap_t* pcap;
|
|
|
|
pcap_thread_pcaplist_t* pcaplist;
|
|
|
|
int snapshot;
|
|
|
|
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!device) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->errbuf[0]) {
|
|
|
|
memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf));
|
|
|
|
}
|
|
|
|
pcap_thread->status = 0;
|
|
|
|
|
|
|
|
if (!(pcaplist = malloc(sizeof(pcap_thread_pcaplist_t)))) {
|
|
|
|
return PCAP_THREAD_ENOMEM;
|
|
|
|
}
|
|
|
|
memcpy(pcaplist, &_pcaplist_defaults, sizeof(pcap_thread_pcaplist_t));
|
|
|
|
if (!(pcaplist->name = strdup(device))) {
|
|
|
|
free(pcaplist);
|
|
|
|
return PCAP_THREAD_ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_PCAP_CREATE
|
|
|
|
if (!(pcap = pcap_create(pcaplist->name, pcap_thread->errbuf))) {
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->monitor) {
|
|
|
|
pcap_thread->status = pcap_can_set_rfmon(pcap);
|
|
|
|
if (pcap_thread->status == 0) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
return PCAP_THREAD_ENOMON;
|
|
|
|
}
|
|
|
|
if (pcap_thread->status != 1) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_can_set_rfmon()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
|
|
|
|
if (pcap_thread->have_timestamp_precision && (pcap_thread->status = pcap_set_tstamp_precision(pcap, pcap_thread->timestamp_precision))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_tstamp_precision()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
|
|
|
|
if (pcap_thread->immediate_mode && (pcap_thread->status = pcap_set_immediate_mode(pcap, 1))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_immediate_mode()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (pcap_thread->monitor && (pcap_thread->status = pcap_set_rfmon(pcap, 1))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_rfmon()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
if (pcap_thread->snaplen && (pcap_thread->status = pcap_set_snaplen(pcap, pcap_thread->snaplen))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_snaplen()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
if (pcap_thread->promiscuous && (pcap_thread->status = pcap_set_promisc(pcap, pcap_thread->promiscuous))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_promisc()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
if (pcap_thread->timeout && (pcap_thread->status = pcap_set_timeout(pcap, pcap_thread->timeout))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_timeout()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
if (pcap_thread->buffer_size && (pcap_thread->status = pcap_set_buffer_size(pcap, pcap_thread->buffer_size))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_buffer_size()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
|
|
|
|
if (pcap_thread->have_timestamp_type && (pcap_thread->status = pcap_set_tstamp_type(pcap, pcap_thread->timestamp_type))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_tstamp_type()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (pcap_thread->activate_mode == PCAP_THREAD_ACTIVATE_MODE_IMMEDIATE) {
|
|
|
|
if ((pcap_thread->status = pcap_activate(pcap))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_activate()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_PCAP_SETDIRECTION
|
|
|
|
#ifdef HAVE_PCAP_DIRECTION_T
|
|
|
|
if (pcap_thread->have_direction && (pcap_thread->status = pcap_setdirection(pcap, pcap_thread->direction))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_setdirection()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#else /* HAVE_PCAP_CREATE */
|
|
|
|
if (!(pcap = pcap_open_live(pcaplist->name, pcap_thread->snaplen, pcap_thread->promiscuous, pcap_thread->timeout, pcap_thread->errbuf))) {
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (pcap_thread->activate_mode == PCAP_THREAD_ACTIVATE_MODE_IMMEDIATE) {
|
|
|
|
if (pcap_thread->filter) {
|
|
|
|
if ((pcap_thread->status = pcap_compile(pcap, &(pcaplist->bpf), pcap_thread->filter, pcap_thread->filter_optimize, pcap_thread->filter_netmask))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_compile()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
pcaplist->have_bpf = 1;
|
|
|
|
pcap_thread->filter_errno = 0;
|
|
|
|
errno = 0;
|
|
|
|
if ((pcap_thread->status = pcap_setfilter(pcap, &(pcaplist->bpf)))) {
|
|
|
|
pcap_freecode(&(pcaplist->bpf));
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_setfilter()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
pcap_thread->filter_errno = errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((snapshot = pcap_snapshot(pcap)) < 0) {
|
|
|
|
pcap_thread->status = snapshot;
|
|
|
|
if (pcaplist->have_bpf)
|
|
|
|
pcap_freecode(&(pcaplist->bpf));
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_snapshot()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
if (snapshot > pcap_thread->snapshot) {
|
|
|
|
pcap_thread->snapshot = snapshot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pcaplist->pcap = pcap;
|
|
|
|
pcaplist->user = user;
|
|
|
|
if (pcap_thread->callback_ipv4_frag.new) {
|
|
|
|
pcaplist->ipv4_frag_ctx = pcap_thread->callback_ipv4_frag.new(pcap_thread->callback_ipv4_frag.conf, user);
|
|
|
|
pcaplist->have_ipv4_frag_ctx = 1;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_ipv6_frag.new) {
|
|
|
|
pcaplist->ipv6_frag_ctx = pcap_thread->callback_ipv6_frag.new(pcap_thread->callback_ipv6_frag.conf, user);
|
|
|
|
pcaplist->have_ipv6_frag_ctx = 1;
|
|
|
|
}
|
|
|
|
if (pcap_thread->pcaplist) {
|
|
|
|
pcaplist->next = pcap_thread->pcaplist;
|
|
|
|
}
|
|
|
|
pcap_thread->pcaplist = pcaplist;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_open_offline(pcap_thread_t* pcap_thread, const char* file, void* user)
|
|
|
|
{
|
|
|
|
pcap_t* pcap;
|
|
|
|
pcap_thread_pcaplist_t* pcaplist;
|
|
|
|
int snapshot;
|
|
|
|
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!file) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->errbuf[0]) {
|
|
|
|
memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf));
|
|
|
|
}
|
|
|
|
pcap_thread->status = 0;
|
|
|
|
|
|
|
|
if (!(pcaplist = malloc(sizeof(pcap_thread_pcaplist_t)))) {
|
|
|
|
return PCAP_THREAD_ENOMEM;
|
|
|
|
}
|
|
|
|
memcpy(pcaplist, &_pcaplist_defaults, sizeof(pcap_thread_pcaplist_t));
|
|
|
|
pcaplist->is_offline = 1;
|
|
|
|
if (!(pcaplist->name = strdup(file))) {
|
|
|
|
free(pcaplist);
|
|
|
|
return PCAP_THREAD_ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION
|
|
|
|
if (pcap_thread->have_timestamp_precision) {
|
|
|
|
if (!(pcap = pcap_open_offline_with_tstamp_precision(pcaplist->name, pcap_thread->timestamp_precision, pcap_thread->errbuf))) {
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
if (!(pcap = pcap_open_offline(pcaplist->name, pcap_thread->errbuf))) {
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->filter) {
|
|
|
|
if ((pcap_thread->status = pcap_compile(pcap, &(pcaplist->bpf), pcap_thread->filter, pcap_thread->filter_optimize, pcap_thread->filter_netmask))) {
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_compile()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
pcaplist->have_bpf = 1;
|
|
|
|
pcap_thread->filter_errno = 0;
|
|
|
|
errno = 0;
|
|
|
|
if ((pcap_thread->status = pcap_setfilter(pcap, &(pcaplist->bpf)))) {
|
|
|
|
pcap_freecode(&(pcaplist->bpf));
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_setfilter()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
pcap_thread->filter_errno = errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((snapshot = pcap_snapshot(pcap)) < 0) {
|
|
|
|
pcap_thread->status = snapshot;
|
|
|
|
if (pcaplist->have_bpf)
|
|
|
|
pcap_freecode(&(pcaplist->bpf));
|
|
|
|
pcap_close(pcap);
|
|
|
|
free(pcaplist->name);
|
|
|
|
free(pcaplist);
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_snapshot()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
if (snapshot > pcap_thread->snapshot) {
|
|
|
|
pcap_thread->snapshot = snapshot;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcaplist->pcap = pcap;
|
|
|
|
pcaplist->user = user;
|
|
|
|
if (pcap_thread->callback_ipv4_frag.new) {
|
|
|
|
pcaplist->ipv4_frag_ctx = pcap_thread->callback_ipv4_frag.new(pcap_thread->callback_ipv4_frag.conf, user);
|
|
|
|
pcaplist->have_ipv4_frag_ctx = 1;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_ipv6_frag.new) {
|
|
|
|
pcaplist->ipv6_frag_ctx = pcap_thread->callback_ipv6_frag.new(pcap_thread->callback_ipv6_frag.conf, user);
|
|
|
|
pcaplist->have_ipv6_frag_ctx = 1;
|
|
|
|
}
|
|
|
|
if (pcap_thread->pcaplist) {
|
|
|
|
pcaplist->next = pcap_thread->pcaplist;
|
|
|
|
}
|
|
|
|
pcap_thread->pcaplist = pcaplist;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_add(pcap_thread_t* pcap_thread, const char* name, pcap_t* pcap, void* user)
|
|
|
|
{
|
|
|
|
(void)pcap_thread;
|
|
|
|
(void)name;
|
|
|
|
(void)pcap;
|
|
|
|
(void)user;
|
|
|
|
|
|
|
|
if (pcap_thread->errbuf[0]) {
|
|
|
|
memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf));
|
|
|
|
}
|
|
|
|
pcap_thread->status = 0;
|
|
|
|
|
|
|
|
return PCAP_THREAD_EOBSOLETE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_activate(pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist;
|
|
|
|
int snapshot;
|
|
|
|
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->errbuf[0]) {
|
|
|
|
memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf));
|
|
|
|
}
|
|
|
|
pcap_thread->status = 0;
|
|
|
|
|
|
|
|
pcap_thread->filter_errno = 0;
|
|
|
|
for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) {
|
|
|
|
if (pcaplist->is_offline) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_PCAP_ACTIVATE
|
|
|
|
if ((pcap_thread->status = pcap_activate(pcaplist->pcap))) {
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_activate()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_PCAP_SETDIRECTION
|
|
|
|
#ifdef HAVE_PCAP_DIRECTION_T
|
|
|
|
if (pcap_thread->have_direction && (pcap_thread->status = pcap_setdirection(pcaplist->pcap, pcap_thread->direction))) {
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_setdirection()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (pcap_thread->filter) {
|
|
|
|
if (pcaplist->have_bpf)
|
|
|
|
pcap_freecode(&(pcaplist->bpf));
|
|
|
|
if ((pcap_thread->status = pcap_compile(pcaplist->pcap, &(pcaplist->bpf), pcap_thread->filter, pcap_thread->filter_optimize, pcap_thread->filter_netmask))) {
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_compile()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
pcaplist->have_bpf = 1;
|
|
|
|
errno = 0;
|
|
|
|
if ((pcap_thread->status = pcap_setfilter(pcaplist->pcap, &(pcaplist->bpf)))) {
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_setfilter()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
if (errno && !pcap_thread->filter_errno)
|
|
|
|
pcap_thread->filter_errno = errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((snapshot = pcap_snapshot(pcaplist->pcap)) < 0) {
|
|
|
|
pcap_thread->status = snapshot;
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_snapshot()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
if (snapshot > pcap_thread->snapshot) {
|
|
|
|
pcap_thread->snapshot = snapshot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_close(pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist;
|
|
|
|
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (pcap_thread->pcaplist) {
|
|
|
|
pcaplist = pcap_thread->pcaplist;
|
|
|
|
pcap_thread->pcaplist = pcaplist->next;
|
|
|
|
|
|
|
|
if (pcap_thread->callback_ipv4_frag.free && pcaplist->have_ipv4_frag_ctx) {
|
|
|
|
pcap_thread->callback_ipv4_frag.free(pcaplist->ipv4_frag_ctx);
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_ipv6_frag.free && pcaplist->have_ipv6_frag_ctx) {
|
|
|
|
pcap_thread->callback_ipv6_frag.free(pcaplist->ipv6_frag_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcaplist->pcap) {
|
|
|
|
pcap_close(pcaplist->pcap);
|
|
|
|
}
|
|
|
|
if (pcaplist->have_bpf) {
|
|
|
|
pcap_freecode(&(pcaplist->bpf));
|
|
|
|
}
|
|
|
|
if (pcaplist->name) {
|
|
|
|
free(pcaplist->name);
|
|
|
|
}
|
|
|
|
free(pcaplist);
|
|
|
|
}
|
|
|
|
pcap_thread->step = 0;
|
|
|
|
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
|
|
if (pcap_thread->pkthdr) {
|
|
|
|
free(pcap_thread->pkthdr);
|
|
|
|
pcap_thread->pkthdr = 0;
|
|
|
|
}
|
|
|
|
if (pcap_thread->pkt) {
|
|
|
|
free(pcap_thread->pkt);
|
|
|
|
pcap_thread->pkt = 0;
|
|
|
|
}
|
|
|
|
if (pcap_thread->pcaplist_pkt) {
|
|
|
|
free(pcap_thread->pcaplist_pkt);
|
|
|
|
pcap_thread->pcaplist_pkt = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Engine
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
|
|
static void _callback(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* pkt)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist;
|
|
|
|
pcap_thread_t* pcap_thread;
|
|
|
|
|
|
|
|
pthread_testcancel();
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
pcaplist->running = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pcap_thread = pcaplist->pcap_thread;
|
|
|
|
|
|
|
|
if (pkthdr->caplen > pcap_thread->snapshot) {
|
|
|
|
if (pcap_thread->dropback) {
|
|
|
|
pcap_thread->dropback(pcaplist->user, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->queue_mode == PCAP_THREAD_QUEUE_MODE_DIRECT) {
|
|
|
|
if (pcap_thread->callback) {
|
|
|
|
pcap_thread->callback(pcaplist->user, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap));
|
|
|
|
} else if (pcaplist->layer_callback) {
|
|
|
|
pcaplist->layer_callback((void*)pcaplist, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap));
|
|
|
|
} else if (pcap_thread->dropback) {
|
|
|
|
pcap_thread->dropback(pcaplist->user, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pthread_mutex_lock(&(pcap_thread->mutex))) {
|
|
|
|
if (pcap_thread->dropback) {
|
|
|
|
pcap_thread->dropback(pcaplist->user, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (pcaplist->running && pcap_thread->running) {
|
|
|
|
if (pcap_thread->pkts < pcap_thread->queue_size) {
|
|
|
|
pcap_thread->pcaplist_pkt[pcap_thread->write_pos] = pcaplist;
|
|
|
|
memcpy(&(pcap_thread->pkthdr[pcap_thread->write_pos]), pkthdr, sizeof(struct pcap_pkthdr));
|
|
|
|
memcpy(&(pcap_thread->pkt[pcap_thread->write_pos * pcap_thread->snapshot]), pkt, pkthdr->caplen);
|
|
|
|
pcap_thread->write_pos++;
|
|
|
|
if (pcap_thread->write_pos == pcap_thread->queue_size) {
|
|
|
|
pcap_thread->write_pos = 0;
|
|
|
|
}
|
|
|
|
pcap_thread->pkts++;
|
|
|
|
|
|
|
|
pthread_cond_signal(&(pcap_thread->have_packets));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pthread_cond_wait(&(pcap_thread->can_write), &(pcap_thread->mutex))) {
|
|
|
|
pcaplist->running = 0;
|
|
|
|
pcap_breakloop(pcaplist->pcap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pthread_mutex_unlock(&(pcap_thread->mutex))) {
|
|
|
|
pcaplist->running = 0;
|
|
|
|
pcap_breakloop(pcaplist->pcap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void* _thread(void* vp)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/*pthread_detach(pthread_self());*/
|
|
|
|
|
|
|
|
if (!vp) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
pcaplist = (pcap_thread_pcaplist_t*)vp;
|
|
|
|
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
pcaplist->running = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pcap_loop() might return -2 to indicate pcap_breakloop() was called
|
|
|
|
* but we do not need to act on that because either this thread has
|
|
|
|
* been cancelled or running has been cleared
|
|
|
|
*/
|
|
|
|
while (pcaplist->running) {
|
|
|
|
pthread_testcancel();
|
|
|
|
ret = pcap_loop(pcaplist->pcap, -1, _callback, (u_char*)pcaplist);
|
2025-04-21 09:31:29 +02:00
|
|
|
if (ret == PCAP_ERROR) {
|
2025-02-08 11:57:11 +01:00
|
|
|
/* TODO: Store pcap_loop() error */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!ret)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcaplist->running = 0;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&(pcaplist->pcap_thread->mutex));
|
|
|
|
pthread_cond_signal(&(pcaplist->pcap_thread->have_packets));
|
|
|
|
pthread_mutex_unlock(&(pcaplist->pcap_thread->mutex));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void _callback2(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* pkt)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist;
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pcaplist = (pcap_thread_pcaplist_t*)user;
|
|
|
|
|
|
|
|
if (!pcaplist->pcap_thread) {
|
|
|
|
pcaplist->running = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (pcaplist->pcap_thread->callback) {
|
|
|
|
pcaplist->pcap_thread->callback(pcaplist->user, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap));
|
|
|
|
} else if (pcaplist->layer_callback) {
|
|
|
|
pcaplist->layer_callback((void*)pcaplist, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap));
|
|
|
|
} else {
|
|
|
|
pcaplist->running = 0;
|
|
|
|
}
|
2025-04-21 09:31:29 +02:00
|
|
|
|
|
|
|
if (pcaplist->timedrun
|
|
|
|
&& (pkthdr->ts.tv_sec > pcaplist->end.tv_sec
|
|
|
|
|| (pkthdr->ts.tv_sec == pcaplist->end.tv_sec && (pkthdr->ts.tv_usec * 1000) >= pcaplist->end.tv_nsec))) {
|
|
|
|
pcap_breakloop(pcaplist->pcap);
|
|
|
|
}
|
2025-02-08 11:57:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_run(pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist;
|
|
|
|
int run = 1, timedrun = 0;
|
|
|
|
struct timeval start = { 0, 0 };
|
|
|
|
struct timespec end = { 0, 0 };
|
|
|
|
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!pcap_thread->pcaplist) {
|
|
|
|
return PCAP_THREAD_NOPCAPS;
|
|
|
|
}
|
|
|
|
if (!pcap_thread->callback && !pcap_thread->use_layers) {
|
|
|
|
return PCAP_THREAD_NOCALLBACK;
|
|
|
|
}
|
|
|
|
if (pcap_thread->use_layers
|
|
|
|
&& !(pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp)) {
|
2025-02-08 11:57:11 +01:00
|
|
|
return PCAP_THREAD_NOCALLBACK;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->errbuf[0]) {
|
|
|
|
memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf));
|
|
|
|
}
|
|
|
|
pcap_thread->status = 0;
|
|
|
|
|
|
|
|
if (pcap_thread->timedrun.tv_sec || pcap_thread->timedrun.tv_usec) {
|
|
|
|
timedrun = 1;
|
|
|
|
if (gettimeofday(&start, 0)) {
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "gettimeofday()");
|
|
|
|
return PCAP_THREAD_ERRNO;
|
|
|
|
}
|
|
|
|
|
|
|
|
end.tv_sec = start.tv_sec + pcap_thread->timedrun.tv_sec
|
|
|
|
+ ((start.tv_usec + pcap_thread->timedrun.tv_usec) / 1000000);
|
|
|
|
end.tv_nsec = ((start.tv_usec + pcap_thread->timedrun.tv_usec) % 1000000) * 1000;
|
|
|
|
} else if (pcap_thread->timedrun_to.tv_sec) {
|
|
|
|
timedrun = 1;
|
|
|
|
|
|
|
|
end.tv_sec = pcap_thread->timedrun_to.tv_sec;
|
|
|
|
end.tv_nsec = pcap_thread->timedrun_to.tv_usec * 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
|
|
if (pcap_thread->use_threads) {
|
|
|
|
int err, all_offline;
|
|
|
|
|
|
|
|
switch (pcap_thread->queue_mode) {
|
|
|
|
case PCAP_THREAD_QUEUE_MODE_COND:
|
|
|
|
case PCAP_THREAD_QUEUE_MODE_DIRECT:
|
|
|
|
if ((err = pthread_mutex_lock(&(pcap_thread->mutex)))) {
|
|
|
|
errno = err;
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pthread_mutex_lock()");
|
|
|
|
return PCAP_THREAD_ERRNO;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PCAP_THREAD_QUEUE_MODE_WAIT:
|
|
|
|
case PCAP_THREAD_QUEUE_MODE_YIELD:
|
|
|
|
case PCAP_THREAD_QUEUE_MODE_DROP:
|
|
|
|
return PCAP_THREAD_EOBSOLETE;
|
|
|
|
default:
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
pthread_mutex_unlock(&(pcap_thread->mutex));
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->pkthdr) {
|
|
|
|
free(pcap_thread->pkthdr);
|
|
|
|
}
|
|
|
|
if (!(pcap_thread->pkthdr = calloc(pcap_thread->queue_size, sizeof(struct pcap_pkthdr)))) {
|
|
|
|
pthread_mutex_unlock(&(pcap_thread->mutex));
|
|
|
|
return PCAP_THREAD_ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->pkt) {
|
|
|
|
free(pcap_thread->pkt);
|
|
|
|
}
|
|
|
|
if (!(pcap_thread->pkt = calloc(pcap_thread->queue_size, pcap_thread->snapshot))) {
|
|
|
|
pthread_mutex_unlock(&(pcap_thread->mutex));
|
|
|
|
return PCAP_THREAD_ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->pcaplist_pkt) {
|
|
|
|
free(pcap_thread->pcaplist_pkt);
|
|
|
|
}
|
|
|
|
if (!(pcap_thread->pcaplist_pkt = calloc(pcap_thread->queue_size, sizeof(pcap_thread_pcaplist_t*)))) {
|
|
|
|
pthread_mutex_unlock(&(pcap_thread->mutex));
|
|
|
|
return PCAP_THREAD_ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->read_pos = 0;
|
|
|
|
pcap_thread->write_pos = 0;
|
|
|
|
pcap_thread->pkts = 0;
|
|
|
|
|
|
|
|
all_offline = 1;
|
|
|
|
for (pcaplist = pcap_thread->pcaplist; all_offline && pcaplist; pcaplist = pcaplist->next) {
|
|
|
|
if (!pcaplist->is_offline) {
|
|
|
|
all_offline = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->running = 1;
|
|
|
|
pcap_thread->was_stopped = 0;
|
|
|
|
err = PCAP_THREAD_OK;
|
|
|
|
|
|
|
|
for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) {
|
|
|
|
pcaplist->pcap_thread = pcap_thread;
|
|
|
|
if (pcap_thread->use_layers) {
|
|
|
|
pcaplist->layer_callback = &pcap_thread_callback;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_ipv4_frag.new && !pcaplist->have_ipv4_frag_ctx) {
|
|
|
|
pcaplist->ipv4_frag_ctx = pcap_thread->callback_ipv4_frag.new(pcap_thread->callback_ipv4_frag.conf, pcaplist->user);
|
|
|
|
pcaplist->have_ipv4_frag_ctx = 1;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_ipv6_frag.new && !pcaplist->have_ipv6_frag_ctx) {
|
|
|
|
pcaplist->ipv6_frag_ctx = pcap_thread->callback_ipv6_frag.new(pcap_thread->callback_ipv6_frag.conf, pcaplist->user);
|
|
|
|
pcaplist->have_ipv6_frag_ctx = 1;
|
|
|
|
}
|
|
|
|
pcaplist->running = 1;
|
|
|
|
|
|
|
|
if ((err = pthread_create(&(pcaplist->thread), 0, _thread, (void*)pcaplist))) {
|
|
|
|
errno = err;
|
|
|
|
err = PCAP_THREAD_ERRNO;
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pthread_create()");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (err == PCAP_THREAD_OK && run && pcap_thread->running) {
|
|
|
|
while (pcap_thread->pkts) {
|
|
|
|
if (!pcap_thread->pcaplist_pkt[pcap_thread->read_pos]) {
|
|
|
|
err = PCAP_THREAD_ENOPCAPLIST;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->callback) {
|
|
|
|
pcap_thread->callback(
|
|
|
|
pcap_thread->pcaplist_pkt[pcap_thread->read_pos]->user,
|
|
|
|
&(pcap_thread->pkthdr[pcap_thread->read_pos]),
|
|
|
|
&(pcap_thread->pkt[pcap_thread->read_pos * pcap_thread->snapshot]),
|
|
|
|
pcap_thread->pcaplist_pkt[pcap_thread->read_pos]->name,
|
|
|
|
pcap_datalink(pcap_thread->pcaplist_pkt[pcap_thread->read_pos]->pcap));
|
|
|
|
} else {
|
|
|
|
pcap_thread_callback(
|
|
|
|
(void*)pcap_thread->pcaplist_pkt[pcap_thread->read_pos],
|
|
|
|
&(pcap_thread->pkthdr[pcap_thread->read_pos]),
|
|
|
|
&(pcap_thread->pkt[pcap_thread->read_pos * pcap_thread->snapshot]),
|
|
|
|
pcap_thread->pcaplist_pkt[pcap_thread->read_pos]->name,
|
|
|
|
pcap_datalink(pcap_thread->pcaplist_pkt[pcap_thread->read_pos]->pcap));
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->pcaplist_pkt[pcap_thread->read_pos] = 0;
|
|
|
|
pcap_thread->read_pos++;
|
|
|
|
if (pcap_thread->read_pos == pcap_thread->queue_size) {
|
|
|
|
pcap_thread->read_pos = 0;
|
|
|
|
}
|
|
|
|
pcap_thread->pkts--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != PCAP_THREAD_OK)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if ((err = pthread_cond_broadcast(&(pcap_thread->can_write)))) {
|
|
|
|
errno = err;
|
|
|
|
err = PCAP_THREAD_ERRNO;
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pthread_cond_broadcast()");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
run = 0;
|
|
|
|
for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) {
|
|
|
|
if (pcaplist->running) {
|
|
|
|
run = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!run)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (timedrun) {
|
|
|
|
struct timeval now;
|
|
|
|
|
|
|
|
if (gettimeofday(&now, 0)) {
|
|
|
|
err = PCAP_THREAD_ERRNO;
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "gettimeofday()");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (now.tv_sec > end.tv_sec
|
|
|
|
|| (now.tv_sec == end.tv_sec && (now.tv_usec * 1000) >= end.tv_nsec)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = pthread_cond_timedwait(&(pcap_thread->have_packets), &(pcap_thread->mutex), &end);
|
|
|
|
if (err == ETIMEDOUT) {
|
|
|
|
err = PCAP_THREAD_OK;
|
|
|
|
} else if (err) {
|
|
|
|
errno = err;
|
|
|
|
err = PCAP_THREAD_ERRNO;
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pthread_cond_timedwait()");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ((err = pthread_cond_wait(&(pcap_thread->have_packets), &(pcap_thread->mutex)))) {
|
|
|
|
errno = err;
|
|
|
|
err = PCAP_THREAD_ERRNO;
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pthread_cond_wait()");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) {
|
|
|
|
pcaplist->running = 0;
|
|
|
|
pcap_breakloop(pcaplist->pcap);
|
|
|
|
if (pcaplist->thread) {
|
|
|
|
pthread_cancel(pcaplist->thread);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&(pcap_thread->mutex));
|
|
|
|
|
|
|
|
for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) {
|
|
|
|
if (pcaplist->thread) {
|
|
|
|
pthread_join(pcaplist->thread, 0);
|
|
|
|
pcaplist->thread = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->running = 0;
|
|
|
|
return err;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
fd_set fds, rfds;
|
|
|
|
int max_fd = 0;
|
|
|
|
struct timeval t1, t2;
|
|
|
|
|
|
|
|
pcap_thread->running = 1;
|
|
|
|
pcap_thread->was_stopped = 0;
|
|
|
|
|
|
|
|
FD_ZERO(&fds);
|
|
|
|
for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) {
|
|
|
|
int fd = pcap_get_selectable_fd(pcaplist->pcap);
|
|
|
|
|
|
|
|
FD_SET(fd, &fds);
|
|
|
|
if (fd > max_fd)
|
|
|
|
max_fd = fd;
|
|
|
|
|
|
|
|
if (!pcaplist->is_offline && (pcap_thread->status = pcap_setnonblock(pcaplist->pcap, 1, pcap_thread->errbuf))) {
|
|
|
|
pcap_thread->running = 0;
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
pcaplist->pcap_thread = pcap_thread;
|
|
|
|
if (pcap_thread->use_layers) {
|
|
|
|
pcaplist->layer_callback = &pcap_thread_callback;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_ipv4_frag.new && !pcaplist->have_ipv4_frag_ctx) {
|
|
|
|
pcaplist->ipv4_frag_ctx = pcap_thread->callback_ipv4_frag.new(pcap_thread->callback_ipv4_frag.conf, pcaplist->user);
|
|
|
|
pcaplist->have_ipv4_frag_ctx = 1;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_ipv6_frag.new && !pcaplist->have_ipv6_frag_ctx) {
|
|
|
|
pcaplist->ipv6_frag_ctx = pcap_thread->callback_ipv6_frag.new(pcap_thread->callback_ipv6_frag.conf, pcaplist->user);
|
|
|
|
pcaplist->have_ipv6_frag_ctx = 1;
|
|
|
|
}
|
2025-04-21 09:31:29 +02:00
|
|
|
pcaplist->running = 1;
|
|
|
|
pcaplist->timedrun = timedrun;
|
|
|
|
pcaplist->end = end;
|
2025-02-08 11:57:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
t1.tv_sec = pcap_thread->timeout / 1000;
|
|
|
|
t1.tv_usec = (pcap_thread->timeout % 1000) * 1000;
|
|
|
|
max_fd++;
|
|
|
|
while (run) {
|
|
|
|
rfds = fds;
|
|
|
|
t2 = t1;
|
|
|
|
if (timedrun) {
|
|
|
|
struct timeval now;
|
|
|
|
struct timeval diff;
|
|
|
|
|
|
|
|
if (gettimeofday(&now, 0)) {
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "gettimeofday()");
|
|
|
|
pcap_thread->running = 0;
|
|
|
|
return PCAP_THREAD_ERRNO;
|
|
|
|
}
|
|
|
|
if (now.tv_sec > end.tv_sec
|
|
|
|
|| (now.tv_sec == end.tv_sec && (now.tv_usec * 1000) >= end.tv_nsec)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end.tv_sec > now.tv_sec) {
|
|
|
|
diff.tv_sec = end.tv_sec - now.tv_sec - 1;
|
|
|
|
diff.tv_usec = 1000000 - now.tv_usec;
|
|
|
|
diff.tv_usec += end.tv_nsec / 1000;
|
|
|
|
if (diff.tv_usec > 1000000) {
|
|
|
|
diff.tv_sec += diff.tv_usec / 1000000;
|
|
|
|
diff.tv_usec %= 1000000;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
diff.tv_sec = 0;
|
|
|
|
if (end.tv_sec == now.tv_sec && (end.tv_nsec / 1000) > now.tv_usec) {
|
|
|
|
diff.tv_usec = (end.tv_nsec / 1000) - now.tv_usec;
|
|
|
|
} else {
|
|
|
|
diff.tv_usec = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (diff.tv_sec < t1.tv_sec || (diff.tv_sec == t1.tv_sec && diff.tv_usec < t1.tv_usec)) {
|
|
|
|
t2 = diff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (select(max_fd, &rfds, 0, 0, &t2) == -1) {
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "select()");
|
|
|
|
pcap_thread->running = 0;
|
|
|
|
return PCAP_THREAD_ERRNO;
|
|
|
|
}
|
|
|
|
|
|
|
|
run = 0;
|
|
|
|
for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) {
|
|
|
|
int packets;
|
|
|
|
|
|
|
|
if (!pcaplist->running) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
run = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
packets = pcap_dispatch(pcaplist->pcap, -1, _callback2, (u_char*)pcaplist);
|
2025-04-21 09:31:29 +02:00
|
|
|
if (packets == PCAP_ERROR) {
|
2025-02-08 11:57:11 +01:00
|
|
|
pcap_thread->status = -1;
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_dispatch()");
|
|
|
|
pcap_thread->running = 0;
|
|
|
|
return PCAP_THREAD_EPCAP;
|
2025-04-21 09:31:29 +02:00
|
|
|
}
|
|
|
|
if (pcaplist->is_offline && !packets) {
|
2025-02-08 11:57:11 +01:00
|
|
|
pcaplist->running = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->running = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_next(pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
const u_char* pkt;
|
|
|
|
struct pcap_pkthdr pkthdr;
|
|
|
|
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!pcap_thread->callback && !pcap_thread->use_layers) {
|
|
|
|
return PCAP_THREAD_NOCALLBACK;
|
|
|
|
}
|
|
|
|
if (pcap_thread->use_layers
|
|
|
|
&& !(pcap_thread->callback_linux_sll
|
2025-04-21 09:31:29 +02:00
|
|
|
|| pcap_thread->callback_linux_sll2
|
|
|
|
|| pcap_thread->callback_ether
|
|
|
|
|| pcap_thread->callback_null
|
|
|
|
|| pcap_thread->callback_loop
|
|
|
|
|| pcap_thread->callback_ieee802
|
|
|
|
|| pcap_thread->callback_gre
|
|
|
|
|| pcap_thread->callback_ip
|
|
|
|
|| pcap_thread->callback_ipv4
|
|
|
|
|| pcap_thread->callback_ipv6
|
|
|
|
|| pcap_thread->callback_icmp
|
|
|
|
|| pcap_thread->callback_icmpv6
|
|
|
|
|| pcap_thread->callback_udp
|
|
|
|
|| pcap_thread->callback_tcp)) {
|
2025-02-08 11:57:11 +01:00
|
|
|
return PCAP_THREAD_NOCALLBACK;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
if (!pcap_thread->pcaplist) {
|
|
|
|
return PCAP_THREAD_NOPCAPS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->errbuf[0]) {
|
|
|
|
memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf));
|
|
|
|
}
|
|
|
|
pcap_thread->status = 0;
|
|
|
|
|
|
|
|
if (!pcap_thread->step) {
|
|
|
|
pcap_thread->step = pcap_thread->pcaplist;
|
|
|
|
}
|
|
|
|
if (!pcap_thread->step) {
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->step->pcap_thread = pcap_thread;
|
|
|
|
if (pcap_thread->callback_ipv4_frag.new && !pcap_thread->step->have_ipv4_frag_ctx) {
|
|
|
|
pcap_thread->step->ipv4_frag_ctx = pcap_thread->callback_ipv4_frag.new(pcap_thread->callback_ipv4_frag.conf, pcap_thread->step->user);
|
|
|
|
pcap_thread->step->have_ipv4_frag_ctx = 1;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback_ipv6_frag.new && !pcap_thread->step->have_ipv6_frag_ctx) {
|
|
|
|
pcap_thread->step->ipv6_frag_ctx = pcap_thread->callback_ipv6_frag.new(pcap_thread->callback_ipv6_frag.conf, pcap_thread->step->user);
|
|
|
|
pcap_thread->step->have_ipv6_frag_ctx = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(pkt = pcap_next(pcap_thread->step->pcap, &pkthdr))) {
|
|
|
|
pcap_thread->status = -1;
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_next()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
if (pcap_thread->callback) {
|
|
|
|
pcap_thread->callback(pcap_thread->step->user, &pkthdr, pkt, pcap_thread->step->name, pcap_datalink(pcap_thread->step->pcap));
|
|
|
|
} else {
|
|
|
|
pcap_thread_callback((void*)pcap_thread->step, &pkthdr, pkt, pcap_thread->step->name, pcap_datalink(pcap_thread->step->pcap));
|
|
|
|
}
|
|
|
|
pcap_thread->step = pcap_thread->step->next;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_next_reset(pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (pcap_thread->running) {
|
|
|
|
return PCAP_THREAD_ERUNNING;
|
|
|
|
}
|
|
|
|
if (!pcap_thread->pcaplist) {
|
|
|
|
return PCAP_THREAD_NOPCAPS;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcap_thread->step = 0;
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_thread_stop(pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist;
|
|
|
|
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!pcap_thread->pcaplist) {
|
|
|
|
return PCAP_THREAD_NOPCAPS;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) {
|
|
|
|
pcaplist->running = 0;
|
|
|
|
pcap_breakloop(pcaplist->pcap);
|
|
|
|
}
|
|
|
|
pcap_thread->running = 0;
|
|
|
|
pcap_thread->was_stopped = 1;
|
|
|
|
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
|
|
pthread_cond_broadcast(&(pcap_thread->have_packets));
|
|
|
|
pthread_cond_broadcast(&(pcap_thread->can_write));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stats
|
|
|
|
*/
|
|
|
|
|
|
|
|
int pcap_thread_stats(pcap_thread_t* pcap_thread, pcap_thread_stats_callback_t callback, u_char* user)
|
|
|
|
{
|
|
|
|
pcap_thread_pcaplist_t* pcaplist;
|
|
|
|
struct pcap_stat stats;
|
|
|
|
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return PCAP_THREAD_EINVAL;
|
|
|
|
}
|
|
|
|
if (!callback) {
|
|
|
|
return PCAP_THREAD_NOCALLBACK;
|
|
|
|
}
|
|
|
|
if (!pcap_thread->pcaplist) {
|
|
|
|
return PCAP_THREAD_NOPCAPS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_thread->errbuf[0]) {
|
|
|
|
memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf));
|
|
|
|
}
|
|
|
|
pcap_thread->status = 0;
|
|
|
|
|
|
|
|
for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) {
|
|
|
|
if (pcaplist->is_offline)
|
|
|
|
continue;
|
|
|
|
if ((pcap_thread->status = pcap_stats(pcaplist->pcap, &stats))) {
|
|
|
|
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_stats()");
|
|
|
|
return PCAP_THREAD_EPCAP;
|
|
|
|
}
|
|
|
|
callback(user, &stats, pcaplist->name, pcap_datalink(pcaplist->pcap));
|
|
|
|
}
|
|
|
|
|
|
|
|
return PCAP_THREAD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Error handling
|
|
|
|
*/
|
|
|
|
|
|
|
|
int pcap_thread_status(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->status;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* pcap_thread_errbuf(const pcap_thread_t* pcap_thread)
|
|
|
|
{
|
|
|
|
if (!pcap_thread) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcap_thread->errbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* pcap_thread_strerr(int error)
|
|
|
|
{
|
|
|
|
switch (error) {
|
|
|
|
case PCAP_THREAD_OK:
|
|
|
|
return 0;
|
|
|
|
case PCAP_THREAD_EPCAP:
|
|
|
|
return PCAP_THREAD_EPCAP_STR;
|
|
|
|
case PCAP_THREAD_ENOMEM:
|
|
|
|
return PCAP_THREAD_ENOMEM_STR;
|
|
|
|
case PCAP_THREAD_ENOMON:
|
|
|
|
return PCAP_THREAD_ENOMON_STR;
|
|
|
|
case PCAP_THREAD_ENODIR:
|
|
|
|
return PCAP_THREAD_ENODIR_STR;
|
|
|
|
case PCAP_THREAD_EINVAL:
|
|
|
|
return PCAP_THREAD_EINVAL_STR;
|
|
|
|
case PCAP_THREAD_EWOULDBLOCK:
|
|
|
|
return PCAP_THREAD_EWOULDBLOCK_STR;
|
|
|
|
case PCAP_THREAD_NOPCAPS:
|
|
|
|
return PCAP_THREAD_NOPCAPS_STR;
|
|
|
|
case PCAP_THREAD_NOCALLBACK:
|
|
|
|
return PCAP_THREAD_NOCALLBACK_STR;
|
|
|
|
case PCAP_THREAD_ERRNO:
|
|
|
|
return PCAP_THREAD_ERRNO_STR;
|
|
|
|
case PCAP_THREAD_NOYIELD:
|
|
|
|
return PCAP_THREAD_NOYIELD_STR;
|
|
|
|
case PCAP_THREAD_EOBSOLETE:
|
|
|
|
return PCAP_THREAD_EOBSOLETE_STR;
|
|
|
|
case PCAP_THREAD_ERUNNING:
|
|
|
|
return PCAP_THREAD_ERUNNING_STR;
|
|
|
|
case PCAP_THREAD_ENOPCAPLIST:
|
|
|
|
return PCAP_THREAD_ENOPCAPLIST_STR;
|
|
|
|
case PCAP_THREAD_ELAYERCB:
|
|
|
|
return PCAP_THREAD_ELAYERCB_STR;
|
|
|
|
}
|
|
|
|
return "UNKNOWN";
|
|
|
|
}
|