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