#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define PCAP_DONT_INCLUDE_PCAP_BPF_H
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/kern_event.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/utsname.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/bpf.h>
#include <net/pktap.h>
#include <net/iptap.h>
#include <fcntl.h>
#include <errno.h>
#include <libproc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
#include <assert.h>
#include "pcap-int.h"
#include "pcap-util.h"
#include "pcap-pktap.h"
static int pcap_cleanup_pktap_interface_internal(const char *ifname, char *ebuf);
#define AUTO_CLONE_IF_DESCRIPTION "libpcap auto cloned device"
#define AUTO_CLONE_IF_DESC_LEN (sizeof(AUTO_CLONE_IF_DESCRIPTION) -1)
#define _CASSERT(x) _Static_assert(x, "compile-time assertion failed " #x)
_CASSERT(offsetof(struct bpf_hdr_ext, bh_tstamp) == offsetof(struct bpf_hdr, bh_tstamp));
_CASSERT(offsetof(struct bpf_hdr_ext, bh_caplen) == offsetof(struct bpf_hdr, bh_caplen));
_CASSERT(offsetof(struct bpf_hdr_ext, bh_datalen) == offsetof(struct bpf_hdr, bh_datalen));
_CASSERT(offsetof(struct bpf_hdr_ext, bh_hdrlen) == offsetof(struct bpf_hdr, bh_hdrlen));
_CASSERT(MAXIMUM_SNAPLEN == BPF_MAXBUFSIZE);
static int
pcap_get_if_attach_count(const char *ifname, char *errbuf)
{
int fd;
int n = 0;
char device[sizeof "/dev/bpf0000000000"];
struct ifreq ifr;
int count = -1;
do {
(void)snprintf(device, sizeof(device), "/dev/bpf%d", n++);
fd = open(device, O_RDONLY);
} while (fd < 0 && errno == EBUSY);
if (fd >= 0) {
bzero(&ifr, sizeof(ifr));
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(fd, BIOCGIFATTACHCOUNT, &ifr) == -1) {
snprintf(errbuf, PCAP_ERRBUF_SIZE, "ioctl BIOCGIFATTACHCOUNT %s failed - %s",
ifname, strerror(errno));
} else {
count = ifr.ifr_intval;
}
close(fd);
}
return (count);
}
static int
pcap_cleanup_pktap_interface_internal(const char *ifname, char *ebuf)
{
int s = -1;
struct if_descreq if_descreq;
struct ifreq ifr;
int status = 0;
if (ifname != NULL) {
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: socket failed - %s",
__func__, strerror(errno));
goto failed;
} else {
bzero(&if_descreq, sizeof(struct if_descreq));
strlcpy(if_descreq.ifdr_name, ifname, sizeof(if_descreq.ifdr_name));
if (ioctl(s, SIOCGIFDESC, &if_descreq) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: ioctl SIOCGIFDESC %s - %s",
__func__, ifname, strerror(errno));
goto failed;
}
if (if_descreq.ifdr_len == 0) {
goto done;
}
if (strncmp((char *)if_descreq.ifdr_desc, AUTO_CLONE_IF_DESCRIPTION,
AUTO_CLONE_IF_DESC_LEN) != 0) {
goto done;
}
if (pcap_get_if_attach_count(ifname, ebuf) != 1) {
goto done;
}
bzero(&ifr, sizeof(struct ifreq));
strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: ioctl(SIOCIFDESTROY) fail - %s",
__func__, strerror(errno));
goto failed;
}
}
}
done:
if (s != -1) {
close(s);
}
return (status);
failed:
status = -1;
goto done;
}
void
pcap_cleanup_pktap_interface(const char *ifname)
{
char errbuf[PCAP_ERRBUF_SIZE];
if (pcap_cleanup_pktap_interface_internal(ifname, errbuf) != 0) {
fprintf(stderr, "%s\n", errbuf);
}
}
char *
pcap_setup_pktap_interface(const char *device, char *ebuf)
{
struct ifreq ifr;
int s = -1;
struct if_nameindex *ifnameindices = NULL, *ifnameindex;
int foundmatch = 0;
struct if_descreq if_descreq;
char *pktap_param = NULL;
int unit = -1;
const char *if_prefix = NULL;
char *ifname = NULL;
ifname = calloc(1, PKTAP_IFXNAMESIZE);
if (ifname == NULL) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc(): %s",
pcap_strerror(errno));
goto fail;
}
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "socket(): %s",
pcap_strerror(errno));
goto fail;
}
if (strncmp(device, PKTAP_IFNAME, strlen(PKTAP_IFNAME)) == 0) {
size_t tocopy;
if_prefix = PKTAP_IFNAME;
pktap_param = strchr(device, ',');
if (pktap_param != NULL)
tocopy = pktap_param - device;
else
tocopy = strlen(device);
if (tocopy + 1 > PKTAP_IFXNAMESIZE) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "device name too long: %s",
pcap_strerror(errno));
goto fail;
}
bcopy(device, ifname, tocopy);
ifname[tocopy] = 0;
sscanf(ifname, PKTAP_IFNAME "%d", &unit);
} else if (strcmp(device, "all") == 0 || strcmp(device, "any") == 0) {
if_prefix = PKTAP_IFNAME;
pktap_param = "all";
unit = -1;
} else if (strncmp(device, IPTAP_IFNAME, strlen(IPTAP_IFNAME)) == 0) {
if_prefix = IPTAP_IFNAME;
if (strlcpy(ifname, device, PKTAP_IFXNAMESIZE) >= PKTAP_IFXNAMESIZE) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "device name too long: %s",
pcap_strerror(errno));
goto fail;
}
sscanf(ifname, IPTAP_IFNAME "%d", &unit);
} else {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "bad device name: %s",
pcap_strerror(errno));
goto fail;
}
if (unit == -1) {
int desclen;
if ((ifnameindices = if_nameindex()) == NULL) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "if_nameindex: %s",
pcap_strerror(errno));
goto fail;
}
for (ifnameindex = ifnameindices; ifnameindex->if_index != 0; ifnameindex++) {
if (strncmp(ifnameindex->if_name, if_prefix, strlen(if_prefix)) != 0)
continue;
bzero(&if_descreq, sizeof(struct if_descreq));
strlcpy(if_descreq.ifdr_name, ifnameindex->if_name, sizeof(if_descreq.ifdr_name));
if (ioctl(s, SIOCGIFDESC, &if_descreq) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "ioctl(SIOCGIFDESC): %s",
pcap_strerror(errno));
goto fail;
}
if (if_descreq.ifdr_len == 0)
continue;
if (strncmp((const char *)if_descreq.ifdr_desc, AUTO_CLONE_IF_DESCRIPTION,
AUTO_CLONE_IF_DESC_LEN) != 0)
continue;
if (pcap_get_if_attach_count(ifnameindex->if_name, ebuf) != 0) {
ebuf[0] = 0;
continue;
}
strlcpy(ifname, ifnameindex->if_name, PKTAP_IFXNAMESIZE);
foundmatch = 1;
}
if (foundmatch == 0) {
memset(&ifr, 0, sizeof(ifr));
(void) strlcpy(ifr.ifr_name, if_prefix, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCIFCREATE, &ifr) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "ioctl(SIOCIFCREATE): %s",
pcap_strerror(errno));
goto fail;
}
snprintf(ifname, PKTAP_IFXNAMESIZE, "%s", ifr.ifr_name);
}
bzero(&if_descreq, sizeof(struct if_descreq));
strlcpy(if_descreq.ifdr_name, ifname, sizeof(if_descreq.ifdr_name));
desclen = snprintf((char *)if_descreq.ifdr_desc, sizeof (if_descreq.ifdr_desc),
"%s - %s.%d", AUTO_CLONE_IF_DESCRIPTION, getprogname(), getpid());
if (desclen < sizeof(if_descreq.ifdr_desc))
if_descreq.ifdr_len = desclen + 1;
else
if_descreq.ifdr_len = sizeof(if_descreq.ifdr_desc);
if (ioctl(s, SIOCSIFDESC, &if_descreq) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "ioctl(SIOCSIFDESC): %s",
pcap_strerror(errno));
goto fail;
}
}
if (pktap_param != NULL) {
int num_filter_entries = 0;
struct pktap_filter pktap_if_filter[PKTAP_MAX_FILTERS];
bzero(pktap_if_filter, sizeof(pktap_if_filter));
while (*pktap_param != '\0') {
char *end_ptr;
struct pktap_filter entry;
size_t len;
bzero(&entry, sizeof(struct pktap_filter));
if (*pktap_param == ',') {
pktap_param++;
continue;
}
if (num_filter_entries >= PKTAP_MAX_FILTERS) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"Too many pktap parameters, max is %u", PKTAP_MAX_FILTERS);
goto fail;
}
end_ptr = strchr(pktap_param, ',');
if (end_ptr == NULL)
len = strlen(pktap_param);
else
len = end_ptr - pktap_param;
if (len > sizeof(entry.filter_param_if_name) - 1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"Interface name too big for filter");
goto fail;
}
if (strcmp(pktap_param, "all") == 0 || strcmp(pktap_param, "any") == 0) {
entry.filter_op = PKTAP_FILTER_OP_PASS;
entry.filter_param = PKTAP_FILTER_PARAM_IF_TYPE;
entry.filter_param_if_type = 0;
} else {
entry.filter_op = PKTAP_FILTER_OP_PASS;
entry.filter_param = PKTAP_FILTER_PARAM_IF_NAME;
strncpy(entry.filter_param_if_name, pktap_param,
MIN(sizeof(entry.filter_param_if_name), len));
}
pktap_if_filter[num_filter_entries] = entry;
num_filter_entries++;
pktap_param += len;
}
if (num_filter_entries > 0) {
struct ifdrv ifdr;
bzero(&ifdr, sizeof(struct ifdrv));
snprintf(ifdr.ifd_name, sizeof(ifdr.ifd_name), "%s", ifname);
ifdr.ifd_cmd = PKTP_CMD_FILTER_SET;
ifdr.ifd_len = sizeof(pktap_if_filter);
ifdr.ifd_data = &pktap_if_filter[0];
if (ioctl(s, SIOCSDRVSPEC, &ifdr) == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "ioctl(SIOCSDRVSPEC): %s",
pcap_strerror(errno));
goto fail;
}
}
}
cleanup:
if (ifnameindices != NULL)
if_freenameindex(ifnameindices);
if (s != -1)
close(s);
return (ifname);
fail:
if (ifname != NULL) {
free(ifname);
ifname = NULL;
}
goto cleanup;;
}
void
pktap_cleanup(pcap_t *p)
{
char errbuf[PCAP_ERRBUF_SIZE];
if (p->cleanup_interface_op != NULL)
p->cleanup_interface_op(p->opt.device, errbuf);
p->pktap_cleanup_op(p);
}
int
pktap_activate(pcap_t *p)
{
int status = 0;
free(p->opt.device);
p->opt.device = p->pktap_ifname;
p->pktap_ifname = NULL;
if (p->snapshot < sizeof(struct pktap_header)) {
p->snapshot = MAXIMUM_SNAPLEN;
}
status = p->pktap_activate_op(p);
if (status != 0)
return (status);
p->pktap_cleanup_op = p->cleanup_op;
p->cleanup_op = pktap_cleanup;
return (status);
}
pcap_t *
pktap_create(const char *device, char *ebuf, int *is_ours)
{
pcap_t *p = NULL;
char *ifname = NULL;
if (device == NULL)
device = "pktap";
if (strncmp(device, PKTAP_IFNAME, strlen(PKTAP_IFNAME)) != 0 &&
strncmp(device, IPTAP_IFNAME, strlen(IPTAP_IFNAME)) != 0 &&
strcmp(device, "all") != 0 &&
strcmp(device, "any") != 0) {
*is_ours = 0;
return (NULL);
}
*is_ours = 1;
p = pcap_create_interface(ebuf, 0);
if (p == NULL)
goto failed;
ifname = pcap_setup_pktap_interface(device, ebuf);
if (ifname == NULL)
goto failed;
p->pktap_ifname = ifname;
p->cleanup_interface_op = pcap_cleanup_pktap_interface_internal;
p->pktap_activate_op = p->activate_op;
p->activate_op = pktap_activate;
return (p);
failed:
if (p != NULL)
pcap_close(p);
if (ifname != NULL)
pcap_cleanup_pktap_interface_internal(ifname, ebuf);
return (NULL);
}
static int
pcap_filter_pktap(pcap_t *pcap, pcap_dumper_t *dumper, struct pcap_if_info *if_info,
const struct pcap_pkthdr *h, const u_char *sp)
{
struct pktap_header *pktp_hdr;
const u_char *pkt_data;
int match = 0;
pktp_hdr = (struct pktap_header *)sp;
if (h->len < sizeof(struct pktap_header) ||
h->caplen < sizeof(struct pktap_header) ||
pktp_hdr->pth_length > h->caplen) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: Packet too short", __func__);
return (0);
}
if (if_info == NULL) {
if_info = pcap_if_info_set_find_by_name(&dumper->dump_if_info_set, pktp_hdr->pth_ifname);
if (if_info == NULL) {
if_info = pcap_if_info_set_add(&dumper->dump_if_info_set, pktp_hdr->pth_ifname, -1,
pktp_hdr->pth_dlt, pcap->snapshot,
pcap->filter_str, pcap->errbuf);
if (if_info == NULL) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_add_if_info(%s) failed",
__func__, pktp_hdr->pth_ifname);
return (0);
}
}
}
if (if_info->if_filter_program.bf_insns == NULL)
match = 1;
else {
struct pcap_pkthdr tmp_hdr;
bcopy(h, &tmp_hdr, sizeof(struct pcap_pkthdr));
tmp_hdr.caplen -= pktp_hdr->pth_length;
tmp_hdr.len -= pktp_hdr->pth_length;
pkt_data = sp + pktp_hdr->pth_length;
match = pcap_offline_filter(&if_info->if_filter_program, &tmp_hdr, pkt_data);
}
return (match);
}
static int
pcapng_dump_shb(pcap_t *pcap, pcap_dumper_t *dumper)
{
pcapng_block_t block = NULL;
int retval;
static struct utsname utsname;
static struct proc_bsdshortinfo bsdinfo;
static int info_done = 0;
if (info_done == 0) {
if (uname(&utsname) == -1) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: uname() failed", __func__);
return (0);
}
if (proc_pidinfo(getpid(), PROC_PIDT_SHORTBSDINFO, 1, &bsdinfo, sizeof(bsdinfo)) < 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: proc_pidinfo(PROC_PIDT_SHORTBSDINFO) failed", __func__);
return (0);
}
info_done = 1;
}
if (dumper->dump_block == NULL) {
dumper->dump_block = pcap_ng_block_alloc(pcap->snapshot + 4096);
if (dumper->dump_block == NULL) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_alloc() failed ", __func__);
return (0);
}
}
block = dumper->dump_block;
if (pcap->shb_added == 0 || dumper->shb_added == 0) {
char buf[256];
retval = pcap_ng_block_reset(block, PCAPNG_BT_SHB);
if (retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_reset(PCAPNG_BT_SHB) failed", __func__);
return (0);
}
retval = pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT,
"section header block");
if(retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_add_option_with_string(PCAPNG_OPT_COMMENT) failed", __func__);
return (0);
}
retval = pcap_ng_block_add_option_with_string(block, PCAPNG_SHB_HARDWARE,
utsname.machine);
if(retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_add_option_with_string(PCAPNG_SHB_HARDWARE) failed", __func__);
return (0);
}
snprintf(buf, sizeof(buf), "%s %s", utsname.sysname, utsname.release);
retval = pcap_ng_block_add_option_with_string(block, PCAPNG_SHB_OS, buf);
if(retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_add_option_with_string(PCAPNG_SHB_OS) failed", __func__);
return (0);
}
snprintf(buf, sizeof(buf), "%s (%s)", bsdinfo.pbsi_comm, pcap_lib_version());
retval = pcap_ng_block_add_option_with_string(block, PCAPNG_SHB_USERAPPL, buf);
if(retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_add_option_with_string(PCAPNG_SHB_USERAPPL) failed", __func__);
return (0);
}
(void) pcap_ng_dump_block(dumper, block);
pcap->shb_added = 1;
dumper->shb_added = 1;
}
return (1);
}
struct pcap_proc_info *
pcap_ng_dump_proc_info(pcap_t *pcap, pcap_dumper_t *dumper, pcapng_block_t block,
struct pcap_proc_info *proc_info)
{
int retval;
struct pcapng_process_information_fields *pib;
if (proc_info->proc_block_dumped != 0)
return (proc_info);
retval = pcap_ng_block_reset(block, PCAPNG_BT_PIB);
if (retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_reset(PCAPNG_BT_PIB) failed", __func__);
return (NULL);
}
pib = pcap_ng_get_process_information_fields(block);
pib->process_id = proc_info->proc_pid;
if (pcap_ng_block_add_option_with_string(block, PCAPNG_PIB_NAME, proc_info->proc_name) != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_add_option_with_string(PCAPNG_PIB_NAME, %s) failed",
__func__, proc_info->proc_name);
return (NULL);
}
if (uuid_is_null(proc_info->proc_uuid) == 0) {
if (pcap_ng_block_add_option_with_uuid(block, PCAPNG_PIB_UUID, proc_info->proc_uuid) != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_add_option_with_uuid(PCAPNG_PIB_UUID) failed",
__func__);
return (NULL);
}
}
(void) pcap_ng_dump_block(dumper, block);
proc_info->proc_block_dumped = 1;
proc_info->proc_dump_index = dumper->dump_proc_info_set.proc_dump_index++;
return (proc_info);
}
static struct pcap_proc_info *
pcap_ng_dump_proc(pcap_t *pcap, pcap_dumper_t *dumper, pcapng_block_t block,
pid_t pid, const char *pcomm, const uuid_t uu)
{
struct pcap_proc_info *proc_info;
proc_info = pcap_proc_info_set_find_uuid(&dumper->dump_proc_info_set, pid, pcomm, uu);
if (proc_info == NULL) {
proc_info = pcap_proc_info_set_add_uuid(&dumper->dump_proc_info_set, pid, pcomm,
uu, pcap->errbuf);
if (proc_info == NULL) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: allocate_proc_info(%s) failed",
__func__, pcomm);
return (NULL);
}
}
proc_info = pcap_ng_dump_proc_info(pcap, dumper, block, proc_info);
return (proc_info);
}
int
pcap_ng_dump_kern_event(pcap_t *pcap, pcap_dumper_t *dumper,
struct kern_event_msg *kev, struct timeval *ts)
{
int retval;
pcapng_block_t block = NULL;
struct pcapng_os_event_fields *osev_fields;
if (pcapng_dump_shb(pcap, dumper) == 0)
return (0);
block = dumper->dump_block;
retval = pcap_ng_block_reset(block, PCAPNG_BT_OSEV);
if (retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_reset(PCAPNG_BT_OSEV) failed", __func__);
return (0);
}
osev_fields = pcap_ng_get_os_event_fields(block);
osev_fields->type = PCAPNG_OSEV_KEV;
osev_fields->timestamp_high = (u_int32_t)ts->tv_sec;
osev_fields->timestamp_low = ts->tv_usec;
osev_fields->len = kev->total_size;
pcap_ng_block_packet_set_data(block, kev, kev->total_size);
(void) pcap_ng_dump_block(dumper, block);
return (1);
}
struct pcap_if_info *
pcap_ng_dump_if_info(pcap_t *pcap, pcap_dumper_t *dumper, pcapng_block_t block,
struct pcap_if_info *if_info)
{
int retval;
struct pcapng_interface_description_fields *idb = NULL;
if (if_info->if_block_dumped != 0) {
return (if_info);
}
retval = pcap_ng_block_reset(block, PCAPNG_BT_IDB);
if (retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_reset(PCAPNG_BT_IDB) failed", __func__);
return (0);
}
idb = pcap_ng_get_interface_description_fields(block);
idb->idb_linktype = dlt_to_linktype(if_info->if_linktype);
idb->idb_snaplen = if_info->if_snaplen;
if (pcap_ng_block_add_option_with_string(block, PCAPNG_IF_NAME,
if_info->if_name) != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_add_option_with_string(PCAPNG_IF_NAME, %s) failed",
__func__, if_info->if_name);
return (0);
}
(void) pcap_ng_dump_block(dumper, block);
if_info->if_block_dumped = 1;
if_info->if_dump_id = dumper->dump_if_info_set.if_dump_id++;
return (if_info);
}
int
pcap_ng_dump_pktap_comment(pcap_t *pcap, pcap_dumper_t *dumper,
const struct pcap_pkthdr *h, const u_char *sp,
const char *comment)
{
pcapng_block_t block = NULL;
struct pktap_header *pktp_hdr;
const u_char *pkt_data;
struct pcap_if_info *if_info = NULL;
struct pcapng_enhanced_packet_fields *epb;
uint64_t ts;
struct pcap_proc_info *proc_info = NULL;
struct pcap_proc_info *e_proc_info = NULL;
uint32_t pktflags = 0;
uint32_t pmdflags = 0;
int retval;
pktp_hdr = (struct pktap_header *)sp;
if (h->len < sizeof(struct pktap_header) ||
h->caplen < sizeof(struct pktap_header) ||
pktp_hdr->pth_length > h->caplen) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: Packet too short", __func__);
return (0);
}
if (pcapng_dump_shb(pcap, dumper) == 0)
return (0);
block = dumper->dump_block;
if_info = pcap_if_info_set_find_by_name(&dumper->dump_if_info_set, pktp_hdr->pth_ifname);
if (if_info == NULL) {
if_info = pcap_if_info_set_add(&dumper->dump_if_info_set, pktp_hdr->pth_ifname, -1,
pktp_hdr->pth_dlt, pcap->snapshot,
pcap->filter_str, pcap->errbuf);
if (if_info == NULL) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_add_if_info(%s) failed",
__func__, pktp_hdr->pth_ifname);
return (0);
}
}
if (pcap_filter_pktap(pcap, dumper, if_info, h, sp) == 0)
return (0);
if_info = pcap_ng_dump_if_info(pcap, dumper, block, if_info);
if (if_info == NULL) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_dump_if_info(%s) failed",
__func__, pktp_hdr->pth_ifname);
return (0);
}
if ((pktp_hdr->pth_pid != -1 && pktp_hdr->pth_pid != 0) ||
pktp_hdr->pth_comm[0] != 0 || uuid_is_null(pktp_hdr->pth_uuid) == 0) {
proc_info = pcap_ng_dump_proc(pcap, dumper, block,
pktp_hdr->pth_pid, pktp_hdr->pth_comm, pktp_hdr->pth_uuid);
if (proc_info == NULL)
return (0);
}
if ((pktp_hdr->pth_epid != -1 && pktp_hdr->pth_epid != 0) ||
pktp_hdr->pth_ecomm[0] != 0 || uuid_is_null(pktp_hdr->pth_euuid) == 0) {
e_proc_info = pcap_ng_dump_proc(pcap, dumper, block,
pktp_hdr->pth_epid, pktp_hdr->pth_ecomm, pktp_hdr->pth_euuid);
if (e_proc_info == NULL)
return (0);
}
retval = pcap_ng_block_reset(block, PCAPNG_BT_EPB);
if (retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_reset(PCAPNG_BT_EPB) failed", __func__);
return (0);
}
pkt_data = sp + pktp_hdr->pth_length;
epb = pcap_ng_get_enhanced_packet_fields(block);
epb->caplen = h->caplen - pktp_hdr->pth_length;
epb->interface_id = if_info->if_dump_id;
epb->len = h->len - pktp_hdr->pth_length;
ts = ((uint64_t)h->ts.tv_sec) * 1000000 + (uint64_t)h->ts.tv_usec;
epb->timestamp_high = ts >> 32;
epb->timestamp_low = ts & 0xffffffff;
pcap_ng_block_packet_set_data(block, pkt_data, epb->caplen);
if (proc_info != NULL)
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_PIB_INDEX, &proc_info->proc_dump_index, 4);
if (e_proc_info != NULL)
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_E_PIB_INDEX, &e_proc_info->proc_dump_index, 4);
if ((pktp_hdr->pth_flags & PTH_FLAG_DIR_IN))
pktflags = PCAPNG_PBF_DIR_INBOUND;
else if ((pktp_hdr->pth_flags & PTH_FLAG_DIR_OUT))
pktflags = PCAPNG_PBF_DIR_OUTBOUND;
if (pktflags != 0)
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_FLAGS , &pktflags, 4);
if (pktp_hdr->pth_svc != -1)
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_SVC , &pktp_hdr->pth_svc, 4);
if (comment != NULL)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
if (pktp_hdr->pth_flags & PTH_FLAG_NEW_FLOW) {
pmdflags |= PCAPNG_EPB_PMDF_NEW_FLOW;
}
#ifdef PTH_FLAG_REXMIT
if (pktp_hdr->pth_flags & PTH_FLAG_REXMIT) {
pmdflags |= PCAPNG_EPB_PMDF_REXMIT;
}
#endif
#ifdef PTH_FLAG_KEEP_ALIVE
if (pktp_hdr->pth_flags & PTH_FLAG_KEEP_ALIVE) {
pmdflags |= PCAPNG_EPB_PMDF_KEEP_ALIVE;
}
#endif
#ifdef PTH_FLAG_SOCKET
if (pktp_hdr->pth_flags & PTH_FLAG_SOCKET) {
pmdflags |= PCAPNG_EPB_PMDF_SOCKET;
}
#endif
#ifdef PTH_FLAG_NEXUS_CHAN
if (pktp_hdr->pth_flags & PTH_FLAG_NEXUS_CHAN) {
pmdflags |= PCAPNG_EPB_PMDF_NEXUS_CHANNEL;
}
#endif
if (pmdflags != 0) {
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_PMD_FLAGS, &pmdflags, 4);
}
(void) pcap_ng_dump_block(dumper, block);
return (1);
}
int
pcap_ng_dump_pktap(pcap_t *pcap, pcap_dumper_t *dumper,
const struct pcap_pkthdr *h, const u_char *sp)
{
return (pcap_ng_dump_pktap_comment(pcap, dumper, h, sp, NULL));
}
int
pcap_ng_dump_decryption_secrets(pcap_t *pcap, pcap_dumper_t *dumper,
const uint32_t type, const size_t len, const uint8_t *sp)
{
int retval;
pcapng_block_t block = NULL;
struct pcapng_decryption_secrets_fields *dsb_fields;
if (len > UINT32_MAX) {
return 0;
}
if (pcapng_dump_shb(pcap, dumper) == 0)
return (0);
block = dumper->dump_block;
retval = pcap_ng_block_reset(block, PCAPNG_BT_DSB);
if (retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_reset(PCAPNG_BT_DSB) failed", __func__);
return (0);
}
dsb_fields = pcap_ng_get_decryption_secrets_fields(block);
dsb_fields->secrets_type = type;
dsb_fields->secrets_length = (uint32_t)len;
pcap_ng_block_packet_set_data(block, sp, (uint32_t)len);
(void) pcap_ng_dump_block(dumper, block);
return (1);
}
int
pcap_apple_set_exthdr(pcap_t *p, int v)
{
int status = -1;
#ifdef BIOCSEXTHDR
if (ioctl(p->fd, BIOCSEXTHDR, (caddr_t)&v) < 0) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSEXTHDR: %s",
pcap_strerror(errno));
status = PCAP_ERROR;
} else {
p->extendedhdr = !!v;
status = 0;
}
#endif
return (status);
}
int
pcap_set_want_pktap(pcap_t *p, int v)
{
p->wantpktap = !!v;
return (0);
}
int
pcap_set_truncation_mode(pcap_t *p, bool on)
{
int status = PCAP_ERROR;
#ifdef BIOCSTRUNCATE
p->truncation = on;
status = 0;
#endif
return (status);
}
int
pcap_set_pktap_hdr_v2(pcap_t *p, bool on)
{
int status = PCAP_ERROR;
#ifdef BIOCSPKTHDRV2
p->pktaphdrv2 = on;
status = 0;
#endif
return (status);
}
static char *
pcap_svc2str(uint32_t svc)
{
static char svcstr[10];
switch (svc) {
case SO_TC_BK_SYS:
return "BK_SYS";
case SO_TC_BK:
return "BK";
case SO_TC_BE:
return "BE";
case SO_TC_RD:
return "RD";
case SO_TC_OAM:
return "OAM";
case SO_TC_AV:
return "AV";
case SO_TC_RV:
return "RV";
case SO_TC_VI:
return "VI";
case SO_TC_VO:
return "VO";
case SO_TC_CTL:
return "CTL";
default:
snprintf(svcstr, sizeof(svcstr), "%u", svc);
return svcstr;
}
}
void
pcap_read_bpf_header(pcap_t *p, u_char *bp, struct pcap_pkthdr *pkthdr)
{
struct bpf_hdr_ext *bhep = ((struct bpf_hdr_ext *)bp);
char tmpbuf[100];
int tlen;
pkthdr->comment[0] = 0;
if (p->extendedhdr == 0)
return;
if (bhep->bh_comm[0] != 0) {
bzero(&tmpbuf, sizeof (tmpbuf));
tlen = snprintf(tmpbuf, sizeof (tmpbuf),
"pid %s.%d svc %s", bhep->bh_comm,
bhep->bh_pid, pcap_svc2str(bhep->bh_svc));
if (tlen > 0)
strlcat(pkthdr->comment,
tmpbuf,
sizeof (pkthdr->comment));
}
if (bhep->bh_pktflags > 0) {
bzero(&tmpbuf, sizeof (tmpbuf));
tlen = snprintf(tmpbuf, sizeof (tmpbuf),
" pktflags 0x%x",
bhep->bh_pktflags);
if (tlen > 0)
strlcat(pkthdr->comment,
tmpbuf,
sizeof (pkthdr->comment));
}
if (bhep->bh_unsent_bytes > 0) {
bzero(&tmpbuf, sizeof (tmpbuf));
tlen = snprintf(tmpbuf, sizeof (tmpbuf),
" unsent %u",
bhep->bh_unsent_bytes);
if (tlen > 0)
strlcat(pkthdr->comment,
tmpbuf,
sizeof (pkthdr->comment));
}
}
#ifdef PTH_FLAG_V2_HDR
static int
pcap_filter_pktap_v2(pcap_t *pcap, pcap_dumper_t *dumper, struct pcap_if_info *if_info,
const struct pcap_pkthdr *h, const u_char *sp)
{
struct pktap_v2_hdr *pktap_v2_hdr;
const u_char *pkt_data;
int match = 0;
const char *ifname;
pktap_v2_hdr = (struct pktap_v2_hdr *)sp;
if (h->len < sizeof(struct pktap_v2_hdr) ||
h->caplen < sizeof(struct pktap_v2_hdr) ||
pktap_v2_hdr->pth_length > h->caplen) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: Packet too short", __func__);
return (0);
}
if (pktap_v2_hdr->pth_ifname_offset == 0) {
return (0);
}
ifname = ((char *) pktap_v2_hdr) + pktap_v2_hdr->pth_ifname_offset;
if (if_info == NULL) {
if_info = pcap_if_info_set_find_by_name(&dumper->dump_if_info_set, ifname);
if (if_info == NULL) {
if_info = pcap_if_info_set_add(&dumper->dump_if_info_set, ifname, -1,
pktap_v2_hdr->pth_dlt, pcap->snapshot,
pcap->filter_str, pcap->errbuf);
if (if_info == NULL) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_add_if_info(%s) failed",
__func__, ifname);
return (0);
}
}
}
if (if_info->if_filter_program.bf_insns == NULL) {
match = 1;
} else {
struct pcap_pkthdr tmp_hdr;
bcopy(h, &tmp_hdr, sizeof(struct pcap_pkthdr));
tmp_hdr.caplen -= pktap_v2_hdr->pth_length;
tmp_hdr.len -= pktap_v2_hdr->pth_length;
pkt_data = sp + pktap_v2_hdr->pth_length;
match = pcap_offline_filter(&if_info->if_filter_program, &tmp_hdr, pkt_data);
}
return (match);
}
#endif
int
pcap_ng_dump_pktap_v2(pcap_t *pcap, pcap_dumper_t *dumper,
const struct pcap_pkthdr *h, const u_char *sp,
const char *comment)
{
#ifdef PTH_FLAG_V2_HDR
pcapng_block_t block = NULL;
struct pktap_v2_hdr *pktap_v2_hdr;
const u_char *pkt_data;
struct pcap_if_info *if_info = NULL;
struct pcapng_enhanced_packet_fields *epb;
uint64_t ts;
struct pcap_proc_info *proc_info = NULL;
struct pcap_proc_info *e_proc_info = NULL;
uint32_t pktflags = 0;
uint32_t pmdflags = 0;
int retval;
const char *ifname = NULL;
const char *comm = NULL;
const uuid_t *uuid = NULL;
const char *e_comm = NULL;
const uuid_t *e_uuid = NULL;
pktap_v2_hdr = (struct pktap_v2_hdr *)sp;
if (h->len < sizeof(struct pktap_v2_hdr) ||
h->caplen < sizeof(struct pktap_v2_hdr) ||
pktap_v2_hdr->pth_length > h->caplen) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: Packet too short", __func__);
return (0);
}
if (pktap_v2_hdr->pth_ifname_offset == 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: No ifame", __func__);
return (0);
}
ifname = ((char *) pktap_v2_hdr) + pktap_v2_hdr->pth_ifname_offset;
if (pcapng_dump_shb(pcap, dumper) == 0)
return (0);
block = dumper->dump_block;
if_info = pcap_if_info_set_find_by_name(&dumper->dump_if_info_set, ifname);
if (if_info == NULL) {
if_info = pcap_if_info_set_add(&dumper->dump_if_info_set, ifname, -1,
pktap_v2_hdr->pth_dlt, pcap->snapshot,
pcap->filter_str, pcap->errbuf);
if (if_info == NULL) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_add_if_info(%s) failed",
__func__, ifname);
return (0);
}
}
if (pcap_filter_pktap_v2(pcap, dumper, if_info, h, sp) == 0)
return (0);
if_info = pcap_ng_dump_if_info(pcap, dumper, block, if_info);
if (if_info == NULL) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_dump_if_info(%s) failed",
__func__, ifname);
return (0);
}
if (pktap_v2_hdr->pth_comm_offset != 0)
comm = ((char *) pktap_v2_hdr) + pktap_v2_hdr->pth_comm_offset;
if (pktap_v2_hdr->pth_uuid_offset != 0)
uuid = (uuid_t *)(((char *) pktap_v2_hdr) + pktap_v2_hdr->pth_uuid_offset);
if ((pktap_v2_hdr->pth_pid != 0 && pktap_v2_hdr->pth_pid != -1) ||
comm != NULL || uuid != NULL) {
proc_info = pcap_ng_dump_proc(pcap, dumper, block,
pktap_v2_hdr->pth_pid,
comm,
*uuid);
if (proc_info == NULL)
return (0);
}
if (pktap_v2_hdr->pth_e_comm_offset != 0)
e_comm = ((char *) pktap_v2_hdr) + pktap_v2_hdr->pth_e_comm_offset;
if (pktap_v2_hdr->pth_e_uuid_offset != 0)
e_uuid = (uuid_t *)(((char *) pktap_v2_hdr) + pktap_v2_hdr->pth_e_uuid_offset);
if ((pktap_v2_hdr->pth_e_pid != 0 && pktap_v2_hdr->pth_e_pid != -1) ||
e_comm != NULL || e_uuid != NULL) {
e_proc_info = pcap_ng_dump_proc(pcap, dumper, block,
pktap_v2_hdr->pth_e_pid,
e_comm,
*e_uuid);
if (e_proc_info == NULL)
return (0);
}
retval = pcap_ng_block_reset(block, PCAPNG_BT_EPB);
if (retval != 0) {
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: pcap_ng_block_reset(PCAPNG_BT_EPB) failed", __func__);
return (0);
}
pkt_data = sp + pktap_v2_hdr->pth_length;
epb = pcap_ng_get_enhanced_packet_fields(block);
epb->caplen = h->caplen - pktap_v2_hdr->pth_length;
epb->interface_id = if_info->if_dump_id;
epb->len = h->len - pktap_v2_hdr->pth_length;
ts = ((uint64_t)h->ts.tv_sec) * 1000000 + (uint64_t)h->ts.tv_usec;
epb->timestamp_high = ts >> 32;
epb->timestamp_low = ts & 0xffffffff;
pcap_ng_block_packet_set_data(block, pkt_data, epb->caplen);
if (proc_info != NULL) {
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_PIB_INDEX, &proc_info->proc_dump_index, 4);
}
if (e_proc_info != NULL) {
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_E_PIB_INDEX, &e_proc_info->proc_dump_index, 4);
}
if ((pktap_v2_hdr->pth_flags & PTH_FLAG_DIR_IN)) {
pktflags = PCAPNG_PBF_DIR_INBOUND;
} else if ((pktap_v2_hdr->pth_flags & PTH_FLAG_DIR_OUT)) {
pktflags = PCAPNG_PBF_DIR_OUTBOUND;
}
if (pktflags != 0) {
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_FLAGS , &pktflags, 4);
}
if (pktap_v2_hdr->pth_svc != (uint16_t)-1) {
uint32_t svc = pktap_v2_hdr->pth_svc;
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_SVC , &svc, 4);
}
if (comment != NULL) {
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
}
if (pktap_v2_hdr->pth_flags & PTH_FLAG_NEW_FLOW) {
pmdflags |= PCAPNG_EPB_PMDF_NEW_FLOW;
}
#ifdef PTH_FLAG_REXMIT
if (pktap_v2_hdr->pth_flags & PTH_FLAG_REXMIT) {
pmdflags |= PCAPNG_EPB_PMDF_REXMIT;
}
#endif
#ifdef PTH_FLAG_KEEP_ALIVE
if (pktap_v2_hdr->pth_flags & PTH_FLAG_KEEP_ALIVE) {
pmdflags |= PCAPNG_EPB_PMDF_KEEP_ALIVE;
}
#endif
#ifdef PTH_FLAG_SOCKET
if (pktap_v2_hdr->pth_flags & PTH_FLAG_SOCKET) {
pmdflags |= PCAPNG_EPB_PMDF_SOCKET;
}
#endif
#ifdef PTH_FLAG_NEXUS_CHAN
if (pktap_v2_hdr->pth_flags & PTH_FLAG_NEXUS_CHAN) {
pmdflags |= PCAPNG_EPB_PMDF_NEXUS_CHANNEL;
}
#endif
if (pmdflags != 0) {
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_PMD_FLAGS, &pmdflags, 4);
}
(void) pcap_ng_dump_block(dumper, block);
return (1);
#else
#pragma unused(dumper, h, sp, comment)
snprintf(pcap->errbuf, PCAP_ERRBUF_SIZE,
"%s: Packet too short", __func__);
return (0);
#endif
}
void
pcap_ng_dump_init_section_info(pcap_dumper_t *dumper)
{
dumper->shb_added = 0;
pcap_if_info_set_clear(&dumper->dump_if_info_set);
pcap_proc_info_set_clear(&dumper->dump_proc_info_set);
}