
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
398 lines
9.1 KiB
C
398 lines
9.1 KiB
C
#include <sys/cdefs.h>
|
|
#include "namespace.h"
|
|
#include <lib.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <minix/i2c.h>
|
|
#include <string.h>
|
|
#include <sys/ioccom.h>
|
|
#include <stdarg.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <minix/if.h>
|
|
#include <minix/bpf.h>
|
|
#include <assert.h>
|
|
|
|
static void rewrite_i2c_netbsd_to_minix(minix_i2c_ioctl_exec_t *out,
|
|
i2c_ioctl_exec_t *in);
|
|
static void rewrite_i2c_minix_to_netbsd(i2c_ioctl_exec_t *out,
|
|
minix_i2c_ioctl_exec_t *in);
|
|
|
|
static void rewrite_i2c_netbsd_to_minix(minix_i2c_ioctl_exec_t *out,
|
|
i2c_ioctl_exec_t *in)
|
|
{
|
|
memset(out, '\0', sizeof(minix_i2c_ioctl_exec_t));
|
|
|
|
out->iie_op = in->iie_op;
|
|
out->iie_addr = in->iie_addr;
|
|
out->iie_cmdlen = I2C_EXEC_MAX_CMDLEN < in->iie_cmdlen ?
|
|
I2C_EXEC_MAX_CMDLEN : in->iie_cmdlen;
|
|
out->iie_buflen = I2C_EXEC_MAX_BUFLEN < in->iie_buflen ?
|
|
I2C_EXEC_MAX_BUFLEN : in->iie_buflen;
|
|
|
|
if (in->iie_cmdlen > 0 && in->iie_cmd != NULL) {
|
|
memcpy(out->iie_cmd, in->iie_cmd, in->iie_cmdlen);
|
|
}
|
|
|
|
if (in->iie_buflen > 0 && in->iie_buf != NULL) {
|
|
memcpy(out->iie_buf, in->iie_buf, in->iie_buflen);
|
|
}
|
|
}
|
|
|
|
static void rewrite_i2c_minix_to_netbsd(i2c_ioctl_exec_t *out,
|
|
minix_i2c_ioctl_exec_t *in)
|
|
{
|
|
/* the only field that changes is iie_buf, everything else is the same */
|
|
if (in->iie_buflen > 0 ) {
|
|
memcpy(out->iie_buf, in->iie_buf, in->iie_buflen);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert a network interface related IOCTL with pointers to a flat format
|
|
* suitable for MINIX3. Return a pointer to the new data on success, or zero
|
|
* (with errno set) on failure. The original request code is given in
|
|
* 'request' and must be replaced by the new request code to be used.
|
|
*/
|
|
static vir_bytes
|
|
ioctl_convert_if_to_minix(void * data, unsigned long * request)
|
|
{
|
|
struct minix_ifmediareq *mifm;
|
|
struct ifmediareq *ifm;
|
|
struct minix_if_clonereq *mifcr;
|
|
struct if_clonereq *ifcr;
|
|
|
|
switch (*request) {
|
|
case SIOCGIFMEDIA:
|
|
ifm = (struct ifmediareq *)data;
|
|
|
|
mifm = (struct minix_ifmediareq *)malloc(sizeof(*mifm));
|
|
if (mifm != NULL) {
|
|
/*
|
|
* The count may exceed MINIX_IF_MAXMEDIA, and should
|
|
* be truncated as needed by the IF implementation.
|
|
*/
|
|
memcpy(&mifm->mifm_ifm, ifm, sizeof(*ifm));
|
|
|
|
*request = MINIX_SIOCGIFMEDIA;
|
|
} else
|
|
errno = ENOMEM;
|
|
|
|
return (vir_bytes)mifm;
|
|
|
|
case SIOCIFGCLONERS:
|
|
ifcr = (struct if_clonereq *)data;
|
|
|
|
mifcr = (struct minix_if_clonereq *)malloc(sizeof(*mifcr));
|
|
if (mifcr != NULL) {
|
|
/*
|
|
* The count may exceed MINIX_IF_MAXCLONERS, and should
|
|
* be truncated as needed by the IF implementation.
|
|
*/
|
|
memcpy(&mifcr->mifcr_ifcr, ifcr, sizeof(*ifcr));
|
|
|
|
*request = MINIX_SIOCIFGCLONERS;
|
|
} else
|
|
errno = ENOMEM;
|
|
|
|
return (vir_bytes)mifcr;
|
|
|
|
default:
|
|
assert(0);
|
|
|
|
errno = ENOTTY;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert a the result of a network interface related IOCTL with pointers from
|
|
* the flat format used to make the call to MINIX3. Called on success only.
|
|
* The given request code is that of the (NetBSD-type) original.
|
|
*/
|
|
static void
|
|
ioctl_convert_if_from_minix(vir_bytes addr, void * data, unsigned long request)
|
|
{
|
|
struct minix_ifmediareq *mifm;
|
|
struct ifmediareq *ifm;
|
|
struct minix_if_clonereq *mifcr;
|
|
struct if_clonereq *ifcr;
|
|
int count;
|
|
|
|
switch (request) {
|
|
case SIOCGIFMEDIA:
|
|
mifm = (struct minix_ifmediareq *)addr;
|
|
ifm = (struct ifmediareq *)data;
|
|
|
|
memcpy(ifm, &mifm->mifm_ifm, sizeof(*ifm));
|
|
|
|
if (ifm->ifm_ulist != NULL && ifm->ifm_count > 0)
|
|
memcpy(ifm->ifm_ulist, mifm->mifm_list,
|
|
ifm->ifm_count * sizeof(ifm->ifm_ulist[0]));
|
|
|
|
break;
|
|
|
|
case SIOCIFGCLONERS:
|
|
mifcr = (struct minix_if_clonereq *)addr;
|
|
ifcr = (struct if_clonereq *)data;
|
|
|
|
memcpy(ifcr, &mifcr->mifcr_ifcr, sizeof(*ifcr));
|
|
|
|
count = (ifcr->ifcr_count < ifcr->ifcr_total) ?
|
|
ifcr->ifcr_count : ifcr->ifcr_total;
|
|
if (ifcr->ifcr_buffer != NULL && count > 0)
|
|
memcpy(ifcr->ifcr_buffer, mifcr->mifcr_buffer,
|
|
count * IFNAMSIZ);
|
|
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert a BPF (Berkeley Packet Filter) related IOCTL with pointers to a flat
|
|
* format suitable for MINIX3. Return a pointer to the new data on success, or
|
|
* zero (with errno set) on failure. The original request code is given in
|
|
* 'request' and must be replaced by the new request code to be used.
|
|
*/
|
|
static vir_bytes
|
|
ioctl_convert_bpf_to_minix(void * data, unsigned long * request)
|
|
{
|
|
struct minix_bpf_program *mbf;
|
|
struct bpf_program *bf;
|
|
struct minix_bpf_dltlist *mbfl;
|
|
struct bpf_dltlist *bfl;
|
|
|
|
switch (*request) {
|
|
case BIOCSETF:
|
|
bf = (struct bpf_program *)data;
|
|
|
|
if (bf->bf_len > __arraycount(mbf->mbf_insns)) {
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
mbf = (struct minix_bpf_program *)malloc(sizeof(*mbf));
|
|
if (mbf != NULL) {
|
|
mbf->mbf_len = bf->bf_len;
|
|
memcpy(mbf->mbf_insns, bf->bf_insns,
|
|
bf->bf_len * sizeof(mbf->mbf_insns[0]));
|
|
|
|
*request = MINIX_BIOCSETF;
|
|
} else
|
|
errno = ENOMEM;
|
|
|
|
return (vir_bytes)mbf;
|
|
|
|
case BIOCGDLTLIST:
|
|
bfl = (struct bpf_dltlist *)data;
|
|
|
|
mbfl = (struct minix_bpf_dltlist *)malloc(sizeof(*mbfl));
|
|
if (mbfl != NULL) {
|
|
/*
|
|
* The length may exceed MINIX_BPF_MAXDLT, and should
|
|
* be truncated as needed by the BPF implementation.
|
|
*/
|
|
memcpy(&mbfl->mbfl_dltlist, bfl, sizeof(*bfl));
|
|
|
|
*request = MINIX_BIOCGDLTLIST;
|
|
} else
|
|
errno = ENOMEM;
|
|
|
|
return (vir_bytes)mbfl;
|
|
|
|
default:
|
|
assert(0);
|
|
|
|
errno = ENOTTY;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert a the result of BPF (Berkeley Packet Filter) related IOCTL with
|
|
* pointers from the flat format used to make the call to MINIX3. Called on
|
|
* success only. The given request code is that of the (NetBSD-type) original.
|
|
*/
|
|
static void
|
|
ioctl_convert_bpf_from_minix(vir_bytes addr, void * data,
|
|
unsigned long request)
|
|
{
|
|
struct minix_bpf_dltlist *mbfl;
|
|
struct bpf_dltlist *bfl;
|
|
|
|
switch (request) {
|
|
case BIOCGDLTLIST:
|
|
mbfl = (struct minix_bpf_dltlist *)addr;
|
|
bfl = (struct bpf_dltlist *)data;
|
|
|
|
memcpy(bfl, &mbfl->mbfl_dltlist, sizeof(*bfl));
|
|
|
|
if (bfl->bfl_list != NULL && bfl->bfl_len > 0)
|
|
memcpy(bfl->bfl_list, mbfl->mbfl_list,
|
|
bfl->bfl_len * sizeof(bfl->bfl_list[0]));
|
|
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Library implementation of FIOCLEX and FIONCLEX.
|
|
*/
|
|
static int
|
|
ioctl_to_setfd(int fd, int mask, int val)
|
|
{
|
|
int fl;
|
|
|
|
if ((fl = fcntl(fd, F_GETFD)) == -1)
|
|
return -1;
|
|
|
|
fl = (fl & ~mask) | val;
|
|
|
|
return fcntl(fd, F_SETFD, fl);
|
|
}
|
|
|
|
/*
|
|
* Library implementation of FIONBIO and FIOASYNC.
|
|
*/
|
|
static int
|
|
ioctl_to_setfl(int fd, void * data, int sfl)
|
|
{
|
|
int arg, fl;
|
|
|
|
arg = *(int *)data;
|
|
|
|
if ((fl = fcntl(fd, F_GETFL)) == -1)
|
|
return -1;
|
|
|
|
if (arg)
|
|
fl |= sfl;
|
|
else
|
|
fl &= ~sfl;
|
|
|
|
return fcntl(fd, F_SETFL, fl & ~O_ACCMODE);
|
|
}
|
|
|
|
/*
|
|
* Library implementation of various deprecated IOCTLs. These particular IOCTL
|
|
* calls change how the file descriptors behave, and have nothing to do with
|
|
* the actual open file. They should therefore be handled by VFS rather than
|
|
* individual device drivers. We rewrite them to use fcntl(2) instead here.
|
|
*/
|
|
static int
|
|
ioctl_to_fcntl(int fd, unsigned long request, void * data)
|
|
{
|
|
switch (request) {
|
|
case FIOCLEX:
|
|
return ioctl_to_setfd(fd, FD_CLOEXEC, FD_CLOEXEC);
|
|
case FIONCLEX:
|
|
return ioctl_to_setfd(fd, FD_CLOEXEC, 0);
|
|
case FIONBIO:
|
|
return ioctl_to_setfl(fd, data, O_NONBLOCK);
|
|
case FIOASYNC:
|
|
return ioctl_to_setfl(fd, data, O_ASYNC);
|
|
case FIOSETOWN: /* XXX TODO */
|
|
case FIOGETOWN: /* XXX TODO */
|
|
default:
|
|
errno = ENOTTY;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int ioctl(int fd, unsigned long request, ...)
|
|
{
|
|
minix_i2c_ioctl_exec_t i2c;
|
|
int r, request_save;
|
|
message m;
|
|
vir_bytes addr;
|
|
void *data;
|
|
va_list ap;
|
|
|
|
va_start(ap, request);
|
|
data = va_arg(ap, void *);
|
|
va_end(ap);
|
|
|
|
/*
|
|
* To support compatibility with interfaces on other systems, certain
|
|
* requests are re-written to flat structures (i.e. without pointers).
|
|
*/
|
|
request_save = request;
|
|
|
|
switch (request) {
|
|
case FIOCLEX:
|
|
case FIONCLEX:
|
|
case FIONBIO:
|
|
case FIOASYNC:
|
|
case FIOSETOWN:
|
|
case FIOGETOWN:
|
|
return ioctl_to_fcntl(fd, request, data);
|
|
|
|
case I2C_IOCTL_EXEC:
|
|
rewrite_i2c_netbsd_to_minix(&i2c, data);
|
|
addr = (vir_bytes) &i2c;
|
|
request = MINIX_I2C_IOCTL_EXEC;
|
|
break;
|
|
|
|
case SIOCGIFMEDIA:
|
|
case SIOCIFGCLONERS:
|
|
if ((addr = ioctl_convert_if_to_minix(data, &request)) == 0)
|
|
return -1; /* errno has already been set */
|
|
break;
|
|
|
|
case BIOCSETF:
|
|
case BIOCGDLTLIST:
|
|
if ((addr = ioctl_convert_bpf_to_minix(data, &request)) == 0)
|
|
return -1; /* errno has already been set */
|
|
break;
|
|
|
|
default:
|
|
/* Keep original as-is */
|
|
addr = (vir_bytes)data;
|
|
break;
|
|
}
|
|
|
|
memset(&m, 0, sizeof(m));
|
|
m.m_lc_vfs_ioctl.fd = fd;
|
|
m.m_lc_vfs_ioctl.req = request;
|
|
m.m_lc_vfs_ioctl.arg = addr;
|
|
|
|
r = _syscall(VFS_PROC_NR, VFS_IOCTL, &m);
|
|
|
|
/*
|
|
* Translate back to original form. Do this on failure as well, as
|
|
* temporarily allocated resources may have to be freed up again.
|
|
*/
|
|
switch (request_save) {
|
|
case I2C_IOCTL_EXEC:
|
|
rewrite_i2c_minix_to_netbsd(data, &i2c);
|
|
break;
|
|
|
|
case SIOCGIFMEDIA:
|
|
case SIOCIFGCLONERS:
|
|
if (r == 0)
|
|
ioctl_convert_if_from_minix(addr, data, request_save);
|
|
free((void *)addr);
|
|
break;
|
|
|
|
case BIOCGDLTLIST:
|
|
if (r == 0)
|
|
ioctl_convert_bpf_from_minix(addr, data, request_save);
|
|
/* FALLTHROUGH */
|
|
case BIOCSETF:
|
|
free((void *)addr);
|
|
break;
|
|
|
|
default:
|
|
/* Nothing to do */
|
|
break;
|
|
}
|
|
|
|
return r;
|
|
}
|