/* 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 . */ /* Ethernet devices module */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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; mach_msg_type_number_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 err_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; NETIF_FOREACH(netif) 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. */ err_t hurdethif_device_init (struct netif *netif) { error_t err; mach_msg_type_number_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_IF; /* 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 (void) { 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; }