wccp.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 80 WCCP Support */
10 
11 #include "squid.h"
12 
13 #if USE_WCCP
14 #include "base/RunnersRegistry.h"
15 #include "comm.h"
16 #include "comm/Connection.h"
17 #include "comm/Loops.h"
18 #include "event.h"
19 #include "fatal.h"
20 #include "SquidConfig.h"
21 #include "tools.h"
22 
23 #define WCCP_PORT 2048
24 #define WCCP_REVISION 0
25 #define WCCP_ACTIVE_CACHES 32
26 #define WCCP_HASH_SIZE 32
27 #define WCCP_BUCKETS 256
28 #define WCCP_CACHE_LEN 4
29 
30 #define WCCP_HERE_I_AM 7
31 #define WCCP_I_SEE_YOU 8
32 #define WCCP_ASSIGN_BUCKET 9
33 
35  int type;
36  int version;
37  int revision;
39  int reserved;
40  int id;
41 };
42 
44  struct in_addr ip_addr; // WCCP on-the-wire in 32-bit IPv4-only.
45  int revision;
47  int reserved;
48 };
49 
51  int32_t type;
52  int32_t version;
53  int32_t change;
54  int32_t id;
55  int32_t number;
56 
58 };
59 
61  int type;
62  int id;
63  int number;
64 };
65 
66 static int theWccpConnection = -1;
67 
69 
71 static int last_change;
72 static int last_id;
74 static unsigned int number_caches;
75 
77 
79 static int wccpLowestIP(void);
81 static void wccpAssignBuckets(void);
82 
83 static void
84 wccpInit(void)
85 {
86  if (!IamPrimaryProcess())
87  return;
88 
89  debugs(80, 5, "wccpInit: Called");
90  memset(&wccp_here_i_am, '\0', sizeof(wccp_here_i_am));
94  last_change = 0;
95  last_id = 0;
97  number_caches = 0;
98 
99  if (!Config.Wccp.router.isAnyAddr())
100  if (!eventFind(wccpHereIam, nullptr))
101  eventAdd("wccpHereIam", wccpHereIam, nullptr, 5.0, 1);
102 }
103 
104 static void
106 {
107  if (!IamPrimaryProcess())
108  return;
109 
110  debugs(80, 5, "wccpConnectionOpen: Called");
111 
112  if (Config.Wccp.router.isAnyAddr()) {
113  debugs(80, 2, "WCCPv1 disabled.");
114  return;
115  }
116 
117  if ( !Config.Wccp.router.setIPv4() ) {
118  debugs(80, DBG_CRITICAL, "WCCPv1 Disabled. Router " << Config.Wccp.router << " is not an IPv4 address.");
119  return;
120  }
121 
122  if ( !Config.Wccp.address.setIPv4() ) {
123  debugs(80, DBG_CRITICAL, "WCCPv1 Disabled. Local address " << Config.Wccp.address << " is not an IPv4 address.");
124  return;
125  }
126 
129 
131  IPPROTO_UDP,
134  "WCCP Socket");
135 
136  if (theWccpConnection < 0)
137  fatal("Cannot open WCCP Port");
138 
140 
141  debugs(80, DBG_IMPORTANT, "Accepting WCCPv1 messages on " << Config.Wccp.address << ", FD " << theWccpConnection << ".");
142 
143  // Sadly WCCP only does IPv4
144 
145  struct sockaddr_in router;
146  Config.Wccp.router.getSockAddr(router);
147  if (connect(theWccpConnection, (struct sockaddr*)&router, sizeof(router)))
148  fatal("Unable to connect WCCP out socket");
149 
150  struct sockaddr_in local;
151  memset(&local, '\0', sizeof(local));
152  socklen_t slen = sizeof(local);
153  if (getsockname(theWccpConnection, (struct sockaddr*)&local, &slen))
154  fatal("Unable to getsockname on WCCP out socket");
155 
156  local_ip = local;
157 }
158 
159 static void
161 {
162  if (!IamPrimaryProcess())
163  return;
164 
165  if (theWccpConnection > -1) {
166  debugs(80, DBG_IMPORTANT, "FD " << theWccpConnection << " Closing WCCPv1 socket");
168  theWccpConnection = -1;
169  }
170 }
171 
172 class WccpRr : public RegisteredRunner
173 {
174 public:
175  void useConfig() override { wccpInit(); wccpConnectionOpen(); }
176  void startReconfigure() override { wccpConnectionClose(); }
177  void syncConfig() override { wccpConnectionOpen(); }
178  void startShutdown() override { wccpConnectionClose(); }
179 };
181 
182 /*
183  * Functions for handling the requests.
184  */
185 
186 /*
187  * Accept the UDP packet
188  */
189 static void
190 wccpHandleUdp(int sock, void *)
191 {
192  Ip::Address from;
193  int len;
194 
195  debugs(80, 6, "wccpHandleUdp: Called.");
196 
197  Comm::SetSelect(sock, COMM_SELECT_READ, wccpHandleUdp, nullptr, 0);
198 
199  memset(&wccp_i_see_you, '\0', sizeof(wccp_i_see_you));
200 
201  len = comm_udp_recvfrom(sock,
202  (void *) &wccp_i_see_you,
203  sizeof(wccp_i_see_you),
204  0,
205  from);
206  debugs(80, 3, "wccpHandleUdp: " << len << " bytes WCCP pkt from " << from <<
207  ": type=" <<
208  (unsigned) ntohl(wccp_i_see_you.type) << ", version=" <<
209  (unsigned) ntohl(wccp_i_see_you.version) << ", change=" <<
210  (unsigned) ntohl(wccp_i_see_you.change) << ", id=" <<
211  (unsigned) ntohl(wccp_i_see_you.id) << ", number=" <<
212  (unsigned) ntohl(wccp_i_see_you.number));
213 
214  if (len < 0)
215  return;
216 
217  if (from != Config.Wccp.router)
218  return;
219 
220  if ((unsigned) ntohl(wccp_i_see_you.version) != (unsigned) Config.Wccp.version)
221  return;
222 
223  if (ntohl(wccp_i_see_you.type) != WCCP_I_SEE_YOU)
224  return;
225 
227  debugs(80, DBG_IMPORTANT, "Ignoring WCCP_I_SEE_YOU from " <<
228  from << " with number of caches set to " <<
229  (int) ntohl(wccp_i_see_you.number));
230 
231  return;
232  }
233 
235 
236  if ((0 == last_change) && (number_caches == (unsigned) ntohl(wccp_i_see_you.number))) {
238  /*
239  * After a WCCP_ASSIGN_BUCKET message, the router should
240  * update the change value. If not, maybe the route didn't
241  * receive our WCCP_ASSIGN_BUCKET message, so send it again.
242  *
243  * Don't update change here. Instead, fall through to
244  * the next block to call wccpAssignBuckets() again.
245  */
246  (void) 0;
247  } else {
249  return;
250  }
251  }
252 
255 
259  }
260  }
261 }
262 
263 static int
265 {
266  unsigned int loop;
267  int found = 0;
268 
269  /*
270  * We sanity checked wccp_i_see_you.number back in wccpHandleUdp()
271  */
272 
273  for (loop = 0; loop < (unsigned) ntohl(wccp_i_see_you.number); ++loop) {
274  assert(loop < WCCP_ACTIVE_CACHES);
275 
277  return 0;
278 
280  found = 1;
281  }
282 
283  return found;
284 }
285 
286 static void
287 wccpHereIam(void *)
288 {
289  debugs(80, 6, "wccpHereIam: Called");
290 
292  double interval = 10.0; // TODO: make this configurable, possibly negotiate with the router.
293  ssize_t sent = comm_udp_send(theWccpConnection, &wccp_here_i_am, sizeof(wccp_here_i_am), 0);
294 
295  // if we failed to send the whole lot, try again at a shorter interval (20%)
296  if (sent != sizeof(wccp_here_i_am)) {
297  int xerrno = errno;
298  debugs(80, 2, "ERROR: failed to send WCCP HERE_I_AM packet: " << xstrerr(xerrno));
299  interval = 2.0;
300  }
301 
302  if (!eventFind(wccpHereIam, nullptr))
303  eventAdd("wccpHereIam", wccpHereIam, nullptr, interval, 1);
304 }
305 
306 static void
308 {
309 
310  struct wccp_assign_bucket_t *wccp_assign_bucket;
311  int wab_len;
312  char *buckets;
313  int buckets_per_cache;
314  unsigned int loop;
315  int bucket = 0;
316  int *caches;
317  int cache_len;
318  char *buf;
319 
320  debugs(80, 6, "wccpAssignBuckets: Called");
322 
323  assert(number_caches > 0);
325 
326  wab_len = sizeof(struct wccp_assign_bucket_t);
327 
328  cache_len = WCCP_CACHE_LEN * number_caches;
329 
330  buf = (char *)xmalloc(wab_len +
331  WCCP_BUCKETS +
332  cache_len);
333 
334  wccp_assign_bucket = (struct wccp_assign_bucket_t *) buf;
335 
336  caches = (int *) (buf + wab_len);
337 
338  buckets = buf + wab_len + cache_len;
339 
340  memset(wccp_assign_bucket, '\0', sizeof(*wccp_assign_bucket));
341 
342  memset(buckets, 0xFF, WCCP_BUCKETS);
343 
344  buckets_per_cache = WCCP_BUCKETS / number_caches;
345 
346  for (loop = 0; loop < number_caches; ++loop) {
347  int i;
348  memcpy(&caches[loop],
350  sizeof(*caches));
351 
352  for (i = 0; i < buckets_per_cache; ++i) {
353  assert(bucket < WCCP_BUCKETS);
354  buckets[bucket] = loop;
355  ++bucket;
356  }
357  }
358 
359  while (bucket < WCCP_BUCKETS) {
360  buckets[bucket] = number_caches - 1;
361  ++bucket;
362  }
363 
364  wccp_assign_bucket->type = htonl(WCCP_ASSIGN_BUCKET);
365  wccp_assign_bucket->id = wccp_i_see_you.id;
366  wccp_assign_bucket->number = wccp_i_see_you.number;
367 
369  buf,
370  wab_len + WCCP_BUCKETS + cache_len,
371  0);
372  last_change = 0;
373  xfree(buf);
374 }
375 
376 #endif /* USE_WCCP */
377 
void startShutdown() override
Definition: wccp.cc:178
void fatal(const char *message)
Definition: fatal.cc:28
const char * xstrerr(int error)
Definition: xstrerror.cc:83
int eventFind(EVH *func, void *arg)
Definition: event.cc:145
#define WCCP_CACHE_LEN
Definition: wccp.cc:28
#define WCCP_HASH_SIZE
Definition: wccp.cc:26
#define WCCP_REVISION
Definition: wccp.cc:24
#define DBG_CRITICAL
Definition: Stream.h:37
#define xmalloc
int32_t number
Definition: wccp.cc:55
int32_t change
Definition: wccp.cc:53
#define WCCP_ASSIGN_BUCKET
Definition: wccp.cc:32
bool isAnyAddr() const
Definition: Address.cc:190
static int last_assign_buckets_change
Definition: wccp.cc:73
Definition: wccp.cc:43
ssize_t comm_udp_send(int s, const void *buf, size_t len, int flags)
Definition: comm.cc:146
static struct wccp_here_i_am_t wccp_here_i_am
Definition: wccp.cc:68
static Ip::Address local_ip
Definition: wccp.cc:76
#define comm_close(x)
Definition: comm.h:36
#define WCCP_PORT
Definition: wccp.cc:23
#define WCCP_HERE_I_AM
Definition: wccp.cc:30
int reserved
Definition: wccp.cc:39
#define WCCP_BUCKETS
Definition: wccp.cc:27
static int theWccpConnection
Definition: wccp.cc:66
int socklen_t
Definition: types.h:137
Definition: wccp.cc:172
#define COMM_NONBLOCKING
Definition: Connection.h:46
static void wccpInit(void)
Definition: wccp.cc:84
char hash[WCCP_HASH_SIZE]
Definition: wccp.cc:46
static void wccpConnectionOpen(void)
Definition: wccp.cc:105
void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note)
Definition: comm.cc:257
static int last_id
Definition: wccp.cc:72
Ip::Address router
Definition: SquidConfig.h:162
int revision
Definition: wccp.cc:37
char hash[WCCP_HASH_SIZE]
Definition: wccp.cc:38
struct SquidConfig::@87 Wccp
static struct tok * buckets[HASHSIZE]
Definition: parse.c:219
unsigned short port() const
Definition: Address.cc:798
static void wccpAssignBuckets(void)
Definition: wccp.cc:307
static void wccpConnectionClose(void)
Definition: wccp.cc:160
#define assert(EX)
Definition: assert.h:17
int32_t type
Definition: wccp.cc:51
static EVH wccpHereIam
Definition: wccp.cc:80
bool setIPv4()
Definition: Address.cc:244
#define COMM_SELECT_READ
Definition: defines.h:24
#define xfree
void EVH(void *)
Definition: event.h:18
struct wccp_cache_entry_t wccp_cache_entry[WCCP_ACTIVE_CACHES]
Definition: wccp.cc:57
#define WCCP_I_SEE_YOU
Definition: wccp.cc:31
int32_t id
Definition: wccp.cc:54
DefineRunnerRegistrator(WccpRr)
static int wccpLowestIP(void)
Definition: wccp.cc:264
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:220
Ip::Address address
Definition: SquidConfig.h:163
static unsigned int number_caches
Definition: wccp.cc:74
#define WCCP_ACTIVE_CACHES
Definition: wccp.cc:25
static struct wccp_i_see_you_t wccp_i_see_you
Definition: wccp.cc:70
#define DBG_IMPORTANT
Definition: Stream.h:38
int reserved
Definition: wccp.cc:47
struct in_addr ip_addr
Definition: wccp.cc:44
void syncConfig() override
Definition: wccp.cc:177
int comm_udp_recvfrom(int fd, void *buf, size_t len, int flags, Ip::Address &from)
Definition: comm.cc:126
int32_t version
Definition: wccp.cc:52
void getSockAddr(struct sockaddr_storage &addr, const int family) const
Definition: Address.cc:944
int revision
Definition: wccp.cc:45
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
static PF wccpHandleUdp
Definition: wccp.cc:78
void startReconfigure() override
Definition: wccp.cc:176
void useConfig() override
Definition: wccp.cc:175
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:107
static int last_change
Definition: wccp.cc:71
void PF(int, void *)
Definition: forward.h:18
class SquidConfig Config
Definition: SquidConfig.cc:12
bool IamPrimaryProcess()
Definition: tools.cc:708

 

Introduction

Documentation

Support

Miscellaneous