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

 

Introduction

Documentation

Support

Miscellaneous