Line data Source code
1 : #define _GNU_SOURCE
2 : #include "utility.h"
3 : #include "../../util/fd_util.h"
4 :
5 : #include <errno.h>
6 : #include <unistd.h>
7 : #include <stdio.h>
8 : #include <stdarg.h>
9 : #include <string.h> /* for explicit_bzero */
10 : #include <time.h>
11 : #include <fcntl.h>
12 : #include <sched.h>
13 : #include <dirent.h>
14 : #include <net/if.h> /* IFF_UP */
15 :
16 : #include <sys/ioctl.h> /* ioctl(2) */
17 : #include <sys/mman.h> /* for mprotect */
18 : #include <sys/stat.h>
19 : #include <sys/socket.h>
20 : #include <sys/mount.h>
21 : #include <sys/syscall.h>
22 : #include <linux/netlink.h>
23 : #include <linux/rtnetlink.h>
24 : #include <linux/unistd.h>
25 :
26 : static int namespace_original_fd = 0;
27 :
28 : void
29 0 : enter_network_namespace( const char * interface ) {
30 0 : char path[ PATH_MAX ];
31 0 : FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "/var/run/netns/%s", interface ) );
32 :
33 0 : if( FD_LIKELY( !namespace_original_fd ) ) {
34 0 : namespace_original_fd = open( "/proc/self/ns/net", O_RDONLY | O_CLOEXEC );
35 0 : if( FD_UNLIKELY( namespace_original_fd < 0 ) )
36 0 : FD_LOG_ERR(( "failed to open /proc/self/ns/net (%i-%s)", errno, fd_io_strerror( errno ) ));
37 0 : }
38 :
39 0 : int fd = open( path, O_RDONLY | O_CLOEXEC );
40 0 : if( FD_UNLIKELY( fd < 0 ) ) FD_LOG_ERR(( "failed to open `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
41 0 : if( FD_UNLIKELY( setns( fd, CLONE_NEWNET ) ) )
42 0 : FD_LOG_ERR(( "failed to enter network namespace `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
43 0 : int ret = close( fd );
44 0 : if( FD_UNLIKELY( ret ) ) FD_LOG_ERR(( "enter_network_namespace %d (%i-%s)", ret, errno, fd_io_strerror( errno ) ));
45 :
46 : /* `ip link set dev lo up`
47 : Done via the ioctl API for simplicity. Requires a dummy socket. */
48 0 : int ifreq_fd = socket( AF_INET, SOCK_DGRAM, 0 );
49 0 : if( FD_UNLIKELY( ifreq_fd<0 ) ) FD_LOG_ERR(( "socket(AF_INET,SOCK_DGRAM,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
50 0 : struct ifreq ifr = { .ifr_name = "lo" };
51 0 : if( FD_UNLIKELY( ioctl( ifreq_fd, SIOCGIFFLAGS, &ifr ) ) ) FD_LOG_ERR(( "ioctl(SIOCGIFFLAGS,\"lo\") failed (%i-%s)", errno, fd_io_strerror( errno ) ));
52 0 : ifr.ifr_flags |= (IFF_UP|IFF_RUNNING);
53 0 : if( FD_UNLIKELY( ioctl( ifreq_fd, SIOCSIFFLAGS, &ifr ) ) ) FD_LOG_ERR(( "ioctl(SIOCGIFFLAGS,\"lo\",IFF_UP|IFF_RUNNING) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
54 0 : if( FD_UNLIKELY( close( ifreq_fd ) ) ) FD_LOG_ERR(( "failed to close dummy UDP socket (%i-%s)", errno, fd_io_strerror( errno ) ));
55 0 : }
56 :
57 : void
58 0 : close_network_namespace_original_fd( void ) {
59 0 : int ret = close( namespace_original_fd );
60 0 : if( FD_UNLIKELY( ret ) ) FD_LOG_ERR(( "leave_network_namespace %d (%i-%s)", ret, errno, fd_io_strerror( errno ) ));
61 0 : }
62 :
63 : void
64 0 : leave_network_namespace( void ) {
65 0 : if( FD_UNLIKELY( !namespace_original_fd ) ) return;
66 :
67 0 : if( FD_UNLIKELY( setns( namespace_original_fd, CLONE_NEWNET ) ) )
68 0 : FD_LOG_ERR(( "failed to enter original network namespace `%d` (%i-%s)",
69 0 : namespace_original_fd, errno, fd_io_strerror( errno ) ));
70 :
71 0 : int ret = close( namespace_original_fd );
72 0 : if( FD_UNLIKELY( ret ) ) FD_LOG_ERR(( "leave_network_namespace %d (%i-%s)", ret, errno, fd_io_strerror( errno ) ));
73 0 : namespace_original_fd = 0;
74 0 : }
75 :
76 : void
77 0 : exit_group( int status ) {
78 0 : syscall( SYS_exit_group, status );
79 0 : }
80 :
81 : void
82 : mkdir_all( const char * _path,
83 : uid_t uid,
84 0 : gid_t gid ) {
85 0 : char path[ PATH_MAX + 1 ] = {0};
86 0 : strncpy( path, _path, PATH_MAX );
87 :
88 0 : char * p = path;
89 0 : if( FD_LIKELY( *p == '/' ) ) p++;
90 0 : while( FD_LIKELY( *p ) ) {
91 0 : if( FD_UNLIKELY( *p == '/' ) ) {
92 0 : *p = '\0';
93 0 : int error = mkdir( path, 0777 );
94 0 : if( FD_UNLIKELY( error && errno != EEXIST ) )
95 0 : FD_LOG_ERR(( "mkdir( `%s` ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ) );
96 0 : if( FD_LIKELY( !error ) ) {
97 : /* only take ownership if we succeeded in creating (did not exist) */
98 0 : if( FD_UNLIKELY( chown( path, uid, gid ) ) )
99 0 : FD_LOG_ERR(( "chown `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
100 0 : if( FD_UNLIKELY( chmod( path, S_IRUSR | S_IWUSR | S_IXUSR ) ) )
101 0 : FD_LOG_ERR(( "chmod `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
102 0 : }
103 :
104 0 : *p = '/';
105 0 : }
106 0 : p++;
107 0 : }
108 :
109 0 : int error = mkdir( path, 0777 );
110 0 : if( FD_UNLIKELY( error && errno != EEXIST ) )
111 0 : FD_LOG_ERR(( "mkdir( `%s` ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ) );
112 0 : if( FD_LIKELY( !error ) ) {
113 : /* only take ownership if we succeeded in creating (did not exist) */
114 0 : if( FD_UNLIKELY( chown( path, uid, gid ) ) )
115 0 : FD_LOG_ERR(( "chown `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
116 0 : if( FD_UNLIKELY( chmod( path, S_IRUSR | S_IWUSR | S_IXUSR ) ) )
117 0 : FD_LOG_ERR(( "chmod `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
118 0 : }
119 0 : }
120 :
121 : void
122 : rmtree( char const * path,
123 0 : int remove_root ) {
124 0 : DIR * dir = opendir( path );
125 0 : if( FD_UNLIKELY( !dir ) ) {
126 0 : if( errno == ENOENT ) return;
127 0 : FD_LOG_ERR(( "opendir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
128 0 : }
129 :
130 0 : struct dirent * entry;
131 0 : errno = 0;
132 0 : while(( entry = readdir( dir ) )) {
133 0 : if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
134 :
135 0 : char path1[ PATH_MAX ];
136 0 : FD_TEST( fd_cstr_printf_check( path1, PATH_MAX, NULL, "%s/%s", path, entry->d_name ) );
137 :
138 0 : struct stat st;
139 0 : if( FD_UNLIKELY( lstat( path1, &st ) ) ) {
140 0 : if( FD_LIKELY( errno == ENOENT ) ) continue;
141 0 : FD_LOG_ERR(( "stat `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) ));
142 0 : }
143 :
144 0 : if( FD_UNLIKELY( S_ISDIR( st.st_mode ) ) ) {
145 0 : rmtree( path1, 1 );
146 0 : } else {
147 0 : if( FD_UNLIKELY( unlink( path1 ) && errno != ENOENT ) )
148 0 : FD_LOG_ERR(( "unlink `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) ));
149 0 : }
150 0 : }
151 :
152 0 : if( FD_UNLIKELY( errno && errno != ENOENT ) ) FD_LOG_ERR(( "readdir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
153 :
154 0 : if( FD_LIKELY( remove_root ) ) {
155 0 : if( FD_UNLIKELY( rmdir( path ) ) ) FD_LOG_ERR(( "rmdir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
156 0 : }
157 0 : if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
158 0 : }
159 :
160 : uint
161 : read_uint_file( char const * path,
162 0 : char const * errmsg_enoent ) {
163 0 : FILE * fp = fopen( path, "r" );
164 0 : if( FD_UNLIKELY( !fp ) ) {
165 0 : if( FD_LIKELY( errno==ENOENT ) ) FD_LOG_ERR(( "%s fopen failed `%s` (%i-%s)", errmsg_enoent, path, errno, fd_io_strerror( errno ) ));
166 0 : else FD_LOG_ERR(( "fopen failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
167 0 : }
168 :
169 0 : uint value = 0U;
170 0 : if( FD_UNLIKELY( 1!=fscanf( fp, "%u\n", &value ) ) ) FD_LOG_ERR(( "failed to read uint from `%s`", path ));
171 0 : if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "fclose failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
172 0 : return value;
173 0 : }
174 :
175 : void
176 : write_uint_file( char const * path,
177 0 : uint value ) {
178 0 : FILE * fp = fopen( path, "w" );
179 0 : if( FD_UNLIKELY( !fp ) ) FD_LOG_ERR(( "fopen failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
180 0 : if( FD_UNLIKELY( fprintf( fp, "%u\n", value ) <= 0 ) ) FD_LOG_ERR(( "fprintf failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
181 0 : if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "fclose failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
182 0 : }
183 :
184 : int
185 3 : internet_routing_interface( void ) {
186 3 : int sock = socket( AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
187 3 : if( FD_UNLIKELY( sock < 0 ) )
188 0 : FD_LOG_ERR(( "error finding default interface, socket(AF_INET,SOCK_DGRAM,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
189 :
190 3 : struct {
191 3 : struct nlmsghdr nlh;
192 3 : struct rtmsg rt;
193 3 : char buf[8192];
194 3 : } request;
195 :
196 3 : memset(&request, 0, sizeof(request));
197 3 : request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
198 3 : request.nlh.nlmsg_flags = NLM_F_REQUEST;
199 3 : request.nlh.nlmsg_type = RTM_GETROUTE;
200 3 : request.rt.rtm_family = AF_INET;
201 3 : request.rt.rtm_dst_len = 32;
202 :
203 3 : struct rtattr *rta = (struct rtattr *)( ( (char *)&request ) + NLMSG_ALIGN( request.nlh.nlmsg_len ) );
204 3 : rta->rta_len = RTA_LENGTH(4);
205 3 : rta->rta_type = RTA_DST;
206 3 : request.nlh.nlmsg_len = NLMSG_ALIGN( request.nlh.nlmsg_len ) + (uint)RTA_LENGTH( 4 );
207 :
208 3 : unsigned int ip = (8 << 24) | (8 << 16) | (8 << 8) | 8;
209 3 : fd_memcpy( RTA_DATA( rta ), &ip, 4 );
210 :
211 3 : if( FD_UNLIKELY( send( sock, &request, request.nlh.nlmsg_len, 0 ) < 0 ) )
212 0 : FD_LOG_ERR(( "error finding default interface, send() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
213 :
214 3 : char response[ 8192 ];
215 3 : long len = recv( sock, response, sizeof(response), 0 );
216 3 : if( FD_UNLIKELY( len == sizeof( response ) ) )
217 0 : FD_LOG_ERR(( "error finding default interface, response too large" ));
218 :
219 3 : struct nlmsghdr *nlh;
220 3 : int result = -1;
221 3 : for( nlh = (struct nlmsghdr *)response; NLMSG_OK( nlh, len ); nlh = NLMSG_NEXT( nlh, len ) ) {
222 3 : struct rtmsg *rt = NLMSG_DATA( nlh );
223 :
224 3 : struct rtattr *rta = RTM_RTA( rt );
225 3 : uint rtl = (uint)RTM_PAYLOAD( nlh );
226 :
227 21 : for (; RTA_OK( rta, rtl ); rta = RTA_NEXT( rta, rtl ) ) {
228 21 : if (rta->rta_type == RTA_OIF) {
229 3 : result = *(int *)RTA_DATA(rta);
230 3 : }
231 21 : }
232 3 : }
233 :
234 3 : if( FD_UNLIKELY( close( sock ) ) )
235 0 : FD_LOG_ERR(( "error finding default interface, close() socket failed (%i-%s)", errno, fd_io_strerror( errno ) ));
236 :
237 3 : return result;
238 3 : }
239 :
240 : /* FIXME: USE FD_LOG_SLEEP / FD_LOG_WAIT_UNTIL */
241 : void
242 0 : nanosleep1( uint secs, uint nanos ) {
243 0 : struct timespec ts = { .tv_sec = secs, .tv_nsec = nanos };
244 0 : struct timespec rem;
245 0 : while( FD_UNLIKELY( nanosleep( &ts, &rem ) ) ) {
246 0 : if( FD_LIKELY( errno == EINTR ) ) ts = rem;
247 0 : else FD_LOG_ERR(( "nanosleep failed (%i-%s)", errno, fd_io_strerror( errno ) ));
248 0 : }
249 0 : }
250 :
251 : void
252 0 : current_executable_path( char path[ static PATH_MAX ] ) {
253 0 : long count = readlink( "/proc/self/exe", path, PATH_MAX );
254 0 : if( FD_UNLIKELY( count < 0 ) ) FD_LOG_ERR(( "readlink(/proc/self/exe) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
255 0 : if( FD_UNLIKELY( count >= PATH_MAX ) ) FD_LOG_ERR(( "readlink(/proc/self/exe) returned truncated path" ));
256 0 : path[ count ] = '\0';
257 0 : }
|