diff options
author | Joan Lledó <joanlluislledo@gmail.com> | 2017-11-13 08:31:46 +0100 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2017-12-18 00:01:02 +0100 |
commit | d3594ddad8fdd4f28f2362ad288acd03ed60eb41 (patch) | |
tree | f76d7dffd2f6e8de6011b0333481a51c70f3c6d2 /lwip/port | |
parent | 0ca198f1f90071a054287c204a3fd1b4ea315e18 (diff) | |
download | hurd-d3594ddad8fdd4f28f2362ad288acd03ed60eb41.tar.gz hurd-d3594ddad8fdd4f28f2362ad288acd03ed60eb41.tar.bz2 hurd-d3594ddad8fdd4f28f2362ad288acd03ed60eb41.zip |
lwip: Add LwIP-based TCP/IP translator
* Makefile (prog-subdirs): Add lwip.
* config.make.in (HAVE_LIBLWIP, liblwip_CFLAGS, liblwip_LIBS): Define
variables.
* configure.ac: Check for liblwip.
* lwip/: New directory.
Diffstat (limited to 'lwip/port')
-rw-r--r-- | lwip/port/include/netif/hurdethif.h | 39 | ||||
-rw-r--r-- | lwip/port/include/netif/hurdloopif.h | 36 | ||||
-rw-r--r-- | lwip/port/include/netif/hurdtunif.h | 65 | ||||
-rw-r--r-- | lwip/port/include/netif/ifcommon.h | 60 | ||||
-rw-r--r-- | lwip/port/netif/hurdethif.c | 573 | ||||
-rw-r--r-- | lwip/port/netif/hurdloopif.c | 112 | ||||
-rw-r--r-- | lwip/port/netif/hurdtunif.c | 721 | ||||
-rw-r--r-- | lwip/port/netif/ifcommon.c | 121 |
8 files changed, 1727 insertions, 0 deletions
diff --git a/lwip/port/include/netif/hurdethif.h b/lwip/port/include/netif/hurdethif.h new file mode 100644 index 00000000..326b1cf9 --- /dev/null +++ b/lwip/port/include/netif/hurdethif.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Joan Lledó. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Ethernet devices module */ + +#ifndef LWIP_HURDETHIF_H +#define LWIP_HURDETHIF_H + +#include <hurd/ports.h> + +#include <lwip/netif.h> +#include <netif/ifcommon.h> + +typedef struct ifcommon hurdethif; + +/* Device initialization */ +error_t hurdethif_device_init (struct netif *netif); + +/* Module initialization */ +error_t hurdethif_module_init (); + +#endif /* LWIP_HURDETHIF_H */ diff --git a/lwip/port/include/netif/hurdloopif.h b/lwip/port/include/netif/hurdloopif.h new file mode 100644 index 00000000..fb5c5b83 --- /dev/null +++ b/lwip/port/include/netif/hurdloopif.h @@ -0,0 +1,36 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Joan Lledó. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Loopback devices module */ + +#ifndef LWIP_HURDLOOPIF_H +#define LWIP_HURDLOOPIF_H + +#include <hurd/ports.h> + +#include <lwip/netif.h> +#include <netif/ifcommon.h> + +typedef struct ifcommon hurdloopif; + +/* Device initialization */ +error_t hurdloopif_device_init (struct netif *netif); + +#endif /* LWIP_HURDLOOPIF_H */ diff --git a/lwip/port/include/netif/hurdtunif.h b/lwip/port/include/netif/hurdtunif.h new file mode 100644 index 00000000..938465bb --- /dev/null +++ b/lwip/port/include/netif/hurdtunif.h @@ -0,0 +1,65 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Joan Lledó. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Tunnel devices module */ + +#ifndef LWIP_HURDTUNIF_H +#define LWIP_HURDTUNIF_H + +#include <hurd/ports.h> + +#include <lwip/netif.h> +#include <netif/ifcommon.h> + +/* Queue of data in the tunnel */ +struct pbufqueue +{ + struct pbuf *head; + struct pbuf **tail; + uint8_t len; +}; + +/* Extension of the common device interface to store tunnel metadata */ +struct hurdtunif +{ + struct ifcommon comm; + + struct trivfs_control *cntl; /* Identify the tunnel device in use */ + file_t underlying; /* Underlying node where the tunnel is bound */ + struct iouser *user; /* Restrict the access to one user at a time */ + struct pbufqueue queue; /* Output queue */ + + /* Concurrent access to the queue */ + pthread_mutex_t lock; + pthread_cond_t read; + pthread_cond_t select; + uint8_t read_blocked; +}; + +struct port_class *tunnel_cntlclass; +struct port_class *tunnel_class; + +/* Device initialization */ +error_t hurdtunif_device_init (struct netif *netif); + +/* Module initialization */ +error_t hurdtunif_module_init (); + +#endif /* LWIP_HURDTUNIF_H */ diff --git a/lwip/port/include/netif/ifcommon.h b/lwip/port/include/netif/ifcommon.h new file mode 100644 index 00000000..15493dc9 --- /dev/null +++ b/lwip/port/include/netif/ifcommon.h @@ -0,0 +1,60 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Joan Lledó. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Common interface for all kinds of devices */ + +#ifndef LWIP_IFCOMMON_H +#define LWIP_IFCOMMON_H + +#include <stdint.h> +#include <sys/types.h> +#include <device/device.h> + +#include <lwip/netif.h> + +/* + * Helper struct to hold private data used to operate your interface. + */ +struct ifcommon +{ + uint16_t type; + device_t ether_port; + struct port_info *readpt; + mach_port_t readptname; + char *devname; + uint16_t flags; + + /* Callbacks */ + error_t (*init) (struct netif * netif); + error_t (*terminate) (struct netif * netif); + error_t (*open) (struct netif * netif); + error_t (*close) (struct netif * netif); + error_t (*update_mtu) (struct netif * netif, uint32_t mtu); + error_t (*change_flags) (struct netif * netif, uint16_t flags); +}; + +error_t if_init (struct netif *netif); +error_t if_terminate (struct netif *netif); +error_t if_change_flags (struct netif *netif, uint16_t flags); + +/* Get the state from a netif */ +#define netif_get_state(netif) ((struct ifcommon *)netif->state) + +#endif /* LWIP_IFCOMMON_H */ diff --git a/lwip/port/netif/hurdethif.c b/lwip/port/netif/hurdethif.c new file mode 100644 index 00000000..bcf2e4dd --- /dev/null +++ b/lwip/port/netif/hurdethif.c @@ -0,0 +1,573 @@ +/* + Copyright (C) 1995, 1996, 1998, 1999, 2000, 2002, 2007, 2017 + Free Software Foundation, Inc. + + Written by Michael I. Bushnell, p/BSG. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Ethernet devices module */ + +#include <netif/hurdethif.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <pthread.h> +#include <error.h> +#include <device/device.h> +#include <device/net_status.h> +#include <net/if.h> +#include <net/if_arp.h> + +#include <lwip/opt.h> +#include <lwip/def.h> +#include <lwip/mem.h> +#include <lwip/pbuf.h> +#include <lwip/stats.h> +#include <lwip/snmp.h> +#include <lwip/ethip6.h> +#include <lwip/etharp.h> + +/* Get the MAC address from an array of int */ +#define GET_HWADDR_BYTE(x,n) (((char*)x)[n]) + +static short ether_filter[] = { +#ifdef NETF_IN + /* We have to tell the packet filtering code that we're interested in + incoming packets. */ + NETF_IN, /* Header. */ +#endif + NETF_PUSHLIT | NETF_NOP, + 1 +}; + +static int ether_filter_len = sizeof (ether_filter) / sizeof (short); + +static struct bpf_insn bpf_ether_filter[] = { + {NETF_IN | NETF_BPF, 0, 0, 0}, /* Header. */ + {BPF_LD | BPF_H | BPF_ABS, 0, 0, 12}, /* Load Ethernet type */ + {BPF_JMP | BPF_JEQ | BPF_K, 2, 0, 0x0806}, /* Accept ARP */ + {BPF_JMP | BPF_JEQ | BPF_K, 1, 0, 0x0800}, /* Accept IPv4 */ + {BPF_JMP | BPF_JEQ | BPF_K, 0, 1, 0x86DD}, /* Accept IPv6 */ + /* + * And return an amount of bytes equal to: + * MSS + IP and transport headers length + Ethernet header length + */ + {BPF_RET | BPF_K, 0, 0, TCP_MSS + 0x28 + PBUF_LINK_HLEN}, + {BPF_RET | BPF_K, 0, 0, 0}, /* Or discard it all */ +}; + +static int bpf_ether_filter_len = sizeof (bpf_ether_filter) / sizeof (short); + +/* Bucket and class for the incoming data */ +struct port_bucket *etherport_bucket; +struct port_class *etherread_class; + +/* Thread for the incoming data */ +static pthread_t input_thread; + +/* Get the device flags */ +static error_t +hurdethif_device_get_flags (struct netif *netif, uint16_t * flags) +{ + error_t err = 0; + size_t count; + struct net_status status; + hurdethif *ethif; + + memset (&status, 0, sizeof (struct net_status)); + + ethif = netif_get_state (netif); + count = NET_STATUS_COUNT; + err = device_get_status (ethif->ether_port, + NET_STATUS, (dev_status_t) & status, &count); + if (err == D_INVALID_OPERATION) + { + /* + * eth-multiplexer doesn't support setting flags. + * We must ignore D_INVALID_OPERATION. + */ + error (0, 0, "%s: hardware doesn't support getting flags.\n", + ethif->devname); + err = 0; + } + else if (err) + error (0, err, "%s: Cannot get hardware flags", ethif->devname); + else + *flags = status.flags; + + return err; +} + +/* Set the device flags */ +static error_t +hurdethif_device_set_flags (struct netif *netif, uint16_t flags) +{ + error_t err = 0; + hurdethif *ethif; + int sflags; + + sflags = flags; + ethif = netif_get_state (netif); + + if (ethif->ether_port == MACH_PORT_NULL) + /* The device is closed */ + return 0; + + err = device_set_status (ethif->ether_port, NET_FLAGS, &sflags, 1); + if (err == D_INVALID_OPERATION) + { + /* + * eth-multiplexer doesn't support setting flags. + * We must ignore D_INVALID_OPERATION. + */ + error (0, 0, "%s: hardware doesn't support setting flags.\n", + ethif->devname); + err = 0; + } + else if (err) + error (0, err, "%s: Cannot set hardware flags", ethif->devname); + else + ethif->flags = flags; + + return err; +} + +/* Use the device interface to access the device */ +static error_t +hurdethif_device_open (struct netif *netif) +{ + error_t err = ERR_OK; + device_t master_device; + hurdethif *ethif = netif_get_state (netif); + + if (ethif->ether_port != MACH_PORT_NULL) + { + error (0, 0, "Already opened: %s", ethif->devname); + return -1; + } + + err = ports_create_port (etherread_class, etherport_bucket, + sizeof (struct port_info), ðif->readpt); + if (err) + { + error (0, err, "ports_create_port on %s", ethif->devname); + } + else + { + ethif->readptname = ports_get_right (ethif->readpt); + mach_port_insert_right (mach_task_self (), ethif->readptname, + ethif->readptname, MACH_MSG_TYPE_MAKE_SEND); + + mach_port_set_qlimit (mach_task_self (), ethif->readptname, + MACH_PORT_QLIMIT_MAX); + + master_device = file_name_lookup (ethif->devname, O_RDWR, 0); + if (master_device != MACH_PORT_NULL) + { + /* The device name here is the path of a device file. */ + err = device_open (master_device, D_WRITE | D_READ, + "eth", ðif->ether_port); + mach_port_deallocate (mach_task_self (), master_device); + if (err) + error (0, err, "device_open on %s", ethif->devname); + else + { + err = device_set_filter (ethif->ether_port, ethif->readptname, + MACH_MSG_TYPE_MAKE_SEND, 0, + (filter_array_t) bpf_ether_filter, + bpf_ether_filter_len); + if (err) + error (0, err, "device_set_filter on %s", ethif->devname); + } + } + else + { + /* No, perhaps a Mach device? */ + int file_errno = errno; + err = get_privileged_ports (0, &master_device); + if (err) + { + error (0, file_errno, "file_name_lookup %s", ethif->devname); + error (0, err, "and cannot get device master port"); + } + else + { + err = device_open (master_device, D_WRITE | D_READ, + ethif->devname, ðif->ether_port); + mach_port_deallocate (mach_task_self (), master_device); + if (err) + { + error (0, file_errno, "file_name_lookup %s", + ethif->devname); + error (0, err, "device_open(%s)", ethif->devname); + } + else + { + err = + device_set_filter (ethif->ether_port, ethif->readptname, + MACH_MSG_TYPE_MAKE_SEND, 0, + (filter_array_t) ether_filter, + ether_filter_len); + if (err) + error (0, err, "device_set_filter on %s", ethif->devname); + } + } + } + } + + return err; +} + +/* Destroy our link to the device */ +static error_t +hurdethif_device_close (struct netif *netif) +{ + hurdethif *ethif = netif_get_state (netif); + + if (ethif->ether_port == MACH_PORT_NULL) + { + error (0, 0, "Already closed: %s", ethif->devname); + return -1; + } + + mach_port_deallocate (mach_task_self (), ethif->readptname); + ethif->readptname = MACH_PORT_NULL; + ports_destroy_right (ethif->readpt); + ethif->readpt = NULL; + device_close (ethif->ether_port); + mach_port_deallocate (mach_task_self (), ethif->ether_port); + ethif->ether_port = MACH_PORT_NULL; + + return ERR_OK; +} + +/* + * Called from lwip when outgoing data is ready + */ +static error_t +hurdethif_output (struct netif *netif, struct pbuf *p) +{ + error_t err; + hurdethif *ethif = netif_get_state (netif); + int count; + uint8_t tried; + + if (p->tot_len != p->len) + /* Drop the packet */ + return ERR_OK; + + tried = 0; + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + do + { + tried++; + err = device_write (ethif->ether_port, D_NOWAIT, 0, + p->payload, p->len, &count); + if (err) + { + if (tried == 2) + /* Too many tries, abort */ + break; + + if (err == EMACH_SEND_INVALID_DEST || err == EMIG_SERVER_DIED) + { + /* Device probably just died, try to reopen it. */ + hurdethif_device_close (netif); + hurdethif_device_open (netif); + } + } + else if (count != p->len) + /* Incomplete package sent, reattempt */ + err = -1; + } + while (err); + + return ERR_OK; +} + +/* + * Called from the demuxer when incoming data is ready + */ +void +hurdethif_input (struct netif *netif, struct net_rcv_msg *msg) +{ + struct pbuf *p, *q; + uint16_t len; + uint16_t off; + uint16_t next_read; + + /* Get the size of the whole packet */ + len = PBUF_LINK_HLEN + + msg->packet_type.msgt_number - sizeof (struct packet_header); + + /* Allocate an empty pbuf chain for the data */ + p = pbuf_alloc (PBUF_RAW, len, PBUF_POOL); + + if (p) + { + /* + * Iterate to fill the pbuf chain. + * + * First read the Ethernet header from msg->header. Then read the + * payload from msg->packet + */ + q = p; + off = 0; + do + { + if (off < PBUF_LINK_HLEN) + { + /* We still haven't ended copying the header */ + next_read = (off + q->len) > PBUF_LINK_HLEN ? + (PBUF_LINK_HLEN - off) : q->len; + memcpy (q->payload, msg->header + off, next_read); + + if ((off + q->len) > PBUF_LINK_HLEN) + memcpy (q->payload + PBUF_LINK_HLEN, + msg->packet + sizeof (struct packet_header), + q->len - next_read); + } + else + /* The header is copyied yet */ + memcpy (q->payload, msg->packet + + sizeof (struct packet_header) + off - PBUF_LINK_HLEN, + q->len); + + off += q->len; + + /* q->tot_len == q->len means this was the last pbuf in the chain */ + if (q->tot_len == q->len) + break; + else + q = q->next; + } + while (1); + + /* Pass the pbuf chain to he input function */ + if (netif->input (p, netif) != ERR_OK) + { + LWIP_DEBUGF (NETIF_DEBUG, ("hurdethif_input: IP input error\n")); + pbuf_free (p); + p = NULL; + } + } +} + +/* Demux incoming RPCs from the device */ +int +hurdethif_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp) +{ + struct net_rcv_msg *msg = (struct net_rcv_msg *) inp; + struct netif *netif; + mach_port_t local_port; + + if (inp->msgh_id != NET_RCV_MSG_ID) + return 0; + + if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == + MACH_MSG_TYPE_PROTECTED_PAYLOAD) + { + struct port_info *pi = ports_lookup_payload (NULL, + inp->msgh_protected_payload, + NULL); + if (pi) + { + local_port = pi->port_right; + ports_port_deref (pi); + } + else + local_port = MACH_PORT_NULL; + } + else + local_port = inp->msgh_local_port; + + for (netif = netif_list; netif; netif = netif->next) + if (local_port == netif_get_state (netif)->readptname) + break; + + if (!netif) + { + if (inp->msgh_remote_port != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), inp->msgh_remote_port); + return 1; + } + + hurdethif_input (netif, msg); + + return 1; +} + +/* + * Update the interface's MTU and the BPF filter + */ +static error_t +hurdethif_device_update_mtu (struct netif *netif, uint32_t mtu) +{ + error_t err = 0; + + netif->mtu = mtu; + + bpf_ether_filter[5].k = mtu + PBUF_LINK_HLEN; + + return err; +} + +/* + * Release all resources of this netif. + * + * Returns 0 on success. + */ +static error_t +hurdethif_device_terminate (struct netif *netif) +{ + /* Free the hook */ + free (netif_get_state (netif)->devname); + free (netif_get_state (netif)); + + return 0; +} + +/* + * Initializes a single device. + * + * The module must be initialized before calling this function. + */ +error_t +hurdethif_device_init (struct netif * netif) +{ + error_t err; + size_t count = 2; + int net_address[2]; + device_t ether_port; + hurdethif *ethif; + + /* + * Replace the hook by a new one with the proper size. + * The old one is in the stack and will be removed soon. + */ + ethif = calloc (1, sizeof (hurdethif)); + if (!ethif) + { + LWIP_DEBUGF (NETIF_DEBUG, ("hurdethif_init: out of memory\n")); + return ERR_MEM; + } + memcpy (ethif, netif_get_state (netif), sizeof (struct ifcommon)); + netif->state = ethif; + + /* Interface type */ + ethif->type = ARPHRD_ETHER; + + /* Set callbacks */ + netif->output = etharp_output; + netif->output_ip6 = ethip6_output; + netif->linkoutput = hurdethif_output; + + ethif->open = hurdethif_device_open; + ethif->close = hurdethif_device_close; + ethif->terminate = hurdethif_device_terminate; + ethif->update_mtu = hurdethif_device_update_mtu; + ethif->change_flags = hurdethif_device_set_flags; + + /* ---- Hardware initialization ---- */ + + /* We need the device to be opened to configure it */ + err = hurdethif_device_open (netif); + if (err) + return err; + + /* Get the MAC address */ + ether_port = netif_get_state (netif)->ether_port; + err = device_get_status (ether_port, NET_ADDRESS, net_address, &count); + if (err) + error (0, err, "%s: Cannot get hardware Ethernet address", + netif_get_state (netif)->devname); + else if (count * sizeof (int) >= ETHARP_HWADDR_LEN) + { + net_address[0] = ntohl (net_address[0]); + net_address[1] = ntohl (net_address[1]); + + /* Set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* Set MAC hardware address */ + netif->hwaddr[0] = GET_HWADDR_BYTE (net_address, 0); + netif->hwaddr[1] = GET_HWADDR_BYTE (net_address, 1); + netif->hwaddr[2] = GET_HWADDR_BYTE (net_address, 2); + netif->hwaddr[3] = GET_HWADDR_BYTE (net_address, 3); + netif->hwaddr[4] = GET_HWADDR_BYTE (net_address, 4); + netif->hwaddr[5] = GET_HWADDR_BYTE (net_address, 5); + } + else + error (0, 0, "%s: Invalid Ethernet address", + netif_get_state (netif)->devname); + + /* Maximum transfer unit: MSS + IP header size + TCP header size */ + netif->mtu = TCP_MSS + 20 + 20; + + /* Enable Ethernet multicasting */ + hurdethif_device_get_flags (netif, &netif_get_state (netif)->flags); + netif_get_state (netif)->flags |= + IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_ALLMULTI; + hurdethif_device_set_flags (netif, netif_get_state (netif)->flags); + + /* + * Up the link, set the interface type to NETIF_FLAG_ETHARP + * and enable other features. + */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP + | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6; + + return ERR_OK; +} + +static void * +hurdethif_input_thread (void *arg) +{ + ports_manage_port_operations_one_thread (etherport_bucket, + hurdethif_demuxer, 0); + + return 0; +} + +/* + * Init the thread for the incoming data. + * + * This function should be called once. + */ +error_t +hurdethif_module_init () +{ + error_t err; + etherport_bucket = ports_create_bucket (); + etherread_class = ports_create_class (0, 0); + + err = pthread_create (&input_thread, 0, hurdethif_input_thread, 0); + if (!err) + pthread_detach (input_thread); + else + { + errno = err; + perror ("pthread_create"); + } + + return err; +} diff --git a/lwip/port/netif/hurdloopif.c b/lwip/port/netif/hurdloopif.c new file mode 100644 index 00000000..ef64b8b6 --- /dev/null +++ b/lwip/port/netif/hurdloopif.c @@ -0,0 +1,112 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Joan Lledó. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Loopback devices module */ + +#include <netif/hurdloopif.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <string.h> + +#include <lwip-util.h> + +/* Set the device flags */ +static error_t +hurdloopif_device_set_flags (struct netif *netif, uint16_t flags) +{ + error_t err = 0; + hurdloopif *loopif; + + loopif = netif_get_state (netif); + loopif->flags = flags; + + return err; +} + +/* + * Update the interface's MTU + */ +static error_t +hurdloopif_device_update_mtu (struct netif *netif, uint32_t mtu) +{ + error_t err = 0; + + netif->mtu = mtu; + + return err; +} + +/* + * Release all resources of this netif. + * + * Returns 0 on success. + */ +static error_t +hurdloopif_device_terminate (struct netif *netif) +{ + /* Free the hook */ + free (netif_get_state (netif)->devname); + free (netif_get_state (netif)); + + return 0; +} + +/* + * Set up the LwIP loopback interface + */ +error_t +hurdloopif_device_init (struct netif * netif) +{ + error_t err = 0; + hurdloopif *loopif; + + /* + * Replace the hook by a new one with the proper size. + * The old one is in the stack and will be removed soon. + */ + loopif = calloc (1, sizeof (hurdloopif)); + if (loopif == NULL) + { + LWIP_DEBUGF (NETIF_DEBUG, ("hurdloopif_init: out of memory\n")); + return ERR_MEM; + } + memcpy (loopif, netif_get_state (netif), sizeof (struct ifcommon)); + netif->state = loopif; + + /* Device name and type */ + loopif->devname = LOOP_DEV_NAME; + loopif->type = ARPHRD_LOOPBACK; + + /* MTU = MSS + IP header + TCP header */ + netif->mtu = TCP_MSS + 20 + 20; + + /* Set flags */ + hurdloopif_device_set_flags (netif, IFF_UP | IFF_RUNNING | IFF_LOOPBACK); + + /* Set callbacks */ + loopif->open = 0; + loopif->close = 0; + loopif->terminate = hurdloopif_device_terminate; + loopif->update_mtu = hurdloopif_device_update_mtu; + loopif->change_flags = hurdloopif_device_set_flags; + + return err; +} diff --git a/lwip/port/netif/hurdtunif.c b/lwip/port/netif/hurdtunif.c new file mode 100644 index 00000000..d7991baa --- /dev/null +++ b/lwip/port/netif/hurdtunif.c @@ -0,0 +1,721 @@ +/* + Copyright (C) 1995,96,98,99,2000,02,17 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Tunnel devices module */ + +#include <netif/hurdtunif.h> + +#include <hurd/trivfs.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <error.h> +#include <sys/mman.h> + +#include <lwip-hurd.h> + +/* Add to the end of the queue */ +static void +enqueue (struct pbufqueue *q, struct pbuf *p) +{ + *(q->tail) = p; + p->next = 0; + q->tail = &p->next; + + q->len++; +} + +/* Get from the head of the queue */ +static struct pbuf * +dequeue (struct pbufqueue *q) +{ + struct pbuf *ret; + + if (!q->head) + return 0; + + ret = q->head; + q->head = q->head->next; + ret->next = 0; + q->len--; + + if (!q->head) + q->tail = &q->head; + + return ret; +} + +/* + * Update the interface's MTU + */ +static error_t +hurdtunif_device_update_mtu (struct netif *netif, uint32_t mtu) +{ + error_t err = 0; + + netif->mtu = mtu; + + return err; +} + +/* Set the device flags */ +static error_t +hurdtunif_device_set_flags (struct netif *netif, uint16_t flags) +{ + error_t err = 0; + struct ifcommon *tunif; + + tunif = netif_get_state (netif); + tunif->flags = flags; + + return err; +} + +/* + * Release all resources of this netif. + * + * Returns 0 on success. + */ +static error_t +hurdtunif_device_terminate (struct netif *netif) +{ + struct pbuf *p; + struct hurdtunif *tunif = (struct hurdtunif *) netif_get_state (netif); + + /* Clear the queue */ + while ((p = dequeue (&tunif->queue)) != 0) + pbuf_free (p); + pthread_cond_destroy (&tunif->read); + pthread_cond_destroy (&tunif->select); + pthread_mutex_destroy (&tunif->lock); + + /* Free the hook */ + free (netif_get_state (netif)->devname); + free (netif_get_state (netif)); + + return 0; +} + +/* + * Called from lwip. + * + * Just enqueue the data. + */ +static error_t +hurdtunif_output (struct netif *netif, struct pbuf *p, + const ip4_addr_t * ipaddr) +{ + error_t err = 0; + struct hurdtunif *tunif; + struct pbuf *pcopy, *oldest; + + tunif = (struct hurdtunif *) netif_get_state (netif); + + /* + * The stack is responsible for allocating and freeing the pbuf p. + * Sometimes it keeps the pbuf for the case it needs to be retransmitted, + * but at other times it frees the pbuf while it's still in our queue, + * that's why we need a copy. + */ + pcopy = pbuf_alloc (PBUF_IP, p->tot_len, PBUF_RAM); + if (pcopy != NULL) + if (pbuf_copy (pcopy, p) != ERR_OK) + { + pbuf_free (pcopy); + pcopy = NULL; + } + + pthread_mutex_lock (&tunif->lock); + + /* Avoid unlimited growth. */ + if (tunif->queue.len > 128) + { + oldest = dequeue (&tunif->queue); + pbuf_free (oldest); + } + + enqueue (&tunif->queue, pcopy); + + if (tunif->read_blocked) + { + tunif->read_blocked = 0; + pthread_cond_broadcast (&tunif->read); + pthread_cond_broadcast (&tunif->select); + } + + pthread_mutex_unlock (&tunif->lock); + + return err; +} + +/* + * Set up the tunnel a new tunnel device + */ +error_t +hurdtunif_device_init (struct netif * netif) +{ + error_t err = 0; + struct hurdtunif *tunif; + char *base_name, *name = netif_get_state (netif)->devname; + + /* + * Replace the hook by a new one with the proper size. + * The old one is in the stack and will be removed soon. + */ + tunif = calloc (1, sizeof (struct hurdtunif)); + if (tunif == NULL) + { + LWIP_DEBUGF (NETIF_DEBUG, ("hurdtunif_init: out of memory\n")); + return ERR_MEM; + } + memcpy (tunif, netif_get_state (netif), sizeof (struct ifcommon)); + netif->state = tunif; + + base_name = strrchr (name, '/'); + if (base_name) + /* The user provided a path */ + base_name++; + else + /* The user provided a name for the tunnel. We'll create it at /dev */ + base_name = name; + + if (base_name != name) + tunif->comm.devname = strdup (name); + else + /* Setting up the translator at /dev/tunX. */ + asprintf (&tunif->comm.devname, "/dev/%s", base_name); + + /* Set the device type */ + tunif->comm.type = ARPHRD_TUNNEL; + + /* MTU = MSS + IP header + TCP header */ + netif->mtu = TCP_MSS + 20 + 20; + + /* Set flags */ + hurdtunif_device_set_flags (netif, + IFF_UP | IFF_RUNNING | IFF_POINTOPOINT | + IFF_NOARP); + + netif->flags = NETIF_FLAG_LINK_UP; + + /* Set the callbacks */ + netif->output = hurdtunif_output; + tunif->comm.open = 0; + tunif->comm.close = 0; + tunif->comm.terminate = hurdtunif_device_terminate; + tunif->comm.update_mtu = hurdtunif_device_update_mtu; + tunif->comm.change_flags = hurdtunif_device_set_flags; + + /* Bind the translator to tunif->comm.devname */ + tunif->underlying = file_name_lookup (tunif->comm.devname, + O_CREAT | O_NOTRANS, 0664); + + if (tunif->underlying == MACH_PORT_NULL) + { + error (0, 0, "%s", tunif->comm.devname); + return -1; + } + + err = trivfs_create_control (tunif->underlying, tunnel_cntlclass, + lwip_bucket, tunnel_class, lwip_bucket, + &tunif->cntl); + + if (!err) + { + mach_port_t right = ports_get_send_right (tunif->cntl); + err = file_set_translator (tunif->underlying, 0, + FS_TRANS_SET | FS_TRANS_ORPHAN, 0, 0, 0, + right, MACH_MSG_TYPE_COPY_SEND); + mach_port_deallocate (mach_task_self (), right); + } + + if (err) + error (0, err, "%s", tunif->comm.devname); + + /* We'll need to get the netif from trivfs operations */ + tunif->cntl->hook = netif; + + /* Output queue initialization */ + tunif->queue.head = 0; + tunif->queue.tail = &tunif->queue.head; + tunif->queue.len = 0; + pthread_mutex_init (&tunif->lock, NULL); + pthread_cond_init (&tunif->read, NULL); + pthread_cond_init (&tunif->select, NULL); + tunif->read_blocked = 0; + + return err; +} + +/* + * Set libports classes + * + * This function should be called once. + */ +error_t +hurdtunif_module_init () +{ + error_t err = 0; + + trivfs_add_control_port_class (&tunnel_cntlclass); + trivfs_add_protid_port_class (&tunnel_class); + + return err; +} + +/* If a new open with read and/or write permissions is requested, + restrict to exclusive usage. */ +static error_t +check_open_hook (struct trivfs_control *cntl, struct iouser *user, int flags) +{ + struct netif *netif; + struct hurdtunif *tunif; + + for (netif = netif_list; netif; netif = netif->next) + { + tunif = (struct hurdtunif *) netif_get_state (netif); + if (tunif->cntl == cntl) + break; + } + + if (netif && flags != O_NORW) + { + if (tunif->user) + return EBUSY; + else + tunif->user = user; + } + + return 0; +} + +/* When a protid is destroyed, check if it is the current user. + If yes, release the interface for other users. */ +static void +pi_destroy_hook (struct trivfs_protid *cred) +{ + struct netif *netif; + struct hurdtunif *tunif; + + if (cred->pi.class != tunnel_class) + return; + + netif = (struct netif *) cred->po->cntl->hook; + tunif = (struct hurdtunif *) netif_get_state (netif); + + if (tunif->user == cred->user) + tunif->user = 0; +} + +/* If this variable is set, it is called every time a new peropen + structure is created and initialized. */ +error_t (*trivfs_check_open_hook) (struct trivfs_control *, + struct iouser *, int) = check_open_hook; + +/* If this variable is set, it is called every time a protid structure + is about to be destroyed. */ +void (*trivfs_protid_destroy_hook) (struct trivfs_protid *) = pi_destroy_hook; + +/* Read data from an IO object. If offset is -1, read from the object + maintained file pointer. If the object is not seekable, offset is + ignored. The amount desired to be read is in AMOUNT. */ +error_t +trivfs_S_io_read (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + char **data, mach_msg_type_number_t * data_len, + loff_t offs, size_t amount) +{ + struct hurdtunif *tunif; + struct pbuf *p; + + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + tunif = + (struct hurdtunif *) + netif_get_state (((struct netif *) cred->po->cntl->hook)); + + pthread_mutex_lock (&tunif->lock); + + while (tunif->queue.len == 0) + { + if (cred->po->openmodes & O_NONBLOCK) + { + pthread_mutex_unlock (&tunif->lock); + return EWOULDBLOCK; + } + + tunif->read_blocked = 1; + if (pthread_hurd_cond_wait_np (&tunif->read, &tunif->lock)) + { + pthread_mutex_unlock (&tunif->lock); + return EINTR; + } + } + + p = dequeue (&tunif->queue); + + if (p->tot_len < amount) + amount = p->tot_len; + if (amount > 0) + { + /* Possibly allocate a new buffer. */ + if (*data_len < amount) + { + *data = mmap (0, amount, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0); + if (*data == MAP_FAILED) + { + pbuf_free (p); + pthread_mutex_unlock (&tunif->lock); + return ENOMEM; + } + } + + /* Copy the constant data into the buffer. */ + memcpy ((char *) *data, p->payload, amount); + } + *data_len = amount; + pbuf_free (p); + + pthread_mutex_unlock (&tunif->lock); + + return 0; +} + +/* Write data to an IO object. If offset is -1, write at the object + maintained file pointer. If the object is not seekable, offset is + ignored. The amount successfully written is returned in amount. A + given user should not have more than one outstanding io_write on an + object at a time; servers implement congestion control by delaying + responses to io_write. Servers may drop data (returning ENOBUFS) + if they receive more than one write when not prepared for it. */ +error_t +trivfs_S_io_write (struct trivfs_protid * cred, + mach_port_t reply, + mach_msg_type_name_t replytype, + char *data, + mach_msg_type_number_t datalen, + off_t offset, mach_msg_type_number_t * amount) +{ + struct netif *netif; + struct pbuf *p, *q; + uint16_t off; + + /* Deny access if they have bad credentials. */ + if (!cred) + return EOPNOTSUPP; + + else if (!(cred->po->openmodes & O_WRITE)) + return EBADF; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + netif = (struct netif *) cred->po->cntl->hook; + + /* Allocate an empty pbuf chain for the data */ + p = pbuf_alloc (PBUF_RAW, datalen, PBUF_POOL); + + if (p) + { + /* Iterate to fill the pbuf chain. */ + q = p; + off = 0; + do + { + memcpy (q->payload, data, q->len); + + off += q->len; + + if (q->tot_len == q->len) + break; + else + q = q->next; + } + while (1); + + /* pass it to the stack */ + if (netif->input (p, netif) != ERR_OK) + { + LWIP_DEBUGF (NETIF_DEBUG, ("trivfs_S_io_write: IP input error\n")); + pbuf_free (p); + p = NULL; + } + + *amount = datalen; + } + + return 0; +} + +/* Tell how much data can be read from the object without blocking for + a "long time" (this should be the same meaning of "long time" used + by the nonblocking flag. */ +kern_return_t +trivfs_S_io_readable (struct trivfs_protid * cred, + mach_port_t reply, mach_msg_type_name_t replytype, + mach_msg_type_number_t * amount) +{ + struct hurdtunif *tunif; + + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + tunif = + (struct hurdtunif *) + netif_get_state (((struct netif *) cred->po->cntl->hook)); + + pthread_mutex_lock (&tunif->lock); + + if (tunif->queue.head) + *amount = tunif->queue.head->tot_len; + else + *amount = 0; + + pthread_mutex_unlock (&tunif->lock); + + return 0; +} + +/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG. + Block until one of the indicated types of i/o can be done "quickly", and + return the types that are then available. ID_TAG is returned as passed; it + is just for the convenience of the user in matching up reply messages with + specific requests sent. */ +static error_t +io_select_common (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + struct timespec *tsp, int *type) +{ + error_t err; + struct hurdtunif *tunif; + + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + /* Make this thread cancellable */ + ports_interrupt_self_on_port_death (cred, reply); + + /* We only deal with SELECT_READ and SELECT_WRITE here. */ + *type &= SELECT_READ | SELECT_WRITE; + + if (*type == 0) + return 0; + + tunif = + (struct hurdtunif *) + netif_get_state (((struct netif *) cred->po->cntl->hook)); + + pthread_mutex_lock (&tunif->lock); + + if (*type & SELECT_WRITE) + { + /* We are always writable. */ + if (tunif->queue.len == 0) + *type &= ~SELECT_READ; + pthread_mutex_unlock (&tunif->lock); + return 0; + } + + while (1) + { + /* There's data on the queue */ + if (tunif->queue.len != 0) + { + *type = SELECT_READ; + pthread_mutex_unlock (&tunif->lock); + return 0; + } + + /* The queue is empty, we must wait */ + tunif->read_blocked = 1; + err = + pthread_hurd_cond_timedwait_np (&tunif->select, &tunif->lock, tsp); + if (err) + { + *type = 0; + pthread_mutex_unlock (&tunif->lock); + + if (err == ETIMEDOUT) + err = 0; + + return err; + } + } +} + +error_t +trivfs_S_io_select (struct trivfs_protid * cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, int *type) +{ + return io_select_common (cred, reply, reply_type, NULL, type); +} + +error_t +trivfs_S_io_select_timeout (struct trivfs_protid * cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + struct timespec ts, int *type) +{ + return io_select_common (cred, reply, reply_type, &ts, type); +} + +/* Change current read/write offset */ +error_t +trivfs_S_io_seek (struct trivfs_protid * cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + off_t offs, int whence, off_t * new_offs) +{ + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + return ESPIPE; +} + +/* Change the size of the file. If the size increases, new blocks are + zero-filled. After successful return, it is safe to reference mapped + areas of the file up to NEW_SIZE. */ +error_t +trivfs_S_file_set_size (struct trivfs_protid * cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + off_t size) +{ + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + return size == 0 ? 0 : EINVAL; +} + +/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and + O_NONBLOCK bits for the IO object. In addition, io_get_openmodes + will tell you which of O_READ, O_WRITE, and O_EXEC the object can + be used for. The O_ASYNC bit affects icky async I/O; good async + I/O is done through io_async which is orthogonal to these calls. */ +error_t +trivfs_S_io_set_all_openmodes (struct trivfs_protid * cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, int mode) +{ + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + return 0; +} + +error_t +trivfs_S_io_set_some_openmodes (struct trivfs_protid * cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, int bits) +{ + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + return 0; +} + +error_t +trivfs_S_io_clear_some_openmodes (struct trivfs_protid * cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, int bits) +{ + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + return 0; +} + +error_t +trivfs_S_io_get_owner (struct trivfs_protid * cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, pid_t * owner) +{ + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + *owner = 0; + return 0; +} + +error_t +trivfs_S_io_mod_owner (struct trivfs_protid * cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + pid_t owner) +{ + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + return EINVAL; +} + +/* Return objects mapping the data underlying this memory object. If + the object can be read then memobjrd will be provided; if the + object can be written then memobjwr will be provided. For objects + where read data and write data are the same, these objects will be + equal, otherwise they will be disjoint. Servers are permitted to + implement io_map but not io_map_cntl. Some objects do not provide + mapping; they will set none of the ports and return an error. Such + objects can still be accessed by io_read and io_write. */ +error_t +trivfs_S_io_map (struct trivfs_protid * cred, + mach_port_t reply, + mach_msg_type_name_t replyPoly, + memory_object_t * rdobj, + mach_msg_type_name_t * rdtype, + memory_object_t * wrobj, mach_msg_type_name_t * wrtype) +{ + if (!cred) + return EOPNOTSUPP; + + if (cred->pi.class != tunnel_class) + return EOPNOTSUPP; + + return EINVAL; +} diff --git a/lwip/port/netif/ifcommon.c b/lwip/port/netif/ifcommon.c new file mode 100644 index 00000000..11ede76d --- /dev/null +++ b/lwip/port/netif/ifcommon.c @@ -0,0 +1,121 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Joan Lledó. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* Common interface for all kinds of devices */ + +#include <netif/ifcommon.h> + +#include <net/if.h> + +#include <lwip/netifapi.h> + +/* Open the device and set the interface up */ +static error_t +if_open (struct netif *netif) +{ + error_t err = 0; + struct ifcommon *ifc = netif_get_state (netif); + + if (ifc->open) + err = ifc->open (netif); + if (!err) + { + /* Up the inerface */ + ifc->flags |= IFF_UP | IFF_RUNNING; + netifapi_netif_set_up (netif); + } + + return err; +} + +/* Close the device and set the interface down */ +static error_t +if_close (struct netif *netif) +{ + error_t err = 0; + struct ifcommon *ifc = netif_get_state (netif); + + if (ifc->close) + err = ifc->close (netif); + if (!err) + { + /* Down the inerface */ + ifc->flags &= ~(IFF_UP | IFF_RUNNING); + netifapi_netif_set_down (netif); + } + + return err; +} + +/* + * Common initialization callback for all kinds of devices. + * + * This function doesn't assume there's a device nor tries to open it. + * If a device is present, it must be opened from the ifc->init() callback. + */ +error_t +if_init (struct netif * netif) +{ + struct ifcommon *ifc = netif_get_state (netif); + + if (netif == NULL) + /* The user provided no interface */ + return -1; + + return ifc->init (netif); +} + +/* Tries to close the device and frees allocated resources */ +error_t +if_terminate (struct netif * netif) +{ + error_t err; + struct ifcommon *ifc = netif_get_state (netif); + + if (netif == NULL) + /* The user provided no interface */ + return -1; + + err = if_close (netif); + if (err) + return err; + + return ifc->terminate (netif); +} + +/* + * Change device flags. + * + * If IFF_UP changes, it opens/closes the device accordingly. + */ +error_t +if_change_flags (struct netif * netif, uint16_t flags) +{ + error_t err; + struct ifcommon *ifc = netif_get_state (netif); + uint16_t oldflags = ifc->flags; + + err = ifc->change_flags (netif, flags); + + if ((oldflags ^ flags) & IFF_UP) /* Bit is different ? */ + ((oldflags & IFF_UP) ? if_close : if_open) (netif); + + return err; +} |