浏览代码

MVP release

tags/v1.0.0
Pascal Gloor 5 年前
父节点
当前提交
e45f60e551
共有 2 个文件被更改,包括 217 次插入59 次删除
  1. 185
    49
      dhcp_protect.c
  2. 32
    10
      dhcp_protect.h

+ 185
- 49
dhcp_protect.c 查看文件

#include <stdint.h> #include <stdint.h>
#include <errno.h> #include <errno.h>
#include <time.h> #include <time.h>
#include <stdarg.h>
#include <syslog.h>
#include <arpa/inet.h>

#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <libnetfilter_queue/libnetfilter_queue.h> #include <libnetfilter_queue/libnetfilter_queue.h>

#include <uthash.h> #include <uthash.h>

#include "dhcp_protect.h" #include "dhcp_protect.h"


// main function // main function
int main(int argc, char **argv) { int main(int argc, char **argv) {
char *configfile; char *configfile;
dp_conf conf; dp_conf conf;
struct nfq_q_handle *qh;


if ( argc == 2 ) { if ( argc == 2 ) {
configfile = argv[1]; configfile = argv[1];
if ( load_config(&conf, configfile) == NULL ) if ( load_config(&conf, configfile) == NULL )
return EXIT_FAILURE; return EXIT_FAILURE;


openlog("dhcp_protect", LOG_PID, LOG_DAEMON);

nfq_start(&conf); nfq_start(&conf);


return 0; return 0;
} }


// syslog function
void dp_log(unsigned char *remoteid, int remoteidlen, char *fmt, ...) {
va_list argList;
char buf[1000];
int offset = remoteidlen*2;
int i;

for(i=0; i<remoteidlen; i++) {
sprintf(buf+(i*2), "%02x", remoteid[i]);
}
buf[offset]=':'; offset++;
buf[offset]=' '; offset++;

va_start(argList, fmt);
vsnprintf(buf+offset, sizeof(buf)-offset-1, fmt, argList);
va_end(argList);

syslog(LOG_DAEMON|LOG_INFO, "%s", buf);
}

// start netfilter queue // start netfilter queue
void nfq_start(dp_conf *conf) { void nfq_start(dp_conf *conf) {
struct nfq_handle *h; struct nfq_handle *h;
conf->bltime = atoi(value); conf->bltime = atoi(value);
else if ( strcmp(name, "queue")==0 ) else if ( strcmp(name, "queue")==0 )
conf->queue = atoi(value); conf->queue = atoi(value);
else if ( strcmp(name, "dryrun")==0 )
conf->dryrun = atoi(value) ? 1 : 0;
else else
fprintf(stderr,"unknown directive '%s', ignored\n", name); fprintf(stderr,"unknown directive '%s', ignored\n", name);


fprintf(stderr,"max_pkt_per_interval value invalid (min 1, max 1000)\n"); fprintf(stderr,"max_pkt_per_interval value invalid (min 1, max 1000)\n");
error=1; error=1;
} }
if ( conf->interval < 10 || conf->interval > 60 ) {
fprintf(stderr,"interval value invalid (min 10, max 60)\n");
if ( conf->interval < 5 || conf->interval > 900 ) {
fprintf(stderr,"interval value invalid (min 5, max 900)\n");
error=1; error=1;
} }
if ( conf->debug < 0 || conf->debug > 1 ) { if ( conf->debug < 0 || conf->debug > 1 ) {
fprintf(stderr,"queue must be a positive integer\n"); fprintf(stderr,"queue must be a positive integer\n");
error=1; error=1;
} }
if ( conf->dryrun < 0 || conf->dryrun > 1 ) {
fprintf(stderr, "dryrun value invalid (0 or 1)\n");
error=1;
}


if ( error ) if ( error )
return NULL; return NULL;


printf("Configuration:\n"); printf("Configuration:\n");
printf("\t%-20s = %4s\n", "dryrun", conf->dryrun ? "Yes" : "No");
printf("\t%-20s = %4s\n", "debug", conf->debug ? "Yes" : "No" );
printf("\t%-20s = %4is\n", "interval", conf->interval);
printf("\t%-20s = %4i\n", "max_pkt_per_interval", conf->pktint); printf("\t%-20s = %4i\n", "max_pkt_per_interval", conf->pktint);
printf("\t%-20s = %4i\n", "interval", conf->interval);
printf("\t%-20s = %4i\n", "debug", conf->debug);
printf("\t%-20s = %4i\n", "blacklist_time", conf->bltime);
printf("\t%-20s = %4is\n", "blacklist_time", conf->bltime);
printf("\t%-20s = %4i\n", "queue", conf->queue); printf("\t%-20s = %4i\n", "queue", conf->queue);




} }


