11#include "netif.h"
22#include "common/io/io.h"
33
4+ #include <arpa/inet.h>
5+ #include <linux/rtnetlink.h>
46#include <net/if.h>
5- #include <stdio.h>
67
78#define FF_STR_INDIR (x ) #x
89#define FF_STR (x ) FF_STR_INDIR(x)
910
10- static bool getDefaultRouteIPv4 (char iface [IF_NAMESIZE + 1 ], uint32_t * ifIndex )
11+ struct req_t {
12+ struct nlmsghdr nlh ;
13+ struct rtmsg rtm ;
14+ struct rtattr rta ;
15+ uint32_t table ;
16+ };
17+
18+ struct Route4Entry {
19+ uint32_t dest ;
20+ uint32_t gateway ;
21+ uint32_t src ;
22+ uint8_t prefix_length ;
23+ uint32_t metric ;
24+ uint32_t ifindex ;
25+ };
26+
27+ static bool getDefaultRouteIPv4 (char iface [IF_NAMESIZE + 1 ], uint32_t * ifIndex , uint32_t * preferredSourceAddr )
1128{
12- FILE * FF_AUTO_CLOSE_FILE netRoute = fopen ("/proc/net/route" , "r" );
13- if (!netRoute ) return false;
29+ int sock_fd = socket (AF_NETLINK , SOCK_RAW | SOCK_CLOEXEC , NETLINK_ROUTE );
30+ if (sock_fd < 0 )
31+ return false;
1432
15- // skip first line
16- FF_UNUSED (fscanf (netRoute , "%*[^\n]\n" ));
33+ // Bind socket
34+ struct sockaddr_nl addr ;
35+ memset (& addr , 0 , sizeof (addr ));
36+ addr .nl_family = AF_NETLINK ;
37+ addr .nl_pid = 0 ; // Let kernel assign PID
38+ addr .nl_groups = 0 ;
1739
18- unsigned long long destination ; //, gateway, flags, refCount, use, metric, mask, mtu, ...
19- while (fscanf (netRoute , "%" FF_STR (IF_NAMESIZE ) "s%llx%*[^\n]" , iface , & destination ) == 2 )
20- {
21- if (destination != 0 ) continue ;
22- * ifIndex = if_nametoindex (iface );
40+ if (bind (sock_fd , (struct sockaddr * )& addr , sizeof (addr )) < 0 ) {
41+ close (sock_fd );
42+ return false;
43+ }
44+
45+ struct req_t req ;
46+ memset (& req , 0 , sizeof (req ));
47+
48+ // Netlink message header
49+ req .nlh .nlmsg_len = sizeof (req );
50+ req .nlh .nlmsg_type = RTM_GETROUTE ;
51+ req .nlh .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP ;
52+ req .nlh .nlmsg_seq = 0 ;
53+ req .nlh .nlmsg_pid = 0 ;
54+
55+ // Route message
56+ req .rtm .rtm_family = AF_INET ;
57+ req .rtm .rtm_dst_len = 0 ;
58+ req .rtm .rtm_src_len = 0 ;
59+ req .rtm .rtm_tos = 0 ;
60+ req .rtm .rtm_table = RT_TABLE_UNSPEC ;
61+ req .rtm .rtm_protocol = RTPROT_UNSPEC ;
62+ req .rtm .rtm_scope = RT_SCOPE_UNIVERSE ;
63+ req .rtm .rtm_type = RTN_UNSPEC ;
64+ req .rtm .rtm_flags = 0 ;
65+
66+ // Route attribute for main table
67+ req .rta .rta_len = RTA_LENGTH (sizeof (uint32_t ));
68+ req .rta .rta_type = RTA_TABLE ;
69+ req .table = RT_TABLE_MAIN ;
70+
71+ struct sockaddr_nl dest_addr ;
72+ memset (& dest_addr , 0 , sizeof (dest_addr ));
73+ dest_addr .nl_family = AF_NETLINK ;
74+ dest_addr .nl_pid = 0 ;
75+ dest_addr .nl_groups = 0 ;
76+
77+ ssize_t sent = sendto (sock_fd , & req , sizeof (req ), 0 ,
78+ (struct sockaddr * )& dest_addr , sizeof (dest_addr ));
79+
80+ if (sent != sizeof (req )) {
81+ close (sock_fd );
82+ return false;
83+ }
84+
85+ struct sockaddr_nl src_addr ;
86+ socklen_t src_addr_len = sizeof (src_addr );
87+ struct iovec iov = {NULL , 0 };
88+ struct msghdr msg ;
89+
90+ memset (& msg , 0 , sizeof (msg ));
91+ msg .msg_name = & src_addr ;
92+ msg .msg_namelen = sizeof (src_addr );
93+ msg .msg_iov = & iov ;
94+ msg .msg_iovlen = 1 ;
95+
96+ ssize_t peek_size = recvmsg (sock_fd , & msg , MSG_PEEK | MSG_TRUNC );
97+ if (peek_size < 0 ) {
98+ close (sock_fd );
99+ return false;
100+ }
101+
102+ uint8_t * buffer = malloc ((size_t )peek_size );
103+
104+ ssize_t received = recvfrom (sock_fd , buffer , (size_t )peek_size , 0 ,
105+ (struct sockaddr * )& src_addr , & src_addr_len );
106+
107+ close (sock_fd );
108+
109+ struct Route4Entry best_gw ;
110+ memset (& best_gw , 0 , sizeof (best_gw ));
111+
112+ for (const struct nlmsghdr * nlh = (struct nlmsghdr * )buffer ;
113+ NLMSG_OK (nlh , received );
114+ nlh = NLMSG_NEXT (nlh , received )) {
115+
116+ if (nlh -> nlmsg_type == NLMSG_DONE )
117+ break ;
118+
119+ if (nlh -> nlmsg_type != RTM_NEWROUTE )
120+ continue ;
121+
122+ struct rtmsg * rtm = (struct rtmsg * )NLMSG_DATA (nlh );
123+ if (rtm -> rtm_family != AF_INET )
124+ continue ;
125+
126+ struct Route4Entry entry ;
127+ memset (& entry , 0 , sizeof (struct Route4Entry ));
128+ entry .prefix_length = rtm -> rtm_dst_len ;
129+
130+ // Parse route attributes
131+ uint64_t rtm_len = RTM_PAYLOAD (nlh );
132+ for (struct rtattr * rta = RTM_RTA (rtm );
133+ RTA_OK (rta , rtm_len );
134+ rta = RTA_NEXT (rta , rtm_len )) {
135+
136+ switch (rta -> rta_type ) {
137+ case RTA_DST :
138+ entry .dest = * (uint32_t * )RTA_DATA (rta );
139+ break ;
140+ case RTA_GATEWAY :
141+ entry .gateway = * (uint32_t * )RTA_DATA (rta );
142+ break ;
143+ case RTA_PREFSRC :
144+ entry .src = * (uint32_t * )RTA_DATA (rta );
145+ break ;
146+ case RTA_PRIORITY :
147+ entry .metric = * (uint32_t * )RTA_DATA (rta );
148+ break ;
149+ case RTA_OIF :
150+ entry .ifindex = * (uint32_t * )RTA_DATA (rta );
151+ break ;
152+ }
153+ }
154+
155+ if (entry .gateway == 0 || entry .dest != 0 || entry .prefix_length != 0 )
156+ continue ;
157+
158+ if (best_gw .gateway == 0 || entry .metric < best_gw .metric ) {
159+ memcpy (& best_gw , & entry , sizeof (struct Route4Entry ));
160+ }
161+ }
162+
163+ free (buffer );
164+
165+ if (best_gw .gateway != 0 ) {
166+ if (ifIndex ) {
167+ * ifIndex = best_gw .ifindex ;
168+ }
169+ if (iface ) {
170+ if (if_indextoname (best_gw .ifindex , iface ) == NULL ) {
171+ iface [0 ] = '\0' ;
172+ }
173+ }
174+ if (preferredSourceAddr ) {
175+ * preferredSourceAddr = best_gw .src ;
176+ }
23177 return true;
24178 }
179+
25180 iface [0 ] = '\0' ;
26181 return false;
27182}
@@ -43,9 +198,9 @@ static bool getDefaultRouteIPv6(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
43198 return false;
44199}
45200
46- bool ffNetifGetDefaultRouteImpl (char iface [IF_NAMESIZE + 1 ], uint32_t * ifIndex )
201+ bool ffNetifGetDefaultRouteImpl (char iface [IF_NAMESIZE + 1 ], uint32_t * ifIndex , uint32_t * preferredSourceAddr )
47202{
48- if (getDefaultRouteIPv4 (iface , ifIndex ))
203+ if (getDefaultRouteIPv4 (iface , ifIndex , preferredSourceAddr ))
49204 return true;
50205
51206 return getDefaultRouteIPv6 (iface , ifIndex );
0 commit comments