1 /*
2  * scamper_rtsock: code to deal with a route socket or equivalent
3  *
4  * $Id: scamper_rtsock.c,v 1.87 2020/06/10 09:09:27 mjl Exp $
5  *
6  *          Matthew Luckie
7  *
8  *          Supported by:
9  *           The University of Waikato
10  *           NLANR Measurement and Network Analysis
11  *           CAIDA
12  *           The WIDE Project
13  *
14  * The purpose of this code is to obtain the outgoing interface's index
15  * using whatever mechanisms the operating system supports.  A route
16  * socket is created where necessary and is kept open for the lifetime
17  * of scamper.
18  *
19  * scamper_rtsock_getifindex returns the interface index on success.
20  * if an error occurs, it returns -1.  as route sockets are unreliable
21  * sockets, if we do not get an expected response, we return -2 to
22  * indicate to the caller to try again.
23  *
24  * Copyright (C) 2003-2006 Matthew Luckie
25  * Copyright (C) 2006-2010 The University of Waikato
26  * Copyright (C) 2014      The Regents of the University of California
27  *
28  * This program is free software; you can redistribute it and/or modify
29  * it under the terms of the GNU General Public License as published by
30  * the Free Software Foundation, version 2.
31  *
32  * This program is distributed in the hope that it will be useful,
33  * but WITHOUT ANY WARRANTY; without even the implied warranty of
34  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
35  * GNU General Public License for more details.
36  *
37  * You should have received a copy of the GNU General Public License
38  * along with this program; if not, write to the Free Software
39  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
40  *
41  */
42 
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46 #include "internal.h"
47 
48 #if defined(__APPLE__)
49 static int broken = -1;
50 #endif
51 
52 /* include support for the netlink socket in linux */
53 #if defined(__linux__)
54 
55 struct rtattr
56 {
57   unsigned short  rta_len;
58   unsigned short  rta_type;
59 };
60 
61 struct rtmsg
62 {
63   unsigned char   rtm_family;
64   unsigned char   rtm_dst_len;
65   unsigned char   rtm_src_len;
66   unsigned char   rtm_tos;
67   unsigned char   rtm_table;
68   unsigned char   rtm_protocol;
69   unsigned char   rtm_scope;
70   unsigned char   rtm_type;
71   unsigned        rtm_flags;
72 };
73 
74 #define RTA_ALIGNTO           4
75 #define RTA_ALIGN(len)        (((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1))
76 #define RTA_LENGTH(len)       (RTA_ALIGN(sizeof(struct rtattr)) + (len))
77 #define RTA_DATA(rta)         ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
78 #define RTA_OK(rta,len)       ((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \
79                                (rta)->rta_len <= (len))
80 #define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \
81                                (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
82 #define RTA_UNSPEC            0
83 #define RTA_DST               1
84 #define RTA_SRC               2
85 #define RTA_IIF               3
86 #define RTA_OIF               4
87 #define RTA_GATEWAY           5
88 #define RTA_PRIORITY          6
89 #define RTA_PREFSRC           7
90 #define RTA_METRICS           8
91 #define RTA_MULTIPATH         9
92 #define RTA_PROTOINFO         10
93 #define RTA_FLOW              11
94 #define RTA_CACHEINFO         12
95 #define RTA_SESSION           13
96 
97 #define RTM_RTA(r)         ((struct rtattr*)(((char*)(r)) + \
98                             NLMSG_ALIGN(sizeof(struct rtmsg))))
99 #define RTM_BASE            0x10
100 #define RTM_NEWROUTE       (RTM_BASE+8)
101 #define RTM_GETROUTE       (RTM_BASE+10)
102 #define NETLINK_ROUTE       0
103 
104 #endif
105 
106 #include "scamper.h"
107 #include "scamper_addr.h"
108 #include "scamper_list.h"
109 #include "scamper_fds.h"
110 #include "scamper_rtsock.h"
111 #include "scamper_privsep.h"
112 #include "scamper_osinfo.h"
113 #include "scamper_debug.h"
114 #include "utils.h"
115 #include "mjl_list.h"
116 
117 extern scamper_addrcache_t *addrcache;
118 
119 #ifndef _WIN32
120 typedef struct rtsock_pair
121 {
122   scamper_route_t *route; /* query */
123   uint16_t         seq;   /* sequence number used */
124   dlist_node_t    *node;  /* pointer to node in pair dlist */
125 } rtsock_pair_t;
126 
127 static pid_t    pid;          /* [unpriviledged] process id */
128 static uint16_t seq   = 0;    /* next sequence number to use */
129 static dlist_t *pairs = NULL; /* list of addresses queried with their seq */
130 
rtsock_pair_alloc(scamper_route_t * route,int seq)131 static rtsock_pair_t *rtsock_pair_alloc(scamper_route_t *route, int seq)
132 {
133   rtsock_pair_t *pair;
134   if((pair = malloc_zero(sizeof(rtsock_pair_t))) == NULL)
135     return NULL;
136   pair->route = route;
137   pair->seq = seq;
138   if((pair->node = dlist_head_push(pairs, pair)) == NULL)
139     {
140       free(pair);
141       return NULL;
142     }
143   route->internal = pair;
144   return pair;
145 }
146 
rtsock_pair_free(rtsock_pair_t * pair)147 static void rtsock_pair_free(rtsock_pair_t *pair)
148 {
149   if(pair == NULL)
150     return;
151   pair->route->internal = NULL;
152   if(pair->node != NULL)
153     dlist_node_pop(pairs, pair->node);
154   free(pair);
155   return;
156 }
157 
rtsock_pair_get(uint16_t seq)158 static rtsock_pair_t *rtsock_pair_get(uint16_t seq)
159 {
160   rtsock_pair_t *pair;
161   dlist_node_t  *node;
162 
163   for(node=dlist_head_node(pairs); node != NULL; node=dlist_node_next(node))
164     {
165       pair = dlist_node_item(node);
166       if(pair->seq != seq)
167 	continue;
168       dlist_node_pop(pairs, node);
169       pair->node = NULL;
170       return pair;
171     }
172 
173   return NULL;
174 }
175 
176 #if defined(HAVE_BSD_ROUTE_SOCKET)
177 #if 0
178 static void rtmsg_dump(const uint8_t *buf, size_t len)
179 {
180   char str[80];
181   size_t i, off = 0;
182   int k = 0;
183 
184   for(i=0; i<len; i++)
185     {
186       if(k == 20)
187 	{
188 	  printerror_msg(__func__, "%s", str);
189 	  k = 0;
190 	  off = 0;
191 	}
192 
193       if(k != 0 && (k % 4) == 0)
194 	string_concat(str, sizeof(str), &off, " ");
195       string_concat(str, sizeof(str), &off, "%02x", buf[i]);
196       k++;
197     }
198 
199   if(k != 0)
200     printerror_msg(__func__, "%s", str);
201   return;
202 }
203 #endif
204 
scamper_rtsock_roundup(size_t len)205 size_t scamper_rtsock_roundup(size_t len)
206 {
207 #ifdef __APPLE__
208   const scamper_osinfo_t *osinfo;
209 
210   if(broken == -1)
211     {
212       osinfo = scamper_osinfo_get();
213       if(osinfo->os_id == SCAMPER_OSINFO_OS_DARWIN &&
214 	 osinfo->os_rel_dots > 0 && osinfo->os_rel[0] >= 10)
215 	broken = 1;
216       else
217 	broken = 0;
218     }
219 
220   if(broken != 0)
221     {
222       if(len > 0)
223 	return (1 + ((len - 1) | (sizeof(uint32_t) - 1)));
224       else
225 	return sizeof(uint32_t);
226     }
227 #endif
228 
229   return ((len > 0) ? (1 + ((len - 1) | (sizeof(long) - 1))) : sizeof(long));
230 }
231 
232 /*
233  * scamper_rtsock_getifindex
234  *
235  * figure out the outgoing interface id / route using route sockets
236  *
237  * route(4) gives an overview of the functions called in here
238  */
scamper_rtsock_getifindex(int fd,scamper_addr_t * dst)239 static int scamper_rtsock_getifindex(int fd, scamper_addr_t *dst)
240 {
241   struct sockaddr_storage sas;
242   struct sockaddr_dl *sdl;
243   struct rt_msghdr *rtm;
244   uint8_t buf[1024];
245   size_t len;
246   ssize_t ss;
247   int slen;
248 
249   if(SCAMPER_ADDR_TYPE_IS_IPV4(dst))
250     sockaddr_compose((struct sockaddr *)&sas, AF_INET, dst->addr, 0);
251   else if(SCAMPER_ADDR_TYPE_IS_IPV6(dst))
252     sockaddr_compose((struct sockaddr *)&sas, AF_INET6, dst->addr, 0);
253   else
254     return -1;
255 
256   if((slen = sockaddr_len((struct sockaddr *)&sas)) <= 0)
257     return -1;
258 
259   len = sizeof(struct rt_msghdr) + scamper_rtsock_roundup(slen) +
260     scamper_rtsock_roundup(sizeof(struct sockaddr_dl));
261   if(len > sizeof(buf))
262     return -1;
263 
264   memset(buf, 0, len);
265   rtm = (struct rt_msghdr *)buf;
266   rtm->rtm_msglen  = len;
267   rtm->rtm_version = RTM_VERSION;
268   rtm->rtm_type    = RTM_GET;
269   rtm->rtm_addrs   = RTA_DST | RTA_IFP;
270   rtm->rtm_pid     = pid;
271   rtm->rtm_seq     = seq;
272   memcpy(buf + sizeof(struct rt_msghdr), &sas, (size_t)slen);
273 
274   sdl = (struct sockaddr_dl *)(buf + sizeof(struct rt_msghdr) +
275 			       scamper_rtsock_roundup(slen));
276   sdl->sdl_family = AF_LINK;
277 
278 #if !defined(__sun__)
279   sdl->sdl_len    = sizeof(struct sockaddr_dl);
280 #endif
281 
282   if((ss = write(fd, buf, len)) < 0 || (size_t)ss != len)
283     {
284       printerror(__func__, "could not write routing socket");
285       return -1;
286     }
287 
288   return 0;
289 }
290 #endif /* HAVE_BSD_ROUTE_SOCKET */
291 
292 #if defined(__linux__)
293 /*
294  * scamper_rtsock_getifindex
295  *
296  * figure out the outgoing interface id / route using linux netlink
297  *
298  * this works on Linux systems with netlink compiled into the kernel.
299  * i think netlink comes compiled into the kernel with most distributions
300  * these days.
301  *
302  * the man pages netlink(3), netlink(7), rtnetlink(3), and rtnetlink(7)
303  * give an overview of the functions and structures used in here, but the
304  * documentation in those man pages is pretty crap.
305  * you'd be better off studying netlink.h and rtnetlink.h
306  */
scamper_rtsock_getifindex(int fd,scamper_addr_t * dst)307 static int scamper_rtsock_getifindex(int fd, scamper_addr_t *dst)
308 {
309   struct nlmsghdr *nlmsg;
310   struct rtmsg    *rtmsg;
311   struct rtattr   *rta;
312   int              error;
313   int              dst_len;
314   uint8_t          buf[1024];
315   int              af;
316 
317   if(SCAMPER_ADDR_TYPE_IS_IPV4(dst))
318     {
319       dst_len  = 4;
320       af       = AF_INET;
321     }
322   else if(SCAMPER_ADDR_TYPE_IS_IPV6(dst))
323     {
324       dst_len  = 16;
325       af       = AF_INET6;
326     }
327   else
328     {
329       return -1;
330     }
331 
332   /*
333    * fill out a route request.
334    * we use the standard netlink header, with a route msg subheader
335    * to query for the outgoing interface.
336    * the message includes one attribute - the destination address
337    * we are querying the route for.
338    */
339   memset(buf, 0, sizeof(buf));
340   nlmsg  = (struct nlmsghdr *)buf;
341   nlmsg->nlmsg_len   = NLMSG_LENGTH(sizeof(struct rtmsg));
342   nlmsg->nlmsg_type  = RTM_GETROUTE;
343   nlmsg->nlmsg_flags = NLM_F_REQUEST;
344   nlmsg->nlmsg_seq   = seq;
345   nlmsg->nlmsg_pid   = pid;
346 
347   /* netlink wants the bit length of each address */
348   rtmsg = NLMSG_DATA(nlmsg);
349   rtmsg->rtm_family  = af;
350   rtmsg->rtm_flags   = 0;
351   rtmsg->rtm_dst_len = dst_len * 8;
352 
353   rta = (struct rtattr *)(buf + NLMSG_ALIGN(nlmsg->nlmsg_len));
354   rta->rta_type = RTA_DST;
355   rta->rta_len  = RTA_LENGTH(dst_len);
356   nlmsg->nlmsg_len += RTA_LENGTH(dst_len);
357   memcpy(RTA_DATA(rta), dst->addr, dst_len);
358 
359   /* send the request */
360   if((error = send(fd, buf, nlmsg->nlmsg_len, 0)) != nlmsg->nlmsg_len)
361     {
362       printerror(__func__, "could not send");
363       return -1;
364     }
365 
366   return 0;
367 }
368 #endif
369 
scamper_rtsock_getroute(scamper_fd_t * fdn,scamper_route_t * route)370 int scamper_rtsock_getroute(scamper_fd_t *fdn, scamper_route_t *route)
371 {
372   int fd;
373 
374   /* get the route socket fd */
375   if((fd = scamper_fd_fd_get(fdn)) == -1)
376     return -1;
377 
378   /* ask the question */
379   if(scamper_rtsock_getifindex(fd, route->dst) != 0)
380     return -1;
381 
382   /* keep track of the question */
383   if(rtsock_pair_alloc(route, seq++) == NULL)
384     return -1;
385   return 0;
386 }
387 
388 #if defined(__linux__)
389 #if 0
390 static void rtattr_dump(struct rtattr *rta)
391 {
392   char *rta_type;
393   char  rta_data[64];
394   int   i;
395 
396   switch(rta->rta_type)
397     {
398     case RTA_UNSPEC:    rta_type = "unspec";    break;
399     case RTA_DST:       rta_type = "dst";       break;
400     case RTA_SRC:       rta_type = "src";       break;
401     case RTA_IIF:       rta_type = "iif";       break;
402     case RTA_OIF:       rta_type = "oif";       break;
403     case RTA_GATEWAY:   rta_type = "gateway";   break;
404     case RTA_PRIORITY:  rta_type = "priority";  break;
405     case RTA_PREFSRC:   rta_type = "prefsrc";   break;
406     case RTA_METRICS:   rta_type = "metrics";   break;
407     case RTA_MULTIPATH: rta_type = "multipath"; break;
408     case RTA_PROTOINFO: rta_type = "protoinfo"; break;
409     case RTA_FLOW:      rta_type = "flow";      break;
410     case RTA_CACHEINFO: rta_type = "cacheinfo"; break;
411     case RTA_SESSION:   rta_type = "session";   break;
412     default:            rta_type = "<unknown>"; break;
413     }
414 
415   for(i=0;i<rta->rta_len-sizeof(struct rtattr)&&i<(sizeof(rta_data)/2)-1;i++)
416     {
417       snprintf(&rta_data[i*2], 3, "%02x",
418 	       *(uint8_t *)(((char *)rta) + sizeof(struct rtattr) + i));
419     }
420 
421   if(i != 0)
422     {
423       scamper_debug(__func__, "type %s len %d data %s",
424 		    rta_type, rta->rta_len-sizeof(struct rtattr), rta_data);
425     }
426   else
427     {
428       scamper_debug(__func__, "type %s\n", rta_type);
429     }
430 
431   return;
432 }
433 #endif
434 
rtsock_parsemsg(uint8_t * buf,size_t len)435 static void rtsock_parsemsg(uint8_t *buf, size_t len)
436 {
437   struct nlmsghdr *nlmsg;
438   struct nlmsgerr *nlerr;
439   struct rtmsg    *rtmsg;
440   struct rtattr   *rta;
441   void            *gwa = NULL;
442   int              ifindex = -1;
443   scamper_addr_t  *gw = NULL;
444   rtsock_pair_t   *pair = NULL;
445   scamper_route_t *route = NULL;
446 
447   if(len < sizeof(struct nlmsghdr))
448     {
449       scamper_debug(__func__, "len %d != %d", len, sizeof(struct nlmsghdr));
450       return;
451     }
452 
453   nlmsg = (struct nlmsghdr *)buf;
454 
455   /* if the message isn't addressed to this pid, drop it */
456   if(nlmsg->nlmsg_pid != pid)
457     return;
458 
459   if((pair = rtsock_pair_get(nlmsg->nlmsg_seq)) == NULL)
460     return;
461   route = pair->route;
462   rtsock_pair_free(pair);
463 
464   if(nlmsg->nlmsg_type == RTM_NEWROUTE)
465     {
466       rtmsg = NLMSG_DATA(nlmsg);
467 
468       /* this is the payload length of the response packet */
469       len = nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));
470 
471       /* hunt through the payload for the RTA_OIF entry */
472       rta = RTM_RTA(rtmsg);
473       while(RTA_OK(rta, len))
474 	{
475 	  switch(rta->rta_type)
476 	    {
477 	    case RTA_OIF:
478 	      ifindex = *(unsigned *)RTA_DATA(rta);
479 	      break;
480 
481 	    case RTA_GATEWAY:
482 	      gwa = RTA_DATA(rta);
483 	      break;
484 	    }
485 	  rta = RTA_NEXT(rta, len);
486 	}
487 
488       if(gwa != NULL)
489 	{
490 	  if(rtmsg->rtm_family == AF_INET)
491 	    gw = scamper_addrcache_get_ipv4(addrcache, gwa);
492 	  else if(rtmsg->rtm_family == AF_INET6)
493 	    gw = scamper_addrcache_get_ipv6(addrcache, gwa);
494 	  else
495 	    route->error = EINVAL;
496 	}
497     }
498   else if(nlmsg->nlmsg_type == NLMSG_ERROR)
499     {
500       nlerr = NLMSG_DATA(nlmsg);
501       route->error = nlerr->error;
502     }
503   else goto skip;
504 
505   route->gw = gw;
506   route->ifindex = ifindex;
507   route->cb(route);
508 
509   return;
510 
511  skip:
512   if(route != NULL) scamper_route_free(route);
513   return;
514 }
515 #endif
516 
517 #if defined(HAVE_BSD_ROUTE_SOCKET)
rtsock_parsemsg(uint8_t * buf,size_t len)518 static void rtsock_parsemsg(uint8_t *buf, size_t len)
519 {
520   struct rt_msghdr   *rtm;
521   struct sockaddr    *addrs[RTAX_MAX];
522   struct sockaddr_dl *sdl;
523   struct sockaddr    *sa;
524   struct in6_addr    *ip6;
525   size_t              off, x;
526   int                 i, tmp, ifindex;
527   void               *addr;
528   scamper_addr_t     *gw;
529   rtsock_pair_t      *pair;
530   scamper_route_t    *route;
531 
532   x = 0;
533   while(x < len)
534     {
535       if(len - x < sizeof(struct rt_msghdr))
536 	{
537 	  scamper_debug(__func__,"len %d != %d",len,sizeof(struct rt_msghdr));
538 	  return;
539 	}
540 
541       /*
542        * check if the message is something we want, and that we have
543        * a pair for it
544        */
545       rtm = (struct rt_msghdr *)(buf + x);
546       if(rtm->rtm_pid != pid ||
547 	 rtm->rtm_msglen > len - x ||
548 	 rtm->rtm_type != RTM_GET ||
549 	 (rtm->rtm_flags & RTF_DONE) == 0 ||
550 	 (pair = rtsock_pair_get(rtm->rtm_seq)) == NULL)
551 	{
552 	  x += rtm->rtm_msglen;
553 	  continue;
554 	}
555 
556       route = pair->route;
557       rtsock_pair_free(pair);
558 
559       ifindex = -1;
560       addr = NULL;
561       gw = NULL;
562 
563       if(rtm->rtm_errno != 0)
564 	{
565 	  route->error = rtm->rtm_errno;
566 	  goto done;
567 	}
568 
569       off = sizeof(struct rt_msghdr);
570       memset(addrs, 0, sizeof(addrs));
571       for(i=0; i<RTAX_MAX; i++)
572 	{
573 	  if(rtm->rtm_addrs & (1 << i))
574 	    {
575 	      addrs[i] = sa = (struct sockaddr *)(buf + x + off);
576 	      if((tmp = sockaddr_len(sa)) <= 0)
577 		{
578 		  printerror_msg(__func__, "unhandled af %d", sa->sa_family);
579 		  route->error = EINVAL;
580 		  goto done;
581 		}
582 	      off += scamper_rtsock_roundup(tmp);
583 	    }
584 	}
585 
586       if((sdl = (struct sockaddr_dl *)addrs[RTAX_IFP]) != NULL)
587 	{
588 	  if(sdl->sdl_family != AF_LINK)
589 	    {
590 	      printerror_msg(__func__, "sdl_family %d", sdl->sdl_family);
591 	      route->error = EINVAL;
592 	      goto done;
593 	    }
594 	  ifindex = sdl->sdl_index;
595 	}
596 
597       if((sa = addrs[RTAX_GATEWAY]) != NULL)
598 	{
599 	  if(sa->sa_family == AF_INET)
600 	    {
601 	      i = SCAMPER_ADDR_TYPE_IPV4;
602 	      addr = &((struct sockaddr_in *)sa)->sin_addr;
603 	    }
604 	  else if(sa->sa_family == AF_INET6)
605 	    {
606 	      /*
607 	       * check to see if the gw address is a link local address.  if
608 	       * it is, then drop the embedded index from the gateway address
609 	       */
610 	      ip6 = &((struct sockaddr_in6 *)sa)->sin6_addr;
611 	      if(IN6_IS_ADDR_LINKLOCAL(ip6))
612 		{
613 		  ip6->s6_addr[2] = 0;
614 		  ip6->s6_addr[3] = 0;
615 		}
616 	      i = SCAMPER_ADDR_TYPE_IPV6;
617 	      addr = ip6;
618 	    }
619 	  else if(sa->sa_family == AF_LINK)
620 	    {
621 	      sdl = (struct sockaddr_dl *)sa;
622 	      if(sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == ETHER_ADDR_LEN)
623 		{
624 		  i = SCAMPER_ADDR_TYPE_ETHERNET;
625 		  addr = sdl->sdl_data + sdl->sdl_nlen;
626 		}
627 	    }
628 
629 	  /*
630 	   * if we have got a gateway address that we know what to do with,
631 	   * then store it here.
632 	   */
633 	  if(addr != NULL &&
634 	     (gw = scamper_addrcache_get(addrcache, i, addr)) == NULL)
635 	    {
636 	      scamper_debug(__func__, "could not get rtsmsg->rr.gw");
637 	      route->error = EINVAL;
638 	      goto done;
639 	    }
640 	}
641 
642     done:
643       route->gw      = gw;
644       route->ifindex = ifindex;
645       route->cb(route);
646       x += rtm->rtm_msglen;
647     }
648 
649   return;
650 }
651 #endif
652 
653 /*
654  * scamper_rtsock_read_cb
655  *
656  * this callback handles reading a message from the route socket.
657  * we check to see if the message is something that we have sent by parsing
658  * the message out.  if we did send the message, then we search for the
659  * address-sequence pair, which matches the sequence number with a route
660  * lookup.
661  * if we get a pair back, then we remove it from the list and look for a
662  * trace matching the address.  we then take the result from the route
663  * lookup and apply it to the trace.
664  */
scamper_rtsock_read_cb(const int fd,void * param)665 void scamper_rtsock_read_cb(const int fd, void *param)
666 {
667   uint8_t buf[2048];
668   ssize_t len;
669 
670   if((len = recv(fd, buf, sizeof(buf), 0)) < 0)
671     {
672       printerror(__func__, "recv failed");
673       return;
674     }
675 
676   if(len > 0)
677     rtsock_parsemsg(buf, len);
678 
679   return;
680 }
681 
scamper_rtsock_close(int fd)682 void scamper_rtsock_close(int fd)
683 {
684   close(fd);
685   return;
686 }
687 
scamper_rtsock_open_fd()688 int scamper_rtsock_open_fd()
689 {
690 #if defined(HAVE_BSD_ROUTE_SOCKET)
691   return socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
692 #elif defined(__linux__)
693   return socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
694 #else
695 #error "route socket support for this system not implemented"
696 #endif
697 }
698 
scamper_rtsock_open()699 int scamper_rtsock_open()
700 {
701   int fd;
702 
703 #if defined(WITHOUT_PRIVSEP)
704   if((fd = scamper_rtsock_open_fd()) == -1)
705 #else
706   if((fd = scamper_privsep_open_rtsock()) == -1)
707 #endif
708     {
709       printerror(__func__, "could not open route socket");
710       return -1;
711     }
712 
713   return fd;
714 }
715 #endif
716 
717 #ifdef _WIN32
scamper_rtsock_getroute4(scamper_route_t * route)718 static int scamper_rtsock_getroute4(scamper_route_t *route)
719 {
720   struct in_addr *in = route->dst->addr;
721   MIB_IPFORWARDROW fw;
722   DWORD dw;
723 
724   if((dw = GetBestRoute(in->s_addr, 0, &fw)) != NO_ERROR)
725     {
726       route->error = dw;
727       return -1;
728     }
729 
730   route->ifindex = fw.dwForwardIfIndex;
731 
732   /* determine the gateway address to use, if one is specified */
733   if((dw = fw.dwForwardNextHop) != 0)
734     {
735       if((route->gw = scamper_addrcache_get_ipv4(addrcache, &dw)) == NULL)
736 	{
737 	  route->error = errno;
738 	  return -1;
739 	}
740     }
741 
742   return 0;
743 }
744 
scamper_rtsock_getroute(scamper_route_t * route)745 int scamper_rtsock_getroute(scamper_route_t *route)
746 {
747   if(SCAMPER_ADDR_TYPE_IS_IPV4(route->dst) &&
748      scamper_rtsock_getroute4(route) == 0)
749     {
750       route->cb(route);
751       return 0;
752     }
753 
754   return -1;
755 }
756 #endif
757 
scamper_route_free(scamper_route_t * route)758 void scamper_route_free(scamper_route_t *route)
759 {
760   if(route == NULL)
761     return;
762 #ifndef _WIN32
763   if(route->internal != NULL)
764     rtsock_pair_free(route->internal);
765 #endif
766   if(route->dst != NULL)
767     scamper_addr_free(route->dst);
768   if(route->gw != NULL)
769     scamper_addr_free(route->gw);
770   free(route);
771   return;
772 }
773 
scamper_route_alloc(scamper_addr_t * dst,void * param,void (* cb)(scamper_route_t * rt))774 scamper_route_t *scamper_route_alloc(scamper_addr_t *dst, void *param,
775 				     void (*cb)(scamper_route_t *rt))
776 {
777   scamper_route_t *route;
778   if((route = malloc_zero(sizeof(scamper_route_t))) == NULL)
779     return NULL;
780   route->dst = scamper_addr_use(dst);
781   route->param = param;
782   route->cb = cb;
783   return route;
784 }
785 
scamper_rtsock_init()786 int scamper_rtsock_init()
787 {
788 #ifndef _WIN32
789   if((pairs = dlist_alloc()) == NULL)
790     {
791       printerror(__func__, "could not allocate pair list");
792       return -1;
793     }
794   pid = getpid();
795 #endif
796 
797   return 0;
798 }
799 
scamper_rtsock_cleanup()800 void scamper_rtsock_cleanup()
801 {
802 #ifndef _WIN32
803   rtsock_pair_t *pair;
804 
805   if(pairs != NULL)
806     {
807       while((pair = dlist_head_pop(pairs)) != NULL)
808 	{
809 	  pair->node = NULL;
810 	  rtsock_pair_free(pair);
811 	}
812 
813       dlist_free(pairs);
814       pairs = NULL;
815     }
816 #endif
817 
818   return;
819 }
820