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/netif/hurdethif.c | |
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/netif/hurdethif.c')
-rw-r--r-- | lwip/port/netif/hurdethif.c | 573 |
1 files changed, 573 insertions, 0 deletions
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; +} |