// decode dhcp packet // decode dhcp packet
u_int32_t dhcp_check(struct nfq_data *nfa, int *verdict, dp_conf *conf) {
int dhcp_check(struct nfq_data *nfa, dp_conf *conf) {
unsigned char *pkt; unsigned char *pkt;
int pktlen; int pktlen;
int offset = 0; int offset = 0;
int found = 0; int found = 0;
uint8_t ipver = 0; uint8_t ipver = 0;
uint8_t ihl = 0; uint8_t ihl = 0;
int i;
//int i;
int rv = NF_ACCEPT;


pktlen = nfq_get_payload(nfa, &pkt); pktlen = nfq_get_payload(nfa, &pkt);


for(i=0; i<pktlen; i++) {
if ( !(i%16) ) printf("\n");
printf("%02x ", pkt[i]);
if ( conf->debug ) printf("got a packet, len = %i\n", pktlen);

/* a bit too much ;)
if ( conf->debug ) {
for(i=0; i<pktlen; i++) {
if ( !(i%16) ) printf("\n");
printf("%02x ", pkt[i]);
}
} }
*/


// can we read the IP proto and IP header length ? // can we read the IP proto and IP header length ?
if ( pktlen > 0 ) { if ( pktlen > 0 ) {


if ( ipver != 4 ) { if ( ipver != 4 ) {
if ( conf->debug ) printf("not an IPv4 packet\n"); if ( conf->debug ) printf("not an IPv4 packet\n");
return NF_ACCEPT;
rv = NF_ACCEPT;
goto end;
} }


// jump over the IPv4 header // jump over the IPv4 header
// minimum packet size, fixed header + magic cookie (4 octets) // minimum packet size, fixed header + magic cookie (4 octets)
if ( pktlen < offset + 4 ) { if ( pktlen < offset + 4 ) {
if ( conf->debug ) printf("packet too small\n"); if ( conf->debug ) printf("packet too small\n");
return NF_ACCEPT;
rv = NF_ACCEPT;
goto end;
} }


// check magic cookie // check magic cookie
if ( pkt[offset] != 99 || pkt[offset+1] != 130 || pkt[offset+2] != 83 || pkt[offset+3] != 99 ) { if ( pkt[offset] != 99 || pkt[offset+1] != 130 || pkt[offset+2] != 83 || pkt[offset+3] != 99 ) {
if ( conf->debug ) printf("invalid magic cookie %02x%02x%02x%02x\n", pkt[offset], pkt[offset+1], pkt[offset+2], pkt[offset+3]);
return NF_ACCEPT;
if ( conf->debug )
printf(
"invalid magic cookie %02x%02x%02x%02x\n",
pkt[offset], pkt[offset+1],
pkt[offset+2], pkt[offset+3]);

rv = NF_ACCEPT;
goto end;
} }
offset+=4; offset+=4;




offset++; offset++;


printf("type : %i\n",type);
// printf("type : %i\n",type);


// padding of 1 octet // padding of 1 octet
if ( type == 0 ) { if ( type == 0 ) {
len = pkt[offset]; len = pkt[offset];
offset++; offset++;


printf("len : %i\n", len);
// printf("len : %i\n", len);


// can the value be read // can the value be read
if ( offset+len>=pktlen ) if ( offset+len>=pktlen )
uint8_t olen = o82[o82off+1]; uint8_t olen = o82[o82off+1];
o82off+=2; o82off+=2;


printf("o82 type=%i len=%i\n", otype, olen);
// printf("o82 type=%i len=%i\n", otype, olen);


// remoteid // remoteid
if ( otype == 2 ) { if ( otype == 2 ) {
// make sure we don't overflow and can read all data // make sure we don't overflow and can read all data
if ( o82off + olen > len ) { if ( o82off + olen > len ) {
if ( conf->debug) printf("option 82.2 data too long\n"); if ( conf->debug) printf("option 82.2 data too long\n");
return NF_ACCEPT;
rv = NF_ACCEPT;
goto end;
} }
else { else {
remoteid = o82 + o82off; remoteid = o82 + o82off;
} }


if ( found ) { if ( found ) {
dp_blacklist_count(conf, remoteid, remoteidlen);
if ( dp_blacklist_check(conf, remoteid, remoteidlen) )
return NF_ACCEPT;
else
return NF_DROP;
// count the packet, even when blacklisted.
dp_accounting_add(conf, remoteid, remoteidlen);

// check if already in the blacklist
if ( dp_blacklist_check(conf, remoteid, remoteidlen) == NF_DROP )
rv = NF_DROP;

// check if it must be added to the blacklist
else if ( dp_accounting_check(conf, remoteid, remoteidlen) == NF_DROP ) {
dp_blacklist_add(conf, remoteid, remoteidlen);
rv = NF_DROP;
}
} }


printf("got a packet, len = %i\n", pktlen);
return NF_ACCEPT;
end:

dp_hash_cleanup(conf);

return rv;
} }


// netfilter queue callback // netfilter queue callback
static int dp_callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) { static int dp_callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) {
struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr(nfa);
dp_conf *conf = (dp_conf*)data; dp_conf *conf = (dp_conf*)data;
int id;
int verdict; int verdict;
u_int32_t id = dhcp_check(nfa, &verdict, conf); /* Treat packet */

if ( ph ) {
id = ntohl (ph->packet_id);
if ( conf->debug ) printf ("received packet with id %d\n", id);
}

verdict = dhcp_check(nfa, conf); /* Treat packet */

// override decision for dryrun
if ( conf->dryrun )
verdict = NF_ACCEPT;

return nfq_set_verdict(qh, id, verdict, 0, NULL); /* Verdict packet */ return nfq_set_verdict(qh, id, verdict, 0, NULL); /* Verdict packet */
} }


void dp_blacklist_count(dp_conf *conf, unsigned char *remoteid, int len) {
int i;
dp_blacklist *bl, *tmp;
void dp_accounting_add(dp_conf *conf, unsigned char *remoteid, int len) {
//int i;
dp_accounting *ac;


// does the element already exist // does the element already exist
HASH_FIND(hh, blacklists, remoteid, len, bl);
HASH_FIND(hh, accountings, remoteid, len, ac);

if ( conf->debug ) printf("AC: add item\n");


// found it, increment the counter // found it, increment the counter
if ( ac ) {
if ( conf->debug ) printf("AC: item found, incrementing\n");
ac->count++;
}
// not found, create a new one
else {
if ( conf->debug ) printf("AC: item not found, creating\n");
ac = malloc(sizeof(dp_accounting));

memcpy(ac->remoteid, remoteid, len);
ac->len = len;
ac->count = 1;

HASH_ADD(hh, accountings, remoteid, len, ac);
}
}

void dp_blacklist_add(dp_conf *conf, unsigned char *remoteid, int len) {
dp_blacklist *bl;

// alrady exists?
HASH_FIND(hh, blacklists, remoteid, len, bl);

if ( conf->debug ) printf("BL: add item\n");

// found an entry, push the expiration further
if ( bl ) { if ( bl ) {
if ( conf->debug ) printf("BL: item found, incrementing\n");
bl->count++;
if ( conf->debug ) printf("BL: item found -> pushing further\n");
bl->expire = time(NULL) + conf->bltime;
} }
// not found, create a new one // not found, create a new one
else { else {
if ( conf->debug ) printf("BL: item not found, new entry in BL\n");
dp_log(remoteid, len, "blacklisting started");
bl = malloc(sizeof(dp_blacklist)); bl = malloc(sizeof(dp_blacklist));

memcpy(bl->remoteid, remoteid, len); memcpy(bl->remoteid, remoteid, len);
bl->len = len; bl->len = len;
bl->count = 1;

if ( conf->debug ) printf("BL: item not found, creating\n");

bl->expire = time(NULL) + conf->bltime;
HASH_ADD(hh, blacklists, remoteid, len, bl); HASH_ADD(hh, blacklists, remoteid, len, bl);
} }
}


printf("count this remoteid ");
for(i=0; i<len; i++) printf("%02x",*(uint8_t*)(remoteid+i));
printf("\n");
void dp_hash_cleanup(dp_conf *conf) {
dp_accounting *ac, *actmp;
dp_blacklist *bl, *bltmp;


// is it time to cleanup the list? // is it time to cleanup the list?
if ( dp_timestamp + conf->interval < time(NULL) ) {
// cleanup every conf->interval seconds
if ( dp_accountingtime + conf->interval < time(NULL) ) {
if ( conf->debug ) printf("cleanup interval\n"); if ( conf->debug ) printf("cleanup interval\n");
dp_timestamp = time(NULL);
HASH_ITER(hh, blacklists, bl, tmp) {
HASH_DEL(blacklists, bl);
free(bl);
dp_accountingtime = time(NULL);
HASH_ITER(hh, accountings, ac, actmp) {
HASH_DEL(accountings, ac);
free(ac);
}
}

// blacklist cleanup check every 1 sec
if ( dp_cleanuptime < time(NULL) ) {
if ( conf->debug ) printf("cleanup BL\n");
dp_cleanuptime = time(NULL);
HASH_ITER(hh, blacklists, bl, bltmp) {
if ( bl->expire < time(NULL) ) {
dp_log(
bl->remoteid, bl->len,
"blacklisting ended");
HASH_DEL(blacklists, bl);
free(bl);
}
} }
} }
} }


int dp_accounting_check(dp_conf *conf, unsigned char *remoteid, int len) {
dp_accounting *ac;

HASH_FIND(hh, accountings, remoteid, len, ac);

if ( conf->debug ) printf("AC Check\n");

if ( ac ) {
if(conf->debug) printf("AC Check: found item %i > %i ?\n", ac->count, conf->pktint);

if ( ac->count > conf->pktint ) {
if(conf->debug) printf("flood detected!\n");
return NF_DROP;
}
}
return NF_ACCEPT;
}

int dp_blacklist_check(dp_conf *conf, unsigned char *remoteid, int len) { int dp_blacklist_check(dp_conf *conf, unsigned char *remoteid, int len) {
dp_blacklist *bl; dp_blacklist *bl;


HASH_FIND(hh, blacklists, remoteid, len, bl); HASH_FIND(hh, blacklists, remoteid, len, bl);


if ( conf->debug ) printf("BL Check\n");

if ( bl ) { if ( bl ) {
if(conf->debug) printf("found item\n");


if ( bl->count > conf->pktint ) {
if(conf->debug) printf("flood detected!\n");
if ( bl->expire > time(NULL) ) {
if ( conf->debug ) printf("blacklisted!\n");
return NF_DROP; return NF_DROP;
} }
} }
return NF_ACCEPT;
} }

+ 32
- 10
dhcp_protect.h 查看文件


typedef struct dp_conf { typedef struct dp_conf {
int pktint; int pktint;
int interval; int interval;
int debug; int debug;
int bltime; int bltime;
int queue; int queue;
int dryrun;
} dp_conf; } dp_conf;


typedef struct dp_blacklist {
char remoteid[256];
typedef struct dp_accounting {
unsigned char remoteid[256];
int len; int len;
int count; int count;
UT_hash_handle hh; UT_hash_handle hh;
} dp_accounting;

typedef struct dp_blacklist {
unsigned char remoteid[256];
int len;
time_t expire;
UT_hash_handle hh;
} dp_blacklist; } dp_blacklist;


// global hash lists
static dp_accounting *accountings = NULL;
static dp_blacklist *blacklists = NULL; static dp_blacklist *blacklists = NULL;
static time_t dp_timestamp = 0;
// timestamp for cleanup interval
static time_t dp_accountingtime = 0;
static time_t dp_cleanuptime = 0;

static int dp_callback (
struct nfq_q_handle*,
struct nfgenmsg*,
struct nfq_data*,
void*);


void usage (char*);
dp_conf *load_config (dp_conf*, char*);
u_int32_t dhcp_check (struct nfq_data*, int*, dp_conf*);
static int dp_callback (struct nfq_q_handle*, struct nfgenmsg*, struct nfq_data*, void*);
void nfq_start (dp_conf*);
void dp_blacklist_count (dp_conf*, unsigned char *, int);
int dp_blacklist_check (dp_conf*, unsigned char *, int);
void usage (char*);
int dhcp_check (struct nfq_data*, dp_conf*);
dp_conf *load_config (dp_conf*, char*);
void nfq_start (dp_conf*);
void dp_accounting_add (dp_conf*, unsigned char *, int);
int dp_accounting_check (dp_conf*, unsigned char *, int);
void dp_blacklist_add (dp_conf*, unsigned char *, int);
int dp_blacklist_check (dp_conf*, unsigned char *, int);
void dp_hash_cleanup (dp_conf*);
void dp_log (unsigned char *, int, char *, ...);

正在加载...
取消
保存