661 lines
18 KiB
C
661 lines
18 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
#include <linux/in.h>
|
|
#include <linux/if_ether.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/ipv6.h>
|
|
#include <linux/tcp.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <pcap.h>
|
|
|
|
#include "osfp_common.h"
|
|
#include "osfp.h"
|
|
#include "osfp_log.h"
|
|
#include "osfp_fingerprint.h"
|
|
#include "osfp_score_db.h"
|
|
|
|
#define DEFAULT_FP_FILE_PATH "./fp.json"
|
|
|
|
#define ETHERNET_HEADER_LEN 14
|
|
#define VLAN_HEADER_LEN 4
|
|
#define IPV4_HEADER_LEN 20
|
|
#define TCP_HEADER_LEN 20
|
|
#define IPV6_HEADER_LEN 40
|
|
|
|
#define VLAN_MAX_LAYER 2
|
|
|
|
|
|
/* Port is just a uint16_t */
|
|
typedef uint16_t Port;
|
|
#define SET_PORT(v, p) ((p) = (v))
|
|
#define COPY_PORT(a,b) ((b) = (a))
|
|
|
|
/* Address */
|
|
typedef struct Address_ {
|
|
char family;
|
|
union {
|
|
uint32_t address_un_data32[4]; /* type-specific field */
|
|
uint16_t address_un_data16[8]; /* type-specific field */
|
|
uint8_t address_un_data8[16]; /* type-specific field */
|
|
struct in6_addr address_un_in6;
|
|
} address;
|
|
} Address;
|
|
|
|
#define addr_data32 address.address_un_data32
|
|
#define addr_data16 address.address_un_data16
|
|
#define addr_data8 address.address_un_data8
|
|
#define addr_in6addr address.address_un_in6
|
|
|
|
#define COPY_ADDRESS(a, b) do { \
|
|
(b)->family = (a)->family; \
|
|
(b)->addr_data32[0] = (a)->addr_data32[0]; \
|
|
(b)->addr_data32[1] = (a)->addr_data32[1]; \
|
|
(b)->addr_data32[2] = (a)->addr_data32[2]; \
|
|
(b)->addr_data32[3] = (a)->addr_data32[3]; \
|
|
} while (0)
|
|
|
|
/* Set the IPv4 addresses into the Addrs of the Packet.
|
|
* Make sure p->ip4h is initialized and validated.
|
|
*
|
|
* We set the rest of the struct to 0 so we can
|
|
* prevent using memset. */
|
|
#define SET_IPV4_SRC_ADDR(p, a) do { \
|
|
(a)->family = AF_INET; \
|
|
(a)->addr_data32[0] = (uint32_t)(p)->iph->saddr; \
|
|
(a)->addr_data32[1] = 0; \
|
|
(a)->addr_data32[2] = 0; \
|
|
(a)->addr_data32[3] = 0; \
|
|
} while (0)
|
|
|
|
#define SET_IPV4_DST_ADDR(p, a) do { \
|
|
(a)->family = AF_INET; \
|
|
(a)->addr_data32[0] = (uint32_t)(p)->iph->daddr; \
|
|
(a)->addr_data32[1] = 0; \
|
|
(a)->addr_data32[2] = 0; \
|
|
(a)->addr_data32[3] = 0; \
|
|
} while (0)
|
|
|
|
/* Set the IPv6 addresses into the Addrs of the Packet.
|
|
* Make sure p->ip6h is initialized and validated. */
|
|
#define SET_IPV6_SRC_ADDR(p, a) do { \
|
|
(a)->family = AF_INET6; \
|
|
(a)->addr_data32[0] = (p)->ip6h->saddr.s6_addr32[0]; \
|
|
(a)->addr_data32[1] = (p)->ip6h->saddr.s6_addr32[1]; \
|
|
(a)->addr_data32[2] = (p)->ip6h->saddr.s6_addr32[2]; \
|
|
(a)->addr_data32[3] = (p)->ip6h->saddr.s6_addr32[3]; \
|
|
} while (0)
|
|
|
|
#define SET_IPV6_DST_ADDR(p, a) do { \
|
|
(a)->family = AF_INET6; \
|
|
(a)->addr_data32[0] = (p)->ip6h->daddr.s6_addr32[0]; \
|
|
(a)->addr_data32[1] = (p)->ip6h->daddr.s6_addr32[1]; \
|
|
(a)->addr_data32[2] = (p)->ip6h->daddr.s6_addr32[2]; \
|
|
(a)->addr_data32[3] = (p)->ip6h->daddr.s6_addr32[3]; \
|
|
} while (0)
|
|
|
|
#define TCP_GET_RAW_SRC_PORT(tcph) ntohs((tcph)->source)
|
|
#define TCP_GET_RAW_DST_PORT(tcph) ntohs((tcph)->dest)
|
|
|
|
#define TCP_GET_SRC_PORT(p) TCP_GET_RAW_SRC_PORT((p)->tcph)
|
|
#define TCP_GET_DST_PORT(p) TCP_GET_RAW_DST_PORT((p)->tcph)
|
|
|
|
/* Set the TCP ports into the Ports of the Packet.
|
|
* Make sure p->tcph is initialized and validated. */
|
|
#define SET_TCP_SRC_PORT(pkt, prt) do { \
|
|
SET_PORT(TCP_GET_SRC_PORT((pkt)), *(prt)); \
|
|
} while (0)
|
|
|
|
#define SET_TCP_DST_PORT(pkt, prt) do { \
|
|
SET_PORT(TCP_GET_DST_PORT((pkt)), *(prt)); \
|
|
} while (0)
|
|
|
|
#define GET_IPV4_SRC_ADDR_U32(p) ((p)->src.addr_data32[0])
|
|
#define GET_IPV4_DST_ADDR_U32(p) ((p)->dst.addr_data32[0])
|
|
#define GET_IPV4_SRC_ADDR_PTR(p) ((p)->src.addr_data32)
|
|
#define GET_IPV4_DST_ADDR_PTR(p) ((p)->dst.addr_data32)
|
|
|
|
#define GET_IPV6_SRC_IN6ADDR(p) ((p)->src.addr_in6addr)
|
|
#define GET_IPV6_DST_IN6ADDR(p) ((p)->dst.addr_in6addr)
|
|
#define GET_IPV6_SRC_ADDR(p) ((p)->src.addr_data32)
|
|
#define GET_IPV6_DST_ADDR(p) ((p)->dst.addr_data32)
|
|
#define GET_TCP_SRC_PORT(p) ((p)->sp)
|
|
#define GET_TCP_DST_PORT(p) ((p)->dp)
|
|
|
|
|
|
typedef struct Packet_ {
|
|
struct ethhdr *ethh;
|
|
struct iphdr *iph;
|
|
struct ipv6hdr *ip6h;
|
|
struct tcphdr *tcph;
|
|
|
|
char srcip[46];
|
|
char dstip[46];
|
|
|
|
Address src;
|
|
Address dst;
|
|
union {
|
|
Port sp;
|
|
// icmp type and code of this packet
|
|
struct {
|
|
uint8_t type;
|
|
uint8_t code;
|
|
} icmp_s;
|
|
};
|
|
union {
|
|
Port dp;
|
|
// icmp type and code of the expected counterpart (for flows)
|
|
struct {
|
|
uint8_t type;
|
|
uint8_t code;
|
|
} icmp_d;
|
|
};
|
|
|
|
int vlan_layer;
|
|
} Packet;
|
|
|
|
|
|
unsigned char *fp_file_path;
|
|
unsigned char *fp_output_file_path;
|
|
FILE *fingerprinting_output_fp;
|
|
|
|
unsigned int debug_enable;
|
|
unsigned char *if_name;
|
|
unsigned char *pcap_file_name;
|
|
unsigned char *bpf_string;
|
|
pcap_t *pcap_handle;
|
|
|
|
int processed_packet;
|
|
int link_type;
|
|
|
|
void usage(void) {
|
|
fprintf(stderr,
|
|
"Usage: osfp_match [ ...options... ] [ 'filter rule' ]\n"
|
|
"\n"
|
|
"Network interface options:\n"
|
|
"\n"
|
|
" -i iface - listen on the specified network interface\n"
|
|
" -r file - read offline pcap data from a given file\n"
|
|
" -f file - read fingerprint database from 'file' (%s)\n",
|
|
DEFAULT_FP_FILE_PATH);
|
|
exit(1);
|
|
}
|
|
|
|
typedef struct EthernetHdr_ {
|
|
uint8_t eth_dst[6];
|
|
uint8_t eth_src[6];
|
|
uint16_t eth_type;
|
|
} __attribute__((__packed__)) EthernetHdr;
|
|
|
|
|
|
int packet_decode_tcp(Packet *p, const unsigned char *data, unsigned int len)
|
|
{
|
|
int ret = -1;
|
|
int tcp_hdr_len;
|
|
struct tcphdr *tcph;
|
|
|
|
if (len < TCP_HEADER_LEN) {
|
|
goto exit;
|
|
}
|
|
|
|
tcph = (struct tcphdr *)data;
|
|
tcp_hdr_len = tcph->doff << 2;
|
|
|
|
if (len < tcp_hdr_len) {
|
|
goto exit;
|
|
}
|
|
|
|
p->tcph = tcph;
|
|
SET_TCP_SRC_PORT(p,&p->sp);
|
|
SET_TCP_DST_PORT(p,&p->dp);
|
|
|
|
ret = 0;
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
int packet_decode_ipv4(Packet *p, const unsigned char *data, unsigned int len)
|
|
{
|
|
int ret = -1;
|
|
int ip_total_len, ip_hdr_len;
|
|
struct iphdr *iph;
|
|
|
|
if (len < IPV4_HEADER_LEN) {
|
|
goto exit;
|
|
}
|
|
|
|
iph = (struct iphdr *)data;
|
|
ip_total_len = ntohs(iph->tot_len);
|
|
ip_hdr_len = iph->ihl << 2;
|
|
|
|
if (ip_hdr_len < IPV4_HEADER_LEN) {
|
|
goto exit;
|
|
}
|
|
|
|
if (ip_total_len < ip_hdr_len) {
|
|
goto exit;
|
|
}
|
|
|
|
if (len < ip_total_len) {
|
|
goto exit;
|
|
}
|
|
|
|
p->iph = iph;
|
|
/* set the address struct */
|
|
SET_IPV4_SRC_ADDR(p,&p->src);
|
|
SET_IPV4_DST_ADDR(p,&p->dst);
|
|
|
|
switch (p->iph->protocol) {
|
|
case IPPROTO_TCP:
|
|
packet_decode_tcp(p, data + ip_hdr_len, ip_total_len - ip_hdr_len);
|
|
break;
|
|
default:
|
|
goto exit;
|
|
}
|
|
|
|
ret = 0;
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
int packet_decode_ipv6(Packet *p, const unsigned char *data, unsigned int len)
|
|
{
|
|
int ret = -1;
|
|
unsigned short ip6_payload_len;
|
|
unsigned char ip6_nexthdr;
|
|
struct ipv6hdr *ip6h;
|
|
|
|
if (len < IPV6_HEADER_LEN) {
|
|
goto exit;
|
|
}
|
|
|
|
ip6h = (struct ipv6hdr *)data;
|
|
ip6_payload_len = ntohs(ip6h->payload_len);
|
|
ip6_nexthdr = ip6h->nexthdr;
|
|
|
|
if (len < IPV6_HEADER_LEN + ip6_payload_len) {
|
|
goto exit;
|
|
}
|
|
|
|
p->ip6h = ip6h;
|
|
SET_IPV6_SRC_ADDR(p,&p->src);
|
|
SET_IPV6_DST_ADDR(p,&p->dst);
|
|
|
|
switch (ip6_nexthdr) {
|
|
case IPPROTO_TCP:
|
|
packet_decode_tcp(p, data + IPV6_HEADER_LEN, ip6_payload_len);
|
|
break;
|
|
default:
|
|
goto exit;
|
|
}
|
|
|
|
ret = 0;
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
int pakcet_decode_network_layer(Packet *p, const unsigned char *data, unsigned int len, unsigned short proto)
|
|
{
|
|
switch (proto) {
|
|
case ETH_P_IP: {
|
|
packet_decode_ipv4(p, data, len);
|
|
break;
|
|
}
|
|
case ETH_P_IPV6: {
|
|
packet_decode_ipv6(p, data, len);
|
|
break;
|
|
}
|
|
case ETH_P_8021Q:
|
|
if (p->vlan_layer > VLAN_MAX_LAYER || len < VLAN_HEADER_LEN) {
|
|
return -1;
|
|
}
|
|
unsigned short vlan_proto = ntohs(*(unsigned short*)(data + 2));
|
|
pakcet_decode_network_layer(p, data + VLAN_HEADER_LEN, len - VLAN_HEADER_LEN, vlan_proto);
|
|
break;
|
|
default:
|
|
printf("L3 proto type: %02x not yet supported\n", proto);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int packet_decode_ethernet(Packet *p, const unsigned char *data, unsigned int len)
|
|
{
|
|
int ret = -1;
|
|
unsigned short proto;
|
|
struct ethhdr *ethh;
|
|
|
|
if (len < ETHERNET_HEADER_LEN) {
|
|
goto exit;
|
|
}
|
|
|
|
ethh = (struct ethhdr *)data;
|
|
proto = ntohs(ethh->h_proto);
|
|
|
|
p->ethh = ethh;
|
|
|
|
ret = pakcet_decode_network_layer(p, data + ETHERNET_HEADER_LEN, len - ETHERNET_HEADER_LEN, proto);
|
|
if (ret != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
void packet_decode_link_layer(Packet *p, const unsigned char *data, unsigned int len, int datalink)
|
|
{
|
|
switch (datalink) {
|
|
case DLT_EN10MB:
|
|
packet_decode_ethernet(p, data, len);
|
|
break;
|
|
default:
|
|
printf("Datalink type: %02x not yet supported\n", link_type);
|
|
pcap_breakloop(pcap_handle);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void packet_decode(Packet *p, const unsigned char *data, unsigned int len, int datalink)
|
|
{
|
|
packet_decode_link_layer(p, data, len, datalink);
|
|
}
|
|
|
|
size_t strlcat(char *dst, const char *src, size_t siz)
|
|
{
|
|
register char *d = dst;
|
|
register const char *s = src;
|
|
register size_t n = siz;
|
|
size_t dlen;
|
|
|
|
/* Find the end of dst and adjust bytes left but don't go past end */
|
|
while (n-- != 0 && *d != '\0')
|
|
d++;
|
|
dlen = d - dst;
|
|
n = siz - dlen;
|
|
|
|
if (n == 0)
|
|
return(dlen + strlen(s));
|
|
while (*s != '\0') {
|
|
if (n != 1) {
|
|
*d++ = *s;
|
|
n--;
|
|
}
|
|
s++;
|
|
}
|
|
*d = '\0';
|
|
|
|
return(dlen + (s - src)); /* count does not include NUL */
|
|
}
|
|
|
|
static const char *PrintInetIPv6(const void *src, char *dst, socklen_t size)
|
|
{
|
|
int i;
|
|
char s_part[6];
|
|
uint16_t x[8];
|
|
memcpy(&x, src, 16);
|
|
|
|
/* current IPv6 format is fixed size */
|
|
if (size < 8 * 5) {
|
|
printf("Too small buffer to write IPv6 address");
|
|
return NULL;
|
|
}
|
|
memset(dst, 0, size);
|
|
for(i = 0; i < 8; i++) {
|
|
snprintf(s_part, sizeof(s_part), "%04x:", htons(x[i]));
|
|
strlcat(dst, s_part, size);
|
|
}
|
|
/* suppress last ':' */
|
|
dst[strlen(dst) - 1] = 0;
|
|
|
|
return dst;
|
|
}
|
|
|
|
const char *PrintInet(int af, const void *src, char *dst, socklen_t size)
|
|
{
|
|
switch (af) {
|
|
case AF_INET:
|
|
snprintf(dst, size, "%u.%u.%u.%u",
|
|
((unsigned char *)src)[0],
|
|
((unsigned char *)src)[1],
|
|
((unsigned char *)src)[2],
|
|
((unsigned char *)src)[3]);
|
|
return dst;
|
|
case AF_INET6:
|
|
/* Format IPv6 without deleting zeroes */
|
|
return PrintInetIPv6(src, dst, size);
|
|
default:
|
|
printf("Unsupported protocol: %d", af);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void example_detect(struct osfp_db *osfp_db, Packet *p)
|
|
{
|
|
int ret;
|
|
char str_buf[1024];
|
|
//unsigned char *iph = (unsigned char *)(p->iph != NULL ? (void *)p->iph : (void *)p->ip6h);
|
|
struct iphdr *iph;
|
|
struct ipv6hdr *ip6h;
|
|
struct tcphdr *tcph;
|
|
unsigned int tcph_len;
|
|
struct osfp_result *result = NULL;
|
|
|
|
printf("Example ipv4 header detect: --------------------------\n");
|
|
|
|
iph = (struct iphdr *)p->iph;
|
|
ip6h = (struct ipv6hdr *)p->ip6h;
|
|
tcph = (struct tcphdr *)p->tcph;
|
|
tcph_len = tcph->doff << 2;
|
|
|
|
if (iph) {
|
|
result = osfp_ipv4_identify(osfp_db, iph, tcph, tcph_len);
|
|
} else if (ip6h) {
|
|
result = osfp_ipv6_identify(osfp_db, ip6h, tcph, tcph_len);
|
|
} else {
|
|
goto exit;
|
|
}
|
|
|
|
if (result == NULL) {
|
|
printf("osfp header match failed, erro: %s\n", "?");
|
|
goto exit;
|
|
}
|
|
|
|
printf("Connection info: %s:%d -> %s:%d\n", p->srcip, p->sp, p->dstip, p->dp);
|
|
printf("Most likely os class: %s\n", osfp_result_os_name_get(result));
|
|
|
|
printf("Details:\n");
|
|
printf("%s\n", osfp_result_score_detail_export(result));
|
|
|
|
exit:
|
|
if (result) {
|
|
osfp_result_free(result);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void process_packet(char *user, struct pcap_pkthdr *h, u_char *pkt)
|
|
{
|
|
int ret;
|
|
struct osfp_db *osfp_db = (struct osfp_db *)user;
|
|
Packet packet = {0}, *p = &packet;
|
|
|
|
// decode packet
|
|
packet_decode(p, pkt, h->len, link_type);
|
|
if (p->tcph == NULL || (p->iph == NULL && p->ip6h == NULL)) {
|
|
goto exit;
|
|
}
|
|
|
|
// only for tcp syn request packet
|
|
if (!p->tcph->syn) {
|
|
goto exit;
|
|
}
|
|
|
|
if (p->tcph->ack) {
|
|
printf("--------------------------- SYN/ACK\n");
|
|
} else {
|
|
printf("--------------------------- SYN\n");
|
|
}
|
|
|
|
if (p->iph) {
|
|
PrintInet(AF_INET, (const void *)&(p->src.addr_data32[0]), p->srcip, sizeof(p->srcip));
|
|
PrintInet(AF_INET, (const void *)&(p->dst.addr_data32[0]), p->dstip, sizeof(p->dstip));
|
|
} else if (p->ip6h) {
|
|
PrintInet(AF_INET6, (const void *)&(p->src.address), p->srcip, sizeof(p->srcip));
|
|
PrintInet(AF_INET6, (const void *)&(p->dst.address), p->dstip, sizeof(p->dstip));
|
|
}
|
|
|
|
// tcp/ip header detect example for user
|
|
example_detect(osfp_db, p);
|
|
|
|
printf("--------------------------- processed packet count %d\n", ++processed_packet);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int r;
|
|
|
|
while ((r = getopt(argc, argv, "+f:i:r:o:d")) != -1) {
|
|
switch(r) {
|
|
case 'f':
|
|
if (fp_file_path) {
|
|
printf("Multiple -f options not supported.\n");
|
|
exit(1);
|
|
}
|
|
fp_file_path = (unsigned char*)optarg;
|
|
break;
|
|
case 'i':
|
|
if (if_name) {
|
|
printf("Multiple -i options not supported.\n");
|
|
exit(1);
|
|
}
|
|
if_name = (unsigned char*)optarg;
|
|
break;
|
|
case 'r':
|
|
if (pcap_file_name) {
|
|
printf("Multiple -r options not supported.\n");
|
|
exit(1);
|
|
}
|
|
pcap_file_name = (unsigned char*)optarg;
|
|
break;
|
|
case 'o':
|
|
if (fp_output_file_path) {
|
|
printf("Multiple -o options not supported.\n");
|
|
exit(1);
|
|
}
|
|
fp_output_file_path = (unsigned char*)optarg;
|
|
break;
|
|
case 'd':
|
|
debug_enable = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (optind < argc) {
|
|
if (optind + 1 == argc) {
|
|
bpf_string = argv[optind];
|
|
} else {
|
|
printf("Filter rule must be a single parameter (use quotes).\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
// fingerprinting out file create
|
|
if (fp_output_file_path) {
|
|
fingerprinting_output_fp = fopen(fp_output_file_path, "a+");
|
|
if (!fingerprinting_output_fp) {
|
|
printf("No such file: %s\n", fp_output_file_path);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
// prepare pcap handle
|
|
|
|
char pcap_err[PCAP_ERRBUF_SIZE];
|
|
|
|
if (pcap_file_name) {
|
|
if (access((char*)pcap_file_name, R_OK)) {
|
|
printf("No such file: %s\n", pcap_file_name);
|
|
exit(1);
|
|
}
|
|
pcap_handle = pcap_open_offline((char*)pcap_file_name, pcap_err);
|
|
if (pcap_handle == NULL ) {
|
|
printf("Pcap file open failed. File name: %s, Err: %s\n", pcap_file_name, pcap_err);
|
|
exit(1);
|
|
}
|
|
} else if (if_name) {
|
|
pcap_handle = pcap_open_live((char*)if_name, 65535, 1, 5, pcap_err);
|
|
if (pcap_handle == NULL) {
|
|
printf("Pcap live open failed. Interface name: %s, Err: %s\n", if_name, pcap_err);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
usage();
|
|
}
|
|
|
|
// setup bpf filter
|
|
if (bpf_string) {
|
|
struct bpf_program bpf_filter;
|
|
|
|
if (pcap_compile(pcap_handle, &bpf_filter, bpf_string, 1, 0) < 0) {
|
|
printf("bpf compilation error %s", pcap_geterr(pcap_handle));
|
|
exit(1);
|
|
}
|
|
|
|
if (pcap_setfilter(pcap_handle, &bpf_filter) < 0) {
|
|
printf("could not set bpf filter %s", pcap_geterr(pcap_handle));
|
|
pcap_freecode(&bpf_filter);
|
|
exit(1);
|
|
}
|
|
pcap_freecode(&bpf_filter);
|
|
}
|
|
|
|
// get link type
|
|
link_type = pcap_datalink(pcap_handle);
|
|
|
|
// create osfp db
|
|
if (fp_file_path == NULL) {
|
|
fp_file_path = DEFAULT_FP_FILE_PATH;
|
|
}
|
|
|
|
if (debug_enable) {
|
|
osfp_log_level_set(OSFP_LOG_LEVEL_DEBUG);
|
|
}
|
|
|
|
struct osfp_db *osfp_db = osfp_db_new(fp_file_path);
|
|
if (osfp_db == NULL) {
|
|
printf("could not create osfp context. fingerprints file: %s\n", fp_file_path);
|
|
exit(1);
|
|
}
|
|
|
|
osfp_score_db_debug_print(osfp_db->score_db);
|
|
|
|
// loop
|
|
while (1) {
|
|
int r = pcap_dispatch(pcap_handle, 0, (pcap_handler)process_packet, (void*)osfp_db);
|
|
if (r < 0) {
|
|
printf("error code: %d, error: %s\n", r, pcap_geterr(pcap_handle));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// destroy osfp db
|
|
osfp_db_free(osfp_db);
|
|
|
|
return 0;
|
|
}
|
|
|