The IP Protocol injected, inspected, detected, infected - Part II

On the previous chapter of "IP injected, inspected, detected, infected" -

Today we will look at the other part - injecting new IP packets, and infecting existing IP packets.

IP Injection - This is raw stuff, human!

Creating Raw Sockets

Lets create a raw socket in mode I, for use with the ICMP protocol. First the include files:
#include <sys/types.h>
#include <sys/socket.h>
Then creating the socket:
int s = socket(PF_NET, SOCK_RAW, IPPROTO_ICMP);
if (s == -1) {
    perror("raw socket():");
    exit(1);
}

Sending A Packet Via The Raw Socket

More include files:
#include <netinet/ip_icmp.h>
And code:
struct icmphdr icmphdr

/* clear out the packet, and fill with contents. */
memset(&icmphdr, 0, sizeof(struct icmphdr));
icmphdr.type = ICMP_ECHO;
icmphdr.un.echo.sequence = 50;  /* just some random number. */
icmphdr.un.echo.id = 48;        /* just some random number. */
icmphdr.checksum =
    in_cksum((unsigned short*)&icmphdr, sizeof(struct icmphdr));

Sending A Packet Via The Raw Socket (Cont.)

Even more include files (for 'inet_aton'):
#include <netinet/in.h>
#include <arpa/inet.h>
And code:
struct sockaddr_in addr;

// prepare the address we're sending the packet to.
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
inet_aton("127.0.0.1", &addr.sin_addr);

// finally, send the packet.
rc = sendto(s,
            &icpmhdr, sizeof(struct icmphdr),
            0 /* flags */,
            (struct sockaddr*)&addr, sizeof(addr));
if (rc == -1) {
    perror("sendto:");
    exit(1);
}

Receiving Packets Using A Raw Socket

Remember - we receive _every_ packet sent to the machine, not just "expected responses".
/* the received packet contains the IP header... */
char rbuf[sizeof(struct iphdr) + sizeof(struct icmp)];
struct sockaddr_in raddr;
socklen_t raddr_len;

// receive the packet that we sent (since we sent it to ourselves,
// and a raw socket sees everything...).
rc = recvfrom(s,
              rbuf, sizeof(rbuf),
              0 /* flags */,
              (struct sockaddr*)&raddr, &raddr_len);
if (rc == -1) {
    perror("recvfrom 1:");
    exit(1);
}

Parse The Received Packet

struct iphdr* iphdr = NULL;
struct icmphdr* recv_icmphdr = NULL;

// we got an IP packet - verify that it contains an ICMP message.
iphdr = (struct iphdr*)rbuf;
if (iphdr->protocol != IPPROTO_ICMP) {
    fprintf(stderr, "Expected ICMP packet, got %u\n", iphdr->protocol);
    exit(1);
}

Parse The Received Packet (Cont.)

// verify that it's an ICMP echo request, with the expected seq. num + id.
icmphdr = (struct icmphdr*)(rbuf + (iphdr->ihl * 4));
if (icmphdr->type != ICMP_ECHOREPLY) {
    fprintf(stderr, "Expected ICMP echo-reply, got %u\n", icmphdr->type);
    exit(1);
}
if (icmphdr->un.echo.sequence != 50) {
    fprintf(stderr,
            "Expected sequence 50, got %d\n", icmphdr->un.echo.sequence);
    exit(1);
}
if (icmphdr->un.echo.id != 48) {
    fprintf(stderr,
            "Expected id 48, got %d\n", icmphdr->un.echo.id);
    exit(1);
}
printf("Got the expected ICMP echo-request\n");

Creating The IP Header Too

In order to create the complete IP packet (including the header), we use a socket option:

char on = 1;
setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));
From now, the kernel expects us to supply a complete IP packet.

Netfilter, ip_queue And Netlink

Defining The Iptables Rule

The 'ipq' Library

Initializing libipq

#include <linux/netfilter.h>  /* \ for libipq methods */
#include <libipq.h>           /* / and types.         */

/* we want to get IP traffic. */
struct ipq_handle *h = ipq_create_handle(0, PF_INET);
if (!h) {
    ipq_perror("ipq_create_handle:");
    exit(1);
}

// set the queuing mode - we want to have the packets copied
// to user space, with up to BUFSIZE octets copied.
#define BUFSIZE 2048
rc = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE);
if (rc < 0) {
    ipq_perror("ipq_set_mode:");
    ipq_destroy_handle(h);
    exit(1);
}

Reading Packets From The Queue

"Judging Them Little Buggers"

A Packet Reading Loop

// loop forever, reading packets.
while (1) {
    int msg_type;

    rc = ipq_read(h, buf, BUFSIZE, 0);
    if (rc < 0) {
        ipq_perror("ipq_read:");
        ipq_destroy_handle(h);
        exit(1);
    }

    msg_type = ipq_message_type(buf);
    switch (msg_type) {
        case NLMSG_ERROR:
            fprintf(stderr, "ipq_read got error %d",
                    ipq_get_msgerr(buf));
            break;
        case IPQM_PACKET:
            {
                ipq_packet_msg_t* msg = ipq_get_packet(buf);
                rc = ipq_set_verdict(h, msg->packet_id, NF_ACCEPT, 0, NULL);
            }
            break;
        default:
            fprintf(stderr, "unknown ipq msg type %d\n", msg_type);
    }
}

References

  1. Raw IP Networking FAQ - http://www.faqs.org/faqs/internet/tcp-ip/raw-ip-faq/
  2. The "libnet" Packet Construction Library - http://www.packetfactory.net/Projects/Libnet/
  3. Iptables Download - http://www.netfilter.org/downloads.html
  4. man libipq
Originally written by Valid HTML 4.01!guy keren