/************************************************************************** * Copyright 2019 Pascal Gloor * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **************************************************************************/ #include #include #include #include #include "dp_nfqueue.h" #include "dp_accounting.h" #include "dp_blacklist.h" #include "dp_helpers.h" #include "dp_dhcpv4.h" #include "dp_dhcpv6.h" // start netfilter queue void *dp_nfq_start(void *data) { dp_conf *conf = (dp_conf*)data; struct nfq_handle *h; struct nfq_q_handle *qh; int fd; if ( ( h = nfq_open() ) == NULL ) { fprintf(stderr,"error during nfq_open() %s\n", strerror(errno)); return NULL; } if ( ( qh = nfq_create_queue(h, conf->queue, &dp_callback, conf) ) == NULL ) { fprintf(stderr, "error during nfq_create_queue() %s\n", strerror(errno)); return NULL; } if ( nfq_set_mode(qh, NFQNL_COPY_PACKET, 1500) < 0 ) { fprintf(stderr,"error during nfq_set_mode() %s\n", strerror(errno)); return NULL; } if ( nfq_set_queue_flags(qh, NFQA_CFG_F_FAIL_OPEN, NFQA_CFG_F_FAIL_OPEN) < 0 ) { fprintf(stderr,"error during nfq_set_queue_flags() %s\n", strerror(errno)); return NULL; } fd = nfq_fd(h); while(1) { static char buf[65536]; int rv; rv = recv(fd, buf, sizeof(buf), 0); switch(rv) { case -1: fprintf(stderr, "recv() error: %s\n", strerror(errno)); return NULL; case 0: fprintf(stderr,"socket is closed!?\n"); return NULL; default: // send packet to callback nfq_handle_packet(h, buf, rv); break; } } return NULL; } // decode dhcp packet int dp_dhcp_check(struct nfq_data *nfa, dp_conf *conf) { unsigned char *pkt; int pktlen; int offset = 0; unsigned char *remoteid = NULL; int remoteidlen = 0; int rv = NF_ACCEPT; pktlen = nfq_get_payload(nfa, &pkt); if ( conf->debug ) printf("got a packet, len = %i\n", pktlen); // can we read the IP proto and IP header length ? if ( pktlen > 0 ) { uint8_t ipver; uint8_t ihl; ipver = pkt[offset]; ipver >>= 4; ihl = pkt[offset]; ihl &= 0x0f; if ( ipver == 4 ) { // jump to DHCPv4 offset += ( ihl * 4 ) + 8; dp_dhcpv4_check(conf, pkt, pktlen, offset, &remoteid, &remoteidlen); } else if ( ipver == 6 ) { // jump to DHCPv6 offset += 40 + 8; dp_dhcpv6_check(conf, pkt, pktlen, offset, &remoteid, &remoteidlen); } else { if ( conf->debug ) printf("not an IPv4 packet\n"); goto end; } } if ( conf->debug ) printf("remoteidlen=%i\n",remoteidlen); if ( remoteidlen>0 ) { if ( conf->debug ) { int i; printf("remoteid: "); for(i=0; ipacket_id); if ( conf->debug ) printf ("received packet with id %d\n", id); } verdict = dp_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 */ }