-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathping.c
More file actions
208 lines (175 loc) · 4.34 KB
/
ping.c
File metadata and controls
208 lines (175 loc) · 4.34 KB
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
/*
* Heavily modified version of the busybox ping.c implementation for the etherwake-nfqueue project
*
* Original Copyright Notice:
* Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
*
* Adapted from the ping in netkit-base 0.10:
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Mike Muuss.
*
* Licensed under GPLv2 or later
*/
#include <stdio.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#include <poll.h>
enum {
DEFDATALEN = 56,
MAXIPLEN = 60,
MAXICMPLEN = 76,
};
int socket_fd = 0;
struct icmp *ping_packet;
struct sockaddr_in receive_addr;
char send_packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
char receive_packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
int timeout = 0;
int get_dest_addr(const char *hostname)
{
struct addrinfo hints, *res;
int ret;
ret = inet_aton(hostname, &receive_addr.sin_addr);
if (ret == 0) {
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_RAW;
hints.ai_family = AF_INET;
if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
fprintf(stderr, "getaddrinfo() failed\n");
return false;
}
memcpy(&receive_addr.sin_addr,
&((struct sockaddr_in *)res->ai_addr)->sin_addr,
sizeof(struct in_addr));
freeaddrinfo(res);
}
return true;
}
static int create_icmp_socket()
{
struct protoent *protocol;
protocol = getprotobyname("icmp");
if (protocol == NULL) {
perror("getprotobyname() failed");
return false;
}
socket_fd = socket(AF_INET, SOCK_RAW, protocol->p_proto);
if (socket_fd < 0) {
if (errno == EPERM) {
fprintf(stderr,
"Failed creating network socket. Are you root?");
} else {
perror("socket() failed");
}
return false;
}
int flags = fcntl(socket_fd, F_GETFL);
fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK);
return true;
}
static uint16_t cal_chksum(uint16_t *addr, int nleft)
{
/*
* Our algorithm is simple, using a 32 bit accumulator,
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
unsigned sum = 0;
while (nleft > 1) {
sum += *addr++;
nleft -= 2;
}
/* Mop up an odd byte, if necessary */
if (nleft == 1) {
sum += *(uint8_t *)addr;
}
/* Add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
return (uint16_t)~sum;
}
static void create_ping_packet(uint16_t process_pid)
{
ping_packet = (struct icmp *)send_packet;
ping_packet->icmp_type = ICMP_ECHO;
ping_packet->icmp_id = process_pid;
ping_packet->icmp_cksum =
cal_chksum((uint16_t *)ping_packet, sizeof(send_packet));
}
int setup_ping(const char *hostname)
{
uint16_t process_pid;
if (!get_dest_addr(hostname)) {
fprintf(stderr,
"Failed getting destination address! Is the address correct?\n");
return false;
}
if (!create_icmp_socket()) {
fprintf(stderr,
"Failed creating ICMP socket?\n");
return false;
}
process_pid = htons(getpid());
create_ping_packet(process_pid);
return true;
}
void alarm_handler(int signum)
{
(void)signum;
timeout = 1;
}
int send_ping()
{
ssize_t c;
int received = 0;
struct icmp *receive_icmp;
if (sendto(socket_fd, ping_packet, DEFDATALEN + ICMP_MINLEN, 0,
(struct sockaddr *)&receive_addr,
sizeof(receive_addr)) < 0) {
perror("sendto()");
}
signal(SIGALRM, alarm_handler);
alarm(1);
struct pollfd poll_struct;
poll_struct.fd = socket_fd;
poll_struct.events = POLLIN;
while (!timeout && !received) {
poll(&poll_struct, 1, 1000);
c = recv(socket_fd, receive_packet, sizeof(receive_packet), 0);
if (c < 0) {
if (errno != EINTR && errno != EAGAIN &&
errno != EWOULDBLOCK)
perror("recv()");
continue;
}
if (c >= 76) { /* ip + icmp */
struct iphdr *iphdr = (struct iphdr *)receive_packet;
receive_icmp =
(struct icmp *)(receive_packet +
(iphdr->ihl
<< 2)); /* skip ip hdr */
if (receive_icmp->icmp_id == ping_packet->icmp_id &&
receive_icmp->icmp_type == ICMP_ECHOREPLY)
received = 1;
}
}
return received;
}
void cleanup_ping()
{
close(socket_fd);
}