1 /** @file WCCP End Point class implementation.
2
3 @section license License
4
5 Licensed to the Apache Software Foundation (ASF) under one
6 or more contributor license agreements. See the NOTICE file
7 distributed with this work for additional information
8 regarding copyright ownership. The ASF licenses this file
9 to you under the Apache License, Version 2.0 (the
10 "License"); you may not use this file except in compliance
11 with the License. You may obtain a copy of the License at
12
13 http://www.apache.org/licenses/LICENSE-2.0
14
15 Unless required by applicable law or agreed to in writing, software
16 distributed under the License is distributed on an "AS IS" BASIS,
17 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 See the License for the specific language governing permissions and
19 limitations under the License.
20 */
21
22 #include "WccpLocal.h"
23 #include "WccpUtil.h"
24 #include "WccpMeta.h"
25 #include <errno.h>
26 #include "tscore/ink_string.h"
27 #include "tscore/ink_defs.h"
28 // ------------------------------------------------------
29 namespace wccp
30 {
31 #if defined IP_RECVDSTADDR
32 #define DSTADDR_SOCKOPT IP_RECVDSTADDR
33 #define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in_addr)))
34 #define dstaddr(x) (CMSG_DATA(x))
35 #elif defined IP_PKTINFO
36 #define DSTADDR_SOCKOPT IP_PKTINFO
37 #define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in_pktinfo)))
38 #define dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr))
39 #else
40 #error "can't determine socket option"
41 #endif
42
43 // ------------------------------------------------------
44 Impl::GroupData &
setKey(const char * key)45 Impl::GroupData::setKey(const char *key)
46 {
47 if ((m_use_security_key = (key != nullptr))) {
48 ink_strlcpy(m_security_key, key, SecurityComp::KEY_SIZE);
49 }
50 return *this;
51 }
52
53 Impl::GroupData &
setSecurity(SecurityOption style)54 Impl::GroupData::setSecurity(SecurityOption style)
55 {
56 m_use_security_opt = true;
57 m_security_opt = style;
58 return *this;
59 }
60
~Impl()61 Impl::~Impl()
62 {
63 this->close();
64 }
65
66 int
open(uint addr)67 Impl::open(uint addr)
68 {
69 struct sockaddr saddr;
70 sockaddr_in &in_addr = reinterpret_cast<sockaddr_in &>(saddr);
71 ats_scoped_fd fd;
72
73 if (ts::NO_FD != m_fd) {
74 log(LVL_INFO, "Attempted to open already open WCCP Endpoint");
75 return -EALREADY;
76 }
77
78 if (ts::NO_FD == (fd = socket(PF_INET, SOCK_DGRAM, 0))) {
79 log_errno(LVL_FATAL, "Failed to create socket");
80 return -errno;
81 }
82
83 if (INADDR_ANY != addr)
84 m_addr = addr; // overridden.
85 memset(&saddr, 0, sizeof(saddr));
86 in_addr.sin_family = AF_INET;
87 in_addr.sin_port = htons(DEFAULT_PORT);
88 in_addr.sin_addr.s_addr = m_addr;
89 int zret = bind(fd, &saddr, sizeof(saddr));
90 if (-1 == zret) {
91 log_errno(LVL_FATAL, "Failed to bind socket to port");
92 this->close();
93 return -errno;
94 }
95 logf(LVL_INFO, "Socket bound to %s:%d", ip_addr_to_str(m_addr), DEFAULT_PORT);
96
97 // Now get the address. Usually the same but possibly different,
98 // certainly if addr was INADDR_ANY.
99 if (INADDR_ANY == m_addr && INADDR_ANY == (m_addr = Get_Local_Address(fd))) {
100 log_errno(LVL_FATAL, "Failed to get local address for socket");
101 this->close();
102 return -errno;
103 }
104
105 // Enable retrieval of destination address on packets.
106 int ip_pktinfo_flag = 1;
107 if (-1 == setsockopt(fd, IPPROTO_IP, DSTADDR_SOCKOPT, &ip_pktinfo_flag, sizeof(ip_pktinfo_flag))) {
108 log_errno(LVL_FATAL, "Failed to enable destination address retrieval");
109 this->close();
110 return -errno;
111 }
112
113 #if defined IP_MTU_DISCOVER
114 /// Disable PMTU on Linux because of a bug in IOS routers.
115 /// WCCP packets are rejected as duplicates if the IP fragment
116 /// identifier is 0, which is the value used when PMTU is enabled.
117 int pmtu = IP_PMTUDISC_DONT;
118 if (-1 == setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu))) {
119 log_errno(LVL_FATAL, "Failed to disable PMTU on WCCP socket.");
120 this->close();
121 return -errno;
122 }
123 #endif
124
125 m_fd = fd.release();
126 return 0;
127 }
128
129 void
close()130 Impl::close()
131 {
132 if (ts::NO_FD != m_fd) {
133 ::close(m_fd);
134 m_fd = ts::NO_FD;
135 }
136 }
137
138 void
useMD5Security(std::string_view const key)139 Impl::useMD5Security(std::string_view const key)
140 {
141 m_use_security_opt = true;
142 m_security_opt = SECURITY_MD5;
143 m_use_security_key = true;
144 memset(m_security_key, 0, SecurityComp::KEY_SIZE);
145 // Great. Have to cast or we get a link error.
146 memcpy(m_security_key, key.data(), std::min(key.size(), static_cast<size_t>(SecurityComp::KEY_SIZE)));
147 }
148
149 SecurityOption
setSecurity(BaseMsg & msg,GroupData const & group) const150 Impl::setSecurity(BaseMsg &msg, GroupData const &group) const
151 {
152 SecurityOption zret = SECURITY_NONE;
153 if (group.m_use_security_opt)
154 zret = group.m_security_opt;
155 else if (m_use_security_opt)
156 zret = m_security_opt;
157 if (group.m_use_security_key)
158 msg.m_security.setKey(group.m_security_key);
159 else if (m_use_security_key)
160 msg.m_security.setKey(m_security_key);
161 return zret;
162 }
163
164 bool
validateSecurity(BaseMsg & msg,GroupData const & group)165 Impl::validateSecurity(BaseMsg &msg, GroupData const &group)
166 {
167 SecurityOption opt = msg.m_security.getOption();
168 if (group.m_use_security_opt) {
169 if (opt != group.m_security_opt)
170 return false;
171 } else if (m_use_security_opt) {
172 if (opt != m_security_opt)
173 return false;
174 }
175 if (opt == SECURITY_MD5) {
176 if (group.m_use_security_key)
177 msg.m_security.setKey(group.m_security_key);
178 else if (m_use_security_key)
179 msg.m_security.setKey(m_security_key);
180 return msg.validateSecurity();
181 }
182 return true;
183 }
184
185 ts::Rv<int>
handleMessage()186 Impl::handleMessage()
187 {
188 ts::Rv<int> zret;
189 ssize_t n; // recv byte count.
190 struct sockaddr src_addr; // sender's address.
191 msghdr recv_hdr;
192 iovec recv_buffer;
193 IpHeader ip_header;
194 static ssize_t const BUFFER_SIZE = 65536;
195 char buffer[BUFFER_SIZE];
196 static size_t const ANC_BUFFER_SIZE = DSTADDR_DATASIZE;
197 char anc_buffer[ANC_BUFFER_SIZE];
198
199 if (ts::NO_FD == m_fd)
200 return -ENOTCONN;
201
202 recv_buffer.iov_base = buffer;
203 recv_buffer.iov_len = BUFFER_SIZE;
204
205 recv_hdr.msg_name = &src_addr;
206 recv_hdr.msg_namelen = sizeof(src_addr);
207 recv_hdr.msg_iov = &recv_buffer;
208 recv_hdr.msg_iovlen = 1;
209 recv_hdr.msg_control = anc_buffer;
210 recv_hdr.msg_controllen = ANC_BUFFER_SIZE;
211 recv_hdr.msg_flags = 0; // output only, make Coverity shut up.
212
213 // coverity[uninit_use_in_call]
214 n = recvmsg(m_fd, &recv_hdr, MSG_TRUNC);
215 if (n > BUFFER_SIZE)
216 return -EMSGSIZE;
217 else if (n < 0)
218 return -errno;
219
220 // Extract the original destination address.
221 ip_header.m_src = access_field(&sockaddr_in::sin_addr, &src_addr).s_addr;
222 for (cmsghdr *anc = CMSG_FIRSTHDR(&recv_hdr); anc; anc = CMSG_NXTHDR(&recv_hdr, anc)) {
223 if (anc->cmsg_level == IPPROTO_IP && anc->cmsg_type == DSTADDR_SOCKOPT) {
224 ip_header.m_dst = ((struct in_addr *)dstaddr(anc))->s_addr;
225 break;
226 }
227 }
228
229 // Check to see if there is a valid header.
230 MsgHeaderComp header;
231 MsgBuffer msg_buffer(buffer, n);
232 if (PARSE_SUCCESS == header.parse(msg_buffer)) {
233 message_type_t msg_type = header.getType();
234 ts::Buffer chunk(buffer, n);
235
236 switch (msg_type) {
237 case HERE_I_AM:
238 this->handleHereIAm(ip_header, chunk);
239 break;
240 case I_SEE_YOU:
241 this->handleISeeYou(ip_header, chunk);
242 break;
243 case REDIRECT_ASSIGN:
244 this->handleRedirectAssign(ip_header, chunk);
245 break;
246 case REMOVAL_QUERY:
247 this->handleRemovalQuery(ip_header, chunk);
248 break;
249 default:
250 fprintf(stderr, "Unknown message type %d ignored.\n", msg_type);
251 break;
252 };
253 } else {
254 fprintf(stderr, "Malformed message ignored.\n");
255 }
256 return zret;
257 }
258
259 ts::Errata
handleHereIAm(IpHeader const &,ts::Buffer const &)260 Impl::handleHereIAm(IpHeader const &, ts::Buffer const &)
261 {
262 return log(LVL_INFO, "Unanticipated WCCP2_HERE_I_AM message ignored");
263 }
264 ts::Errata
handleISeeYou(IpHeader const &,ts::Buffer const &)265 Impl::handleISeeYou(IpHeader const &, ts::Buffer const & /* data ATS_UNUSED */)
266 {
267 return log(LVL_INFO, "Unanticipated WCCP2_I_SEE_YOU message ignored.");
268 }
269 ts::Errata
handleRedirectAssign(IpHeader const &,ts::Buffer const &)270 Impl::handleRedirectAssign(IpHeader const &, ts::Buffer const & /* data ATS_UNUSED */)
271 {
272 return log(LVL_INFO, "Unanticipated WCCP2_REDIRECT_ASSIGN message ignored.");
273 }
274 ts::Errata
handleRemovalQuery(IpHeader const &,ts::Buffer const &)275 Impl::handleRemovalQuery(IpHeader const &, ts::Buffer const & /* data ATS_UNUSED */)
276 {
277 return log(LVL_INFO, "Unanticipated WCCP2_REMOVAL_QUERY message ignored.");
278 }
279 // ------------------------------------------------------
GroupData()280 CacheImpl::GroupData::GroupData() : m_proc_name(NULL), m_assignment_pending(false) {}
281
282 CacheImpl::GroupData &
seedRouter(uint32_t addr)283 CacheImpl::GroupData::seedRouter(uint32_t addr)
284 {
285 // Be nice and don't add it if it's already there.
286 if (m_seed_routers.end() == find_by_member(m_seed_routers, &SeedRouter::m_addr, addr))
287 m_seed_routers.push_back(SeedRouter(addr));
288 return *this;
289 }
290
291 time_t
removeSeedRouter(uint32_t addr)292 CacheImpl::GroupData::removeSeedRouter(uint32_t addr)
293 {
294 time_t zret = 0;
295 std::vector<SeedRouter>::iterator begin = m_seed_routers.begin();
296 std::vector<SeedRouter>::iterator end = m_seed_routers.end();
297 std::vector<SeedRouter>::iterator spot = std::find_if(begin, end, ts::predicate(&SeedRouter::m_addr, addr));
298
299 if (end != spot) {
300 zret = spot->m_xmit;
301 m_seed_routers.erase(spot);
302 }
303
304 return zret;
305 }
306
307 CacheImpl::GroupData &
setKey(const char * key)308 CacheImpl::GroupData::setKey(const char *key)
309 {
310 return static_cast<self &>(this->super::setKey(key));
311 }
312
313 CacheImpl::GroupData &
setSecurity(SecurityOption style)314 CacheImpl::GroupData::setSecurity(SecurityOption style)
315 {
316 return static_cast<self &>(this->super::setSecurity(style));
317 }
318
319 CacheImpl::CacheBag::iterator
findCache(uint32_t addr)320 CacheImpl::GroupData::findCache(uint32_t addr)
321 {
322 return std::find_if(m_caches.begin(), m_caches.end(), ts::predicate(&CacheData::idAddr, addr));
323 }
324
325 CacheImpl::RouterBag::iterator
findRouter(uint32_t addr)326 CacheImpl::GroupData::findRouter(uint32_t addr)
327 {
328 return std::find_if(m_routers.begin(), m_routers.end(), ts::predicate(&RouterData::m_addr, addr));
329 }
330
331 void
resizeCacheSources()332 CacheImpl::GroupData::resizeCacheSources()
333 {
334 int count = m_routers.size();
335 for (CacheBag::iterator spot = m_caches.begin(), limit = m_caches.end(); spot != limit; ++spot) {
336 spot->m_src.resize(count);
337 }
338 }
339
RouterData()340 inline CacheImpl::RouterData::RouterData() : m_addr(0), m_generation(0), m_rapid(0), m_assign(false), m_send_caps(false) {}
341
RouterData(uint32_t addr)342 inline CacheImpl::RouterData::RouterData(uint32_t addr)
343 : m_addr(addr), m_generation(0), m_rapid(0), m_assign(false), m_send_caps(false)
344 {
345 }
346
347 time_t
pingTime(time_t now) const348 CacheImpl::RouterData::pingTime(time_t now) const
349 {
350 time_t tx = m_xmit.m_time + (m_rapid ? TIME_UNIT / 10 : TIME_UNIT);
351 return tx < now ? 0 : tx - now;
352 }
353
354 time_t
waitTime(time_t now) const355 CacheImpl::RouterData::waitTime(time_t now) const
356 {
357 return m_assign ? 0 : this->pingTime(now);
358 }
359
360 uint32_t
idAddr() const361 detail::cache::CacheData::idAddr() const
362 {
363 return m_id.getAddr();
364 }
365
366 CacheImpl::GroupData &
defineServiceGroup(ServiceGroup const & svc,ServiceGroup::Result * result)367 CacheImpl::defineServiceGroup(ServiceGroup const &svc, ServiceGroup::Result *result)
368 {
369 uint8_t svc_id = svc.getSvcId();
370 GroupMap::iterator spot = m_groups.find(svc_id);
371 GroupData *group; // service with target ID.
372 ServiceGroup::Result zret;
373 if (spot == m_groups.end()) { // not defined
374 group = &(m_groups[svc_id]);
375 group->m_svc = svc;
376 group->m_id.initDefaultHash(m_addr);
377 zret = ServiceGroup::DEFINED;
378 } else {
379 group = &spot->second;
380 zret = group->m_svc == svc ? ServiceGroup::EXISTS : ServiceGroup::CONFLICT;
381 }
382 if (result)
383 *result = zret;
384 return *group;
385 }
386
387 time_t
waitTime(time_t now) const388 CacheImpl::GroupData::waitTime(time_t now) const
389 {
390 time_t zret = std::numeric_limits<time_t>::max();
391 // Active routers.
392 for (RouterBag::const_iterator router = m_routers.begin(), router_limit = m_routers.end(); router != router_limit && zret;
393 ++router) {
394 zret = std::min(zret, router->waitTime(now));
395 }
396 // Seed routers.
397 for (std::vector<SeedRouter>::const_iterator router = m_seed_routers.begin(), router_limit = m_seed_routers.end();
398 router != router_limit && zret; ++router) {
399 time_t tx = router->m_xmit + TIME_UNIT;
400 if (tx < now)
401 zret = 0;
402 else
403 zret = std::min(tx - now, zret);
404 }
405 // Assignment
406 if (m_assignment_pending) {
407 time_t tx = m_generation_time + (3 * TIME_UNIT / 2);
408 if (tx < now)
409 zret = 0;
410 else
411 zret = std::min(tx - now, zret);
412 }
413
414 return zret;
415 }
416
417 bool
processUp()418 CacheImpl::GroupData::processUp()
419 {
420 bool zret = false;
421 const char *proc_pid_path = this->getProcName();
422 if (proc_pid_path == NULL || proc_pid_path[0] == '\0') {
423 zret = true; // No process to track, always chatter
424 } else {
425 // Look for the pid file
426 ats_scoped_fd fd{open(proc_pid_path, O_RDONLY)};
427 if (fd >= 0) {
428 char buffer[256];
429 ssize_t read_count = read(fd, buffer, sizeof(buffer) - 1);
430 if (read_count > 0) {
431 buffer[read_count] = '\0';
432 int pid = atoi(buffer);
433 if (pid > 0) {
434 // If the process is still running, it has an entry in the proc file system, (Linux only)
435 sprintf(buffer, "/proc/%d/status", pid);
436 ats_scoped_fd fd2{open(buffer, O_RDONLY)};
437 if (fd2 >= 0) {
438 zret = true;
439 }
440 }
441 }
442 }
443 }
444 return zret;
445 }
446
447 bool
cullRouters(time_t now)448 CacheImpl::GroupData::cullRouters(time_t now)
449 {
450 bool zret = false;
451 size_t idx = 0, n = m_routers.size();
452 while (idx < n) {
453 RouterData &router = m_routers[idx];
454 if (router.m_recv.m_time + TIME_UNIT * 3 < now) {
455 uint32_t addr = router.m_addr;
456 // Clip the router by copying down and resizing.
457 // Must do all caches as well.
458 --n; // Decrement router counter first.
459 if (idx < n)
460 router = m_routers[n];
461 m_routers.resize(n);
462 for (CacheBag::iterator cache = m_caches.begin(), cache_limit = m_caches.end(); cache != cache_limit; ++cache) {
463 if (idx < n)
464 cache->m_src[idx] = cache->m_src[n];
465 cache->m_src.resize(n);
466 }
467 // Put it back in the seeds.
468 this->seedRouter(addr);
469 zret = true; // Router was culled, report it to caller.
470 logf(LVL_INFO, "Router " ATS_IP_PRINTF_CODE " timed out and was removed from the active list.", ATS_IP_OCTETS(addr));
471 } else {
472 ++idx; // move to next router.
473 }
474 }
475 if (zret)
476 this->viewChanged(now);
477 return zret;
478 }
479
480 CacheImpl::GroupData &
viewChanged(time_t now)481 CacheImpl::GroupData::viewChanged(time_t now)
482 {
483 m_generation += 1;
484 m_generation_time = now;
485 m_assign_info.setActive(false); // invalidate current assignment.
486 m_assignment_pending = m_routers.size() && m_caches.size();
487 // Cancel any pending assignment transmissions.
488 ts::for_each(m_routers, ts::assign_member(&RouterData::m_assign, false));
489 logf(LVL_DEBUG, "Service group %d view change (%d)", m_svc.getSvcId(), m_generation);
490
491 return *this;
492 }
493
494 Cache::Service &
setKey(const char * key)495 Cache::Service::setKey(const char *key)
496 {
497 m_group->setKey(key);
498 return *this;
499 }
500
501 Cache::Service &
setSecurity(SecurityOption opt)502 Cache::Service::setSecurity(SecurityOption opt)
503 {
504 m_group->setSecurity(opt);
505 return *this;
506 }
507
508 CacheImpl &
seedRouter(uint8_t id,uint32_t addr)509 CacheImpl::seedRouter(uint8_t id, uint32_t addr)
510 {
511 GroupMap::iterator spot = m_groups.find(id);
512 if (spot != m_groups.end())
513 spot->second.seedRouter(addr);
514 return *this;
515 }
516
517 bool
isConfigured() const518 CacheImpl::isConfigured() const
519 {
520 return INADDR_ANY != m_addr && m_groups.size() > 0;
521 }
522
523 int
open(uint32_t addr)524 CacheImpl::open(uint32_t addr)
525 {
526 int zret = this->super::open(addr);
527 // If the socket was successfully opened, go through the
528 // services and update the local service descriptor.
529 if (0 <= zret) {
530 for (GroupMap::iterator spot = m_groups.begin(), limit = m_groups.end(); spot != limit; ++spot) {
531 spot->second.m_id.setAddr(m_addr);
532 }
533 }
534 return zret;
535 }
536
537 time_t
waitTime() const538 CacheImpl::waitTime() const
539 {
540 time_t now = time(0);
541 return ts::minima(m_groups, &GroupData::waitTime, now);
542 }
543
544 void
generateHereIAm(HereIAmMsg & msg,GroupData & group)545 CacheImpl::generateHereIAm(HereIAmMsg &msg, GroupData &group)
546 {
547 msg.fill(group, group.m_id, this->setSecurity(msg, group));
548 msg.finalize();
549 }
550
551 void
generateHereIAm(HereIAmMsg & msg,GroupData & group,RouterData & router)552 CacheImpl::generateHereIAm(HereIAmMsg &msg, GroupData &group, RouterData &router)
553 {
554 SecurityOption sec_opt = this->setSecurity(msg, group);
555
556 msg.fill(group, group.m_id, sec_opt);
557 if (router.m_local_cache_id.getSize())
558 msg.m_cache_id.setUnassigned(false);
559
560 msg.fill_caps(router);
561 msg.finalize();
562 }
563
564 void
generateRedirectAssign(RedirectAssignMsg & msg,GroupData & group)565 CacheImpl::generateRedirectAssign(RedirectAssignMsg &msg, GroupData &group)
566 {
567 msg.fill(group, this->setSecurity(msg, group));
568 msg.finalize();
569 }
570
571 ts::Errata
checkRouterAssignment(GroupData const & group,RouterViewComp const & comp) const572 CacheImpl::checkRouterAssignment(GroupData const &group, RouterViewComp const &comp) const
573 {
574 detail::Assignment const &ainfo = group.m_assign_info;
575 // If group doesn't have an active assignment, always match w/o checking.
576 ts::Errata zret; // default is success.
577
578 // if active assignment and data we can check, then check.
579 if (ainfo.isActive() && !comp.isEmpty()) {
580 // Validate the assignment key.
581 if (ainfo.getKey().getAddr() != comp.getKeyAddr() || ainfo.getKey().getChangeNumber() != comp.getKeyChangeNumber()) {
582 log(zret, LVL_INFO, "Router assignment key did not match.");
583 ;
584 } else if (ServiceGroup::HASH_ONLY == group.m_cache_assign) {
585 // Still not sure how much checking we really want or should
586 // do here. For now, we'll just leave the checks validating
587 // the assignment key.
588 } else if (ServiceGroup::MASK_ONLY == group.m_cache_assign) {
589 // The data passed back is useless. In practice the interesting
590 // data in the mask case is in the Assignment Map Component
591 // which the router seems to send when using mask assignment.
592 }
593 }
594 return zret;
595 }
596
597 int
housekeeping()598 CacheImpl::housekeeping()
599 {
600 int zret = 0;
601 sockaddr_in dst_addr;
602 sockaddr *addr_ptr = reinterpret_cast<sockaddr *>(&dst_addr);
603 time_t now = time(0);
604 static size_t const BUFFER_SIZE = 4096;
605 MsgBuffer msg_buffer;
606 char msg_data[BUFFER_SIZE];
607 msg_buffer.set(msg_data, BUFFER_SIZE);
608
609 // Set up everything except the IP address.
610 memset(&dst_addr, 0, sizeof(dst_addr));
611 dst_addr.sin_family = AF_INET;
612 dst_addr.sin_port = htons(DEFAULT_PORT);
613
614 // Walk the service groups and do their housekeeping.
615 for (GroupMap::iterator svc_spot = m_groups.begin(), svc_limit = m_groups.end(); svc_spot != svc_limit; ++svc_spot) {
616 GroupData &group = svc_spot->second;
617
618 // Check to see if it's time for an assignment.
619 if (group.m_assignment_pending && group.m_generation_time + ASSIGN_WAIT <= now) {
620 // Is a valid assignment possible?
621 if (group.m_assign_info.fill(group, m_addr)) {
622 group.m_assign_info.setActive(true);
623 ts::for_each(group.m_routers, ts::assign_member(&RouterData::m_assign, true));
624 }
625
626 // Always clear because no point in sending an assign we can't generate.
627 group.m_assignment_pending = false;
628 }
629
630 group.cullRouters(now); // TBD UPDATE VIEW!
631
632 // Check to see if the related service is up
633 if (group.processUp()) {
634 // Check the active routers for scheduled packets.
635 for (RouterBag::iterator rspot = group.m_routers.begin(), rend = group.m_routers.end(); rspot != rend; ++rspot) {
636 dst_addr.sin_addr.s_addr = rspot->m_addr;
637 if (0 == rspot->pingTime(now)) {
638 HereIAmMsg here_i_am;
639 here_i_am.setBuffer(msg_buffer);
640 this->generateHereIAm(here_i_am, group, *rspot);
641 zret = sendto(m_fd, msg_data, here_i_am.getCount(), 0, addr_ptr, sizeof(dst_addr));
642 if (0 <= zret) {
643 rspot->m_xmit.set(now, group.m_generation);
644 rspot->m_send_caps = false;
645 logf(LVL_DEBUG, "Sent HERE_I_AM for service group %d to router %s%s[#%d,%lu].", group.m_svc.getSvcId(),
646 ip_addr_to_str(rspot->m_addr), rspot->m_rapid ? " [rapid] " : " ", group.m_generation, now);
647 if (rspot->m_rapid)
648 --(rspot->m_rapid);
649 } else {
650 logf_errno(LVL_WARN, "Failed to send to router " ATS_IP_PRINTF_CODE " - ", ATS_IP_OCTETS(rspot->m_addr));
651 }
652 } else if (rspot->m_assign) {
653 RedirectAssignMsg redirect_assign;
654 redirect_assign.setBuffer(msg_buffer);
655 this->generateRedirectAssign(redirect_assign, group);
656 zret = sendto(m_fd, msg_data, redirect_assign.getCount(), 0, addr_ptr, sizeof(dst_addr));
657 if (0 <= zret)
658 rspot->m_assign = false;
659 }
660 }
661 }
662
663 // Seed routers.
664 for (std::vector<SeedRouter>::iterator sspot = group.m_seed_routers.begin(), slimit = group.m_seed_routers.end();
665 sspot != slimit; ++sspot) {
666 // Check to see if the related service is up
667 if (group.processUp()) {
668 HereIAmMsg here_i_am;
669 here_i_am.setBuffer(msg_buffer);
670 // Is the router due for a ping?
671 if (sspot->m_xmit + TIME_UNIT > now)
672 continue; // no
673
674 this->generateHereIAm(here_i_am, group);
675
676 dst_addr.sin_addr.s_addr = sspot->m_addr;
677 zret = sendto(m_fd, msg_data, here_i_am.getCount(), 0, addr_ptr, sizeof(dst_addr));
678 if (0 <= zret) {
679 logf(LVL_DEBUG, "Sent HERE_I_AM for SG %d to seed router %s [gen=#%d,t=%lu,n=%lu].", group.m_svc.getSvcId(),
680 ip_addr_to_str(sspot->m_addr), group.m_generation, now, here_i_am.getCount());
681 sspot->m_xmit = now;
682 sspot->m_count += 1;
683 } else
684 logf(LVL_DEBUG, "Error [%d:%s] sending HERE_I_AM for SG %d to seed router %s [#%d,%lu].", zret, strerror(errno),
685 group.m_svc.getSvcId(), ip_addr_to_str(sspot->m_addr), group.m_generation, now);
686 }
687 }
688 }
689 return zret;
690 }
691
692 ts::Errata
handleISeeYou(IpHeader const &,ts::Buffer const & chunk)693 CacheImpl::handleISeeYou(IpHeader const & /* ip_hdr ATS_UNUSED */, ts::Buffer const &chunk)
694 {
695 ts::Errata zret;
696 ISeeYouMsg msg;
697 // Set if our view of the group changes enough to bump the
698 // generation number.
699 bool view_changed = false;
700 time_t now = time(0); // don't call this over and over.
701 int parse = msg.parse(chunk);
702
703 if (PARSE_SUCCESS != parse)
704 return logf(LVL_INFO, "Ignored malformed [%d] WCCP2_I_SEE_YOU message.", parse);
705
706 ServiceGroup svc(msg.m_service);
707 GroupMap::iterator spot = m_groups.find(svc.getSvcId());
708 if (spot == m_groups.end())
709 return logf(LVL_INFO, "WCCP2_I_SEE_YOU ignored - service group %d not found.", svc.getSvcId());
710
711 GroupData &group = spot->second;
712
713 if (!this->validateSecurity(msg, group))
714 return log(LVL_INFO, "Ignored WCCP2_I_SEE_YOU with invalid security.\n");
715
716 if (svc != group.m_svc)
717 return logf(LVL_INFO, "WCCP2_I_SEE_YOU ignored - service group definition %d does not match.\n", svc.getSvcId());
718
719 if (-1 == msg.m_router_id.findFromAddr(m_addr))
720 return logf(LVL_INFO, "WCCP2_I_SEE_YOU ignored -- cache not in from list.\n");
721
722 logf(LVL_DEBUG, "Received WCCP2_I_SEE_YOU for group %d.", group.m_svc.getSvcId());
723
724 // Preferred address for router.
725 uint32_t router_addr = msg.m_router_id.idElt().getAddr();
726 // Where we sent our packet.
727 uint32_t to_addr = msg.m_router_id.getToAddr();
728 uint32_t recv_id = msg.m_router_id.idElt().getRecvId();
729 RouterBag::iterator ar_spot; // active router
730 int router_idx; // index in active routers.
731
732 CapComp &caps = msg.m_capabilities;
733 // Handle the router that sent us this.
734 ar_spot = find_by_member(group.m_routers, &RouterData::m_addr, router_addr);
735 if (ar_spot == group.m_routers.end()) {
736 // This is a new router that's replied to one of our pings.
737 // Need to do various setup and reply things to get the connection
738 // established.
739
740 // Remove this from the seed routers and copy the last packet
741 // sent time.
742 RouterData r(router_addr); // accumulate state before we commit it.
743 r.m_xmit.m_time = group.removeSeedRouter(to_addr);
744
745 // Validate capabilities.
746 ServiceGroup::PacketStyle ps;
747 ServiceGroup::CacheAssignmentStyle as;
748 const char *caps_tag = caps.isEmpty() ? "default" : "router";
749
750 // No caps -> use GRE forwarding.
751 ps = caps.isEmpty() ? ServiceGroup::GRE : caps.getPacketForwardStyle();
752 if (ServiceGroup::GRE & ps & group.m_packet_forward)
753 r.m_packet_forward = ServiceGroup::GRE;
754 else if (ServiceGroup::L2 & ps & group.m_packet_forward)
755 r.m_packet_forward = ServiceGroup::L2;
756 else
757 logf(zret, LVL_WARN, "Packet forwarding (config=%d, %s=%d) did not match.", group.m_packet_forward, caps_tag, ps);
758
759 // No caps -> use GRE return.
760 ps = caps.isEmpty() ? ServiceGroup::GRE : caps.getPacketReturnStyle();
761 if (ServiceGroup::GRE & ps & group.m_packet_return)
762 r.m_packet_return = ServiceGroup::GRE;
763 else if (ServiceGroup::L2 & ps & group.m_packet_return)
764 r.m_packet_return = ServiceGroup::L2;
765 else
766 logf(zret, LVL_WARN, "Packet return (local=%d, %s=%d) did not match.", group.m_packet_return, caps_tag, ps);
767
768 // No caps -> use HASH assignment.
769 as = caps.isEmpty() ? ServiceGroup::HASH_ONLY : caps.getCacheAssignmentStyle();
770 if (ServiceGroup::HASH_ONLY & as & group.m_cache_assign)
771 r.m_cache_assign = ServiceGroup::HASH_ONLY;
772 else if (ServiceGroup::MASK_ONLY & as & group.m_cache_assign) {
773 r.m_cache_assign = ServiceGroup::MASK_ONLY;
774 group.m_id.initDefaultMask(m_addr); // switch to MASK style ID.
775 } else
776 logf(zret, LVL_WARN, "Cache assignment (local=%d, %s=%d) did not match.", group.m_cache_assign, caps_tag, as);
777
778 if (!zret) {
779 // cancel out, can't use this packet because we reject the router.
780 return logf(zret, LVL_WARN, "Router %s rejected because of capabilities mismatch.", ip_addr_to_str(router_addr));
781 }
782
783 group.m_routers.push_back(r);
784 ar_spot = group.m_routers.end() - 1;
785 view_changed = true;
786 logf(LVL_INFO, "Added source router %s to view %d", ip_addr_to_str(router_addr), group.m_svc.getSvcId());
787 } else {
788 // Existing router. Update the receive ID in the assignment object.
789 group.m_assign_info.updateRouterId(router_addr, recv_id, msg.m_router_view.getChangeNumber());
790 // Check the assignment to see if we need to send it again.
791 ts::Errata status = this->checkRouterAssignment(group, msg.m_router_view);
792 if (status.size()) {
793 ar_spot->m_assign = true; // schedule an assignment message.
794 logf(status, LVL_INFO,
795 "Router assignment reported from " ATS_IP_PRINTF_CODE " did not match local assignment. Resending assignment.\n ",
796 ATS_IP_OCTETS(router_addr));
797 }
798 }
799 time_t then = ar_spot->m_recv.m_time; // used for comparisons later.
800 ar_spot->m_recv.set(now, recv_id);
801 ar_spot->m_generation = msg.m_router_view.getChangeNumber();
802 router_idx = ar_spot - group.m_routers.begin();
803 // Reply with our own capability options iff the router sent one to us.
804 // This is a violation of the spec but it's what we have to do in practice
805 // for mask assignment.
806 ar_spot->m_send_caps = !caps.isEmpty();
807
808 // For all the other listed routers, seed them if they're not
809 // already active.
810 uint32_t nr = msg.m_router_view.getRouterCount();
811 for (uint32_t idx = 0; idx < nr; ++idx) {
812 uint32_t addr = msg.m_router_view.getRouterAddr(idx);
813 if (group.m_routers.end() == find_by_member(group.m_routers, &RouterData::m_addr, addr))
814 group.seedRouter(addr);
815 }
816
817 // Update/Install the caches.
818 // TBD: Must bump view if a router fails to report a cache it reported
819 // in its last packet.
820 group.resizeCacheSources();
821 uint32_t nc = msg.m_router_view.getCacheCount();
822 for (uint32_t idx = 0; idx < nc; ++idx) {
823 CacheIdBox &cache = msg.m_router_view.cacheId(idx);
824 CacheBag::iterator ac_spot = group.findCache(cache.getAddr());
825 if (group.m_caches.end() == ac_spot) {
826 group.m_caches.push_back(CacheData());
827 ac_spot = group.m_caches.end() - 1;
828 ac_spot->m_src.resize(group.m_routers.size());
829 logf(LVL_INFO, "Added cache %s to view %d", ip_addr_to_str(cache.getAddr()), group.m_svc.getSvcId());
830 view_changed = true;
831 } else {
832 // Check if the cache wasn't reported last time but was reported
833 // this time. In that case we need to bump the view to trigger
834 // assignment generation.
835 if (ac_spot->m_src[router_idx].m_time != then)
836 view_changed = true;
837 }
838 ac_spot->m_id.fill(cache);
839 // If cache is this cache, update data in router record.
840 if (cache.getAddr() == m_addr)
841 ar_spot->m_local_cache_id.fill(cache);
842 ac_spot->m_src[router_idx].set(now, recv_id);
843 }
844
845 if (view_changed)
846 group.viewChanged(now);
847
848 return zret;
849 }
850
851 ts::Errata
handleRemovalQuery(IpHeader const &,ts::Buffer const & chunk)852 CacheImpl::handleRemovalQuery(IpHeader const & /* ip_hdr ATS_UNUSED */, ts::Buffer const &chunk)
853 {
854 ts::Errata zret;
855 RemovalQueryMsg msg;
856 time_t now = time(0);
857 int parse = msg.parse(chunk);
858
859 if (PARSE_SUCCESS != parse)
860 return log(LVL_INFO, "Ignored malformed WCCP2_REMOVAL_QUERY message.");
861
862 ServiceGroup svc(msg.m_service);
863 GroupMap::iterator spot = m_groups.find(svc.getSvcId());
864 if (spot == m_groups.end())
865 return logf(LVL_INFO, "WCCP2_REMOVAL_QUERY ignored - service group %d not found.", svc.getSvcId());
866
867 GroupData &group = spot->second;
868
869 if (!this->validateSecurity(msg, group))
870 return log(LVL_INFO, "Ignored WCCP2_REMOVAL_QUERY with invalid security.\n");
871
872 if (svc != group.m_svc)
873 return logf(LVL_INFO, "WCCP2_REMOVAL_QUERY ignored - service group definition %d does not match.\n", svc.getSvcId());
874
875 uint32_t target_addr = msg.m_query.getCacheAddr(); // intended cache
876 if (m_addr == target_addr) {
877 uint32_t raddr = msg.m_query.getRouterAddr();
878 RouterBag::iterator router = group.findRouter(raddr);
879 if (group.m_routers.end() != router) {
880 router->m_rapid = true; // do rapid responses.
881 router->m_recv.set(now, msg.m_query.getRecvId());
882 logf(LVL_INFO, "WCCP2_REMOVAL_QUERY from router " ATS_IP_PRINTF_CODE ".\n", ATS_IP_OCTETS(raddr));
883 } else {
884 logf(LVL_INFO, "WCCP2_REMOVAL_QUERY from unknown router " ATS_IP_PRINTF_CODE ".\n", ATS_IP_OCTETS(raddr));
885 }
886 } else {
887 // Not an error in the multi-cast case, so just log under debug.
888 logf(LVL_DEBUG,
889 "WCCP2_REMOVAL_QUERY ignored -- target cache address " ATS_IP_PRINTF_CODE
890 " did not match local address " ATS_IP_PRINTF_CODE "\n.",
891 ATS_IP_OCTETS(target_addr), ATS_IP_OCTETS(m_addr));
892 }
893
894 logf(LVL_DEBUG, "Received WCCP2_REMOVAL_QUERY for group %d.", group.m_svc.getSvcId());
895
896 return zret;
897 }
898 // ------------------------------------------------------
899 inline uint32_t
idAddr() const900 detail::router::CacheData::idAddr() const
901 {
902 return m_id.getAddr();
903 }
904
GroupData()905 RouterImpl::GroupData::GroupData() {}
906
907 RouterImpl::CacheBag::iterator
findCache(uint32_t addr)908 RouterImpl::GroupData::findCache(uint32_t addr)
909 {
910 return std::find_if(m_caches.begin(), m_caches.end(), ts::predicate(&CacheData::idAddr, addr));
911 }
912
913 RouterImpl::GroupData &
defineServiceGroup(ServiceGroup const & svc,ServiceGroup::Result * result)914 RouterImpl::defineServiceGroup(ServiceGroup const &svc, ServiceGroup::Result *result)
915 {
916 uint8_t svc_id = svc.getSvcId();
917 GroupMap::iterator spot = m_groups.find(svc_id);
918 GroupData *group; // service with target ID.
919 ServiceGroup::Result zret;
920 if (spot == m_groups.end()) { // not defined
921 group = &(m_groups[svc_id]);
922 group->m_svc = svc;
923 zret = ServiceGroup::DEFINED;
924 } else {
925 group = &spot->second;
926 zret = group->m_svc == svc ? ServiceGroup::EXISTS : ServiceGroup::CONFLICT;
927 }
928 if (result)
929 *result = zret;
930 return *group;
931 }
932
933 void
resizeRouterSources()934 RouterImpl::GroupData::resizeRouterSources()
935 {
936 ts::for_each(m_routers, &RouterData::resize, m_caches.size());
937 }
938
939 ts::Errata
handleHereIAm(IpHeader const & ip_hdr,ts::Buffer const & chunk)940 RouterImpl::handleHereIAm(IpHeader const &ip_hdr, ts::Buffer const &chunk)
941 {
942 ts::Errata zret;
943 HereIAmMsg msg;
944 static GroupData nil_group; // scratch until I clean up the security.
945 // Set if our view of the group changes enough to bump the
946 // generation number.
947 bool view_changed = false;
948 int i; // scratch index var.
949 time_t now = time(0); // don't call this over and over.
950 int parse = msg.parse(chunk);
951
952 if (PARSE_SUCCESS != parse)
953 return log(LVL_INFO, "Ignored malformed WCCP2_HERE_I_AM message.\n");
954
955 if (!this->validateSecurity(msg, nil_group))
956 return log(LVL_INFO, "Ignored WCCP2_HERE_I_AM with invalid security.\n");
957
958 ServiceGroup svc(msg.m_service);
959 ServiceGroup::Result r;
960 GroupData &group = this->defineServiceGroup(svc, &r);
961 if (ServiceGroup::CONFLICT == r)
962 return logf(LVL_INFO, "WCCP2_HERE_I_AM ignored - service group %d definition does not match.\n", svc.getSvcId());
963 else if (ServiceGroup::DEFINED == r)
964 return logf(LVL_INFO, "Service group %d defined by WCCP2_HERE_I_AM.\n", svc.getSvcId());
965
966 // Check if this cache is already known.
967 uint32_t cache_addr = msg.m_cache_id.getAddr();
968 int cache_idx;
969 uint32_t cache_gen;
970 CacheBag::iterator cache = group.findCache(cache_addr);
971 if (cache == group.m_caches.end()) { // not known
972 group.m_caches.push_back(CacheData());
973 // Vector modified, need clean end value.
974 cache = group.m_caches.end() - 1;
975 cache->m_recv_count = 0;
976 group.resizeRouterSources();
977 view_changed = true;
978 } else {
979 // Did the cache mention us specifically?
980 // If so, make sure the sequence # is correct.
981 RouterIdElt *me = msg.m_cache_view.findf_router_elt(m_addr);
982 if (me && me->getRecvId() != cache->m_recv_count)
983 return logf(LVL_INFO, "Discarded out of date (recv=%d, local=%ld) WCCP2_HERE_I_AM.\n", me->getRecvId(), cache->m_recv_count);
984 }
985
986 cache_gen = msg.m_cache_view.getChangeNumber();
987
988 cache_idx = cache - group.m_caches.begin();
989 cache->m_id.fill(msg.m_cache_id.cacheId());
990 cache->m_recv.set(now, cache_gen);
991 cache->m_pending = true;
992 cache->m_to_addr = ip_hdr.m_dst;
993
994 // Add any new routers
995 i = msg.m_cache_view.getRouterCount();
996 while (i-- > 0) {
997 uint32_t addr = msg.m_cache_view.routerElt(i).getAddr();
998 RouterBag::iterator spot = find_by_member(group.m_routers, &RouterData::m_addr, addr);
999 if (spot == group.m_routers.end()) {
1000 group.m_routers.push_back(RouterData());
1001 // Can't count on previous end value, modified container.
1002 spot = group.m_routers.end() - 1;
1003 spot->m_addr = addr;
1004 spot->m_src.resize(group.m_caches.size());
1005 view_changed = true;
1006 }
1007 spot->m_src[cache_idx].set(now, cache_gen);
1008 }
1009
1010 if (view_changed)
1011 ++(group.m_generation);
1012 return zret;
1013 }
1014
1015 void
generateISeeYou(ISeeYouMsg & msg,GroupData & group,CacheData & cache)1016 RouterImpl::generateISeeYou(ISeeYouMsg &msg, GroupData &group, CacheData &cache)
1017 {
1018 int i;
1019 size_t n_routers = group.m_routers.size();
1020 size_t n_caches = group.m_caches.size();
1021
1022 // Not handling multi-cast so target caches is hardwired to 1.
1023 msg.fill(group, this->setSecurity(msg, group), group.m_assign_info, 1, n_routers, n_caches);
1024
1025 // Fill in ID data not done by fill.
1026 msg.m_router_id.setIdElt(m_addr, cache.m_recv_count + 1).setToAddr(cache.m_to_addr).setFromAddr(0, cache.idAddr());
1027 ;
1028
1029 // Fill view routers.
1030 i = 0;
1031 for (RouterBag::iterator router = group.m_routers.begin(), router_limit = group.m_routers.end(); router != router_limit;
1032 ++router, ++i) {
1033 msg.m_router_view.setRouterAddr(i, router->m_addr);
1034 }
1035
1036 // Fill view caches.
1037 i = 0;
1038 for (CacheBag::iterator spot = group.m_caches.begin(), limit = group.m_caches.end(); spot != limit; ++spot, ++i) {
1039 // TBD: This needs to track memory because cache ID elements
1040 // turn out to be variable sized.
1041 // msg.m_router_view.cacheId(i) = spot->m_id;
1042 }
1043
1044 msg.finalize();
1045 }
1046
1047 int
xmitISeeYou()1048 RouterImpl::xmitISeeYou()
1049 {
1050 int zret = 0;
1051 ISeeYouMsg msg;
1052 MsgBuffer buffer;
1053 sockaddr_in dst_addr;
1054 time_t now = time(0);
1055 static size_t const BUFFER_SIZE = 4096;
1056 char *data = static_cast<char *>(alloca(BUFFER_SIZE));
1057
1058 memset(&dst_addr, 0, sizeof(dst_addr));
1059 dst_addr.sin_family = AF_INET;
1060 dst_addr.sin_port = htons(DEFAULT_PORT);
1061 buffer.set(data, BUFFER_SIZE);
1062
1063 // Send out messages for each service group.
1064 for (GroupMap::iterator svc_spot = m_groups.begin(), svc_limit = m_groups.end(); svc_spot != svc_limit; ++svc_spot) {
1065 GroupData &group = svc_spot->second;
1066
1067 // Check each active cache in the group.
1068 for (CacheBag::iterator cache = group.m_caches.begin(), cache_limit = group.m_caches.end(); cache != cache_limit; ++cache) {
1069 if (!cache->m_pending)
1070 continue;
1071
1072 msg.setBuffer(buffer);
1073 this->generateISeeYou(msg, group, *cache);
1074 dst_addr.sin_addr.s_addr = cache->m_id.getAddr();
1075 zret = sendto(m_fd, data, msg.getCount(), 0, reinterpret_cast<sockaddr *>(&dst_addr), sizeof(dst_addr));
1076 if (0 <= zret) {
1077 cache->m_xmit.set(now, group.m_generation);
1078 cache->m_pending = false;
1079 cache->m_recv_count = msg.m_router_id.getRecvId();
1080 logf(LVL_DEBUG, "I_SEE_YOU -> %s\n", ip_addr_to_str(cache->m_id.getAddr()));
1081 } else {
1082 log_errno(LVL_WARN, "Router transmit failed -");
1083 return zret;
1084 }
1085 }
1086 }
1087 return zret;
1088 }
1089
1090 int
housekeeping()1091 RouterImpl::housekeeping()
1092 {
1093 return this->xmitISeeYou();
1094 }
1095
1096 bool
isConfigured() const1097 RouterImpl::isConfigured() const
1098 {
1099 return false;
1100 }
1101 // ------------------------------------------------------
EndPoint()1102 EndPoint::EndPoint() {}
1103
~EndPoint()1104 EndPoint::~EndPoint() {}
1105
EndPoint(self const & that)1106 EndPoint::EndPoint(self const &that) : m_ptr(that.m_ptr) {}
1107
1108 inline EndPoint::ImplType *
instance()1109 EndPoint::instance()
1110 {
1111 return m_ptr ? m_ptr.get() : this->make();
1112 }
1113
1114 EndPoint &
setAddr(uint32_t addr)1115 EndPoint::setAddr(uint32_t addr)
1116 {
1117 this->instance()->m_addr = addr;
1118 logf(LVL_DEBUG, "Endpoint address set to %s\n", ip_addr_to_str(addr));
1119 return *this;
1120 }
1121
1122 bool
isConfigured() const1123 EndPoint::isConfigured() const
1124 {
1125 return m_ptr && m_ptr->isConfigured();
1126 }
1127
1128 int
open(uint32_t addr)1129 EndPoint::open(uint32_t addr)
1130 {
1131 return this->instance()->open(addr);
1132 }
1133
1134 void
useMD5Security(std::string_view const key)1135 EndPoint::useMD5Security(std::string_view const key)
1136 {
1137 this->instance()->useMD5Security(key);
1138 }
1139
1140 int
getSocket() const1141 EndPoint::getSocket() const
1142 {
1143 return m_ptr ? m_ptr->m_fd : ts::NO_FD;
1144 }
1145
1146 int
housekeeping()1147 EndPoint::housekeeping()
1148 {
1149 // Don't force an instance because if there isn't one,
1150 // there's no socket either.
1151 return m_ptr && ts::NO_FD != m_ptr->m_fd ? m_ptr->housekeeping() : -ENOTCONN;
1152 }
1153
1154 ts::Rv<int>
handleMessage()1155 EndPoint::handleMessage()
1156 {
1157 return m_ptr ? m_ptr->handleMessage() :
1158 ts::Rv<int>(-ENOTCONN, log(LVL_INFO, "EndPoint::handleMessage called on unconnected instance"));
1159 }
1160 // ------------------------------------------------------
Cache()1161 Cache::Cache() {}
1162
~Cache()1163 Cache::~Cache() {}
1164
1165 EndPoint::ImplType *
make()1166 Cache::make()
1167 {
1168 m_ptr.reset(new ImplType);
1169 return m_ptr.get();
1170 }
1171
1172 inline Cache::ImplType *
instance()1173 Cache::instance()
1174 {
1175 return static_cast<ImplType *>(this->super::instance());
1176 }
1177
1178 inline Cache::ImplType *
impl()1179 Cache::impl()
1180 {
1181 return static_cast<ImplType *>(m_ptr.get());
1182 }
1183
1184 inline Cache::ImplType const *
impl() const1185 Cache::impl() const
1186 {
1187 return static_cast<ImplType *>(m_ptr.get());
1188 }
1189
1190 Cache::Service
defineServiceGroup(ServiceGroup const & svc,ServiceGroup::Result * result)1191 Cache::defineServiceGroup(ServiceGroup const &svc, ServiceGroup::Result *result)
1192 {
1193 return Service(*this, this->instance()->defineServiceGroup(svc, result));
1194 }
1195
1196 time_t
waitTime() const1197 Cache::waitTime() const
1198 {
1199 return m_ptr ? this->impl()->waitTime() : std::numeric_limits<time_t>::max();
1200 }
1201
1202 Cache &
addSeedRouter(uint8_t id,uint32_t addr)1203 Cache::addSeedRouter(uint8_t id, uint32_t addr)
1204 {
1205 this->instance()->seedRouter(id, addr);
1206 return *this;
1207 }
1208
1209 ts::Errata
loadServicesFromFile(const char * path)1210 Cache::loadServicesFromFile(const char *path)
1211 {
1212 return this->instance()->loadServicesFromFile(path);
1213 }
1214 // ------------------------------------------------------
Router()1215 Router::Router() {}
1216
~Router()1217 Router::~Router() {}
1218
1219 EndPoint::ImplType *
make()1220 Router::make()
1221 {
1222 m_ptr.reset(new ImplType);
1223 return m_ptr.get();
1224 }
1225
1226 inline Router::ImplType *
instance()1227 Router::instance()
1228 {
1229 return static_cast<ImplType *>(this->super::instance());
1230 }
1231
1232 inline Router::ImplType *
impl()1233 Router::impl()
1234 {
1235 return static_cast<ImplType *>(m_ptr.get());
1236 }
1237 // ------------------------------------------------------
1238 } // namespace wccp
1239