]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ICMPv6.cc
Import IPv6 support from squid3-ipv6 branch to 3-HEAD.
[thirdparty/squid.git] / src / ICMPv6.cc
1 /*
2 * $Id: ICMPv6.cc,v 1.1 2007/12/14 23:11:45 amosjeffries Exp $
3 *
4 * DEBUG: section 42 ICMP Pinger program
5 * AUTHOR: Duane Wessels, Amos Jeffries
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34 //#define SQUID_HELPER 1
35
36 #include "squid.h"
37
38 #if USE_ICMP && USE_IPV6
39
40 #include "SquidTime.h"
41 #include "Debug.h"
42 #include "ICMPv6.h"
43 #include "ICMPPinger.h"
44
45 // Some system headers are only neeed internally here.
46 // They should not be included via the header.
47
48 #if HAVE_NETINET_IP6_H
49 #include <netinet/ip6.h>
50 #endif
51
52 // ICMPv6 OP-Codes
53 // see http://www.iana.org/assignments/icmpv6-parameters
54 // NP: LowPktStr is for codes 0-127
55 static const char *icmp6LowPktStr[] =
56 {
57 "ICMP 0", // 0
58 "Destination Unreachable", // 1 - RFC2463
59 "Packet Too Big", // 2 - RFC2463
60 "Time Exceeded", // 3 - RFC2463
61 "Parameter Problem", // 4 - RFC2463
62 "ICMP 5", // 5
63 "ICMP 6", // 6
64 "ICMP 7", // 7
65 "ICMP 8", // 8
66 "ICMP 9", // 9
67 "ICMP 10" // 10
68 };
69
70 // NP: HighPktStr is for codes 128-255
71 static const char *icmp6HighPktStr[] =
72 {
73 "Echo Request", // 128 - RFC2463
74 "Echo Reply", // 129 - RFC2463
75 "Multicast Listener Query", // 130 - RFC2710
76 "Multicast Listener Report", // 131 - RFC2710
77 "Multicast Listener Done", // 132 - RFC2710
78 "Router Solicitation", // 133 - RFC4861
79 "Router Advertisement", // 134 - RFC4861
80 "Neighbor Solicitation", // 135 - RFC4861
81 "Neighbor Advertisement", // 136 - RFC4861
82 "Redirect Message", // 137 - RFC4861
83 "Router Renumbering", // 138 - Crawford
84 "ICMP Node Information Query", // 139 - RFC4620
85 "ICMP Node Information Response", // 140 - RFC4620
86 "Inverse Neighbor Discovery Solicitation", // 141 - RFC3122
87 "Inverse Neighbor Discovery Advertisement", // 142 - RFC3122
88 "Version 2 Multicast Listener Report", // 143 - RFC3810
89 "Home Agent Address Discovery Request", // 144 - RFC3775
90 "Home Agent Address Discovery Reply", // 145 - RFC3775
91 "Mobile Prefix Solicitation", // 146 - RFC3775
92 "Mobile Prefix Advertisement", // 147 - RFC3775
93 "Certification Path Solicitation", // 148 - RFC3971
94 "Certification Path Advertisement", // 149 - RFC3971
95 "ICMP Experimental (150)", // 150 - RFC4065
96 "Multicast Router Advertisement", // 151 - RFC4286
97 "Multicast Router Solicitation", // 152 - RFC4286
98 "Multicast Router Termination", // 153 - [RFC4286]
99 "ICMP 154",
100 "ICMP 155",
101 "ICMP 156",
102 "ICMP 157",
103 "ICMP 158",
104 "ICMP 159",
105 "ICMP 160"
106 };
107
108 ICMPv6::ICMPv6() : ICMP()
109 {
110 ; // nothing new.
111 }
112
113 ICMPv6::~ICMPv6()
114 {
115 Close();
116 }
117
118 int
119 ICMPv6::Open(void)
120 {
121 icmp_sock = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
122
123 if (icmp_sock < 0) {
124 debugs(50, 0, HERE << " icmp_sock: " << xstrerror());
125 return -1;
126 }
127
128 icmp_ident = getpid() & 0xffff;
129 debugs(42, 1, "pinger: ICMPv6 socket opened");
130
131 return icmp_sock;
132 }
133
134 /**
135 * Generates an RFC 4443 ICMPv6 ECHO Packet and sends into the network.
136 */
137 void
138 ICMPv6::SendEcho(IPAddress &to, int opcode, const char *payload, int len)
139 {
140 int x;
141 LOCAL_ARRAY(char, pkt, MAX_PKT6_SZ);
142 struct icmp6_hdr *icmp = NULL;
143 icmpEchoData *echo = NULL;
144 struct addrinfo *S = NULL;
145 size_t icmp6_pktsize = 0;
146
147 memset(pkt, '\0', MAX_PKT6_SZ);
148 icmp = (struct icmp6_hdr *)pkt;
149
150 /*
151 * cevans - beware signed/unsigned issues in untrusted data from
152 * the network!!
153 */
154 if (len < 0) {
155 len = 0;
156 }
157
158 // Construct ICMPv6 ECHO header
159 icmp->icmp6_type = ICMP6_ECHO_REQUEST;
160 icmp->icmp6_code = 0;
161 icmp->icmp6_cksum = 0;
162 icmp->icmp6_id = icmp_ident;
163 icmp->icmp6_seq = (u_short) icmp_pkts_sent++;
164
165 icmp6_pktsize = sizeof(struct icmp6_hdr);
166
167
168 // Fill ICMPv6 ECHO data content
169 echo = (icmpEchoData *) (pkt + sizeof(icmp6_hdr));
170 echo->opcode = (unsigned char) opcode;
171 echo->tv = current_time;
172
173 icmp6_pktsize += sizeof(struct timeval) + sizeof(char);
174
175 if (payload)
176 {
177 if (len > MAX_PAYLOAD)
178 len = MAX_PAYLOAD;
179
180 xmemcpy(echo->payload, payload, len);
181
182 icmp6_pktsize += len;
183 }
184
185 icmp->icmp6_cksum = CheckSum((u_short *) icmp, icmp6_pktsize);
186
187 to.GetAddrInfo(S);
188 ((sockaddr_in6*)S->ai_addr)->sin6_port = 0;
189
190 assert(icmp6_pktsize <= MAX_PKT6_SZ);
191
192 debugs(42, 5, HERE << "Send ICMPv6 packet to " << to << ".");
193
194 x = sendto(icmp_sock,
195 (const void *) pkt,
196 icmp6_pktsize,
197 0,
198 S->ai_addr,
199 S->ai_addrlen);
200
201 if(x < 0) {
202 debugs(42, 1, HERE << "Error sending to ICMPv6 packet to " << to << ". ERR: " << xstrerror());
203 }
204 debugs(42,9, HERE << "x=" << x);
205
206 Log(to, 0, NULL, 0, 0);
207 }
208
209 /**
210 * Reads an RFC 4443 ICMPv6 ECHO-REPLY Packet from the network.
211 */
212 void
213 ICMPv6::Recv(void)
214 {
215 int n;
216 struct addrinfo *from = NULL;
217 // struct ip6_hdr *ip = NULL;
218 static char *pkt = NULL;
219 struct icmp6_hdr *icmp6 = NULL;
220 icmpEchoData *echo = NULL;
221 struct timeval now;
222 static pingerReplyData preply;
223
224 if(icmp_sock < 0) {
225 debugs(42,0, HERE << "dropping ICMPv6 read. No socket!?");
226 return;
227 }
228
229 if (pkt == NULL) {
230 pkt = (char *)xmalloc(MAX_PKT6_SZ);
231 }
232
233 preply.from.InitAddrInfo(from);
234
235 n = recvfrom(icmp_sock,
236 (void *)pkt,
237 MAX_PKT6_SZ,
238 0,
239 from->ai_addr,
240 &from->ai_addrlen);
241
242 preply.from = *from;
243
244 #if GETTIMEOFDAY_NO_TZP
245
246 gettimeofday(&now);
247
248 #else
249
250 gettimeofday(&now, NULL);
251
252 #endif
253
254 debugs(42, 8, HERE << n << " bytes from " << preply.from);
255
256 // FIXME INET6 : The IPv6 Header (ip6_hdr) is not availble directly >:-(
257 //
258 // TTL still has to come from the IP header somewhere.
259 // still need to strip and process it properly.
260 // probably have to rely on RTT as given by timestamp in data sent and current.
261 /* IPv6 Header Structures (linux)
262 struct ip6_hdr
263
264 // fields (via simple define)
265 #define ip6_vfc // N.A
266 #define ip6_flow // N/A
267 #define ip6_plen // payload length.
268 #define ip6_nxt // expect to be type 0x3a - ICMPv6
269 #define ip6_hlim // MAX hops (always 64, but no guarantee)
270 #define ip6_hops // HOPS!!! (can it be true??)
271
272
273 ip = (struct ip6_hdr *) pkt;
274 pkt += sizeof(ip6_hdr);
275
276 debugs(42,0, HERE << "ip6_nxt=" << ip->ip6_nxt <<
277 ", ip6_plen=" << ip->ip6_plen <<
278 ", ip6_hlim=" << ip->ip6_hlim <<
279 ", ip6_hops=" << ip->ip6_hops <<
280 " ::: 40 == sizef(ip6_hdr) == " << sizeof(ip6_hdr)
281 );
282 */
283
284 icmp6 = (struct icmp6_hdr *) pkt;
285 pkt += sizeof(icmp6_hdr);
286
287 if (icmp6->icmp6_type != ICMP6_ECHO_REPLY) {
288
289 switch(icmp6->icmp6_type) {
290 case 134:
291 case 135:
292 case 136:
293 /* ignore Router/Neighbour Advertisements */
294 break;
295
296 default:
297 debugs(42, 8, HERE << preply.from << " said: " << icmp6->icmp6_type << "/" << (int)icmp6->icmp6_code << " " <<
298 ( icmp6->icmp6_type&0x80 ? icmp6HighPktStr[(int)(icmp6->icmp6_type&0x7f)] : icmp6LowPktStr[(int)(icmp6->icmp6_type&0x7f)] )
299 );
300 }
301 return;
302 }
303
304 if (icmp6->icmp6_id != icmp_ident) {
305 debugs(42, 8, HERE << "dropping ICMPv6 read. IDENT check failed. ident=='" << icmp_ident << "'=='" << icmp6->icmp6_id << "'");
306 return;
307 }
308
309 echo = (icmpEchoData *) pkt;
310
311 preply.opcode = echo->opcode;
312
313 preply.rtt = tvSubMsec(echo->tv, now);
314
315 /*
316 * FIXME INET6: Without access to the IPv6-Hops header we must rely on the total RTT
317 * and could caculate the hops from that, but it produces some weird value mappings using ipHops
318 * for now everything is 1 v6 hop away with variant RTT
319 * WANT: preply.hops = ip->ip6_hops; // ipHops(ip->ip_hops);
320 */
321 preply.hops = 1;
322
323 preply.psize = n - /* sizeof(ip6_hdr) - */ sizeof(icmp6_hdr) - (sizeof(icmpEchoData) - MAX_PKT6_SZ);
324
325 /* Ensure the response packet has safe payload size */
326 if( preply.psize > (unsigned short) MAX_PKT6_SZ) {
327 preply.psize = MAX_PKT6_SZ;
328 }
329 else if( preply.psize < (unsigned short)0) {
330 preply.psize = 0;
331 }
332
333 Log(preply.from,
334 icmp6->icmp6_type,
335 ( icmp6->icmp6_type&0x80 ? icmp6HighPktStr[(int)(icmp6->icmp6_type&0x7f)] : icmp6LowPktStr[(int)(icmp6->icmp6_type&0x7f)] ),
336 preply.rtt,
337 preply.hops);
338
339 /* send results of the lookup back to squid.*/
340 control.SendResult(preply, (sizeof(pingerReplyData) - PINGER_PAYLOAD_SZ + preply.psize) );
341 }
342
343 #endif /* USE_ICMP && USE_IPV6 */