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