Packet Interception Application: Simple Firewall

In continuation with the previous blog, we will now have a look at a basic agent which can act as a firewall. This firewall will drop all the packets outgoing to a particular IP destination and let other packets go through.

First we ensure that the following IPTABLES rule is installed:

sudo iptables -A OUTPUT -p ip -j NFQUEUE --queue-num 0

We can validate this by listing all the iptable rules using:  

sudo iptables –list

Note: Since the rule is installed but no handler/agent exists yet, no packets will go through just yet. So it is important to flush the iptables when no agent is running. This can be done using:

sudo iptables –flush

Assuming that the above rule is successfully installed, the user space agent needs to perform following functions:

  • bind nfnetlink_queue library handle as nf_queue_handler for socket AF_INET
  • bind the socket to the queue-number specified in iptables command
  • handle packets in a callback function.

A basic agent which intercepts a packet and issues NF_ACCEPT verdict can be coded like this: 

static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data 
*nfa, void *data){ 
u_int32_t id = treat_pkt(nfa, &verdict); /* User defined Treat packet function*/ 
return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL); /* Verdict packet */ 

} 

int main(int agrc, char **argv){ 
h = nfq_open(); /*open library handle*/ 
nfq_bind_pf(h, AF_INET); /*bind handle tp AF_INET*/ 
qh = nfq_create_queue(h, 0, &cb, NULL); /*bind socket to queue number 0 and 
register callback function cb*/ 
for(;;){ 
if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) { 
nfq_handle_packet(h, buf, rv); 
continue; 

} 

} 

nfq_close(h); /*Close library handle*/ 

}

Here cb is a callback function which we registered before receiving packets. This callback function is where we can modify, accept or drop a packet. Below is one such example of a callback function which drops packets destined to a particular IP. 

static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data 
*nfa, void *data){ 

size_t payloadLen = 0; 

unsigned char *foo; 

struct ipv4hdr *ipv4hdr; 

uint32_t id = 0; 

struct nfqnl_msg_packet_hdr *ph; 

payloadLen = nfq_get_payload(nfa, &foo); 

ipv4hdr = (struct ipv4hdr *)foo; 

ph = nfq_get_msg_packet_hdr(nfa); 

id = ntohl(ph->packet_id); 


if (ipv4hdr->dst_ip == <<integer IP>>) 

return nfq_set_verdict(qh, id, NF_DROP, 0, NULL); 

else 

return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL); 

}

 

We use nfq_get_payload in order to cast the received packet data into our required data structure. In this case we have used the following struct for casting into ipv4hdr variable.

//IPV4 header structure RFC791 ref. 

struct ipv4hdr{ 

uint8_t version_ihl; 

uint8_t type_of_service; 

uint16_t len; 

uint16_t id; 

uint16_t flags_frags; 

uint8_t live_time; 

uint8_t hw_proto; 

uint16_t checksum; 

uint32_t src_ip; 

uint32_t dst_ip; 

uint32_t options_padding; 

};

nfq_set_verdict is used to either DROP or ACCEPT the packet depending on the requirements. Note that checksum recalculation is not required when no modification is done. 

In the next blog, we will present a use case in SDN where netfilter capabilities can be used.


 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s