Pascal Gloor 5 years ago
parent
commit
01f3b47ea8
4 changed files with 107 additions and 68 deletions
  1. 3
    3
      Makefile
  2. 35
    26
      dhcp_protect.c
  3. 20
    4
      dhcp_protect.h
  4. 49
    35
      perftest.pl

+ 3
- 3
Makefile View File

rm -f $(TARGETS) $(OBJ) rm -f $(TARGETS) $(OBJ)


install: install:
systemctl stop dhcp_protect
systemctl stop dhcp_protect || true
cp dhcp_protect /usr/local/bin/ cp dhcp_protect /usr/local/bin/
cp dhcp_protect.conf /usr/local/etc/ cp dhcp_protect.conf /usr/local/etc/
cp dhcp_protect.service /etc/systemd/system/ cp dhcp_protect.service /etc/systemd/system/
systemctl start dhcp_protect systemctl start dhcp_protect


uninstall: uninstall:
systemctl stop dhcp_protect
systemctl disable dhcp_protect
systemctl stop dhcp_protect || true
systemctl disable dhcp_protect || true
rm -f /usr/local/bin/dhcp_protect rm -f /usr/local/bin/dhcp_protect
rm -f /usr/local/etc/dhcp_protect.conf rm -f /usr/local/etc/dhcp_protect.conf
rm -f /etc/systemd/system/dhcp_protect.service rm -f /etc/systemd/system/dhcp_protect.service

+ 35
- 26
dhcp_protect.c View File

configfile = argv[1]; configfile = argv[1];
} }
else { else {
usage(argv[0]);
dp_usage(argv[0]);
return EXIT_FAILURE; return EXIT_FAILURE;
} }


if ( load_config(&conf, configfile) == NULL )
if ( dp_load_config(&conf, configfile) == NULL )
return EXIT_FAILURE; return EXIT_FAILURE;


openlog("dhcp_protect", LOG_PID, LOG_DAEMON); openlog("dhcp_protect", LOG_PID, LOG_DAEMON);


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


return 0; return 0;
} }
} }


// start netfilter queue // start netfilter queue
void nfq_start(dp_conf *conf) {
void dp_nfq_start(dp_conf *conf) {
struct nfq_handle *h; struct nfq_handle *h;
struct nfq_q_handle *qh; struct nfq_q_handle *qh;
int fd; int fd;
} }


// display usage // display usage
void usage(char *prog) {
void dp_usage(char *prog) {
fprintf(stderr,"Usage: %s <configuration file>\n",prog); fprintf(stderr,"Usage: %s <configuration file>\n",prog);
} }




// load configuration file // load configuration file
dp_conf *load_config(dp_conf *conf, char *file) {
dp_conf *dp_load_config(dp_conf *conf, char *file) {
FILE *fh; FILE *fh;
char *line = NULL; char *line = NULL;
size_t len = 0; size_t len = 0;
} }


// decode dhcp packet // decode dhcp packet
int dhcp_check(struct nfq_data *nfa, dp_conf *conf) {
int dp_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;
unsigned char *remoteid = NULL; unsigned char *remoteid = NULL;
int remoteidlen = 0; int remoteidlen = 0;
int found = 0;
uint8_t ipver = 0;
uint8_t ihl = 0;
//int i;
int rv = NF_ACCEPT; int rv = NF_ACCEPT;


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


// 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 ) {
uint8_t ipver;
uint8_t ihl;

ipver = pkt[offset]; ipver = pkt[offset];
ipver >>= 4; ipver >>= 4;
ihl = pkt[offset]; ihl = pkt[offset];
if ( ipver == 4 ) { if ( ipver == 4 ) {
// jump to DHCPv4 // jump to DHCPv4
offset += ( ihl * 4 ) + 8; offset += ( ihl * 4 ) + 8;
dhcpv4_check(conf, pkt, pktlen, offset, &remoteid, &remoteidlen);
dp_dhcpv4_check(conf, pkt, pktlen, offset, &remoteid, &remoteidlen);
} }
else if ( ipver == 6 ) { else if ( ipver == 6 ) {
// jump to DHCPv6 // jump to DHCPv6
offset += 48 + 8;
dhcpv6_check(conf, pkt, pktlen, offset, &remoteid, &remoteidlen);
offset += 40 + 8;
dp_dhcpv6_check(conf, pkt, pktlen, offset, &remoteid, &remoteidlen);
} }
else { else {
if ( conf->debug ) printf("not an IPv4 packet\n"); if ( conf->debug ) printf("not an IPv4 packet\n");
} }


if ( remoteidlen>0 ) { if ( remoteidlen>0 ) {
if ( conf->debug ) {
int i;
printf("remoteid: ");
for(i=0; i<remoteidlen; i++)
printf("%02x", remoteid[i]);
printf("\n");
}
// count the packet, even when blacklisted. // count the packet, even when blacklisted.
dp_accounting_add(conf, remoteid, remoteidlen); dp_accounting_add(conf, remoteid, remoteidlen);


return rv; return rv;
} }


void dhcpv6_check(dp_conf *conf, unsigned char *pkt, int pktlen, int offset, unsigned char *remoteid, int *remoteidlen) {
void dp_dhcpv6_check(dp_conf *conf, unsigned char *pkt, int pktlen, int offset, unsigned char **remoteid, int *remoteidlen) {


while(offset<pktlen) { while(offset<pktlen) {
uint8_t msgtype = (uint8_t)pkt[offset]; uint8_t msgtype = (uint8_t)pkt[offset];
uint8_t code = (uint8_t)pkt[offset]; uint8_t code = (uint8_t)pkt[offset];
uint8_t len = (uint8_t)pkt[offset+1]; uint8_t len = (uint8_t)pkt[offset+1];


offset+2;
offset+=2;


if ( code == 1 ) { // Client identifier / DUID if ( code == 1 ) { // Client identifier / DUID
remoteid = pkt+offset;
*remoteidlen = len;
break;
// make sure there's enough space
if ( offset + len <= pktlen ) {
*remoteid = pkt+offset;
*remoteidlen = len;
break;
}
} }
offset+=len; offset+=len;
} }
} }




