|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( conf->debug ) printf("got a packet, len = %i\n", pktlen); |
|
|
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 ) { |
|
|
ipver = pkt[offset]; |
|
|
ipver = pkt[offset]; |
|
|
|
|
|
|
|
|
ihl = pkt[offset]; |
|
|
ihl = pkt[offset]; |
|
|
ihl &= 0x0f; |
|
|
ihl &= 0x0f; |
|
|
|
|
|
|
|
|
if ( ipver != 4 ) { |
|
|
|
|
|
|
|
|
if ( ipver == 4 ) { |
|
|
|
|
|
// jump to DHCPv4 |
|
|
|
|
|
offset += ( ihl * 4 ) + 8; |
|
|
|
|
|
dhcpv4_check(conf, pkt, pktlen, offset, &remoteid, &remoteidlen); |
|
|
|
|
|
} |
|
|
|
|
|
else if ( ipver == 6 ) { |
|
|
|
|
|
// jump to DHCPv6 |
|
|
|
|
|
offset += 48 + 8; |
|
|
|
|
|
dhcpv6_check(conf, pkt, pktlen, offset, &remoteid, &remoteidlen); |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
if ( conf->debug ) printf("not an IPv4 packet\n"); |
|
|
if ( conf->debug ) printf("not an IPv4 packet\n"); |
|
|
rv = NF_ACCEPT; |
|
|
|
|
|
goto end; |
|
|
goto end; |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// jump over the IPv4 header |
|
|
|
|
|
offset += ihl * 4; |
|
|
|
|
|
|
|
|
if ( remoteidlen>0 ) { |
|
|
|
|
|
// 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; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
end: |
|
|
|
|
|
|
|
|
|
|
|
dp_hash_cleanup(conf); |
|
|
|
|
|
|
|
|
// jump over UDP + DHCP header |
|
|
|
|
|
offset += 8 + 28 + 16 + 64 + 128; |
|
|
|
|
|
|
|
|
return rv; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void dhcpv6_check(dp_conf *conf, unsigned char *pkt, int pktlen, int offset, unsigned char *remoteid, int *remoteidlen) { |
|
|
|
|
|
|
|
|
|
|
|
while(offset<pktlen) { |
|
|
|
|
|
uint8_t msgtype = (uint8_t)pkt[offset]; |
|
|
|
|
|
|
|
|
|
|
|
switch(msgtype) { |
|
|
|
|
|
case 11: // RELAY-FORW |
|
|
|
|
|
case 12: // RELAY-REPL |
|
|
|
|
|
offset += 2 + 16 + 16; // msg-type, hop-count, link-addr, peer-addr |
|
|
|
|
|
break; |
|
|
|
|
|
default: // all other msgtypes |
|
|
|
|
|
offset += 2; // msg-type, hop-count |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
while(offset+1<pktlen) { |
|
|
|
|
|
uint8_t code = (uint8_t)pkt[offset]; |
|
|
|
|
|
uint8_t len = (uint8_t)pkt[offset+1]; |
|
|
|
|
|
|
|
|
|
|
|
offset+2; |
|
|
|
|
|
|
|
|
|
|
|
if ( code == 1 ) { // Client identifier / DUID |
|
|
|
|
|
remoteid = pkt+offset; |
|
|
|
|
|
*remoteidlen = len; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
offset+=len; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ( *remoteidlen>0 ) |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void 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 hwlenpos = offset + 2; // remember where the hw addr len is if we need to fallback to it |
|
|
|
|
|
|
|
|
|
|
|
// jump DHCP header |
|
|
|
|
|
offset += 28 + 16 + 64 + 128; |
|
|
|
|
|
|
|
|
// 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"); |
|
|
rv = NF_ACCEPT; |
|
|
|
|
|
goto end; |
|
|
|
|
|
|
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// check magic cookie |
|
|
// check magic cookie |
|
|
|
|
|
|
|
|
"invalid magic cookie %02x%02x%02x%02x\n", |
|
|
"invalid magic cookie %02x%02x%02x%02x\n", |
|
|
pkt[offset], pkt[offset+1], |
|
|
pkt[offset], pkt[offset+1], |
|
|
pkt[offset+2], pkt[offset+3]); |
|
|
pkt[offset+2], pkt[offset+3]); |
|
|
|
|
|
|
|
|
rv = NF_ACCEPT; |
|
|
|
|
|
goto end; |
|
|
|
|
|
|
|
|
return; |
|
|
} |
|
|
} |
|
|
offset+=4; |
|
|
offset+=4; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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"); |
|
|
rv = NF_ACCEPT; |
|
|
|
|
|
goto end; |
|
|
|
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
remoteid = o82 + o82off; |
|
|
remoteid = o82 + o82off; |
|
|
remoteidlen = olen; |
|
|
|
|
|
found=1; |
|
|
|
|
|
|
|
|
*remoteidlen = olen; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
offset+=len; |
|
|
offset+=len; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if ( found ) { |
|
|
|
|
|
// 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; |
|
|
|
|
|
|
|
|
// if we didn't find opt82.2, we fallback to the hw addr |
|
|
|
|
|
if ( *remoteidlen == 0 ) { |
|
|
|
|
|
// nope, we won't overflow |
|
|
|
|
|
if ( (uint8_t)pkt[hwlenpos] <= 16 ) { |
|
|
|
|
|
remoteid = pkt+hwaddrpos; |
|
|
|
|
|
remoteidlen = (uint8_t)pkt[hwlenpos]; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
end: |
|
|
|
|
|
|
|
|
|
|
|
dp_hash_cleanup(conf); |
|
|
|
|
|
|
|
|
|
|
|
return rv; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// netfilter queue callback |
|
|
// netfilter queue callback |