| 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 |