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

 

Introduction

Documentation

Support

Miscellaneous