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