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.
#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); }
#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));
#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); }
/* 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); }
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); }
// 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");
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.
iptables -I INPUT 1 --in-interface lo --proto icmp --jump QUEUE
#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); }
unsigned char buf[BUFSIZE]; int rc = ipq_read(h, buf, BUFSIZE, 0 /*blocking*/);
int msg_type = ipq_message_type(buf);
IPQM_PACKET
- a real (IP)
packet.
int rc = ipq_set_verdict(h, msg->packet_id, NF_ACCEPT, 0, NULL);The '0' and 'NULL' mean "forward the original packet, I'm not supplying a replacement payload".
NF_ACCEPT
- allow the packet to continue its route.
NF_DROP
- drop the packet (without telling anyone, shh!).
// 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); } }