dnscap/src/pcap-thread/pcap_thread.c

3963 lines
127 KiB
C
Raw Normal View History

/*
* Author Jerry Lundström <jerry@dns-oarc.net>
* Copyright (c) 2016-2025 OARC, Inc.
* 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);
static void pcap_thread_callback_linux_sll2(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length);
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;
}
if (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) {
return PCAP_THREAD_ELAYERCB;
}
if (pcap_thread->running) {
return PCAP_THREAD_ERUNNING;
}
pcap_thread->callback_linux_sll = callback_linux_sll;
return PCAP_THREAD_OK;
}
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;
}
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
|| pcap_thread->callback_linux_sll2
|| 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
|| pcap_thread->callback_linux_sll2
|| 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
|| pcap_thread->callback_linux_sll2
|| 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
|| pcap_thread->callback_linux_sll2
|| 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
|| pcap_thread->callback_linux_sll2
|| 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
|| 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_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
|| 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_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
|| 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_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
|| 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) {
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
|| 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) {
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
|| 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) {
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
|| 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) {
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;
/*
* The header for null is in host byte order but may not be
* in the same endian as host if coming from a savefile
*/
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;
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
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);
}
}
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);
}
}
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);
if (ret == PCAP_ERROR) {
/* 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;
}
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);
}
}
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
|| 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)) {
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;
}
pcaplist->running = 1;
pcaplist->timedrun = timedrun;
pcaplist->end = end;
}
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);
if (packets == PCAP_ERROR) {
pcap_thread->status = -1;
PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_dispatch()");
pcap_thread->running = 0;
return PCAP_THREAD_EPCAP;
}
if (pcaplist->is_offline && !packets) {
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
|| 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)) {
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";
}