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