]> git.ipfire.org Git - thirdparty/squid.git/blob - src/icmp/Icmp4.cc
e1c05163b5d19dfee91552bf8ffe86f7997efede
[thirdparty/squid.git] / src / icmp / Icmp4.cc
1 /*
2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 42 ICMP Pinger program */
10
11 //#define SQUID_HELPER 1
12
13 #include "squid.h"
14
15 #if USE_ICMP
16
17 #include "Debug.h"
18 #include "Icmp4.h"
19 #include "IcmpPinger.h"
20 #include "leakcheck.h"
21 #include "SquidTime.h"
22
23 static const char *
24 IcmpPacketType(uint8_t v)
25 {
26 static const char *icmpPktStr[] = {
27 "Echo Reply",
28 "ICMP 1",
29 "ICMP 2",
30 "Destination Unreachable",
31 "Source Quench",
32 "Redirect",
33 "ICMP 6",
34 "ICMP 7",
35 "Echo",
36 "ICMP 9",
37 "ICMP 10",
38 "Time Exceeded",
39 "Parameter Problem",
40 "Timestamp",
41 "Timestamp Reply",
42 "Info Request",
43 "Info Reply",
44 "Out of Range Type"
45 };
46
47 if (v > 17) {
48 static char buf[50];
49 snprintf(buf, sizeof(buf), "ICMP %u (invalid)", v);
50 return buf;
51 }
52
53 return icmpPktStr[v];
54 }
55
56 Icmp4::Icmp4() : Icmp()
57 {
58 ;
59 }
60
61 Icmp4::~Icmp4()
62 {
63 Close();
64 }
65
66 int
67 Icmp4::Open(void)
68 {
69 icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
70
71 if (icmp_sock < 0) {
72 int xerrno = errno;
73 debugs(50, DBG_CRITICAL, MYNAME << " icmp_sock: " << xstrerr(xerrno));
74 return -1;
75 }
76
77 icmp_ident = getpid() & 0xffff;
78 debugs(42, DBG_IMPORTANT, "pinger: ICMP socket opened.");
79
80 return icmp_sock;
81 }
82
83 void
84 Icmp4::SendEcho(Ip::Address &to, int opcode, const char *payload, int len)
85 {
86 int x;
87 LOCAL_ARRAY(char, pkt, MAX_PKT4_SZ);
88
89 struct icmphdr *icmp = NULL;
90 icmpEchoData *echo;
91 size_t icmp_pktsize = sizeof(struct icmphdr);
92 struct addrinfo *S = NULL;
93
94 memset(pkt, '\0', MAX_PKT4_SZ);
95
96 icmp = (struct icmphdr *) (void *) pkt;
97
98 /*
99 * cevans - beware signed/unsigned issues in untrusted data from
100 * the network!!
101 */
102 if (len < 0) {
103 len = 0;
104 }
105
106 // Construct ICMP packet header
107 icmp->icmp_type = ICMP_ECHO;
108 icmp->icmp_code = 0;
109 icmp->icmp_cksum = 0;
110 icmp->icmp_id = icmp_ident;
111 icmp->icmp_seq = (unsigned short) icmp_pkts_sent;
112 ++icmp_pkts_sent;
113
114 // Construct ICMP packet data content
115 echo = (icmpEchoData *) (icmp + 1);
116 echo->opcode = (unsigned char) opcode;
117 memcpy(&echo->tv, &current_time, sizeof(struct timeval));
118
119 icmp_pktsize += sizeof(struct timeval) + sizeof(char);
120
121 if (payload) {
122 if (len > MAX_PAYLOAD)
123 len = MAX_PAYLOAD;
124
125 memcpy(echo->payload, payload, len);
126
127 icmp_pktsize += len;
128 }
129
130 icmp->icmp_cksum = CheckSum((unsigned short *) icmp, icmp_pktsize);
131
132 to.getAddrInfo(S);
133 ((sockaddr_in*)S->ai_addr)->sin_port = 0;
134 assert(icmp_pktsize <= MAX_PKT4_SZ);
135
136 debugs(42, 5, HERE << "Send ICMP packet to " << to << ".");
137
138 x = sendto(icmp_sock,
139 (const void *) pkt,
140 icmp_pktsize,
141 0,
142 S->ai_addr,
143 S->ai_addrlen);
144
145 if (x < 0) {
146 int xerrno = errno;
147 debugs(42, DBG_IMPORTANT, MYNAME << "ERROR: sending to ICMP packet to " << to << ": " << xstrerr(xerrno));
148 }
149
150 Log(to, ' ', NULL, 0, 0);
151 Ip::Address::FreeAddr(S);
152 }
153
154 void
155 Icmp4::Recv(void)
156 {
157 int n;
158 struct addrinfo *from = NULL;
159 int iphdrlen = sizeof(iphdr);
160 struct iphdr *ip = NULL;
161 struct icmphdr *icmp = NULL;
162 static char *pkt = NULL;
163 struct timeval now;
164 icmpEchoData *echo;
165 static pingerReplyData preply;
166
167 if (icmp_sock < 0) {
168 debugs(42, DBG_CRITICAL, HERE << "No socket! Recv() should not be called.");
169 return;
170 }
171
172 if (pkt == NULL)
173 pkt = (char *)xmalloc(MAX_PKT4_SZ);
174
175 Ip::Address::InitAddr(from);
176 n = recvfrom(icmp_sock,
177 (void *)pkt,
178 MAX_PKT4_SZ,
179 0,
180 from->ai_addr,
181 &from->ai_addrlen);
182
183 if (n <= 0) {
184 debugs(42, DBG_CRITICAL, HERE << "Error when calling recvfrom() on ICMP socket.");
185 Ip::Address::FreeAddr(from);
186 return;
187 }
188
189 preply.from = *from;
190
191 #if GETTIMEOFDAY_NO_TZP
192
193 gettimeofday(&now);
194
195 #else
196
197 gettimeofday(&now, NULL);
198
199 #endif
200
201 debugs(42, 8, HERE << n << " bytes from " << preply.from);
202
203 ip = (struct iphdr *) (void *) pkt;
204
205 #if HAVE_STRUCT_IPHDR_IP_HL
206
207 iphdrlen = ip->ip_hl << 2;
208
209 #else /* HAVE_STRUCT_IPHDR_IP_HL */
210 #if WORDS_BIGENDIAN
211
212 iphdrlen = (ip->ip_vhl >> 4) << 2;
213
214 #else
215
216 iphdrlen = (ip->ip_vhl & 0xF) << 2;
217
218 #endif
219 #endif /* HAVE_STRUCT_IPHDR_IP_HL */
220
221 icmp = (struct icmphdr *) (void *) (pkt + iphdrlen);
222
223 if (icmp->icmp_type != ICMP_ECHOREPLY) {
224 Ip::Address::FreeAddr(from);
225 return;
226 }
227
228 if (icmp->icmp_id != icmp_ident) {
229 Ip::Address::FreeAddr(from);
230 return;
231 }
232
233 echo = (icmpEchoData *) (void *) (icmp + 1);
234
235 preply.opcode = echo->opcode;
236
237 preply.hops = ipHops(ip->ip_ttl);
238
239 struct timeval tv;
240 memcpy(&tv, &echo->tv, sizeof(struct timeval));
241 preply.rtt = tvSubMsec(tv, now);
242
243 preply.psize = n - iphdrlen - (sizeof(icmpEchoData) - MAX_PKT4_SZ);
244
245 if (preply.psize < 0) {
246 debugs(42, DBG_CRITICAL, HERE << "Malformed ICMP packet.");
247 Ip::Address::FreeAddr(from);
248 return;
249 }
250
251 control.SendResult(preply, (sizeof(pingerReplyData) - MAX_PKT4_SZ + preply.psize) );
252
253 Log(preply.from, icmp->icmp_type, IcmpPacketType(icmp->icmp_type), preply.rtt, preply.hops);
254 Ip::Address::FreeAddr(from);
255 }
256
257 #endif /* USE_ICMP */
258