void dhcpv4_check(dp_conf *conf, unsigned char *pkt, int pktlen, int offset, unsigned char *remoteid, int *remoteidlen) {
void dp_dhcpv4_check(dp_conf *conf, unsigned char *pkt, int pktlen, int offset, unsigned char **remoteid, int *remoteidlen) {
int hwaddrpos = offset + 28; // remember where the hw addr is if we need to fallback to it int hwaddrpos = offset + 28; // remember where the hw addr is if we need to fallback to it
int hwlenpos = offset + 2; // remember where the hw addr len is if we need to fallback to it int hwlenpos = offset + 2; // remember where the hw addr len is if we need to fallback to it






// parse TLV options // parse TLV options
while(offset<pktlen && !found) {
while(offset<pktlen && remoteidlen==0) {
uint8_t type = pkt[offset]; uint8_t type = pkt[offset];
uint8_t len; uint8_t len;


int o82off = 0; int o82off = 0;


// loop until the end, +2 to ensure we can read type and length // loop until the end, +2 to ensure we can read type and length
while(o82off+2<len && !found) {
while(o82off+2<len && remoteidlen==0) {
uint8_t otype = o82[o82off]; uint8_t otype = o82[o82off];
uint8_t olen = o82[o82off+1]; uint8_t olen = o82[o82off+1];
o82off+=2; o82off+=2;
break; break;
} }
else { else {
remoteid = o82 + o82off;
*remoteid = o82 + o82off;
*remoteidlen = olen; *remoteidlen = olen;
} }
} }
if ( *remoteidlen == 0 ) { if ( *remoteidlen == 0 ) {
// nope, we won't overflow // nope, we won't overflow
if ( (uint8_t)pkt[hwlenpos] <= 16 ) { if ( (uint8_t)pkt[hwlenpos] <= 16 ) {
remoteid = pkt+hwaddrpos;
remoteidlen = (uint8_t)pkt[hwlenpos];
*remoteid = pkt+hwaddrpos;
*remoteidlen = (uint8_t)pkt[hwlenpos];
} }
} }
} }
if ( conf->debug ) printf ("received packet with id %d\n", id); if ( conf->debug ) printf ("received packet with id %d\n", id);
} }


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


// override decision for dryrun // override decision for dryrun
if ( conf->dryrun ) if ( conf->dryrun )

+ 20
- 4
dhcp_protect.h View File

struct nfq_data*, struct nfq_data*,
void*); void*);


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_usage (char*);
dp_conf *dp_load_config (dp_conf*, char*);
void dp_nfq_start (dp_conf*);
void dp_accounting_add (dp_conf*, unsigned char *, int); void dp_accounting_add (dp_conf*, unsigned char *, int);
int dp_accounting_check (dp_conf*, unsigned char *, int); int dp_accounting_check (dp_conf*, unsigned char *, int);
void dp_blacklist_add (dp_conf*, unsigned char *, int); void dp_blacklist_add (dp_conf*, unsigned char *, int);
int dp_blacklist_check (dp_conf*, unsigned char *, int); int dp_blacklist_check (dp_conf*, unsigned char *, int);
void dp_hash_cleanup (dp_conf*); void dp_hash_cleanup (dp_conf*);
void dp_log (unsigned char *, int, char *, ...); void dp_log (unsigned char *, int, char *, ...);
int dp_dhcp_check (struct nfq_data*, dp_conf*);

void dp_dhcpv4_check (
dp_conf*,
unsigned char*,
int,
int,
unsigned char**,
int*);

void dp_dhcpv6_check (
dp_conf*,
unsigned char*,
int,
int,
unsigned char**,
int*);

+ 49
- 35
perftest.pl View File



use strict; use strict;
use IO::Socket::INET; use IO::Socket::INET;
use IO::Socket::INET6;
use Time::HiRes qw(sleep); use Time::HiRes qw(sleep);
use warnings; use warnings;


