]>
Commit | Line | Data |
---|---|---|
cc192b50 | 1 | /* |
bf95c10a | 2 | * Copyright (C) 1996-2022 The Squid Software Foundation and contributors |
cc192b50 | 3 | * |
bbc27441 AJ |
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. | |
cc192b50 | 7 | */ |
bbc27441 AJ |
8 | |
9 | /* DEBUG: section 42 ICMP Pinger program */ | |
10 | ||
cc192b50 | 11 | //#define SQUID_HELPER 1 |
12 | ||
582c2af2 | 13 | #include "squid.h" |
cc192b50 | 14 | |
15 | #if USE_ICMP | |
16 | ||
675b8408 | 17 | #include "debug/Stream.h" |
b826ffb5 AJ |
18 | #include "Icmp4.h" |
19 | #include "IcmpPinger.h" | |
98cacedb | 20 | #include "time/gadgets.h" |
cc192b50 | 21 | |
618e3200 AJ |
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 | } | |
cc192b50 | 54 | |
b826ffb5 | 55 | Icmp4::Icmp4() : Icmp() |
26ac0430 AJ |
56 | { |
57 | ; | |
cc192b50 | 58 | } |
59 | ||
b826ffb5 | 60 | Icmp4::~Icmp4() |
cc192b50 | 61 | { |
62 | Close(); | |
63 | } | |
64 | ||
65 | int | |
b826ffb5 | 66 | Icmp4::Open(void) |
cc192b50 | 67 | { |
68 | icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); | |
69 | ||
70 | if (icmp_sock < 0) { | |
b69e9ffa AJ |
71 | int xerrno = errno; |
72 | debugs(50, DBG_CRITICAL, MYNAME << " icmp_sock: " << xstrerr(xerrno)); | |
cc192b50 | 73 | return -1; |
74 | } | |
75 | ||
76 | icmp_ident = getpid() & 0xffff; | |
879c39f7 | 77 | debugs(42, DBG_IMPORTANT, "ICMP socket opened."); |
cc192b50 | 78 | |
79 | return icmp_sock; | |
80 | } | |
81 | ||
82 | void | |
b7ac5457 | 83 | Icmp4::SendEcho(Ip::Address &to, int opcode, const char *payload, int len) |
cc192b50 | 84 | { |
85 | int x; | |
86 | LOCAL_ARRAY(char, pkt, MAX_PKT4_SZ); | |
87 | ||
88 | struct icmphdr *icmp = NULL; | |
89 | icmpEchoData *echo; | |
90 | size_t icmp_pktsize = sizeof(struct icmphdr); | |
91 | struct addrinfo *S = NULL; | |
92 | ||
2b9a2bba SDJ |
93 | static_assert(sizeof(*icmp) + sizeof(*echo) <= sizeof(pkt), "our custom ICMPv4 Echo payload fits the packet buffer"); |
94 | ||
cc192b50 | 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 | */ | |
26ac0430 | 103 | if (len < 0) { |
cc192b50 | 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; | |
aec55359 FC |
112 | icmp->icmp_seq = (unsigned short) icmp_pkts_sent; |
113 | ++icmp_pkts_sent; | |
cc192b50 | 114 | |
115 | // Construct ICMP packet data content | |
2b9a2bba | 116 | echo = reinterpret_cast<icmpEchoData *>(reinterpret_cast<char *>(pkt) + sizeof(*icmp)); |
cc192b50 | 117 | echo->opcode = (unsigned char) opcode; |
c3c6695b | 118 | memcpy(&echo->tv, ¤t_time, sizeof(struct timeval)); |
cc192b50 | 119 | |
120 | icmp_pktsize += sizeof(struct timeval) + sizeof(char); | |
121 | ||
26ac0430 | 122 | if (payload) { |
cc192b50 | 123 | if (len > MAX_PAYLOAD) |
124 | len = MAX_PAYLOAD; | |
125 | ||
41d00cd3 | 126 | memcpy(echo->payload, payload, len); |
cc192b50 | 127 | |
128 | icmp_pktsize += len; | |
129 | } | |
130 | ||
f45dd259 | 131 | icmp->icmp_cksum = CheckSum((unsigned short *) icmp, icmp_pktsize); |
cc192b50 | 132 | |
4dd643d5 | 133 | to.getAddrInfo(S); |
cc192b50 | 134 | ((sockaddr_in*)S->ai_addr)->sin_port = 0; |
135 | assert(icmp_pktsize <= MAX_PKT4_SZ); | |
136 | ||
bf95c10a | 137 | debugs(42, 5, "Send ICMP packet to " << to << "."); |
cc192b50 | 138 | |
139 | x = sendto(icmp_sock, | |
26ac0430 AJ |
140 | (const void *) pkt, |
141 | icmp_pktsize, | |
142 | 0, | |
143 | S->ai_addr, | |
144 | S->ai_addrlen); | |
cc192b50 | 145 | |
26ac0430 | 146 | if (x < 0) { |
b69e9ffa AJ |
147 | int xerrno = errno; |
148 | debugs(42, DBG_IMPORTANT, MYNAME << "ERROR: sending to ICMP packet to " << to << ": " << xstrerr(xerrno)); | |
cc192b50 | 149 | } |
150 | ||
151 | Log(to, ' ', NULL, 0, 0); | |
851614a8 | 152 | Ip::Address::FreeAddr(S); |
cc192b50 | 153 | } |
154 | ||
155 | void | |
b826ffb5 | 156 | Icmp4::Recv(void) |
cc192b50 | 157 | { |
158 | int n; | |
159 | struct addrinfo *from = NULL; | |
160 | int iphdrlen = sizeof(iphdr); | |
161 | struct iphdr *ip = NULL; | |
162 | struct icmphdr *icmp = NULL; | |
163 | static char *pkt = NULL; | |
164 | struct timeval now; | |
165 | icmpEchoData *echo; | |
166 | static pingerReplyData preply; | |
167 | ||
26ac0430 | 168 | if (icmp_sock < 0) { |
bf95c10a | 169 | debugs(42, DBG_CRITICAL, "No socket! Recv() should not be called."); |
cc192b50 | 170 | return; |
171 | } | |
172 | ||
173 | if (pkt == NULL) | |
174 | pkt = (char *)xmalloc(MAX_PKT4_SZ); | |
175 | ||
851614a8 | 176 | Ip::Address::InitAddr(from); |
cc192b50 | 177 | n = recvfrom(icmp_sock, |
178 | (void *)pkt, | |
179 | MAX_PKT4_SZ, | |
180 | 0, | |
181 | from->ai_addr, | |
182 | &from->ai_addrlen); | |
183 | ||
618e3200 | 184 | if (n <= 0) { |
d816f28d | 185 | debugs(42, DBG_CRITICAL, "ERROR: when calling recvfrom() on ICMP socket."); |
851614a8 | 186 | Ip::Address::FreeAddr(from); |
618e3200 AJ |
187 | return; |
188 | } | |
189 | ||
cc192b50 | 190 | preply.from = *from; |
191 | ||
192 | #if GETTIMEOFDAY_NO_TZP | |
193 | ||
194 | gettimeofday(&now); | |
195 | ||
196 | #else | |
197 | ||
198 | gettimeofday(&now, NULL); | |
199 | ||
200 | #endif | |
201 | ||
bf95c10a | 202 | debugs(42, 8, n << " bytes from " << preply.from); |
cc192b50 | 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 | ||
f1402d4e | 224 | if (icmp->icmp_type != ICMP_ECHOREPLY) { |
851614a8 | 225 | Ip::Address::FreeAddr(from); |
cc192b50 | 226 | return; |
f1402d4e | 227 | } |
cc192b50 | 228 | |
f1402d4e | 229 | if (icmp->icmp_id != icmp_ident) { |
851614a8 | 230 | Ip::Address::FreeAddr(from); |
cc192b50 | 231 | return; |
f1402d4e | 232 | } |
cc192b50 | 233 | |
234 | echo = (icmpEchoData *) (void *) (icmp + 1); | |
235 | ||
236 | preply.opcode = echo->opcode; | |
237 | ||
238 | preply.hops = ipHops(ip->ip_ttl); | |
239 | ||
c3c6695b | 240 | struct timeval tv; |
241 | memcpy(&tv, &echo->tv, sizeof(struct timeval)); | |
242 | preply.rtt = tvSubMsec(tv, now); | |
cc192b50 | 243 | |
244 | preply.psize = n - iphdrlen - (sizeof(icmpEchoData) - MAX_PKT4_SZ); | |
245 | ||
618e3200 | 246 | if (preply.psize < 0) { |
d816f28d | 247 | debugs(42, DBG_CRITICAL, "ERROR: Malformed ICMP packet."); |
851614a8 | 248 | Ip::Address::FreeAddr(from); |
618e3200 AJ |
249 | return; |
250 | } | |
251 | ||
cc192b50 | 252 | control.SendResult(preply, (sizeof(pingerReplyData) - MAX_PKT4_SZ + preply.psize) ); |
253 | ||
618e3200 | 254 | Log(preply.from, icmp->icmp_type, IcmpPacketType(icmp->icmp_type), preply.rtt, preply.hops); |
851614a8 | 255 | Ip::Address::FreeAddr(from); |
cc192b50 | 256 | } |
257 | ||
258 | #endif /* USE_ICMP */ | |
f53969cc | 259 |