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 #include <config.h>
11 
12 #if defined(HOST_DARWIN) || defined(HOST_BSD)
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 
ves_icall_System_Net_NetworkInformation_MacOsIPInterfaceProperties_ParseRouteInfo_internal(MonoString * iface,MonoArray ** gw_addr_list)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 	goto_if_nok (&error, leave);
81 
82 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
83 		rtm = (struct rt_msghdr *)next;
84 		if (rtm->rtm_version != RTM_VERSION)
85 			continue;
86 		if (rtm->rtm_index != ifindex)
87 			continue;
88 		if ((in = gateway_from_rtm(rtm)) == 0)
89 			continue;
90 
91 		MonoString *addr_string;
92 		char addr [16], *ptr;
93 		int len;
94 
95 		ptr = (char *) &in;
96 		len = snprintf(addr, sizeof(addr), "%u.%u.%u.%u",
97 			(unsigned char) ptr [0],
98 			(unsigned char) ptr [1],
99 			(unsigned char) ptr [2],
100 			(unsigned char) ptr [3]);
101 
102 		if ((len >= sizeof(addr)) || (len < 0))
103 			// snprintf output truncated
104 			continue;
105 
106 		addr_string = mono_string_new_checked (domain, addr, &error);
107 		goto_if_nok (&error, leave);
108 		mono_array_setref (*gw_addr_list, gwnum, addr_string);
109 		gwnum++;
110 	}
111 leave:
112 	g_free (buf);
113 	return is_ok (&error);
114 }
115 
gateway_from_rtm(struct rt_msghdr * rtm)116 in_addr_t gateway_from_rtm(struct rt_msghdr *rtm)
117 {
118 	struct sockaddr *gw;
119 	unsigned int l;
120 
121 	struct sockaddr *addr = (struct sockaddr *)(rtm + 1);
122 	l = roundup(addr->sa_len, sizeof(long)); \
123 	gw = (struct sockaddr *)((char *) addr + l); \
124 
125 	if (rtm->rtm_addrs & RTA_GATEWAY) {
126 		if(gw->sa_family == AF_INET) {
127 			struct sockaddr_in *sockin = (struct sockaddr_in *)gw;
128 			return(sockin->sin_addr.s_addr);
129 		}
130 	}
131 
132 	return 0;
133 }
134 
135 #endif /* #if defined(HOST_DARWIN) || defined(HOST_BSD) */
136