]> git.ipfire.org Git - thirdparty/squid.git/blob - src/icmp/Icmp4.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / icmp / Icmp4.cc
1 /*
2 * Copyright (C) 1996-2015 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 debugs(50, DBG_CRITICAL, HERE << " icmp_sock: " << xstrerror());
73 return -1;
74 }
75
76 icmp_ident = getpid() & 0xffff;
77 debugs(42, DBG_IMPORTANT, "pinger: ICMP socket opened.");
78
79 return icmp_sock;
80 }
81
82 void
83 Icmp4::SendEcho(Ip::Address &to, int opcode, const char *payload, int len)
84 {
85 int x;
86 LOCAL_ARRAY(char, pkt, MAX_PKT4_SZ);
87
88 struct icmphdr *icmp = NULL;
89 icmpEchoData *echo;
90 size_t icmp_pktsize = sizeof(struct icmphdr);
91 struct addrinfo *S = NULL;
92
93 memset(pkt, '\0', MAX_PKT4_SZ);
94
95 icmp = (struct icmphdr *) (void *) pkt;
96
97 /*
98 * cevans - beware signed/unsigned issues in untrusted data from
99 * the network!!
100 */
101 if (len < 0) {
102 len = 0;
103 }
104
105 // Construct ICMP packet header
106 icmp->icmp_type = ICMP_ECHO;
107 icmp->icmp_code = 0;
108 icmp->icmp_cksum = 0;
109 icmp->icmp_id = icmp_ident;
110 icmp->icmp_seq = (unsigned short) icmp_pkts_sent;
111 ++icmp_pkts_sent;
112
113 // Construct ICMP packet data content
114 echo = (icmpEchoData *) (icmp + 1);
115 echo->opcode = (unsigned char) opcode;
116 memcpy(&echo->tv, &current_time, sizeof(struct timeval));
117
118 icmp_pktsize += sizeof(struct timeval) + sizeof(char);
119
120 if (payload) {
121 if (len > MAX_PAYLOAD)
122 len = MAX_PAYLOAD;
123
124 memcpy(echo->payload, payload, len);
125
126 icmp_pktsize += len;
127 }
128
129 icmp->icmp_cksum = CheckSum((unsigned short *) icmp, icmp_pktsize);
130
131 to.getAddrInfo(S);
132 ((sockaddr_in*)S->ai_addr)->sin_port = 0;
133 assert(icmp_pktsize <= MAX_PKT4_SZ);
134
135 debugs(42, 5, HERE << "Send ICMP packet to " << to << ".");
136
137 x = sendto(icmp_sock,
138 (const void *) pkt,
139 icmp_pktsize,
140 0,
141 S->ai_addr,
142 S->ai_addrlen);
143
144 if (x < 0) {
145 debugs(42, DBG_IMPORTANT, HERE << "Error sending to ICMP packet to " << to << ". ERR: " << xstrerror());
146 }
147
148 Log(to, ' ', NULL, 0, 0);
149 Ip::Address::FreeAddr(S);
150 }
151
152 void
153 Icmp4::Recv(void)
154 {
155 int n;
156 struct addrinfo *from = NULL;
157 int iphdrlen = sizeof(iphdr);
158 struct iphdr *ip = NULL;
159 struct icmphdr *icmp = NULL;
160 static char *pkt = NULL;
161 struct timeval now;
162 icmpEchoData *echo;
163 static pingerReplyData preply;
164
165 if (icmp_sock < 0) {
166 debugs(42, DBG_CRITICAL, HERE << "No socket! Recv() should not be called.");
167 return;
168 }
169
170 if (pkt == NULL)
171 pkt = (char *)xmalloc(MAX_PKT4_SZ);
172
173 Ip::Address::InitAddr(from);
174 n = recvfrom(icmp_sock,
175 (void *)pkt,
176 MAX_PKT4_SZ,
177 0,
178 from->ai_addr,
179 &from->ai_addrlen);
180
181 if (n <= 0) {
182 debugs(42, DBG_CRITICAL, HERE << "Error when calling recvfrom() on ICMP socket.");
183 Ip::Address::FreeAddr(from);
184 return;
185 }
186
187 preply.from = *from;
188
189 #if GETTIMEOFDAY_NO_TZP
190
191 gettimeofday(&now);
192
193 #else
194
195 gettimeofday(&now, NULL);
196
197 #endif
198
199 debugs(42, 8, HERE << n << " bytes from " << preply.from);
200
201 ip = (struct iphdr *) (void *) pkt;
202
203 #if HAVE_STRUCT_IPHDR_IP_HL
204
205 iphdrlen = ip->ip_hl << 2;
206
207 #else /* HAVE_STRUCT_IPHDR_IP_HL */
208 #if WORDS_BIGENDIAN
209
210 iphdrlen = (ip->ip_vhl >> 4) << 2;
211
212 #else
213
214 iphdrlen = (ip->ip_vhl & 0xF) << 2;
215
216 #endif
217 #endif /* HAVE_STRUCT_IPHDR_IP_HL */
218
219 icmp = (struct icmphdr *) (void *) (pkt + iphdrlen);
220
221 if (icmp->icmp_type != ICMP_ECHOREPLY) {
222 Ip::Address::FreeAddr(from);
223 return;
224 }
225
226 if (icmp->icmp_id != icmp_ident) {
227 Ip::Address::FreeAddr(from);
228 return;
229 }
230
231 echo = (icmpEchoData *) (void *) (icmp + 1);
232
233 preply.opcode = echo->opcode;
234
235 preply.hops = ipHops(ip->ip_ttl);
236
237 struct timeval tv;
238 memcpy(&tv, &echo->tv, sizeof(struct timeval));
239 preply.rtt = tvSubMsec(tv, now);
240
241 preply.psize = n - iphdrlen - (sizeof(icmpEchoData) - MAX_PKT4_SZ);
242
243 if (preply.psize < 0) {
244 debugs(42, DBG_CRITICAL, HERE << "Malformed ICMP packet.");
245 Ip::Address::FreeAddr(from);
246 return;
247 }
248
249 control.SendResult(preply, (sizeof(pingerReplyData) - MAX_PKT4_SZ + preply.psize) );
250
251 Log(preply.from, icmp->icmp_type, IcmpPacketType(icmp->icmp_type), preply.rtt, preply.hops);
252 Ip::Address::FreeAddr(from);
253 }
254
255 #endif /* USE_ICMP */
256