forked from DhavalKapil/icmptunnel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
icmp.c
244 lines (198 loc) · 5.53 KB
/
icmp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/**
* icmp.c
*/
#include "icmp.h"
#include <arpa/inet.h>
#include <stdint.h>
#include <string.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
/**
* Function to calculate checksum
*/
uint16_t in_cksum(uint16_t *addr, int len);
/**
* Function to fill up common headers for IP and ICMP
*/
void prepare_headers(struct iphdr *ip, struct icmphdr *icmp);
/**
* Function to set packet type as ECHO
*/
void set_echo_type(struct icmp_packet *packet)
{
packet->type = ICMP_ECHO;
}
/**
* Function to set packet type as REPLY
*/
void set_reply_type(struct icmp_packet *packet)
{
packet->type = ICMP_ECHOREPLY;
}
/**
* Function to open a socket for icmp
*/
int open_icmp_socket()
{
int sock_fd, on = 1;
sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sock_fd == -1) {
perror("Unable to open ICMP socket\n");
exit(EXIT_FAILURE);
}
// Providing IP Headers
if (setsockopt(sock_fd, IPPROTO_IP, IP_HDRINCL, (const char *)&on, sizeof(on)) == -1) {
perror("Unable to set IP_HDRINCL socket option\n");
exit(EXIT_FAILURE);
}
return sock_fd;
}
/**
* Function to bind the socket to INADDR_ANY
*/
void bind_icmp_socket(int sock_fd)
{
struct sockaddr_in servaddr;
// Initializing servaddr to bind to all interfaces
memset(&servaddr, 0, sizeof(struct sockaddr_in));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
// binding the socket
if (bind(sock_fd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)) == -1) {
perror("Unable to bind\n");
exit(EXIT_FAILURE);
}
}
/**
* Function to send ICMP Packet
*/
void send_icmp_packet(int sock_fd, struct icmp_packet *packet_details)
{
// Source and destination IPs
struct in_addr src_addr;
struct in_addr dest_addr;
struct iphdr *ip;
struct icmphdr *icmp;
char *icmp_payload;
int packet_size;
char *packet;
struct sockaddr_in servaddr;
inet_pton(AF_INET, packet_details->src_addr, &src_addr);
inet_pton(AF_INET, packet_details->dest_addr, &dest_addr);
packet_size = sizeof(struct iphdr) + sizeof(struct icmphdr) + packet_details->payload_size;
packet = calloc(packet_size, sizeof(uint8_t));
if (packet == NULL) {
perror("No memory available\n");
close_icmp_socket(sock_fd);
exit(EXIT_FAILURE);
}
// Initializing header and payload pointers
ip = (struct iphdr *)packet;
icmp = (struct icmphdr *)(packet + sizeof(struct iphdr));
icmp_payload = (char *)(packet + sizeof(struct iphdr) + sizeof(struct icmphdr));
prepare_headers(ip, icmp);
ip->tot_len = htons(packet_size);
ip->saddr = src_addr.s_addr;
ip->daddr = dest_addr.s_addr;
memcpy(icmp_payload, packet_details->payload, packet_details->payload_size);
icmp->type = packet_details->type;
icmp->checksum = 0;
icmp->checksum = in_cksum((unsigned short *)icmp, sizeof(struct icmphdr) + packet_details->payload_size);
memset(&servaddr, 0, sizeof(struct sockaddr_in));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = dest_addr.s_addr;
// Sending the packet
sendto(sock_fd, packet, packet_size, 0, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in));
free(packet);
}
/**
* Function to receive an ICMP packet
*/
void receive_icmp_packet(int sock_fd, struct icmp_packet *packet_details)
{
struct sockaddr_in src_addr;
struct sockaddr_in dest_addr;
struct iphdr *ip;
struct icmphdr *icmp;
char *icmp_payload;
int packet_size;
char *packet;
int src_addr_size;
packet = calloc(MTU, sizeof(uint8_t));
if (packet == NULL) {
perror("No memory available\n");
close_icmp_socket(sock_fd);
exit(-1);
}
src_addr_size = sizeof(struct sockaddr_in);
// Receiving packet
packet_size = recvfrom(sock_fd, packet, MTU, 0, (struct sockaddr *)&(src_addr), &src_addr_size);
ip = (struct iphdr *)packet;
icmp = (struct icmphdr *)(packet + sizeof(struct iphdr));
icmp_payload = (char *)(packet + sizeof(struct iphdr) + sizeof(struct icmphdr));
// Filling up packet_details
inet_ntop(AF_INET, &(ip->saddr), packet_details->src_addr, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(ip->daddr), packet_details->dest_addr, INET_ADDRSTRLEN);
packet_details->type = icmp->type;
packet_details->payload_size = packet_size - sizeof(struct iphdr) - sizeof(struct icmphdr);
packet_details->payload = calloc(packet_details->payload_size, sizeof(uint8_t));
if (packet_details->payload == NULL) {
perror("No memory available\n");
close_icmp_socket(sock_fd);
exit(-1);
}
memcpy(packet_details->payload, icmp_payload, packet_details->payload_size);
free(packet);
}
/**
* Function to close the icmp socket
*/
void close_icmp_socket(int sock_fd)
{
close(sock_fd);
}
/**
* Function to calculate checksum
*/
uint16_t in_cksum(uint16_t *addr, int len)
{
int nleft = len;
uint32_t sum = 0;
uint16_t *w = addr;
uint16_t answer = 0;
// Adding 16 bits sequentially in sum
while (nleft > 1) {
sum += *w;
nleft -= 2;
w++;
}
// If an odd byte is left
if (nleft == 1) {
*(unsigned char *) (&answer) = *(unsigned char *) w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
/**
* Function to fill up common headers for IP and ICMP
*/
void prepare_headers(struct iphdr *ip, struct icmphdr *icmp)
{
ip->version = 4;
ip->ihl = 5;
ip->tos = 0;
ip->id = rand();
ip->frag_off = 0;
ip->ttl = 255;
ip->protocol = IPPROTO_ICMP;
icmp->code = 0;
icmp->un.echo.sequence = rand();
icmp->un.echo.id = rand();
icmp->checksum = 0;
}