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);
}
}