Add mono/metadata/mono-route.c for non-Linux ParseRouteInfo_local
[mono.git] / mono / metadata / mono-route.c
1 /*
2  * mono-route.c: Read the network routing tables using sysctl(3) calls
3  *               Required for Unix-like systems that don't have Linux's /proc/net/route
4  *
5  * Author:
6  *   Ben Woods (woodsb02@gmail.com)
7  */
8
9 #if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD)
10
11 #include <sys/socket.h>
12 #include <net/if.h>
13 #include <net/if_dl.h>
14 #include <net/route.h>
15 #include <netinet/in.h>
16 #include <sys/param.h>
17 #include <sys/sysctl.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <mono/metadata/object.h>
21 #include <mono/metadata/mono-route.h>
22
23 extern MonoBoolean ves_icall_System_Net_NetworkInformation_MacOsIPInterfaceProperties_ParseRouteInfo_internal(MonoString *iface, MonoArray **gw_addr_list)
24 {
25         size_t needed;
26         in_addr_t in;
27         int mib[6];
28         int num_gws=0, gwnum=0;
29         unsigned int ifindex = 0;
30         char *buf, *next, *lim, *ifacename;
31         struct rt_msghdr *rtm;
32
33         MonoDomain *domain = mono_domain_get ();
34
35         ifacename = mono_string_to_utf8(iface);
36         if ((ifindex = if_nametoindex(ifacename)) == 0)
37                 return FALSE;
38         g_free(ifacename);
39
40         // MIB array defining data to read from sysctl
41         mib[0] = CTL_NET;       // Networking
42         mib[1] = PF_ROUTE;      // Routing messages
43         mib[2] = 0;             // Protocol number (always zero)
44         mib[3] = AF_INET;       // Address family (IPv4)
45         mib[4] = NET_RT_DUMP;   // Dump routing table
46         mib[5] = 0;             //
47
48         // First sysctl call with oldp set to NULL to determine size of available data
49         if (sysctl(mib, G_N_ELEMENTS(mib), NULL, &needed, NULL, 0) < 0)
50                 return FALSE;
51
52         // Allocate suffcient memory for available data based on the previous sysctl call
53         if ((buf = malloc(needed)) == NULL)
54                 return FALSE;
55
56         // Second sysctl call to retrieve data into appropriately sized buffer
57         if (sysctl(mib, G_N_ELEMENTS(mib), buf, &needed, NULL, 0) < 0)
58                 return FALSE;
59
60         lim = buf + needed;
61         for (next = buf; next < lim; next += rtm->rtm_msglen) {
62                 rtm = (struct rt_msghdr *)next;
63                 if (rtm->rtm_version != RTM_VERSION)
64                         continue;
65                 if (rtm->rtm_index != ifindex)
66                         continue;
67                 if((in = gateway_from_rtm(rtm)) == 0)
68                         continue;
69                 num_gws++;
70         }
71
72         *gw_addr_list = mono_array_new(domain, mono_get_string_class (), num_gws);
73
74         for (next = buf; next < lim; next += rtm->rtm_msglen) {
75                 rtm = (struct rt_msghdr *)next;
76                 if (rtm->rtm_version != RTM_VERSION)
77                         continue;
78                 if (rtm->rtm_index != ifindex)
79                         continue;
80                 if ((in = gateway_from_rtm(rtm)) == 0)
81                         continue;
82
83                 MonoString *addr_string;
84                 char addr [16], *ptr;
85                 int len;
86
87                 ptr = (char *) &in;
88                 len = snprintf(addr, sizeof(addr), "%u.%u.%u.%u",
89                         (unsigned char) ptr [0],
90                         (unsigned char) ptr [1],
91                         (unsigned char) ptr [2],
92                         (unsigned char) ptr [3]);
93
94                 if ((len >= sizeof(addr)) || (len < 0))
95                         // snprintf output truncated
96                         continue;
97
98                 addr_string = mono_string_new (domain, addr);
99                 mono_array_setref (*gw_addr_list, gwnum, addr_string);
100                 gwnum++;
101         }
102         free(buf);
103         return TRUE;
104 }
105
106 in_addr_t gateway_from_rtm(struct rt_msghdr *rtm)
107 {
108         struct sockaddr *gw;
109         unsigned int l;
110
111         struct sockaddr *addr = (struct sockaddr *)(rtm + 1);
112         l = roundup(addr->sa_len, sizeof(long)); \
113         gw = (struct sockaddr *)((char *) addr + l); \
114
115         if (rtm->rtm_addrs & RTA_GATEWAY) {
116                 if(gw->sa_family == AF_INET) {
117                         struct sockaddr_in *sockin = (struct sockaddr_in *)gw;
118                         return(sockin->sin_addr.s_addr);
119                 }
120         }
121
122         return 0;
123 }
124
125 #endif /* #if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD) */