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