1 #include "protoRouteMgr.h"
2 #include "protoDebug.h"
3 
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <sys/ioctl.h>
8 #include <netinet/in.h>
9 #include <arpa/inet.h>
10 #include <net/if.h>
11 #include <errno.h>
12 
13 // BSD specific includes
14 #include <sys/sysctl.h>
15 #include <net/route.h>
16 #include <net/if_dl.h>
17 
18 class BsdRouteMgr : public ProtoRouteMgr
19 {
20     public:
21         BsdRouteMgr();
22         virtual ~BsdRouteMgr();
23 
24         virtual bool Open(const void* userData = NULL);
IsOpen() const25         virtual bool IsOpen() const {return (descriptor >= 0);}
26         virtual void Close();
27 
28         virtual bool GetAllRoutes(ProtoAddress::Type addrType,
29                                   ProtoRouteTable&   routeTable);
30 
31         virtual bool GetRoute(const ProtoAddress& dst,
32                               unsigned int        prefixLen,
33                               ProtoAddress&       gw,
34                               unsigned int&       ifIndex,
35                               int&                metric);
36 
37         virtual bool SetRoute(const ProtoAddress& dst,
38                               unsigned int        prefixLen,
39                               const ProtoAddress& gw,
40                               unsigned int        ifIndex,
41                               int                 metric);
42 
43         virtual bool DeleteRoute(const ProtoAddress& dst,
44                                  unsigned int        prefixLen,
45                                  const ProtoAddress& gw,
46                                  unsigned int        ifIndex);
47 
48         // (TBD) make this actually do something
SetForwarding(bool)49         virtual bool SetForwarding(bool /*state*/) {return true;}
50 
GetInterfaceIndex(const char * interfaceName)51         virtual unsigned int GetInterfaceIndex(const char* interfaceName)
52         {
53             return ProtoSocket::GetInterfaceIndex(interfaceName);
54         }
55         virtual bool GetInterfaceAddressList(unsigned int        ifIndex,
56                                              ProtoAddress::Type  addrType,
57                                              ProtoAddressList& addrList);
GetInterfaceName(unsigned int interfaceIndex,char * buffer,unsigned int buflen)58         virtual bool GetInterfaceName(unsigned int  interfaceIndex,
59                                       char*         buffer,
60                                       unsigned int  buflen)
61         {
62             return ProtoSocket::GetInterfaceName(interfaceIndex, buffer, buflen);
63         }
64 
65 
66     private:
67         int     descriptor;
68         pid_t   pid;
69         UINT32  sequence;
70 
71 };  // end class BsdRouteMgr
72 
73 
Create(Type)74 ProtoRouteMgr* ProtoRouteMgr::Create(Type /*type*/)
75 {
76     // TBD - support alternative route mgr "types" (e.g. ZEBRA)
77     return (static_cast<ProtoRouteMgr*>(new BsdRouteMgr));
78 }
79 
BsdRouteMgr()80 BsdRouteMgr::BsdRouteMgr()
81  : descriptor(-1), sequence(0)
82 {
83 
84 }
85 
~BsdRouteMgr()86 BsdRouteMgr::~BsdRouteMgr()
87 {
88     Close();
89 }
90 
Open(const void *)91 bool BsdRouteMgr::Open(const void* /*userData*/)
92 {
93     if (IsOpen()) Close();
94     if ((descriptor = socket(AF_ROUTE, SOCK_RAW, 0)) < 0)
95     {
96         PLOG(PL_ERROR, "BsdRouteMgr::Open() socket(AF_ROUTE) error: %s\n",
97                 strerror(errno));
98         return false;
99     }
100     else
101     {
102         pid = (UINT32)getpid();
103         return true;
104     }
105 }  // end BsdRouteMgr::Open()
106 
Close()107 void BsdRouteMgr::Close()
108 {
109     if (IsOpen())
110     {
111         close(descriptor);
112         descriptor = -1;
113     }
114 }  // end BsdRouteMgr::Close()
115 
116 /*
117 #define ROUNDUP(a, size) (((a) & ((size)-1)) ?                          \
118                                 (1 + ((a) | ((size)-1)))                \
119                                 : (a))
120 
121 #define NEXT_SA(sa)   sa = (struct sockaddr *) ((caddr_t) (sa) + ((sa)->sa_len ?    \
122                 ROUNDUP((sa)->sa_len, sizeof(u_long))                   \
123                 : sizeof(u_long)));
124 */
125 
126 // IMPORTANT NOTE:  These "oldie but goodie macros above are wrong on a 64-bit machine!!!
127 //                  The ones below do the right thing.
128 
129 #define ROUNDUP(a, size) (((a) & ((size)-1)) ?                          \
130                                 (1 + ((a) | ((size)-1)))                \
131                                 : (a))
132 
133 #define NEXT_SA(sa)   sa = (struct sockaddr *) ((caddr_t) (sa) + ((sa)->sa_len ?    \
134                 ROUNDUP((sa)->sa_len, sizeof(UINT32))                   \
135                 : sizeof(UINT32)));
136 
GetAllRoutes(ProtoAddress::Type addrType,ProtoRouteTable & routeTable)137 bool BsdRouteMgr::GetAllRoutes(ProtoAddress::Type addrType,
138                                ProtoRouteTable&   routeTable)
139 {
140     int mib[6];
141     mib[0] = CTL_NET;
142     mib[1] = PF_ROUTE;
143     mib[2] = 0;
144 
145     switch (addrType)
146     {
147         case ProtoAddress::IPv4:
148             mib[3] = PF_INET;
149             break;
150 #ifdef HAVE_IPV6
151         case ProtoAddress::IPv6:
152             mib[3] = PF_INET6;
153             break;
154 #endif // HAVE_IPV6
155         default:
156             PLOG(PL_ERROR, "BsdRouteMgr::GetAllRoutes() unsupported addr family\n");
157             return false;
158     }
159     mib[4] = NET_RT_DUMP;
160     mib[5] = 0;
161 
162     char* buf;;
163     size_t len;
164     if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
165     {
166         PLOG(PL_ERROR, "BsdRouteMgr::GetAllRoutes() sysctl() error: %s\n",
167                 strerror(errno));
168         return false;
169     }
170 
171     if (!(buf = new char[len]))
172     {
173         PLOG(PL_ERROR, "BsdRouteMgr::GetAllRoutes() malloc(%d) error: %s\n",
174                 len, strerror(errno));
175         return false;
176     }
177 
178     if (sysctl(mib, 6, buf, &len, NULL, 0) < 0)
179     {
180             delete[] buf;
181             PLOG(PL_ERROR, "BsdRouteMgr::GetAllRoutes() sysctl() error: %s\n", strerror(errno));
182             return false;
183     }
184 
185     char* end = buf + len;
186     char* next = buf;
187     while (next < end)
188     {
189         // (void*) casts here to avoid cast-align mis-warning
190         struct rt_msghdr* rtm = (struct rt_msghdr*)((void*)next);
191         struct sockaddr* addr = (struct sockaddr*)(rtm + 1);
192         ProtoAddress dst, gw;
193         dst.Invalidate();
194         gw.Invalidate();
195         int prefixLen = -1;
196         for (int i = 0; i < RTAX_MAX; i++)
197         {
198             if (0 != (rtm->rtm_addrs & (0x01 << i)))
199             {
200                 switch (i)
201                 {
202                     case RTAX_DST:
203                     {
204                         dst.SetSockAddr(*addr);
205                         //TRACE("RTAX_DST: %s\t\t", dst.GetHostString());
206                         break;
207                     }
208                     case RTAX_GATEWAY:
209                     {
210                         gw.SetSockAddr(*addr);
211                         //TRACE("RTAX_GWY: %s\n", gw.GetHostString());
212                         break;
213                     }
214                     case RTAX_NETMASK:
215                     {
216                         const unsigned char* ptr = (const unsigned char*)(&addr->sa_data[2]);
217                         if (NULL != ptr)
218                         {
219                             unsigned int maskSize = addr->sa_len ? (addr->sa_len - (int)(ptr - (unsigned char*)addr)) : 0;
220                             prefixLen = 0;
221                             for (unsigned int i = 0; i < maskSize; i++)
222                             {
223                                 if (0xff == *ptr)
224                                 {
225                                     prefixLen += 8;
226                                     ptr++;
227                                 }
228                                 else
229                                 {
230                                     unsigned char bit = 0x80;
231                                     while (0 != (bit & *ptr))
232                                     {
233                                         bit >>= 1;
234                                         prefixLen += 1;
235                                     }
236                                     break;
237                                 }
238                             }
239                         }
240                         //TRACE("BsdRouteMgr::GetAllRoutes() recvd RTA_NETMASK: %d\n", prefixLen);
241                         break;
242                     }
243                     case RTAX_GENMASK:
244                     {
245                         PLOG(PL_ERROR, "BsdRouteMgr::GetAllRoutes() recvd RTA_GENMASK ...\n");
246                         break;
247                     }
248                     default:
249                     {
250                         PLOG(PL_ERROR, "BsdRouteMgr::GetAllRoutes() recvd unhandled RTA: %d\n", i);
251                         break;
252                     }
253                 }  // end switch(i)
254                 NEXT_SA(addr);
255             }  // end if(mask[i] is set)
256         }  // end for(i=0..RTAX_MAX)
257         if (dst.IsValid())
258         {
259             bool setRoute = true;
260 #ifdef MACOSX
261             // Don't fetch cloned routes (TBD - investigate further)
262             if (0 != (rtm->rtm_flags & RTF_WASCLONED))
263                 setRoute = false;
264 #endif  // MACOSX
265             if (0 == prefixLen)
266             {
267                 // This makes sure default routes w/ valid gateway "trump" device default route
268                 // (TBD - deal with multiple default route entries)
269                 if ((NULL != routeTable.GetDefaultEntry()) && !gw.IsValid())
270                     setRoute = false;
271             }
272             if (prefixLen < 0) prefixLen = dst.GetLength() << 3;
273             if (0 == (rtm->rtm_flags & RTF_GATEWAY)) gw.Invalidate();
274 
275             if (setRoute)
276             {
277                 if (!routeTable.SetRoute(dst, prefixLen, gw, rtm->rtm_index))
278                 {
279                     PLOG(PL_ERROR, "BsdRouteMgr::GetAllRoutes() error creating table entry\n");
280                     delete[] buf;
281                     return false;
282                 }
283             }
284         }
285         next += rtm->rtm_msglen;
286     }
287     delete[] buf;
288     return true;
289 
290 }  // end BsdRouteMgr::GetAllRoutes()
291 
SetRoute(const ProtoAddress & dst,unsigned int prefixLen,const ProtoAddress & gw,unsigned ifIndex,int)292 bool BsdRouteMgr::SetRoute(const ProtoAddress& dst,
293                            unsigned int        prefixLen,
294                            const ProtoAddress& gw,
295                            unsigned            ifIndex,
296                            int                 /*metric*/)
297 {
298     // Construct a RTM_ADD request
299     int buffer[256];  // we use "int" for alignment safety
300     memset(buffer, 0, 256*sizeof(int));
301     unsigned int sockaddrLen = 0;
302     switch (dst.GetType())
303     {
304         case ProtoAddress::IPv4:
305             sockaddrLen = sizeof(struct sockaddr_in);
306             break;
307 #ifdef HAVE_IPV6
308         case ProtoAddress::IPv6:
309             sockaddrLen = sizeof(struct sockaddr_in6);
310             break;
311 #endif // HAVE_IPV6
312         default:
313             PLOG(PL_ERROR, "BsdRouteMgr::SetRoute() invalid dst address type!\n");
314             return false;
315     }
316     struct rt_msghdr* rtm = (struct rt_msghdr*)buffer;
317     rtm->rtm_msglen = sizeof(struct rt_msghdr);
318     rtm->rtm_version = RTM_VERSION;
319     rtm->rtm_type = RTM_ADD;
320     rtm->rtm_addrs = RTA_DST;
321     rtm->rtm_flags = RTF_UP;
322 
323     if (prefixLen < (unsigned int)(dst.GetLength() << 3))
324     {
325         // address bits past mask _must_ be ZERO, so we test for that here
326         unsigned int index = prefixLen >> 3;
327         const char* ptr = dst.GetRawHostAddress();
328         if (0 != ((0x00ff >> (prefixLen & 0x07)) & ptr[index]))
329         {
330             PLOG(PL_ERROR, "BsdRouteMgr::SetRoute() invalid address for given mask\n");
331             return false;
332         }
333         while (++index < dst.GetLength())
334         {
335             if (0 != ptr[index] )
336             {
337                 PLOG(PL_ERROR, "BsdRouteMgr::SetRoute() invalid address for given mask\n");
338                 return false;
339             }
340         }
341         rtm->rtm_addrs |= RTA_NETMASK;
342 #ifdef RTF_MASK
343         rtm->rtm_flags |= RTF_MASK;
344 #endif // RTF_MASK
345     }
346     else
347     {
348         rtm->rtm_flags |= RTF_HOST;
349     }
350 
351     if (gw.IsValid())
352     {
353         rtm->rtm_addrs |= RTA_GATEWAY;
354         rtm->rtm_flags |= RTF_GATEWAY;
355     }
356     else if (ifIndex != 0)
357     {
358         rtm->rtm_addrs |= RTA_GATEWAY;
359         rtm->rtm_index = ifIndex;
360     }
361     else
362     {
363         PLOG(PL_ERROR, "BsdRouteMgr::SetRoute() invalid gateway address\n");
364         return false;
365     }
366 
367     rtm->rtm_pid = pid;
368     int seq = sequence++;
369     rtm->rtm_seq = seq;
370 
371 
372     struct sockaddr* addr = (struct sockaddr*)(rtm + 1);
373     for (int i = 0; i < RTAX_MAX; i++)
374     {
375         if (0 != (rtm->rtm_addrs & (0x01 << i)))
376         {
377             switch (i)
378             {
379                 case RTAX_DST:
380                     //TRACE("Adding RTAX_DST...\n");
381                     memcpy(addr, &dst.GetSockAddr(), sockaddrLen);
382                     rtm->rtm_msglen += sockaddrLen;
383                     break;
384                 case RTAX_GATEWAY:
385                     if (0 != (rtm->rtm_flags & RTF_GATEWAY))
386                     {
387                         //TRACE("Adding RTAX_GATEWAY...\n");
388                         memcpy(addr, &gw.GetSockAddr(), sockaddrLen);
389                         rtm->rtm_msglen += sockaddrLen;
390                     }
391                     else
392                     {
393                         //TRACE("Adding RTAX_GATEWAY (IF)...\n");
394                         struct sockaddr_dl* sdl = (struct sockaddr_dl*)((void*)addr);
395                         sdl->sdl_len = sizeof(struct sockaddr_dl);
396                         sdl->sdl_family = AF_LINK;
397                         sdl->sdl_index = ifIndex;
398                         sdl->sdl_type = 0;
399                         sdl->sdl_nlen = 0;
400                         sdl->sdl_alen = 0;
401                         sdl->sdl_slen = 0;
402                         rtm->rtm_msglen += sizeof(struct sockaddr_dl);
403                     }
404                     break;
405                 case RTAX_NETMASK:
406                 {
407                     if (prefixLen > 0)
408                     {
409                         unsigned char* ptr = (unsigned char*)(&addr->sa_data[2]);
410                         unsigned int numBytes = prefixLen >> 3;
411                         memset(ptr, 0xff, numBytes);
412                         unsigned int remainder = prefixLen & 0x07;
413                         if (remainder)
414                         {
415                             ptr[numBytes] = 0xff << (8 - remainder);
416                             addr->sa_len = 5 + numBytes;
417                         }
418                         else
419                         {
420                             addr->sa_len = (ptr - (unsigned char*)addr) + numBytes;
421                         }
422                         rtm->rtm_msglen += ROUNDUP(addr->sa_len, sizeof(u_long));
423                     }
424                     else
425                     {
426                         rtm->rtm_msglen += sizeof(u_long);
427                         addr->sa_len = 4;
428                     }
429                     break;
430                 }
431                 default:
432                     break;
433             }  // end switch(i)
434             NEXT_SA(addr);
435         }  // end if (rtm_addrs[i])
436     }  // end for(i=0..RTAX_MAX)
437 
438 
439     // Send RTM_ADD request to routing socket
440     int result = send(descriptor, rtm, rtm->rtm_msglen, 0);
441     if ((result < 0) && (EEXIST == errno))
442     {
443         // route already exists, so "change" it
444         rtm->rtm_type = RTM_CHANGE;
445         // Send RTM_CHANGE request to routing socket
446         result = send(descriptor, rtm, rtm->rtm_msglen, 0);
447     }
448     if ((int)rtm->rtm_msglen != result)
449     {
450         PLOG(PL_ERROR, "BsdRouteMgr::SetRoute() send() error: %s\n",
451                 strerror(errno));
452         return false;
453     }
454 
455     // Recv the result
456     while (1)
457     {
458         ProtoAddress destination, gateway;
459         destination.Invalidate();
460         gateway.Invalidate();
461         int msgLen = recv(descriptor, rtm, 256*sizeof(int), 0);
462         if (msgLen < 0)
463         {
464             if (errno != EINTR)
465             {
466                 PLOG(PL_ERROR, "BsdRouteMgr::SetRoute() recv() error: %s\n", strerror(errno));
467                 return false;
468             }
469             else
470             {
471                 continue;
472             }
473         }
474         if ((seq == rtm->rtm_seq) &&
475             (pid == rtm->rtm_pid))
476         {
477             TRACE("matching response ...\n");
478             struct sockaddr* addr = (struct sockaddr*)(rtm + 1);
479             for (int i = 0; i < RTAX_MAX; i++)
480             {
481                 if (0 != (rtm->rtm_addrs & (0x01 << i)))
482                 {
483                     switch (i)
484                     {
485                         case RTAX_DST:
486                         {
487                             destination.SetSockAddr(*addr);
488                             //TRACE("RTAX_DST: %s\t\t", dst.GetHostString());
489                             break;
490                         }
491                         case RTAX_GATEWAY:
492                         {
493                             gateway.SetSockAddr(*addr);
494                             //TRACE("RTAX_GWY: %s\n", gw.GetHostString());
495                             break;
496                         }
497                         case RTAX_NETMASK:
498                         {
499                             const unsigned char* ptr = (const unsigned char*)(&addr->sa_data[2]);
500                             if (ptr)
501                             {
502                                 unsigned int maskSize = addr->sa_len ? (addr->sa_len - (int)(ptr - (unsigned char*)addr)) : 0;
503                                 prefixLen = 0;
504                                 for (unsigned int i = 0; i < maskSize; i++)
505                                 {
506                                     if (0xff == *ptr)
507                                     {
508                                         prefixLen += 8;
509                                         ptr++;
510                                     }
511                                     else
512                                     {
513                                         unsigned char bit = 0x80;
514                                         while (0 != (bit & *ptr))
515                                         {
516                                             bit >>= 1;
517                                             prefixLen += 1;
518                                         }
519                                     }
520                                 }
521                             }
522                             //TRACE("RTAX_NMSK: %d\t\t", prefixLen);
523                             break;
524                         }
525                         case RTAX_GENMASK:
526                         {
527                             TRACE("BsdRouteMgr::SetRoute() recvd RTA_GENMASK ...\n");
528                             break;
529                         }
530                         default:
531                         {
532                             TRACE("BsdRouteMgr::SetRoute() recvd unhandled RTA: %d\n", i);
533                             break;
534                         }
535                     }  // end switch(i)
536                     NEXT_SA(addr);
537                 }  // end if(mask(i) is set)
538             }  // end for (i=0..RTAX_MAX)
539             if (0 != (rtm->rtm_flags & RTF_DONE))
540             {
541                 if (destination.IsValid())
542                 {
543                     //TRACE("BsdRouteMgr::SetRoute() successfully added route\n");
544                     return true;
545                 }
546                 else
547                 {
548                     PLOG(PL_ERROR, "BsdRouteMgr::SetRoute() completed with invalid dst\n");
549                     return false;
550                 }
551             }
552         }
553         else
554         {
555             TRACE("BsdRouteMgr::SetRoute() recvd non-matching response\n");
556         }
557     }  // end while (1)
558     return false;
559 }  // end BsdRouteMgr::SetRoute()
560 
561 // Currently deletes _all_ routes regardless of gateway or index
DeleteRoute(const ProtoAddress & dst,unsigned int prefixLen,const ProtoAddress &,unsigned int ifIndex)562 bool BsdRouteMgr::DeleteRoute(const ProtoAddress& dst,
563                               unsigned int        prefixLen,
564                               const ProtoAddress& /*gateway*/,
565                               unsigned int        ifIndex)
566 {
567     ProtoAddress gw;
568     int metric;
569     while (GetRoute(dst, prefixLen, gw, ifIndex, metric))
570     {
571     // Construct a RTM_DELETE request
572     int buffer[256];  // we use "int" for alignment safety
573     memset(buffer, 0, 256*sizeof(int));
574     unsigned int sockaddrLen = 0;
575     switch (dst.GetType())
576     {
577         case ProtoAddress::IPv4:
578             sockaddrLen = sizeof(struct sockaddr_in);
579             break;
580 #ifdef HAVE_IPV6
581         case ProtoAddress::IPv6:
582             sockaddrLen = sizeof(struct sockaddr_in6);
583             break;
584 #endif // HAVE_IPV6
585         default:
586             PLOG(PL_ERROR, "BsdRouteMgr::DeleteRoute() invalid dst address type!\n");
587             return false;
588     }
589     struct rt_msghdr* rtm = (struct rt_msghdr*)buffer;
590     rtm->rtm_msglen = sizeof(struct rt_msghdr);
591     rtm->rtm_version = RTM_VERSION;
592     rtm->rtm_type = RTM_DELETE;
593     rtm->rtm_addrs = RTA_DST;
594     rtm->rtm_flags = 0;
595 
596     if (prefixLen < (unsigned int)(dst.GetLength() << 3))
597     {
598         // address bits past mask _must_ be ZERO, so we test for that here
599         unsigned int index = prefixLen >> 3;
600         const char* ptr = dst.GetRawHostAddress();
601         if (0 != ((0x00ff >> (prefixLen & 0x07)) & ptr[index]))
602         {
603             PLOG(PL_ERROR, "BsdRouteMgr::DeleteRoute() invalid address for given mask\n");
604             return false;
605         }
606         while (++index < dst.GetLength())
607         {
608             if (0 != ptr[index] )
609             {
610                 PLOG(PL_ERROR, "BsdRouteMgr::DeleteRoute() invalid address for given mask\n");
611                 return false;
612             }
613         }
614         rtm->rtm_addrs |= RTA_NETMASK;
615     }
616     else
617     {
618         rtm->rtm_flags |= RTF_HOST;
619     }
620 
621     // (TBD) IPv6 host flags???
622     if (gw.IsValid())
623     {
624         rtm->rtm_addrs |= RTA_GATEWAY;
625         rtm->rtm_flags |= RTF_GATEWAY;
626     }
627     else if (ifIndex > 0)
628     {
629         rtm->rtm_addrs |= RTA_GATEWAY;
630     }
631 
632     rtm->rtm_pid = pid;
633     int seq = sequence++;
634     rtm->rtm_seq = seq;
635 
636     struct sockaddr* addr = (struct sockaddr*)(rtm + 1);
637     for (int i = 0; i < RTAX_MAX; i++)
638     {
639         if (0 != (rtm->rtm_addrs & (0x01 << i)))
640         {
641             switch (i)
642             {
643                 case RTAX_DST:
644                     memcpy(addr, &dst.GetSockAddr(), sockaddrLen);
645                     rtm->rtm_msglen += sockaddrLen;
646                     break;
647                 case RTAX_GATEWAY:
648                     if (0 != (rtm->rtm_flags & RTF_GATEWAY))
649                     {
650                         //TRACE("Adding RTAX_GATEWAY...\n");
651                         memcpy(addr, &gw.GetSockAddr(), sockaddrLen);
652                         rtm->rtm_msglen += sockaddrLen;
653                     }
654                     else
655                     {
656                         //TRACE("Adding RTAX_GATEWAY (IF)...\n");
657                         struct sockaddr_dl* sdl = (struct sockaddr_dl*)((void*)addr);
658                         sdl->sdl_len = sizeof(struct sockaddr_dl);
659                         sdl->sdl_family = AF_LINK;
660                         sdl->sdl_index = ifIndex;
661                         sdl->sdl_type = 0;
662                         sdl->sdl_nlen = 0;
663                         sdl->sdl_alen = 0;
664                         sdl->sdl_slen = 0;
665                         rtm->rtm_msglen += sizeof(struct sockaddr_dl);
666                     }
667                     break;
668                 case RTAX_NETMASK:
669                 {
670                     unsigned char* ptr = (unsigned char*)(&addr->sa_data[2]);
671                     if (prefixLen > 0)
672                     {
673                         unsigned int numBytes = prefixLen >> 3;
674                         memset(ptr, 0xff, numBytes);
675                         unsigned int remainder = prefixLen & 0x07;
676                         if (remainder)
677                         {
678                             ptr[numBytes] = 0xff << (8 - remainder);
679                             addr->sa_len = 5 + numBytes;
680                         }
681                         else
682                         {
683                             addr->sa_len = (ptr - (unsigned char*)addr) + numBytes;
684                         }
685                         rtm->rtm_msglen += ROUNDUP(addr->sa_len, sizeof(u_long));
686                     }
687                     else
688                     {
689                         rtm->rtm_msglen += sizeof(u_long);
690                         addr->sa_len = 4;
691 
692                     }
693                     break;
694                 }
695                 default:
696                     break;
697             }  // end switch(i)
698             NEXT_SA(addr);
699         }  // end if (rtm_addrs[i])
700     }  // end for(i=0..RTAX_MAX)
701 
702     // Send RTM_ADD request to netlink socket
703     int result = send(descriptor, rtm, rtm->rtm_msglen, 0);
704     if ((result < 0) && (EEXIST == errno))
705     {
706         // route already exists, so "change" it
707         rtm->rtm_type = RTM_CHANGE;
708         // Send RTM_CHANGE request to netlink socket
709         result = send(descriptor, rtm, rtm->rtm_msglen, 0);
710     }
711     if ((int)rtm->rtm_msglen != result)
712     {
713         // This will occur when there is no matching route to delete
714         PLOG(PL_WARN, "BsdRouteMgr::DeleteRoute() send() warning: %s\n",
715                 strerror(errno));
716         return false;
717     }
718 
719     // Recv the result
720     bool complete = false;
721     while (!complete)
722     {
723         ProtoAddress destination, gateway, genmask;
724         destination.Invalidate();
725         gateway.Invalidate();
726         genmask.Invalidate();
727         int msgLen = recv(descriptor, rtm, 256*sizeof(int), 0);
728         if (msgLen < 0)
729         {
730             if (errno != EINTR)
731             {
732                 PLOG(PL_ERROR, "BsdRouteMgr::DeleteRoute() recv() error: %s\n", strerror(errno));
733                 return false;
734             }
735             else
736             {
737                 continue;
738             }
739         }
740         if ((seq == rtm->rtm_seq) &&
741             (pid == rtm->rtm_pid))
742         {
743             struct sockaddr* addr = (struct sockaddr*)(rtm + 1);
744             for (int i = 0; i < RTAX_MAX; i++)
745             {
746                 if (0 != (rtm->rtm_addrs & (0x01 << i)))
747                 {
748                     switch (i)
749                     {
750                         case RTAX_DST:
751                         {
752                             destination.SetSockAddr(*addr);
753                             //TRACE("RTAX_DST: %s\t\t", destination.GetHostString());
754                             break;
755                         }
756                         case RTAX_GATEWAY:
757                         {
758                             gateway.SetSockAddr(*addr);
759                             //TRACE("RTAX_GWY: %s\n", gateway.GetHostString());
760                             break;
761                         }
762                         case RTAX_NETMASK:
763                         {
764                             const unsigned char* ptr = (const unsigned char*)(&addr->sa_data[2]);
765                             if (ptr)
766                             {
767                                 unsigned int maskSize = addr->sa_len ? (addr->sa_len - (int)(ptr - (unsigned char*)addr)) : 0;
768                                 prefixLen = 0;
769                                 for (unsigned int i = 0; i < maskSize; i++)
770                                 {
771                                     if (0xff == *ptr)
772                                     {
773                                         prefixLen += 8;
774                                         ptr++;
775                                     }
776                                     else
777                                     {
778                                         unsigned char bit = 0x80;
779                                         while (0 != (bit & *ptr))
780                                         {
781                                             bit >>= 1;
782                                             prefixLen += 1;
783                                         }
784                                     }
785                                 }
786                             }
787                             //TRACE("RTAX_NMSK: %d\t\t", prefixLen);
788                             break;
789                         }
790                         case RTAX_GENMASK:
791                         {
792                             TRACE("BsdRouteMgr::DeleteRoute() recvd RTA_GENMASK ...\n");
793                             break;
794                         }
795                         default:
796                         {
797                             TRACE("BsdRouteMgr::DeleteRoute() recvd unhandled RTAX: %d\n", i);
798                             break;
799                         }
800                     }  // end switch(i)
801                     NEXT_SA(addr);
802                 }  // end if(mask(i) is set)
803             }  // end for (i=0..RTAX_MAX)
804             if (0 != (rtm->rtm_flags & RTF_DONE))
805             {
806                 if (destination.IsValid())
807                 {
808                     complete = true;
809                     break;
810                 }
811                 else
812                 {
813                     PLOG(PL_ERROR, "BsdRouteMgr::DeleteRoute() completed with invalid dst\n");
814                     return false;
815                 }
816             }
817         }
818         else
819         {
820             TRACE("BsdRouteMgr::DeleteRoute() recvd non-matching response\n");
821         }  // if/else (matchingResponse)
822     }  // end while (!complete)
823     }
824     return true;
825 }  // end BsdRouteMgr::DeleteRoute()
826 
GetRoute(const ProtoAddress & dst,unsigned int prefixLen,ProtoAddress & gw,unsigned int & ifIndex,int & metric)827 bool BsdRouteMgr::GetRoute(const ProtoAddress& dst,
828                            unsigned int        prefixLen,
829                            ProtoAddress&       gw,
830                            unsigned int&       ifIndex,
831                            int&                metric)
832 {
833     // Init return values
834     gw.Invalidate();
835     ifIndex = 0;
836 
837     // Construct a RTM_GET request
838     int buffer[256];  // we use "int" for alignment safety
839     memset(buffer, 0, 256*sizeof(int));
840     unsigned int sockaddrLen = 0;
841     switch (dst.GetType())
842     {
843         case ProtoAddress::IPv4:
844             sockaddrLen = sizeof(struct sockaddr_in);
845             break;
846 #ifdef HAVE_IPV6
847         case ProtoAddress::IPv6:
848             sockaddrLen = sizeof(struct sockaddr_in6);
849             break;
850 #endif // HAVE_IPV6
851         default:
852             PLOG(PL_ERROR, "BsdRouteMgr::GetRoute() invalid dst address type!\n");
853             return false;
854     }
855     struct rt_msghdr* rtm = (struct rt_msghdr*)buffer;
856     rtm->rtm_msglen = sizeof(struct rt_msghdr);
857     rtm->rtm_version = RTM_VERSION;
858     rtm->rtm_type = RTM_GET;
859     rtm->rtm_addrs = RTA_DST;
860     rtm->rtm_flags = 0;
861 
862     if (prefixLen < (unsigned int)(dst.GetLength() << 3))
863     {
864         // address bits past mask _must_ be ZERO, so we test for that here
865         unsigned int index = prefixLen >> 3;
866         const char* ptr = dst.GetRawHostAddress();
867         if (0 != ((0x00ff >> (prefixLen & 0x07)) & ptr[index]))
868         {
869             PLOG(PL_ERROR, "BsdRouteMgr::GetRoute() invalid address for given mask\n");
870             return false;
871         }
872         while (++index < dst.GetLength())
873         {
874             if (0 != ptr[index] )
875             {
876                 PLOG(PL_ERROR, "BsdRouteMgr::GetRoute() invalid address for given mask\n");
877                 return false;
878             }
879         }
880         rtm->rtm_addrs |= RTA_NETMASK;
881     }
882     else
883     {
884         rtm->rtm_flags |= RTF_HOST;
885     }
886 
887     rtm->rtm_pid = pid;
888     int seq = sequence++;
889     rtm->rtm_seq = seq;
890 
891     struct sockaddr* addr = (struct sockaddr*)(rtm + 1);
892     for (int i = 0; i < RTAX_MAX; i++)
893     {
894         if (0 != (rtm->rtm_addrs & (0x01 << i)))
895         {
896             switch (i)
897             {
898                 case RTAX_DST:
899                     memcpy(addr, &dst.GetSockAddr(), sockaddrLen);
900                     rtm->rtm_msglen += sockaddrLen;
901                     break;
902                 case RTAX_NETMASK:
903                 {
904                     unsigned char* ptr = (unsigned char*)(&addr->sa_data[2]);
905                     if (prefixLen > 0)
906                     {
907                         unsigned int numBytes = prefixLen >> 3;
908                         memset(ptr, 0xff, numBytes);
909                         unsigned int remainder = prefixLen & 0x07;
910                         if (remainder)
911                         {
912                             ptr[numBytes] = 0xff << (8 - remainder);
913                             addr->sa_len = 5 + numBytes;
914                         }
915                         else
916                         {
917                             addr->sa_len = (ptr - (unsigned char*)addr) + numBytes;
918                         }
919                         rtm->rtm_msglen += ROUNDUP(addr->sa_len, sizeof(u_long));
920                     }
921                     else
922                     {
923                         rtm->rtm_msglen += sizeof(u_long);
924                         addr->sa_len = 4;
925 
926                     }
927                     break;
928                 }
929                 default:
930                     break;
931             }  // end switch(i)
932             NEXT_SA(addr);
933         }  // end if (rtm_addrs[i])
934     }  // end for(i=0..RTAX_MAX)
935 
936     // Send request to netlink socket
937     int result = send(descriptor, rtm, rtm->rtm_msglen, 0);
938     if ((int)rtm->rtm_msglen != result)
939     {
940         PLOG(PL_ERROR, "BsdRouteMgr::GetRoute() send() error: %s\n", strerror(errno));
941         return false;
942     }
943 
944     // Recv the result
945     while (1)
946     {
947         int msgLen = recv(descriptor, rtm, 256*sizeof(int), 0);
948         if (msgLen < 0)
949         {
950             if (errno != EINTR)
951             {
952                 PLOG(PL_ERROR, "BsdRouteMgr::GetRoute() recv() error: %s\n", strerror(errno));
953                 return false;
954             }
955             else
956             {
957                 continue;
958             }
959         }
960         if ((RTM_GET == rtm->rtm_type) &&
961             (seq == rtm->rtm_seq) &&
962             (pid == rtm->rtm_pid))
963         {
964             struct sockaddr* addr = (struct sockaddr*)(rtm + 1);
965             ProtoAddress destination;
966             destination.Invalidate();
967             for (int i = 0; i < RTAX_MAX; i++)
968             {
969                 if (0 != (rtm->rtm_addrs & (0x01 << i)))
970                 {
971                     switch (i)
972                     {
973                         case RTAX_DST:
974                         {
975                             destination.SetSockAddr(*addr);
976                             //TRACE("RTAX_DST: %s\t\t", dst.GetHostString());
977                             break;
978                         }
979                         case RTAX_GATEWAY:
980                         {
981                             switch (addr->sa_family)
982                             {
983 #ifdef HAVE_IPV6
984                                 case AF_INET6:
985 #endif // HAVE_IPV6
986                                 case AF_INET:
987                                     gw.SetSockAddr(*addr);
988                                     //TRACE("RTAX_GWY: %s\n", gw.GetHostString());
989                                     break;
990                                 case AF_LINK:
991                                     ifIndex = ((struct sockaddr_dl*)((void*)addr))->sdl_index;
992                                     //TRACE("RTAX_GWY: ifIndex:%d\n", ifIndex);
993                                     break;
994                                 default:
995                                     PLOG(PL_ERROR, "BsdRouteMgr::GetRoute() recvd unknown sa_family\n");
996                                     break;
997                             }
998                             break;
999                         }
1000                         case RTAX_NETMASK:
1001                         {
1002                             const unsigned char* ptr = (const unsigned char*)(&addr->sa_data[2]);
1003                             if (ptr)
1004                             {
1005                                 unsigned int maskSize = addr->sa_len ? (addr->sa_len - (int)(ptr - (unsigned char*)addr)) : 0;
1006                                 unsigned int prefixLen = 0;
1007                                 for (unsigned int i = 0; i < maskSize; i++)
1008                                 {
1009                                     if (0xff == *ptr)
1010                                     {
1011                                         prefixLen += 8;
1012                                         ptr++;
1013                                     }
1014                                     else
1015                                     {
1016                                         unsigned char bit = 0x80;
1017                                         while (0 != (bit & *ptr))
1018                                         {
1019                                             bit >>= 1;
1020                                             prefixLen += 1;
1021                                         }
1022                                     }
1023                                     break;
1024                                 }
1025                                 //TRACE("BsdRouteMgr::GetRoute() recvd RTAX_NMSK: %d\n", prefixLen);
1026                             }
1027                             break;
1028                         }
1029                         case RTAX_GENMASK:
1030                         {
1031                             TRACE("BsdRouteMgr::GetRoute() recvd RTA_GENMASK ...\n");
1032                             break;
1033                         }
1034                         default:
1035                         {
1036                             TRACE("BsdRouteMgr::GetRoute() recvd unhandled RTA: %d\n", i);
1037                             break;
1038                         }
1039                     }  // end switch(i)
1040                     NEXT_SA(addr);
1041                 }  // end if (mask[i] is set)
1042             }  // end for(i=0..RTAX_MAX)
1043             if (0 != (rtm->rtm_flags & RTF_DONE))
1044             {
1045                 if (destination.IsValid())
1046                 {
1047                     //if (!gw.IsValid()) gw.Reset(destination.GetType());
1048                     //if (prefixLen < 0) prefixLen = dst.GetLength() << 3;
1049                     // (TBD) get actual metric
1050                     metric = -1;
1051                     return true;
1052                 }
1053                 else
1054                 {
1055                     return false;
1056                 }
1057             }
1058         }
1059         else
1060         {
1061             TRACE("BsdRouteMgr::GetRoute() recvd non-matching response\n");
1062         }
1063     }  // end while (1)
1064     return false;
1065 }  // end BsdRouteMgr::GetRoute()
1066 
GetInterfaceAddressList(unsigned int ifIndex,ProtoAddress::Type addrType,ProtoAddressList & addrList)1067 bool BsdRouteMgr::GetInterfaceAddressList(unsigned int        ifIndex,
1068                                           ProtoAddress::Type  addrType,
1069                                           ProtoAddressList&   addrList)
1070 {
1071     ProtoAddressList localAddrList;  // keep link & site local addrs separate and add at end
1072     // Init return values
1073     int mib[6];
1074     mib[0] = CTL_NET;
1075     mib[1] = AF_ROUTE;
1076     mib[2] = 0;
1077 
1078     switch (addrType)
1079     {
1080         case ProtoAddress::IPv4:
1081             mib[3] = AF_INET;
1082             break;
1083 #ifdef HAVE_IPV6
1084         case ProtoAddress::IPv6:
1085             mib[3] = AF_INET6;
1086             break;
1087 #endif // HAVE_IPV6
1088         default:
1089             PLOG(PL_ERROR, "BsdRouteMgr::GetInterfaceAddressList() unsupported addr family\n");
1090             break;
1091     }
1092     mib[4] = NET_RT_IFLIST;
1093     mib[5] = ifIndex;
1094 
1095     char* buf;;
1096     size_t len;
1097     if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
1098     {
1099         PLOG(PL_ERROR, "BsdRouteMgr::GetInterfaceAddressList() sysctl() error: %s\n",
1100                 strerror(errno));
1101         return false;
1102     }
1103 
1104     if (!(buf = new char[len]))
1105     {
1106         PLOG(PL_ERROR, "BsdRouteMgr::GetInterfaceAddressList() malloc(%d) error: %s\n",
1107                 len, strerror(errno));
1108         return false;
1109     }
1110 
1111     if (sysctl(mib, 6, buf, &len, NULL, 0) < 0)
1112     {
1113         delete[] buf;
1114         PLOG(PL_ERROR, "BsdRouteMgr::GetInterfaceAddressList() sysctl() error: %s\n", strerror(errno));
1115         return false;
1116     }
1117 
1118     char* end = buf + len;
1119     char* next = buf;
1120     while (next < end)
1121     {
1122         struct if_msghdr* ifm = (struct if_msghdr*)((void*)next);
1123         switch (ifm->ifm_type)
1124         {
1125             case RTM_IFINFO:
1126             {
1127                 // TRACE("received RTM_IFINFO message ...\n");
1128                 // (this can give us the the link layer addr)
1129                 break;
1130             }
1131             case RTM_NEWADDR:
1132             {
1133                 //TRACE("received RTM_NEWADDR message ...\n");
1134                 struct ifa_msghdr* ifam = (struct ifa_msghdr*)ifm;
1135                 struct sockaddr* addr = (struct sockaddr*)(ifam + 1);
1136                 for (int i = 0; i < RTAX_MAX; i++)
1137                 {
1138                     if (0 != (ifam->ifam_addrs & (0x01 << i)))
1139                     {
1140                         switch (i)
1141                         {
1142                             case RTAX_IFA:
1143                                 //TRACE("received RTAX_IFA ...index:%d\n", ifam->ifam_index);
1144                                 if (ifIndex == ifam->ifam_index)
1145                                 {
1146                                     ProtoAddress addrTemp;
1147                                     addrTemp.SetSockAddr(*addr);
1148                                     if (addrTemp.IsValid())
1149                                     {
1150                                         if (addrTemp.IsLinkLocal() || addrTemp.IsSiteLocal())
1151                                         {
1152                                             if (!localAddrList.Insert(addrTemp))
1153                                                 PLOG(PL_ERROR, "BsdRouteMgr::GetInterfaceAddressList() error: unable to add addr to local list\n");
1154                                         }
1155                                         else
1156                                         {
1157                                             if (!addrList.Insert(addrTemp))
1158                                                 PLOG(PL_ERROR, "BsdRouteMgr::GetInterfaceAddressList() error: unable to add addr to list\n");
1159                                         }
1160                                     }
1161                                 }
1162                                 break;
1163                             case RTAX_BRD:
1164                                 //TRACE("received RTAX_BRD ...\n");
1165                                 break;
1166                             case RTAX_NETMASK:
1167                                 //TRACE("received RTAX_NETMASK ...\n");
1168                                 break;
1169                             default:
1170                                 //TRACE("unhandled RTAX type:%d\n", i);
1171                                 break;
1172                         }
1173                         NEXT_SA(addr);
1174                     }
1175                 }  // end for(i=0..RTAX_MAX)
1176                 break;
1177             }
1178             default:
1179                 PLOG(PL_ERROR, "BsdRouteMgr::GetInterfaceAddressList() warning: unhandled IFM message type:%d\n", ifm->ifm_type);
1180                 break;
1181 
1182         }
1183         next += ifm->ifm_msglen;
1184     }
1185     delete[] buf;
1186 
1187     ProtoAddressList::Iterator iterator(localAddrList);
1188     ProtoAddress localAddr;
1189     while (iterator.GetNextAddress(localAddr))
1190     {
1191         if (!addrList.Insert(localAddr))
1192             PLOG(PL_ERROR, "BsdRouteMgr::GetInterfaceAddressList() error: unable to add local addr to list\n");
1193     }
1194     if (addrList.IsEmpty())
1195         PLOG(PL_WARN, "BsdRouteMgr::GetInterfaceAddressList() warning: no addresses found\n");
1196     return true;
1197 }  // end BsdRouteMgr::GetInterfaceAddressList()
1198 
1199