xref: /minix/minix/net/lwip/lwip.c (revision fb9c64b2)
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