
This commit adds a new TCP/IP service to MINIX 3. As its core, the service uses the lwIP TCP/IP stack for maintenance reasons. The service aims to be compatible with NetBSD userland, including its low-level network management utilities. It also aims to support modern features such as IPv6. In summary, the new LWIP service has support for the following main features: - TCP, UDP, RAW sockets with mostly standard BSD API semantics; - IPv6 support: host mode (complete) and router mode (partial); - most of the standard BSD API socket options (SO_); - all of the standard BSD API message flags (MSG_); - the most used protocol-specific socket and control options; - a default loopback interface and the ability to create one more; - configuration-free ethernet interfaces and driver tracking; - queuing and multiple concurrent requests to each ethernet driver; - standard ioctl(2)-based BSD interface management; - radix tree backed, destination-based routing; - routing sockets for standard BSD route reporting and management; - multicast traffic and multicast group membership tracking; - Berkeley Packet Filter (BPF) devices; - standard and custom sysctl(7) nodes for many internals; - a slab allocation based, hybrid static/dynamic memory pool model. Many of its modules come with fairly elaborate comments that cover many aspects of what is going on. The service is primarily a socket driver built on top of the libsockdriver library, but for BPF devices it is at the same time also a character driver. Change-Id: Ib0c02736234b21143915e5fcc0fda8fe408f046f
383 lines
9.2 KiB
C
383 lines
9.2 KiB
C
/* LWIP service - lwip.c - main program and dispatch code */
|
|
|
|
#include "lwip.h"
|
|
#include "tcpisn.h"
|
|
#include "mcast.h"
|
|
#include "ethif.h"
|
|
#include "rtsock.h"
|
|
#include "route.h"
|
|
#include "bpfdev.h"
|
|
|
|
#include "lwip/init.h"
|
|
#include "lwip/sys.h"
|
|
#include "lwip/timeouts.h"
|
|
#include "arch/cc.h"
|
|
|
|
static int running, recheck_timer;
|
|
static minix_timer_t lwip_timer;
|
|
|
|
static void expire_lwip_timer(int);
|
|
|
|
/*
|
|
* Return the system uptime in milliseconds. Also remember that lwIP retrieved
|
|
* the system uptime during this call, so that we know to check for timer
|
|
* updates at the end of the current iteration of the message loop.
|
|
*/
|
|
uint32_t
|
|
sys_now(void)
|
|
{
|
|
|
|
recheck_timer = TRUE;
|
|
|
|
/* TODO: avoid 64-bit arithmetic if possible. */
|
|
return (uint32_t)(((uint64_t)getticks() * 1000) / sys_hz());
|
|
}
|
|
|
|
/*
|
|
* Check if and when lwIP has its next timeout, and set or cancel our timer
|
|
* accordingly.
|
|
*/
|
|
static void
|
|
set_lwip_timer(void)
|
|
{
|
|
uint32_t next_timeout;
|
|
clock_t ticks;
|
|
|
|
/* Ask lwIP when the next alarm is supposed to go off, if any. */
|
|
next_timeout = sys_timeouts_sleeptime();
|
|
|
|
/*
|
|
* Set or update the lwIP timer. We rely on set_timer() asking the
|
|
* kernel for an alarm only if the timeout is different from the one we
|
|
* gave it last time (if at all). However, due to conversions between
|
|
* absolute and relative times, and the fact that we cannot guarantee
|
|
* that the uptime itself does not change while executing these
|
|
* routines, set_timer() will sometimes be issuing a kernel call even
|
|
* if the alarm has not changed. Not a huge deal, but fixing this will
|
|
* require a different interface to lwIP and/or the timers library.
|
|
*/
|
|
if (next_timeout != (uint32_t)-1) {
|
|
/*
|
|
* Round up the next timeout (which is in milliseconds) to the
|
|
* number of clock ticks to add to the current time. Avoid any
|
|
* potential for overflows, no matter how unrealistic..
|
|
*/
|
|
if (next_timeout > TMRDIFF_MAX / sys_hz())
|
|
ticks = TMRDIFF_MAX;
|
|
else
|
|
ticks = (next_timeout * sys_hz() + 999) / 1000;
|
|
|
|
set_timer(&lwip_timer, ticks, expire_lwip_timer, 0 /*unused*/);
|
|
} else
|
|
cancel_timer(&lwip_timer); /* not really needed.. */
|
|
}
|
|
|
|
/*
|
|
* The timer for lwIP timeouts has gone off. Check timeouts, and possibly set
|
|
* a new timer.
|
|
*/
|
|
static void
|
|
expire_lwip_timer(int arg __unused)
|
|
{
|
|
|
|
/* Let lwIP do its work. */
|
|
sys_check_timeouts();
|
|
|
|
/*
|
|
* See if we have to update our timer for the next lwIP timer. Doing
|
|
* this here, rather than from the main loop, avoids one kernel call.
|
|
*/
|
|
set_lwip_timer();
|
|
|
|
recheck_timer = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Check whether we should adjust our local timer based on a change in the next
|
|
* lwIP timeout.
|
|
*/
|
|
static void
|
|
check_lwip_timer(void)
|
|
{
|
|
|
|
/*
|
|
* We make the assumption that whenever lwIP starts a timer, it will
|
|
* need to retrieve the current time. Thus, whenever sys_now() is
|
|
* called, we set the 'recheck_timer' flag. Here, we check whether to
|
|
* (re)set our lwIP timer only if the flag is set. As a result, we do
|
|
* not have to mess with timers for literally every incoming message.
|
|
*
|
|
* When lwIP stops a timer, it does not call sys_now(), and thus, we
|
|
* may miss such updates. However, timers being stopped should be rare
|
|
* and getting too many alarm messages is not a big deal.
|
|
*/
|
|
if (!recheck_timer)
|
|
return;
|
|
|
|
set_lwip_timer();
|
|
|
|
/* Reset the flag for the next message loop iteration. */
|
|
recheck_timer = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Return a random number, for use by lwIP.
|
|
*/
|
|
uint32_t
|
|
lwip_hook_rand(void)
|
|
{
|
|
|
|
/*
|
|
* The current known uses of this hook are for selection of initial
|
|
* TCP/UDP port numbers and for multicast-related timer randomness.
|
|
* The former case exists only to avoid picking the same starting port
|
|
* numbers after a reboot. After that, simple sequential iteration of
|
|
* the port numbers is used. The latter case varies the response time
|
|
* for sending multicast messages. Thus, none of the current uses of
|
|
* this function require proper randomness, and so we use the simplest
|
|
* approach, with time-based initialization to cover the reboot case.
|
|
* The sequential port number selection could be improved upon, but
|
|
* such an extension would probably bypass this hook anyway.
|
|
*/
|
|
return lrand48();
|
|
}
|
|
|
|
/*
|
|
* Create a new socket, with the given domain, type, and protocol, for the user
|
|
* process identified by 'user_endpt'. On success, return the new socket's
|
|
* identifier, with the libsockevent socket stored in 'sock' and an operations
|
|
* table stored in 'ops'. On failure, return a negative error code.
|
|
*/
|
|
static sockid_t
|
|
alloc_socket(int domain, int type, int protocol, endpoint_t user_endpt,
|
|
struct sock ** sock, const struct sockevent_ops **ops)
|
|
{
|
|
|
|
switch (domain) {
|
|
case PF_INET:
|
|
#ifdef INET6
|
|
case PF_INET6:
|
|
#endif /* INET6 */
|
|
switch (type) {
|
|
case SOCK_STREAM:
|
|
return tcpsock_socket(domain, protocol, sock, ops);
|
|
|
|
case SOCK_DGRAM:
|
|
return udpsock_socket(domain, protocol, sock, ops);
|
|
|
|
case SOCK_RAW:
|
|
if (!util_is_root(user_endpt))
|
|
return EACCES;
|
|
|
|
return rawsock_socket(domain, protocol, sock, ops);
|
|
|
|
default:
|
|
return EPROTOTYPE;
|
|
}
|
|
|
|
case PF_ROUTE:
|
|
return rtsock_socket(type, protocol, sock, ops);
|
|
|
|
case PF_LINK:
|
|
return lnksock_socket(type, protocol, sock, ops);
|
|
|
|
default:
|
|
/* This means that the service has been misconfigured. */
|
|
printf("socket() with unsupported domain %d\n", domain);
|
|
|
|
return EAFNOSUPPORT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize the service.
|
|
*/
|
|
static int
|
|
init(int type __unused, sef_init_info_t * init __unused)
|
|
{
|
|
|
|
/*
|
|
* Initialize the random number seed. See the lwip_hook_rand() comment
|
|
* on why this weak random number source is currently sufficient.
|
|
*/
|
|
srand48(clock_time(NULL));
|
|
|
|
/* Initialize the lwIP library. */
|
|
lwip_init();
|
|
|
|
/* Initialize the socket events library. */
|
|
sockevent_init(alloc_socket);
|
|
|
|
/* Initialize various helper modules. */
|
|
mempool_init();
|
|
tcpisn_init();
|
|
mcast_init();
|
|
|
|
/* Initialize the high-level socket modules. */
|
|
ipsock_init();
|
|
tcpsock_init();
|
|
udpsock_init();
|
|
rawsock_init();
|
|
|
|
/* Initialize the various network interface modules. */
|
|
ifdev_init();
|
|
loopif_init();
|
|
ethif_init();
|
|
|
|
/* Initialize the network device driver module. */
|
|
ndev_init();
|
|
|
|
/* Initialize the low-level socket modules. */
|
|
rtsock_init();
|
|
lnksock_init();
|
|
|
|
/* Initialize the routing module. */
|
|
route_init();
|
|
|
|
/* Initialize other device modules. */
|
|
bpfdev_init();
|
|
|
|
/*
|
|
* Initialize the MIB module, after all other modules have registered
|
|
* their subtrees with this module.
|
|
*/
|
|
mibtree_init();
|
|
|
|
/*
|
|
* After everything else has been initialized, set up the default
|
|
* configuration - in particular, a loopback interface.
|
|
*/
|
|
ifconf_init();
|
|
|
|
/*
|
|
* Initialize the master timer for all the lwIP timers. Just in case
|
|
* lwIP starts a timer right away, perform a first check upon entry of
|
|
* the message loop.
|
|
*/
|
|
init_timer(&lwip_timer);
|
|
|
|
recheck_timer = TRUE;
|
|
|
|
running = TRUE;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Perform initialization using the System Event Framework (SEF).
|
|
*/
|
|
static void
|
|
startup(void)
|
|
{
|
|
|
|
sef_setcb_init_fresh(init);
|
|
/*
|
|
* This service requires stateless restarts, in that several parts of
|
|
* the system (including VFS and drivers) expect that if restarted,
|
|
* this service comes back up with a new endpoint. Therefore, do not
|
|
* set a _restart callback here.
|
|
*
|
|
* TODO: support for live update.
|
|
*
|
|
* TODO: support for immediate shutdown if no sockets are in use, as
|
|
* also done by UDS. For now, we never shut down immediately, giving
|
|
* other processes the opportunity to close sockets on system shutdown.
|
|
*/
|
|
|
|
sef_startup();
|
|
}
|
|
|
|
/*
|
|
* The lwIP-based TCP/IP sockets driver.
|
|
*/
|
|
int
|
|
main(void)
|
|
{
|
|
message m;
|
|
int r, ipc_status;
|
|
|
|
startup();
|
|
|
|
while (running) {
|
|
/*
|
|
* For various reasons, the loopback interface does not pass
|
|
* packets back into the stack right away. Instead, it queues
|
|
* them up for later processing. We do that processing here.
|
|
*/
|
|
ifdev_poll();
|
|
|
|
/*
|
|
* Unfortunately, lwIP does not tell us when it starts or stops
|
|
* timers. This means that we have to check ourselves every
|
|
* time we have called into lwIP. For simplicity, we perform
|
|
* the check here.
|
|
*/
|
|
check_lwip_timer();
|
|
|
|
if ((r = sef_receive_status(ANY, &m, &ipc_status)) != OK) {
|
|
if (r == EINTR)
|
|
continue; /* sef_cancel() was called */
|
|
|
|
panic("sef_receive_status failed: %d", r);
|
|
}
|
|
|
|
/* Process the received message. */
|
|
if (is_ipc_notify(ipc_status)) {
|
|
switch (m.m_source) {
|
|
case CLOCK:
|
|
expire_timers(m.m_notify.timestamp);
|
|
|
|
break;
|
|
|
|
case DS_PROC_NR:
|
|
/* Network drivers went up and/or down. */
|
|
ndev_check();
|
|
|
|
break;
|
|
|
|
default:
|
|
printf("unexpected notify from %d\n",
|
|
m.m_source);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
switch (m.m_source) {
|
|
case MIB_PROC_NR:
|
|
rmib_process(&m, ipc_status);
|
|
|
|
break;
|
|
|
|
case VFS_PROC_NR:
|
|
/* Is this a socket device request? */
|
|
if (IS_SDEV_RQ(m.m_type)) {
|
|
sockevent_process(&m, ipc_status);
|
|
|
|
break;
|
|
}
|
|
|
|
/* Is this a character (or block) device request? */
|
|
if (IS_CDEV_RQ(m.m_type) || IS_BDEV_RQ(m.m_type)) {
|
|
bpfdev_process(&m, ipc_status);
|
|
|
|
break;
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
default:
|
|
/* Is this a network device driver response? */
|
|
if (IS_NDEV_RS(m.m_type)) {
|
|
ndev_process(&m, ipc_status);
|
|
|
|
break;
|
|
}
|
|
|
|
printf("unexpected message %d from %d\n",
|
|
m.m_type, m.m_source);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|