Only attempt this if you are familiar with:
* C/C++
* IP Tables
* IP Networking
And I really don't recommend doing this on a live server! If, once you've coded your funky new firewall it drops packets and you can't get SSH access to kill it, then you'll see why.
Right, now here's an interesting library which comes with IP Route 2 for linux. Using this library you can tell your IP stack to put packets in a queue, you can then write an app to read through the packets and either drop or accept them (even modify them if you read further into it). The great thing is, this is all in user space so there's none of that messy kernel re-compiling.
Why? Well I'll give you an example.
Say you find IP tables limiting for example, because you can't tell it to allow packets to port 666 only when there's a full moon. Bad example, infact as you can get your firewall todo pretty much anything you can possibly think of its quite an interesting concept. I ended up using this todo pattern recognition on the packet size and source address to detect dDos attacks and it worked pretty well. As its all in user space you can load config files and make a fully blown firewall.
Now to continue, you're going to need all the usual tools suchas gcc and you can write this in either C or C++.
You're also going to need the libraries and IP route 2 installed.
Once you've installed the devel libraries for netfilter and networking etc. You will need to check that you have both the iptable_filter and ip_queue modules loaded:
# lsmod | grep iptable_filter
# lsmod | grep ip_queue
If not, then load them!
# modprobe iptable_filter
# modprobe ip_queue
Right now, you can use the action 'QUEUE' in iptables, for example to queue any tcp packets:
# iptables -A OUTPUT -p tcp -j QUEUE
Note that these packets will sit in the queue until the application runs, so as I mentioned, don't do this unless you're on a terminal.
Now for the code.
Include some standard libraries, which you will probably need
#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
Right now we include some standard socket libraries
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
Include the netfilter library, make sure this is installed
#include <linux/netfilter.h>
Now we include the ip queue library, note if you're compiling in c++ then tell the compiler to pretend its C like so:
extern "C" { // compile in c
#include <libipq.h>
}
Or for those just keeping it in C:
#include <libipq.h>
Now we include the structs for ip, tcp and udp packets (obviously icmp and others are available as well.
If you want to know what the structs are then read the header files which should give you a good pointer.
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
Define the buffer size for reading off the queue
#define BUFSIZE 2048
using namespace std;
Here's a clean function which makes sure the handle is destroyed incase we need to quit, pass the handle to the function to die
static void die(struct ipq_handle *h)
{
ipq_perror("passer");
ipq_destroy_handle(h);
exit(1);
}
Now here's another function which you might find helpful for easily converting between an int and a readable IP. Pass it an unsigned 32 and it returns the string.
char * inttoip ( __u32 ip ) {
in_addr src_addr;
src_addr.s_addr = (ip);
return inet_ntoa(src_addr);
}
int main() {
// packet counter
unsigned short int packet_count = 0;
int status;
unsigned char buf[BUFSIZE];
struct ipq_handle *h;
cout << "Creating IP Queue handle..\n";
h = ipq_create_handle(0, PF_INET);
if (!h)
die(h);
cout << "Handle created, setting mode to " << IPQ_COPY_PACKET << " .\n";
status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE);
if (status < 0)
die(h);
cout << "IP Queue ready, listening for all queued packets. \n";
do{
status = ipq_read(h, buf, BUFSIZE, 0);
if (status < 0)
die(h);
if (ipq_message_type(buf) == NLMSG_ERROR ){
fprintf(stderr, "Received error message %d\n", ipq_get_msgerr(buf));
cout <<"make sure you are root and ip_queue module is loaded.\n";
exit(1);
}
else if (ipq_message_type(buf) == IPQM_PACKET) {
ipq_packet_msg_t *m = ipq_get_packet(buf);
struct iphdr *ip = (struct iphdr*) m->payload;
Now for quick reference, here's the struct for iphdr:
__u8 tos;
__u16 tot_len;
__u16 id;
__u16 frag_off;
__u8 ttl;
__u8 protocol;
__u16 check;
__u32 saddr;
__u32 daddr;
And if, for example, the protocol was TCP, then you can typecast the packet to a tcp struct like so:
struct tcphdr *tcp = (struct tcphdr*) (m->payload + (4 * ip->ihl));
Once we have that we can do:
if ( tcp->dport == '666' && IS_FULL_MOON && USER_IS_WEARING_SOCKS)
status = ipq_set_verdict(h, m->packet_id,NF_ACCEPT, 0, NULL);
else
status = ipq_set_verdict(h, m->packet_id,NF_DROP, 0, NULL);
}
else{
fprintf(stderr, "Unknown message type :\n");
}
} while (1);
ipq_destroy_handle(h);
return 0;
}
No comments:
Post a Comment