Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added sampling to dnscap #15

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
8 changes: 8 additions & 0 deletions src/dnscap.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
.Op Fl E Ar datetime
.Op Fl P Ar plugin.so
.Op Fl U Ar str
.Op Fl q Ar nth
.Sh DESCRIPTION
.Nm
is a network capture utility designed specifically for DNS traffic. It
Expand Down Expand Up @@ -353,6 +354,13 @@ Enable immediate mode on interfaces.
Append "and
.Ar str
" to the pcap filter.
.It Fl q Ar nth
Causes the output to be sampled after the application of all other filters.
Only every nth dns initiation will be output and responses are only output
if they correspond to one of the sampled queries. This option cannot be
used with option
.Fl s
[r] due to obvious filter reasons.
.El
.Pp
If started with no options,
Expand Down
100 changes: 96 additions & 4 deletions src/dnscap.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ static const char version_fmt[] = "V1.0-OARC-r%d (%s)";
#include <time.h>
#include <unistd.h>
#include <pwd.h>
#include "../uthash/uthash.h" //for the hash used in the sample module

#ifdef __linux__
extern char *strptime(const char *, const char *, struct tm *);
Expand Down Expand Up @@ -287,6 +288,19 @@ struct plugin {
};
LIST(struct plugin) plugins;

typedef struct{
UT_hash_handle hh; //makes the structure hashable using "uthash/uthash.h"
iaddr from;
int sport,dport,transaction_id;
iaddr to;
}samplePacket;

typedef struct{
iaddr from;
int sport,dport,transaction_id;
iaddr to;
}sample_lookup_key;

/* Forward. */

static void setsig(int, int);
Expand Down Expand Up @@ -384,6 +398,10 @@ static unsigned long long mem_limit = (unsigned) MEM_MAX; // process memory li
static int mem_limit_set = 1; // Should be configurable
const char DROPTOUSER[] = "nobody";
static pcap_thread_t pcap_thread = PCAP_THREAD_T_INIT;
static int sample = FALSE;
static unsigned sampleAmount;
static unsigned querycount;
static samplePacket *allSampleQueries = NULL;

/* Public. */

Expand Down Expand Up @@ -694,7 +712,8 @@ help_1(void) {
"\t[-w <base> [-W <suffix>] [-k <cmd>]] [-t <lim>] [-c <lim>] [-C <lim>]\n"
"\t[-x <pat>]+ [-X <pat>]+\n"
"\t[-B <datetime>] [-E <datetime>]\n"
"\t[-P plugin.so] [-U <str>]\n",
"\t[-P plugin.so] [-U <str>]\n"
"\t[-q <nth>]\n",
ProgramName);
}

Expand Down Expand Up @@ -755,6 +774,8 @@ help_2(void) {
"\t-E <datetime> end collecting at this date and time\n"
"\t-M set monitor mode on interfaces\n"
"\t-D set immediate mode on interfaces\n"
"\t-q <nth> output only every nth DNS query and only output responses\n \
if they correspond to one of the sampled queries\n"
);
}

Expand Down Expand Up @@ -782,7 +803,7 @@ parse_args(int argc, char *argv[]) {
INIT_LIST(myregexes);
INIT_LIST(plugins);
while ((ch = getopt(argc, argv,
"a:bc:de:fgh:i:k:l:m:pr:s:t:u:w:x:"
"a:bc:de:fgh:i:k:l:m:pq:r:s:t:u:w:x:"
#ifdef USE_SECCOMP
"y"
#endif
Expand Down Expand Up @@ -898,6 +919,19 @@ parse_args(int argc, char *argv[]) {
}
dir_wanted = u;
break;
case 'q':
#if HAVE_NS_INITPARSE && HAVE_NS_PARSERR && HAVE_NS_SPRINTRR
sample = TRUE;
sampleAmount = atoi(optarg);
if(sampleAmount == 0)
usage("-q takes only unsigned integer values != 0");
querycount = 0;
#else
fprintf(stderr, "%s must be compiled with libbind to use the -q option.\n",
ProgramName);
exit(1);
#endif
break;
case 'h':
u = 0;
for (p = optarg; *p; p++)
Expand Down Expand Up @@ -1099,6 +1133,9 @@ parse_args(int argc, char *argv[]) {
usage("the -L and -l options are mutually exclusive");
if (background && (dumptrace || preso))
usage("the -b option is incompatible with -d and -g");
if(sample && ((dir_wanted & DIR_INITIATE) == 0))
usage("the -q option is incompatible with -s r");

if (dumptrace >= 1) {
endpoint_ptr ep;
const char *sep;
Expand Down Expand Up @@ -2339,8 +2376,63 @@ network_pkt(const char *descr, my_bpftimeval ts, unsigned pf,
abort();
}
}
output(descr, from, to, proto, flags, sport, dport, ts,
pkt_copy, olen, dnspkt, dnslen);

/*sampling*/
#if HAVE_NS_INITPARSE && HAVE_NS_PARSERR && HAVE_NS_SPRINTRR
if (sample == TRUE)
{
ns_msg dnsmsgSample;
ns_initparse(dnspkt,dnslen,&dnsmsgSample);
samplePacket *currentQuery;

unsigned keylen = offsetof(samplePacket,to) //keylen is used to define which fields of the hash structure are added
+ sizeof(to) //as a compound key. Here, the key is composed of all fields between (and including)
- offsetof(samplePacket,from); //samplePacket->to and samplePacket->from (from, sport, dport, transaction_id, to)

if(dns.qr == 0)
{
querycount++;

if(querycount % sampleAmount == 0)
{
currentQuery = calloc(1,sizeof(*currentQuery));
assert(currentQuery != NULL);

currentQuery->from = from;
currentQuery->to = to;
currentQuery->sport = sport;
currentQuery->dport = dport;
currentQuery->transaction_id = ns_msg_id(dnsmsgSample);

HASH_ADD(hh,allSampleQueries,from,keylen,currentQuery);
output(descr,from,to,proto,flags,sport,dport,ts,pkt_copy,olen,dnspkt,dnslen);
}
}
else
{
sample_lookup_key *lookup_key = (sample_lookup_key*)calloc(1,sizeof(*lookup_key));
assert(lookup_key != NULL);

lookup_key->from = to;
lookup_key->to = from;
lookup_key->dport = sport;
lookup_key->sport = dport;
lookup_key->transaction_id = ns_msg_id(dnsmsgSample);

HASH_FIND(hh,allSampleQueries,&lookup_key->from,keylen,currentQuery);
if(currentQuery)
{
HASH_DEL(allSampleQueries,currentQuery);
free(currentQuery);
output(descr,from,to,proto,flags,sport,dport,ts,pkt_copy,olen,dnspkt,dnslen);
}
free(lookup_key);
}
}else
{
output(descr,from,to,proto,flags,sport,dport,ts,pkt_copy,olen,dnspkt,dnslen);
}
#endif /* HAVE_NS_INITPARSE && HAVE_NS_PARSERR && HAVE_NS_SPRINTRR */
}

/*
Expand Down
Loading