exit 1; exit 1;
} }


if ( $dest !~ /^(\d+\.\d+\.\d+\.\d+)$/ ) {
print STDERR "destination IP must be a valid IPv4 address\n";
if ( $dest !~ /^(\d+\.\d+\.\d+\.\d+)$/ && $dest !~ /^[0-9a-fA-F:]+$/ ) {
print STDERR "destination IP must be a valid IPv4/IPv6 address\n";
exit 1; exit 1;
} }


sub main { sub main {
my($clients, $leasetime, $floodclients, $floodpps, $dest, $destport)=@_; my($clients, $leasetime, $floodclients, $floodpps, $dest, $destport)=@_;


# pseudo packet, the only really relevant thing
# is that option 82 is present and at the right place
my $hdr = ""; my $hdr = "";
my $sock;

if ( $dest =~ /:/ ) { # IPv6
$sock = IO::Socket::INET6->new(
Proto => 'udp',
PeerAddr => $dest,
PeerPort => $destport,
);

$hdr .= pack("H2", "01"); # msg-type
$hdr .= pack("H6", "000000"); # transaction-id
$hdr .= pack("C", 1); # DUID option
$hdr .= pack("C", 6); # DUID length
}
else { # IPv4
$sock = IO::Socket::INET->new(
Proto => 'udp',
PeerAddr => "$dest:$destport",
);
# pseudo packet, the only really relevant thing
# is that option 82 is present and at the right place
$hdr .= pack("H2","01"); # Opcode, bootrequest
$hdr .= pack("H2","01"); # Hardware type, ethernet
$hdr .= pack("H2","06"); # HW addr len, MAC = 6
$hdr .= pack("H2","01"); # hops, 1 relay
$hdr .= pack("H8","00000000"); # transaction ID
$hdr .= pack("H4","0000"); # secs
$hdr .= pack("H4","0000"); # flags
$hdr .= pack("H8","00000000"); # ciaddr
$hdr .= pack("H8","00000000"); # yiaddr
$hdr .= pack("H8","00000000"); # siaddr
$hdr .= pack("H8","00000000"); # giaddr
$hdr .= pack("H32","0"x32); # cihwaddr
$hdr .= pack("H128","0"x128); # hostname
$hdr .= pack("H256","0"x256); # boot filename
$hdr .= pack("H8","63825363"); # magic cookie

$hdr .= pack("C", 82); # opt 82
$hdr .= pack("C", 21); # opt 82 len
$hdr .= pack("C", 1); # subopt 1 (circuitid)
$hdr .= pack("C", 11); # subopt 1 len
$hdr .= "Hello World";
$hdr .= pack("C", 2); # subopt 2 (remoteid)
$hdr .= pack("C", 6); # subopt 2 len (mac)
}



$hdr .= pack("H2","01"); # Opcode, bootrequest
$hdr .= pack("H2","01"); # Hardware type, ethernet
$hdr .= pack("H2","06"); # HW addr len, MAC = 6
$hdr .= pack("H2","01"); # hops, 1 relay
$hdr .= pack("H8","00000000"); # transaction ID
$hdr .= pack("H4","0000"); # secs
$hdr .= pack("H4","0000"); # flags
$hdr .= pack("H8","00000000"); # ciaddr
$hdr .= pack("H8","00000000"); # yiaddr
$hdr .= pack("H8","00000000"); # siaddr
$hdr .= pack("H8","00000000"); # giaddr
$hdr .= pack("H32","0"x32); # cihwaddr
$hdr .= pack("H128","0"x128); # hostname
$hdr .= pack("H256","0"x256); # boot filename
$hdr .= pack("H8","63825363"); # magic cookie

$hdr .= pack("C", 82); # opt 82
$hdr .= pack("C", 21); # opt 82 len
$hdr .= pack("C", 1); # subopt 1 (circuitid)
$hdr .= pack("C", 11); # subopt 1 len
$hdr .= "Hello World";
$hdr .= pack("C", 2); # subopt 2 (remoteid)
$hdr .= pack("C", 6); # subopt 2 len (mac)




if ( $floodclients > 0 ) { if ( $floodclients > 0 ) {
if ( !$pid ) { if ( !$pid ) {


my $int = 1/$floodpps/$floodclients; my $int = 1/$floodpps/$floodclients;
my $sock = IO::Socket::INET->new(
Proto => 'udp',
PeerAddr => "$dest:$destport",
);


while(1) { while(1) {
for(my $i=0; $i<$floodclients; $i++) { for(my $i=0; $i<$floodclients; $i++) {
} }


my $int = $leasetime/3/$clients; my $int = $leasetime/3/$clients;
my $sock = IO::Socket::INET->new(
Proto => 'udp',
PeerAddr => "$dest:$destport",
);


while(1) { while(1) {
for(my $i=0; $i<$clients; $i++) { for(my $i=0; $i<$clients; $i++) {

Loading…
Cancel
Save