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

 

Introduction

Documentation

Support

Miscellaneous