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 "Icmp6.h" |
---|
19 | #include "IcmpPinger.h" |
---|
20 | #include "leakcheck.h" |
---|
21 | #include "SquidTime.h" |
---|
22 | |
---|
23 | // Some system headers are only neeed internally here. |
---|
24 | // They should not be included via the header. |
---|
25 | |
---|
26 | #if HAVE_NETINET_IP6_H |
---|
27 | #include <netinet/ip6.h> |
---|
28 | #endif |
---|
29 | |
---|
30 | // Icmp6 OP-Codes |
---|
31 | // see http://www.iana.org/assignments/icmpv6-parameters |
---|
32 | static const char * |
---|
33 | IcmpPacketType(uint8_t v) |
---|
34 | { |
---|
35 | // NP: LowPktStr is for codes 0-127 |
---|
36 | static const char *icmp6LowPktStr[] = { |
---|
37 | "ICMPv6 0", // 0 |
---|
38 | "Destination Unreachable", // 1 - RFC2463 |
---|
39 | "Packet Too Big", // 2 - RFC2463 |
---|
40 | "Time Exceeded", // 3 - RFC2463 |
---|
41 | "Parameter Problem", // 4 - RFC2463 |
---|
42 | }; |
---|
43 | |
---|
44 | // low codes 1-4 registered |
---|
45 | if (0 < v && v < 5) |
---|
46 | return icmp6LowPktStr[(int)(v&0x7f)]; |
---|
47 | |
---|
48 | // NP: HighPktStr is for codes 128-255 |
---|
49 | static const char *icmp6HighPktStr[] = { |
---|
50 | "Echo Request", // 128 - RFC2463 |
---|
51 | "Echo Reply", // 129 - RFC2463 |
---|
52 | "Multicast Listener Query", // 130 - RFC2710 |
---|
53 | "Multicast Listener Report", // 131 - RFC2710 |
---|
54 | "Multicast Listener Done", // 132 - RFC2710 |
---|
55 | "Router Solicitation", // 133 - RFC4861 |
---|
56 | "Router Advertisement", // 134 - RFC4861 |
---|
57 | "Neighbor Solicitation", // 135 - RFC4861 |
---|
58 | "Neighbor Advertisement", // 136 - RFC4861 |
---|
59 | "Redirect Message", // 137 - RFC4861 |
---|
60 | "Router Renumbering", // 138 - Crawford |
---|
61 | "ICMP Node Information Query", // 139 - RFC4620 |
---|
62 | "ICMP Node Information Response", // 140 - RFC4620 |
---|
63 | "Inverse Neighbor Discovery Solicitation", // 141 - RFC3122 |
---|
64 | "Inverse Neighbor Discovery Advertisement", // 142 - RFC3122 |
---|
65 | "Version 2 Multicast Listener Report", // 143 - RFC3810 |
---|
66 | "Home Agent Address Discovery Request", // 144 - RFC3775 |
---|
67 | "Home Agent Address Discovery Reply", // 145 - RFC3775 |
---|
68 | "Mobile Prefix Solicitation", // 146 - RFC3775 |
---|
69 | "Mobile Prefix Advertisement", // 147 - RFC3775 |
---|
70 | "Certification Path Solicitation", // 148 - RFC3971 |
---|
71 | "Certification Path Advertisement", // 149 - RFC3971 |
---|
72 | "ICMP Experimental (150)", // 150 - RFC4065 |
---|
73 | "Multicast Router Advertisement", // 151 - RFC4286 |
---|
74 | "Multicast Router Solicitation", // 152 - RFC4286 |
---|
75 | "Multicast Router Termination", // 153 - [RFC4286] |
---|
76 | }; |
---|
77 | |
---|
78 | // high codes 127-153 registered |
---|
79 | if (127 < v && v < 154) |
---|
80 | return icmp6HighPktStr[(int)(v&0x7f)]; |
---|
81 | |
---|
82 | // give all others a generic display |
---|
83 | static char buf[50]; |
---|
84 | snprintf(buf, sizeof(buf), "ICMPv6 %u", v); |
---|
85 | return buf; |
---|
86 | } |
---|
87 | |
---|
88 | Icmp6::Icmp6() : Icmp() |
---|
89 | { |
---|
90 | ; // nothing new. |
---|
91 | } |
---|
92 | |
---|
93 | Icmp6::~Icmp6() |
---|
94 | { |
---|
95 | Close(); |
---|
96 | } |
---|
97 | |
---|
98 | int |
---|
99 | Icmp6::Open(void) |
---|
100 | { |
---|
101 | icmp_sock = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); |
---|
102 | |
---|
103 | if (icmp_sock < 0) { |
---|
104 | debugs(50, DBG_CRITICAL, HERE << " icmp_sock: " << xstrerror()); |
---|
105 | return -1; |
---|
106 | } |
---|
107 | |
---|
108 | icmp_ident = getpid() & 0xffff; |
---|
109 | debugs(42, DBG_IMPORTANT, "pinger: ICMPv6 socket opened"); |
---|
110 | |
---|
111 | return icmp_sock; |
---|
112 | } |
---|
113 | |
---|
114 | /** |
---|
115 | * Generates an RFC 4443 Icmp6 ECHO Packet and sends into the network. |
---|
116 | */ |
---|
117 | void |
---|
118 | Icmp6::SendEcho(Ip::Address &to, int opcode, const char *payload, int len) |
---|
119 | { |
---|
120 | int x; |
---|
121 | LOCAL_ARRAY(char, pkt, MAX_PKT6_SZ); |
---|
122 | struct icmp6_hdr *icmp = NULL; |
---|
123 | icmpEchoData *echo = NULL; |
---|
124 | struct addrinfo *S = NULL; |
---|
125 | size_t icmp6_pktsize = 0; |
---|
126 | |
---|
127 | memset(pkt, '\0', MAX_PKT6_SZ); |
---|
128 | icmp = (struct icmp6_hdr *)pkt; |
---|
129 | |
---|
130 | /* |
---|
131 | * cevans - beware signed/unsigned issues in untrusted data from |
---|
132 | * the network!! |
---|
133 | */ |
---|
134 | if (len < 0) { |
---|
135 | len = 0; |
---|
136 | } |
---|
137 | |
---|
138 | // Construct Icmp6 ECHO header |
---|
139 | icmp->icmp6_type = ICMP6_ECHO_REQUEST; |
---|
140 | icmp->icmp6_code = 0; |
---|
141 | icmp->icmp6_cksum = 0; |
---|
142 | icmp->icmp6_id = icmp_ident; |
---|
143 | icmp->icmp6_seq = (unsigned short) icmp_pkts_sent; |
---|
144 | ++icmp_pkts_sent; |
---|
145 | |
---|
146 | icmp6_pktsize = sizeof(struct icmp6_hdr); |
---|
147 | |
---|
148 | // Fill Icmp6 ECHO data content |
---|
149 | echo = (icmpEchoData *) (pkt + sizeof(icmp6_hdr)); |
---|
150 | echo->opcode = (unsigned char) opcode; |
---|
151 | memcpy(&echo->tv, ¤t_time, sizeof(struct timeval)); |
---|
152 | |
---|
153 | icmp6_pktsize += sizeof(struct timeval) + sizeof(char); |
---|
154 | |
---|
155 | if (payload) { |
---|
156 | if (len > MAX_PAYLOAD) |
---|
157 | len = MAX_PAYLOAD; |
---|
158 | |
---|
159 | memcpy(echo->payload, payload, len); |
---|
160 | |
---|
161 | icmp6_pktsize += len; |
---|
162 | } |
---|
163 | |
---|
164 | icmp->icmp6_cksum = CheckSum((unsigned short *) icmp, icmp6_pktsize); |
---|
165 | |
---|
166 | to.getAddrInfo(S); |
---|
167 | ((sockaddr_in6*)S->ai_addr)->sin6_port = 0; |
---|
168 | |
---|
169 | assert(icmp6_pktsize <= MAX_PKT6_SZ); |
---|
170 | |
---|
171 | debugs(42, 5, HERE << "Send Icmp6 packet to " << to << "."); |
---|
172 | |
---|
173 | x = sendto(icmp_sock, |
---|
174 | (const void *) pkt, |
---|
175 | icmp6_pktsize, |
---|
176 | 0, |
---|
177 | S->ai_addr, |
---|
178 | S->ai_addrlen); |
---|
179 | |
---|
180 | if (x < 0) { |
---|
181 | debugs(42, DBG_IMPORTANT, HERE << "Error sending to ICMPv6 packet to " << to << ". ERR: " << xstrerror()); |
---|
182 | } |
---|
183 | debugs(42,9, HERE << "x=" << x); |
---|
184 | |
---|
185 | Log(to, 0, NULL, 0, 0); |
---|
186 | Ip::Address::FreeAddr(S); |
---|
187 | } |
---|
188 | |
---|
189 | /** |
---|
190 | * Reads an RFC 4443 Icmp6 ECHO-REPLY Packet from the network. |
---|
191 | */ |
---|
192 | void |
---|
193 | Icmp6::Recv(void) |
---|
194 | { |
---|
195 | int n; |
---|
196 | struct addrinfo *from = NULL; |
---|
197 | // struct ip6_hdr *ip = NULL; |
---|
198 | static char *pkt = NULL; |
---|
199 | struct icmp6_hdr *icmp6header = NULL; |
---|
200 | icmpEchoData *echo = NULL; |
---|
201 | struct timeval now; |
---|
202 | static pingerReplyData preply; |
---|
203 | |
---|
204 | if (icmp_sock < 0) { |
---|
205 | debugs(42, DBG_CRITICAL, HERE << "dropping ICMPv6 read. No socket!?"); |
---|
206 | return; |
---|
207 | } |
---|
208 | |
---|
209 | if (pkt == NULL) { |
---|
210 | pkt = (char *)xmalloc(MAX_PKT6_SZ); |
---|
211 | } |
---|
212 | |
---|
213 | Ip::Address::InitAddr(from); |
---|
214 | |
---|
215 | n = recvfrom(icmp_sock, |
---|
216 | (void *)pkt, |
---|
217 | MAX_PKT6_SZ, |
---|
218 | 0, |
---|
219 | from->ai_addr, |
---|
220 | &from->ai_addrlen); |
---|
221 | |
---|
222 | if (n <= 0) { |
---|
223 | debugs(42, DBG_CRITICAL, HERE << "Error when calling recvfrom() on ICMPv6 socket."); |
---|
224 | Ip::Address::FreeAddr(from); |
---|
225 | return; |
---|
226 | } |
---|
227 | |
---|
228 | preply.from = *from; |
---|
229 | |
---|
230 | #if GETTIMEOFDAY_NO_TZP |
---|
231 | |
---|
232 | gettimeofday(&now); |
---|
233 | |
---|
234 | #else |
---|
235 | |
---|
236 | gettimeofday(&now, NULL); |
---|
237 | |
---|
238 | #endif |
---|
239 | |
---|
240 | debugs(42, 8, HERE << n << " bytes from " << preply.from); |
---|
241 | |
---|
242 | // FIXME INET6 : The IPv6 Header (ip6_hdr) is not availble directly >:-( |
---|
243 | // |
---|
244 | // TTL still has to come from the IP header somewhere. |
---|
245 | // still need to strip and process it properly. |
---|
246 | // probably have to rely on RTT as given by timestamp in data sent and current. |
---|
247 | /* IPv6 Header Structures (linux) |
---|
248 | struct ip6_hdr |
---|
249 | |
---|
250 | // fields (via simple define) |
---|
251 | #define ip6_vfc // N.A |
---|
252 | #define ip6_flow // N/A |
---|
253 | #define ip6_plen // payload length. |
---|
254 | #define ip6_nxt // expect to be type 0x3a - ICMPv6 |
---|
255 | #define ip6_hlim // MAX hops (always 64, but no guarantee) |
---|
256 | #define ip6_hops // HOPS!!! (can it be true??) |
---|
257 | |
---|
258 | ip = (struct ip6_hdr *) pkt; |
---|
259 | pkt += sizeof(ip6_hdr); |
---|
260 | |
---|
261 | debugs(42, DBG_CRITICAL, HERE << "ip6_nxt=" << ip->ip6_nxt << |
---|
262 | ", ip6_plen=" << ip->ip6_plen << |
---|
263 | ", ip6_hlim=" << ip->ip6_hlim << |
---|
264 | ", ip6_hops=" << ip->ip6_hops << |
---|
265 | " ::: 40 == sizef(ip6_hdr) == " << sizeof(ip6_hdr) |
---|
266 | ); |
---|
267 | */ |
---|
268 | |
---|
269 | icmp6header = (struct icmp6_hdr *) pkt; |
---|
270 | pkt += sizeof(icmp6_hdr); |
---|
271 | |
---|
272 | if (icmp6header->icmp6_type != ICMP6_ECHO_REPLY) { |
---|
273 | |
---|
274 | switch (icmp6header->icmp6_type) { |
---|
275 | case 134: |
---|
276 | case 135: |
---|
277 | case 136: |
---|
278 | /* ignore Router/Neighbour Advertisements */ |
---|
279 | break; |
---|
280 | |
---|
281 | default: |
---|
282 | debugs(42, 8, HERE << preply.from << " said: " << icmp6header->icmp6_type << "/" << (int)icmp6header->icmp6_code << " " << |
---|
283 | IcmpPacketType(icmp6header->icmp6_type)); |
---|
284 | } |
---|
285 | Ip::Address::FreeAddr(from); |
---|
286 | return; |
---|
287 | } |
---|
288 | |
---|
289 | if (icmp6header->icmp6_id != icmp_ident) { |
---|
290 | debugs(42, 8, HERE << "dropping Icmp6 read. IDENT check failed. ident=='" << icmp_ident << "'=='" << icmp6header->icmp6_id << "'"); |
---|
291 | Ip::Address::FreeAddr(from); |
---|
292 | return; |
---|
293 | } |
---|
294 | |
---|
295 | echo = (icmpEchoData *) pkt; |
---|
296 | |
---|
297 | preply.opcode = echo->opcode; |
---|
298 | |
---|
299 | struct timeval tv; |
---|
300 | memcpy(&tv, &echo->tv, sizeof(struct timeval)); |
---|
301 | preply.rtt = tvSubMsec(tv, now); |
---|
302 | |
---|
303 | /* |
---|
304 | * FIXME INET6: Without access to the IPv6-Hops header we must rely on the total RTT |
---|
305 | * and could caculate the hops from that, but it produces some weird value mappings using ipHops |
---|
306 | * for now everything is 1 v6 hop away with variant RTT |
---|
307 | * WANT: preply.hops = ip->ip6_hops; // ipHops(ip->ip_hops); |
---|
308 | */ |
---|
309 | preply.hops = 1; |
---|
310 | |
---|
311 | preply.psize = n - /* sizeof(ip6_hdr) - */ sizeof(icmp6_hdr) - (sizeof(icmpEchoData) - MAX_PKT6_SZ); |
---|
312 | |
---|
313 | /* Ensure the response packet has safe payload size */ |
---|
314 | if ( preply.psize > (unsigned short) MAX_PKT6_SZ) { |
---|
315 | preply.psize = MAX_PKT6_SZ; |
---|
316 | } else if ( preply.psize < (unsigned short)0) { |
---|
317 | preply.psize = 0; |
---|
318 | } |
---|
319 | |
---|
320 | Log(preply.from, |
---|
321 | icmp6header->icmp6_type, |
---|
322 | IcmpPacketType(icmp6header->icmp6_type), |
---|
323 | preply.rtt, |
---|
324 | preply.hops); |
---|
325 | |
---|
326 | /* send results of the lookup back to squid.*/ |
---|
327 | control.SendResult(preply, (sizeof(pingerReplyData) - PINGER_PAYLOAD_SZ + preply.psize) ); |
---|
328 | Ip::Address::FreeAddr(from); |
---|
329 | } |
---|
330 | |
---|
331 | #endif /* USE_ICMP */ |
---|
332 | |
---|