Icmp4.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 "Icmp4.h"
21 #include "IcmpPinger.h"
22 #include "time/gadgets.h"
23 
24 static const char *
25 IcmpPacketType(uint8_t v)
26 {
27  static const char *icmpPktStr[] = {
28  "Echo Reply",
29  "ICMP 1",
30  "ICMP 2",
31  "Destination Unreachable",
32  "Source Quench",
33  "Redirect",
34  "ICMP 6",
35  "ICMP 7",
36  "Echo",
37  "ICMP 9",
38  "ICMP 10",
39  "Time Exceeded",
40  "Parameter Problem",
41  "Timestamp",
42  "Timestamp Reply",
43  "Info Request",
44  "Info Reply",
45  "Out of Range Type"
46  };
47 
48  if (v > 17) {
49  static char buf[50];
50  snprintf(buf, sizeof(buf), "ICMP %u (invalid)", v);
51  return buf;
52  }
53 
54  return icmpPktStr[v];
55 }
56 
58 {
59  ;
60 }
61 
63 {
64  Close();
65 }
66 
67 int
69 {
70  icmp_sock = xsocket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
71 
72  if (icmp_sock < 0) {
73  int xerrno = errno;
74  debugs(50, DBG_CRITICAL, MYNAME << " icmp_sock: " << xstrerr(xerrno));
75  return -1;
76  }
77 
78  icmp_ident = getpid() & 0xffff;
79  debugs(42, DBG_IMPORTANT, "ICMP socket opened.");
80 
81  return icmp_sock;
82 }
83 
84 void
85 Icmp4::SendEcho(Ip::Address &to, int opcode, const char *payload, int len)
86 {
87  int x;
88  LOCAL_ARRAY(char, pkt, MAX_PKT4_SZ);
89 
90  struct icmphdr *icmp = nullptr;
91  icmpEchoData *echo;
92  size_t icmp_pktsize = sizeof(struct icmphdr);
93  struct addrinfo *S = nullptr;
94 
95  static_assert(sizeof(*icmp) + sizeof(*echo) <= sizeof(pkt), "our custom ICMPv4 Echo payload fits the packet buffer");
96 
97  memset(pkt, '\0', MAX_PKT4_SZ);
98 
99  icmp = (struct icmphdr *) (void *) pkt;
100 
101  /*
102  * cevans - beware signed/unsigned issues in untrusted data from
103  * the network!!
104  */
105  if (len < 0) {
106  len = 0;
107  }
108 
109  // Construct ICMP packet header
110  icmp->icmp_type = ICMP_ECHO;
111  icmp->icmp_code = 0;
112  icmp->icmp_cksum = 0;
113  icmp->icmp_id = icmp_ident;
114  icmp->icmp_seq = (unsigned short) icmp_pkts_sent;
115  ++icmp_pkts_sent;
116 
117  // Construct ICMP packet data content
118  echo = reinterpret_cast<icmpEchoData *>(reinterpret_cast<char *>(pkt) + sizeof(*icmp));
119  echo->opcode = (unsigned char) opcode;
120  memcpy(&echo->tv, &current_time, sizeof(struct timeval));
121 
122  icmp_pktsize += sizeof(icmpEchoData) - MAX_PAYLOAD;
123 
124  if (payload) {
125  if (len > MAX_PAYLOAD)
126  len = MAX_PAYLOAD;
127 
128  memcpy(echo->payload, payload, len);
129 
130  icmp_pktsize += len;
131  }
132 
133  icmp->icmp_cksum = CheckSum((unsigned short *) icmp, icmp_pktsize);
134 
135  to.getAddrInfo(S, AF_INET);
136  Assure(S);
137 
138  ((sockaddr_in*)S->ai_addr)->sin_port = 0;
139  assert(icmp_pktsize <= MAX_PKT4_SZ);
140 
141  debugs(42, 5, "Send ICMP packet to " << to << ".");
142 
143  x = xsendto(icmp_sock,
144  pkt,
145  icmp_pktsize,
146  0,
147  S->ai_addr,
148  S->ai_addrlen);
149 
150  if (x < 0) {
151  int xerrno = errno;
152  debugs(42, DBG_IMPORTANT, "ERROR: sending ICMP packet to " << to << ": " << xstrerr(xerrno));
153  }
154 
155  Log(to, ' ', "", 0, 0);
157 }
158 
159 void
161 {
162  int n;
163  struct addrinfo *from = nullptr;
164  int iphdrlen = sizeof(iphdr);
165  struct iphdr *ip = nullptr;
166  struct icmphdr *icmp = nullptr;
167  static char *pkt = nullptr;
168  struct timeval now;
169  icmpEchoData *echo;
170  static pingerReplyData preply;
171 
172  if (icmp_sock < 0) {
173  debugs(42, DBG_CRITICAL, "No socket! Recv() should not be called.");
174  return;
175  }
176 
177  if (pkt == nullptr)
178  pkt = (char *)xmalloc(MAX_PKT4_SZ);
179 
180  Ip::Address::InitAddr(from);
181  n = xrecvfrom(icmp_sock,
182  pkt,
183  MAX_PKT4_SZ,
184  0,
185  from->ai_addr,
186  &from->ai_addrlen);
187 
188  if (n <= 0) {
189  debugs(42, DBG_CRITICAL, "ERROR: when calling recvfrom() on ICMP socket.");
190  Ip::Address::FreeAddr(from);
191  return;
192  }
193 
194  preply.from = *from;
195 
196 #if GETTIMEOFDAY_NO_TZP
197 
198  gettimeofday(&now);
199 
200 #else
201 
202  gettimeofday(&now, nullptr);
203 
204 #endif
205 
206  debugs(42, 8, n << " bytes from " << preply.from);
207 
208  ip = (struct iphdr *) (void *) pkt;
209  if (n < static_cast<int>(sizeof(*ip))) {
210  debugs(42, 2, "short packet: only " << n << " bytes; expecting at least " << sizeof(*ip) << "-byte IP header");
211  Ip::Address::FreeAddr(from);
212  return;
213  }
214 
215 #if HAVE_STRUCT_IPHDR_IP_HL
216 
217  iphdrlen = ip->ip_hl << 2;
218 
219 #else /* HAVE_STRUCT_IPHDR_IP_HL */
220 #if WORDS_BIGENDIAN
221 
222  iphdrlen = (ip->ip_vhl >> 4) << 2;
223 
224 #else
225 
226  iphdrlen = (ip->ip_vhl & 0xF) << 2;
227 
228 #endif
229 #endif /* HAVE_STRUCT_IPHDR_IP_HL */
230 
231  if (iphdrlen < 20 || n < iphdrlen) {
232  debugs(42, 2, "bogus IP header length " << iphdrlen << " in " << n << "-byte packet");
233  Ip::Address::FreeAddr(from);
234  return;
235  }
236  icmp = (struct icmphdr *) (void *) (pkt + iphdrlen);
237  const int icmpAvail = n - iphdrlen;
238  if (icmpAvail < static_cast<int>(sizeof(*icmp))) {
239  debugs(42, 2, "short ICMP header: only " << icmpAvail << " bytes available; expecting at least " << sizeof(*icmp));
240  Ip::Address::FreeAddr(from);
241  return;
242  }
243 
244  if (icmp->icmp_type != ICMP_ECHOREPLY) {
245  Ip::Address::FreeAddr(from);
246  return;
247  }
248 
249  if (icmp->icmp_id != icmp_ident) {
250  Ip::Address::FreeAddr(from);
251  return;
252  }
253 
254  echo = (icmpEchoData *) (void *) (icmp + 1);
255 
256  const auto echoHdr = static_cast<int>(sizeof(icmpEchoData) - MAX_PAYLOAD);
257  const auto icmpDataLen = icmpAvail - sizeof(*icmp);
258  if (icmpDataLen < echoHdr) { // do not read past end of the packet
259  debugs(42, 2, "short ICMP echo data: " << icmpDataLen << " bytes; expecting " << echoHdr);
260  Ip::Address::FreeAddr(from);
261  return;
262  }
263 
264  preply.opcode = echo->opcode;
265 
266  preply.hops = ipHops(ip->ip_ttl);
267 
268  struct timeval tv;
269  memcpy(&tv, &echo->tv, sizeof(struct timeval));
270  preply.rtt = tvSubMsec(tv, now);
271 
272  // Payload length = (ICMP total data) - (opcode + timeval)
273  preply.psize = icmpDataLen - echoHdr;
274  if (preply.psize > MAX_PAYLOAD)
275  preply.psize = MAX_PAYLOAD;
276 
277  if (preply.psize < 0) {
278  debugs(42, DBG_CRITICAL, "ERROR: Malformed ICMP packet.");
279  Ip::Address::FreeAddr(from);
280  return;
281  }
282 
283  control.SendResult(preply, (sizeof(pingerReplyData) - PINGER_PAYLOAD_SZ + preply.psize));
284 
285  Log(preply.from, icmp->icmp_type, IcmpPacketType(icmp->icmp_type), preply.rtt, preply.hops);
286  Ip::Address::FreeAddr(from);
287 }
288 
289 #endif /* USE_ICMP */
290 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
#define DBG_CRITICAL
Definition: Stream.h:37
#define xmalloc
static const char * IcmpPacketType(uint8_t v)
Definition: Icmp4.cc:25
#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
#define ICMP_ECHOREPLY
Definition: Icmp4.h:105
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
unsigned char opcode
Definition: Icmp.h:38
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition: gadgets.cc:18
#define MAX_PAYLOAD
Definition: Icmp.h:18
#define icmphdr
Definition: Icmp4.h:27
int Open() override
Start pinger helper and initiate control channel.
Definition: Icmp4.cc:68
char payload[MAX_PAYLOAD]
Definition: Icmp.h:48
unsigned char opcode
Definition: Icmp.h:47
Ip::Address from
Definition: Icmp.h:37
#define assert(EX)
Definition: assert.h:17
int psize
Definition: Icmp.h:41
#define Assure(condition)
Definition: Assure.h:35
int hops
Definition: Icmp.h:40
int xsocket(int domain, int type, int protocol)
POSIX socket(2) equivalent.
Definition: socket.h:128
void Recv(void) override
Handle ICMP responses.
Definition: Icmp4.cc:160
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
#define iphdr
Definition: Icmp4.h:28
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
#define ICMP_ECHO
Definition: Icmp4.h:101
~Icmp4() override
Definition: Icmp4.cc:62
struct timeval tv
Definition: Icmp.h:46
#define DBG_IMPORTANT
Definition: Stream.h:38
#define MYNAME
Definition: Stream.h:219
void SendEcho(Ip::Address &, int, const char *, int) override
Definition: Icmp4.cc:85
Icmp4()
Definition: Icmp4.cc:57
#define IPPROTO_ICMP
Definition: Icmp4.h:109
#define MAX_PKT4_SZ
Definition: Icmp.h:19
int CheckSum(unsigned short *ptr, int size)
Calculate a packet checksum.
Definition: Icmp.cc:39
int icmp_pkts_sent
Definition: pinger.cc:97
#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
Definition: Icmp.h:67
int ipHops(int ttl)
Definition: Icmp.cc:68

 

Introduction

Documentation

Support

Miscellaneous