1 /* LWIP service - lwip.c - main program and dispatch code */ 2 3 #include "lwip.h" 4 #include "tcpisn.h" 5 #include "mcast.h" 6 #include "ethif.h" 7 #include "rtsock.h" 8 #include "route.h" 9 #include "bpfdev.h" 10 11 #include "lwip/init.h" 12 #include "lwip/sys.h" 13 #include "lwip/timeouts.h" 14 #include "arch/cc.h" 15 16 static int running, recheck_timer; 17 static minix_timer_t lwip_timer; 18 19 static void expire_lwip_timer(int); 20 21 /* 22 * Return the system uptime in milliseconds. Also remember that lwIP retrieved 23 * the system uptime during this call, so that we know to check for timer 24 * updates at the end of the current iteration of the message loop. 25 */ 26 uint32_t 27 sys_now(void) 28 { 29 30 recheck_timer = TRUE; 31 32 /* TODO: avoid 64-bit arithmetic if possible. */ 33 return (uint32_t)(((uint64_t)getticks() * 1000) / sys_hz()); 34 } 35 36 /* 37 * Check if and when lwIP has its next timeout, and set or cancel our timer 38 * accordingly. 39 */ 40 static void 41 set_lwip_timer(void) 42 { 43 uint32_t next_timeout; 44 clock_t ticks; 45 46 /* Ask lwIP when the next alarm is supposed to go off, if any. */ 47 next_timeout = sys_timeouts_sleeptime(); 48 49 /* 50 * Set or update the lwIP timer. We rely on set_timer() asking the 51 * kernel for an alarm only if the timeout is different from the one we 52 * gave it last time (if at all). However, due to conversions between 53 * absolute and relative times, and the fact that we cannot guarantee 54 * that the uptime itself does not change while executing these 55 * routines, set_timer() will sometimes be issuing a kernel call even 56 * if the alarm has not changed. Not a huge deal, but fixing this will 57 * require a different interface to lwIP and/or the timers library. 58 */ 59 if (next_timeout != (uint32_t)-1) { 60 /* 61 * Round up the next timeout (which is in milliseconds) to the 62 * number of clock ticks to add to the current time. Avoid any 63 * potential for overflows, no matter how unrealistic.. 64 */ 65 if (next_timeout > TMRDIFF_MAX / sys_hz()) 66 ticks = TMRDIFF_MAX; 67 else 68 ticks = (next_timeout * sys_hz() + 999) / 1000; 69 70 set_timer(&lwip_timer, ticks, expire_lwip_timer, 0 /*unused*/); 71 } else 72 cancel_timer(&lwip_timer); /* not really needed.. */ 73 } 74 75 /* 76 * The timer for lwIP timeouts has gone off. Check timeouts, and possibly set 77 * a new timer. 78 */ 79 static void 80 expire_lwip_timer(int arg __unused) 81 { 82 83 /* Let lwIP do its work. */ 84 sys_check_timeouts(); 85 86 /* 87 * See if we have to update our timer for the next lwIP timer. Doing 88 * this here, rather than from the main loop, avoids one kernel call. 89 */ 90 set_lwip_timer(); 91 92 recheck_timer = FALSE; 93 } 94 95 /* 96 * Check whether we should adjust our local timer based on a change in the next 97 * lwIP timeout. 98 */ 99 static void 100 check_lwip_timer(void) 101 { 102 103 /* 104 * We make the assumption that whenever lwIP starts a timer, it will 105 * need to retrieve the current time. Thus, whenever sys_now() is 106 * called, we set the 'recheck_timer' flag. Here, we check whether to 107 * (re)set our lwIP timer only if the flag is set. As a result, we do 108 * not have to mess with timers for literally every incoming message. 109 * 110 * When lwIP stops a timer, it does not call sys_now(), and thus, we 111 * may miss such updates. However, timers being stopped should be rare 112 * and getting too many alarm messages is not a big deal. 113 */ 114 if (!recheck_timer) 115 return; 116 117 set_lwip_timer(); 118 119 /* Reset the flag for the next message loop iteration. */ 120 recheck_timer = FALSE; 121 } 122 123 /* 124 * Return a random number, for use by lwIP. 125 */ 126 uint32_t 127 lwip_hook_rand(void) 128 { 129 130 /* 131 * The current known uses of this hook are for selection of initial 132 * TCP/UDP port numbers and for multicast-related timer randomness. 133 * The former case exists only to avoid picking the same starting port 134 * numbers after a reboot. After that, simple sequential iteration of 135 * the port numbers is used. The latter case varies the response time 136 * for sending multicast messages. Thus, none of the current uses of 137 * this function require proper randomness, and so we use the simplest 138 * approach, with time-based initialization to cover the reboot case. 139 * The sequential port number selection could be improved upon, but 140 * such an extension would probably bypass this hook anyway. 141 */ 142 return lrand48(); 143 } 144 145 /* 146 * Create a new socket, with the given domain, type, and protocol, for the user 147 * process identified by 'user_endpt'. On success, return the new socket's 148 * identifier, with the libsockevent socket stored in 'sock' and an operations 149 * table stored in 'ops'. On failure, return a negative error code. 150 */ 151 static sockid_t 152 alloc_socket(int domain, int type, int protocol, endpoint_t user_endpt, 153 struct sock ** sock, const struct sockevent_ops **ops) 154 { 155 156 switch (domain) { 157 case PF_INET: 158 #ifdef INET6 159 case PF_INET6: 160 #endif /* INET6 */ 161 switch (type) { 162 case SOCK_STREAM: 163 return tcpsock_socket(domain, protocol, sock, ops); 164 165 case SOCK_DGRAM: 166 return udpsock_socket(domain, protocol, sock, ops); 167 168 case SOCK_RAW: 169 if (!util_is_root(user_endpt)) 170 return EACCES; 171 172 return rawsock_socket(domain, protocol, sock, ops); 173 174 default: 175 return EPROTOTYPE; 176 } 177 178 case PF_ROUTE: 179 return rtsock_socket(type, protocol, sock, ops); 180 181 case PF_LINK: 182 return lnksock_socket(type, protocol, sock, ops); 183 184 default: 185 /* This means that the service has been misconfigured. */ 186 printf("socket() with unsupported domain %d\n", domain); 187 188 return EAFNOSUPPORT; 189 } 190 } 191 192 /* 193 * Initialize the service. 194 */ 195 static int 196 init(int type __unused, sef_init_info_t * init __unused) 197 { 198 199 /* 200 * Initialize the random number seed. See the lwip_hook_rand() comment 201 * on why this weak random number source is currently sufficient. 202 */ 203 srand48(clock_time(NULL)); 204 205 /* Initialize the lwIP library. */ 206 lwip_init(); 207 208 /* Initialize the socket events library. */ 209 sockevent_init(alloc_socket); 210 211 /* Initialize various helper modules. */ 212 mempool_init(); 213 tcpisn_init(); 214 mcast_init(); 215 216 /* Initialize the high-level socket modules. */ 217 ipsock_init(); 218 tcpsock_init(); 219 udpsock_init(); 220 rawsock_init(); 221 222 /* Initialize the various network interface modules. */ 223 ifdev_init(); 224 loopif_init(); 225 ethif_init(); 226 227 /* Initialize the network device driver module. */ 228 ndev_init(); 229 230 /* Initialize the low-level socket modules. */ 231 rtsock_init(); 232 lnksock_init(); 233 234 /* Initialize the routing module. */ 235 route_init(); 236 237 /* Initialize other device modules. */ 238 bpfdev_init(); 239 240 /* 241 * Initialize the MIB module, after all other modules have registered 242 * their subtrees with this module. 243 */ 244 mibtree_init(); 245 246 /* 247 * After everything else has been initialized, set up the default 248 * configuration - in particular, a loopback interface. 249 */ 250 ifconf_init(); 251 252 /* 253 * Initialize the master timer for all the lwIP timers. Just in case 254 * lwIP starts a timer right away, perform a first check upon entry of 255 * the message loop. 256 */ 257 init_timer(&lwip_timer); 258 259 recheck_timer = TRUE; 260 261 running = TRUE; 262 263 return OK; 264 } 265 266 /* 267 * Perform initialization using the System Event Framework (SEF). 268 */ 269 static void 270 startup(void) 271 { 272 273 sef_setcb_init_fresh(init); 274 /* 275 * This service requires stateless restarts, in that several parts of 276 * the system (including VFS and drivers) expect that if restarted, 277 * this service comes back up with a new endpoint. Therefore, do not 278 * set a _restart callback here. 279 * 280 * TODO: support for live update. 281 * 282 * TODO: support for immediate shutdown if no sockets are in use, as 283 * also done by UDS. For now, we never shut down immediately, giving 284 * other processes the opportunity to close sockets on system shutdown. 285 */ 286 287 sef_startup(); 288 } 289 290 /* 291 * The lwIP-based TCP/IP sockets driver. 292 */ 293 int 294 main(void) 295 { 296 message m; 297 int r, ipc_status; 298 299 startup(); 300 301 while (running) { 302 /* 303 * For various reasons, the loopback interface does not pass 304 * packets back into the stack right away. Instead, it queues 305 * them up for later processing. We do that processing here. 306 */ 307 ifdev_poll(); 308 309 /* 310 * Unfortunately, lwIP does not tell us when it starts or stops 311 * timers. This means that we have to check ourselves every 312 * time we have called into lwIP. For simplicity, we perform 313 * the check here. 314 */ 315 check_lwip_timer(); 316 317 if ((r = sef_receive_status(ANY, &m, &ipc_status)) != OK) { 318 if (r == EINTR) 319 continue; /* sef_cancel() was called */ 320 321 panic("sef_receive_status failed: %d", r); 322 } 323 324 /* Process the received message. */ 325 if (is_ipc_notify(ipc_status)) { 326 switch (m.m_source) { 327 case CLOCK: 328 expire_timers(m.m_notify.timestamp); 329 330 break; 331 332 case DS_PROC_NR: 333 /* Network drivers went up and/or down. */ 334 ndev_check(); 335 336 break; 337 338 default: 339 printf("unexpected notify from %d\n", 340 m.m_source); 341 } 342 343 continue; 344 } 345 346 switch (m.m_source) { 347 case MIB_PROC_NR: 348 rmib_process(&m, ipc_status); 349 350 break; 351 352 case VFS_PROC_NR: 353 /* Is this a socket device request? */ 354 if (IS_SDEV_RQ(m.m_type)) { 355 sockevent_process(&m, ipc_status); 356 357 break; 358 } 359 360 /* Is this a character (or block) device request? */ 361 if (IS_CDEV_RQ(m.m_type) || IS_BDEV_RQ(m.m_type)) { 362 bpfdev_process(&m, ipc_status); 363 364 break; 365 } 366 367 /* FALLTHROUGH */ 368 default: 369 /* Is this a network device driver response? */ 370 if (IS_NDEV_RS(m.m_type)) { 371 ndev_process(&m, ipc_status); 372 373 break; 374 } 375 376 printf("unexpected message %d from %d\n", 377 m.m_type, m.m_source); 378 } 379 } 380 381 return 0; 382 } 383