分类:
2010-05-28 16:40:02
We are going to begin with looking at some of the sample code provided in the man page of libipq. Its not completely obvious whats going on here with the direction and flow of packets, so I'll try and explain as we go.
Before you try running the code, you need to do a couple things:
- modprobe iptable_filter
- modprobe ip_queue
- iptables rule to direct packets to QUEUE
The ones we will be using are:
- iptables -A OUTPUT -p icmp -j QUEUE
- iptables -A INPUT -p tcp -j QUEUE
- iptables -A INPUT -p udp -j QUEUE
The code begins... We have some includes, one of which is for libipq, and the other is netfilter. We use the libipq one for all of the functions prefixed with ipq_ and the netfilter header is for some of the constants we use through out. Other useful headers include the ip header, tcp header, and any other packet type that you might want to be able to map into a structure.
#include#include #include #define BUFSIZE 2048 static void die(struct ipq_handle *h) { ipq_perror("passer"); ipq_destroy_handle(h); exit(1); } int main(int argc, char **argv) { int status; unsigned char buf[BUFSIZE];
Here we need a ipq_handle in order to use later when we want to do things, such as read packets and do any other libipq operations. Note we also have a buf[BUFSIZE] above this, which is a static sized buffer for packet data. We create the handle, and move or die trying.
struct ipq_handle *h; h = ipq_create_handle(0, PF_INET); if (!h) die(h);
Now we determine the mode that we want to use to get packets. The different modes are IPQ_COPY_META and IPQ_COPY_PACKET. Basically we need to know if we want payloads as well as meta-data. This also initiates the transfer of packets from the QUEUE to user-space. If we specify IPQ_COPY_PACKET, then the 3rd argument is the size of the payload we are going to want. If this fails we die.
status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE); if (status < 0) die(h); do{
Next we read the contents of a packet, with ipq_read. This says to read into buf, for a maximum size of BUFSIZE (we don't want to overflow the buffer right...). The last arg, in this case 0, is the timeout for the read operation. This is the same as a select() function timeout.
status = ipq_read(h, buf, BUFSIZE, 0); if (status < 0) die(h); switch (ipq_message_type(buf)) { case NLMSG_ERROR: fprintf(stderr, "Received error message %d\n", ipq_get_msgerr(buf)); break;
Based on the message type, we can determine an error (NLMSG_ERROR) or if we got a packet (IPQM_PACKET). Then we use the packet_msg_t to access the packet, and then set the verdict (ACCEPT or DROP). ACCEPT allows the packet through the netfilter hooks, and into the appropriate service. DROP makes the packet disappear.
Also in the IPQM_PACKET block we have some additional code which is not in the man page. This is just an example of how to access parts of the packets, such as the IP layer header and the TCP header. To point the headers at the right places you basically just need the size of each previous header. Then by offsetting from the m->payload pointer we can access the different portions of the packet. Once we have this, you can get any of the data contained in the header, such as the port in this example.
case IPQM_PACKET: { ipq_packet_msg_t *m = ipq_get_packet(buf); struct iphdr *ip = (struct iphdr*) m->payload; struct tcphdr *tcp = (struct tcphdr*) (m->payload + (4 * ip->ihl)); int port = htons(tcp->dest); status = ipq_set_verdict(h, m->packet_id, NF_ACCEPT, 0, NULL); if (status < 0) die(h); break; } default: fprintf(stderr, "Unknown message type!\n"); break; } } while (1);
After we are done, we cleanup and exit. Need to remove the hook to cleanly end all of the ipq stuff.
ipq_destroy_handle(h); return 0; }