diff options
Diffstat (limited to 'pfinet/ethernet.c')
-rw-r--r-- | pfinet/ethernet.c | 304 |
1 files changed, 198 insertions, 106 deletions
diff --git a/pfinet/ethernet.c b/pfinet/ethernet.c index bb5632de..0fd76706 100644 --- a/pfinet/ethernet.c +++ b/pfinet/ethernet.c @@ -1,5 +1,7 @@ -/* - Copyright (C) 1995, 1996 Free Software Foundation, Inc. +/* + Copyright (C) 1995, 1996, 1998, 1999, 2000, 2002, 2007 + Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. This file is part of the GNU Hurd. @@ -18,28 +20,38 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ +/* Do not include glue-include/linux/errno.h */ +#define _HACK_ERRNO_H +#include "pfinet.h" + #include <device/device.h> #include <device/net_status.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> #include <netinet/in.h> #include <string.h> +#include <error.h> +#include <fcntl.h> -#include "pfinet.h" - -static char *ethername; +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/if_arp.h> -device_t ether_port; struct port_class *etherreadclass; -struct port_info *readpt; -mach_port_t readptname; -struct device ether_dev; +struct ether_device +{ + struct ether_device *next; + device_t ether_port; + struct port_info *readpt; + mach_port_t readptname; + struct device dev; +}; + +/* Linked list of all ethernet devices. */ +struct ether_device *ether_dev; struct enet_statistics retbuf; -static struct condition more_packets = CONDITION_INITIALIZER; /* Mach doesn't provide this. DAMN. */ struct enet_statistics * @@ -48,35 +60,45 @@ ethernet_get_stats (struct device *dev) return &retbuf; } -int +int ethernet_stop (struct device *dev) { return 0; } void -ethernet_set_multi (struct device *dev, int numaddrs, void *addrs) +ethernet_set_multi (struct device *dev) { - assert (numaddrs == 0); } -static short ether_filter[] = +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, - NETF_PUSHZERO | NETF_OR, + 1 }; +static int ether_filter_len = sizeof (ether_filter) / sizeof (short); + +/* The BPF instruction allows IP and ARP packets */ +static struct bpf_insn bpf_ether_filter[] = +{ + {NETF_IN|NETF_BPF, /* Header. */ 0, 0, 0}, + {40, 0, 0, 12}, + {21, 1, 0, 2054}, + {21, 0, 1, 2048}, + {6, 0, 0, 1500}, + {6, 0, 0, 0}, +}; +static int bpf_ether_filter_len = sizeof (bpf_ether_filter) / sizeof (short); -static int ether_filter_len = 3; static struct port_bucket *etherport_bucket; -void -mark_bh (int arg) -{ - condition_broadcast (&more_packets); -} -any_t +static any_t ethernet_thread (any_t arg) { ports_manage_port_operations_one_thread (etherport_bucket, @@ -92,72 +114,113 @@ ethernet_demuxer (mach_msg_header_t *inp, struct net_rcv_msg *msg = (struct net_rcv_msg *) inp; struct sk_buff *skb; int datalen; + struct ether_device *edev; + struct device *dev = 0; if (inp->msgh_id != NET_RCV_MSG_ID) return 0; - - if (inp->msgh_local_port != readptname) + + for (edev = ether_dev; edev; edev = edev->next) + if (inp->msgh_local_port == edev->readptname) + dev = &edev->dev; + + if (! dev) { if (inp->msgh_remote_port != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), inp->msgh_remote_port); return 1; } - - datalen = ETH_HLEN + + datalen = ETH_HLEN + msg->packet_type.msgt_number - sizeof (struct packet_header); - mutex_lock (&global_lock); + __mutex_lock (&net_bh_lock); skb = alloc_skb (datalen, GFP_ATOMIC); - skb->len = datalen; - skb->dev = ðer_dev; + skb_put (skb, datalen); + skb->dev = dev; /* Copy the two parts of the frame into the buffer. */ bcopy (msg->header, skb->data, ETH_HLEN); - bcopy (msg->packet + sizeof (struct packet_header), + bcopy (msg->packet + sizeof (struct packet_header), skb->data + ETH_HLEN, datalen - ETH_HLEN); /* Drop it on the queue. */ + skb->protocol = eth_type_trans (skb, dev); netif_rx (skb); - mutex_unlock (&global_lock); + __mutex_unlock (&net_bh_lock); return 1; } -any_t -input_work_thread (any_t arg) + +void +ethernet_initialize (void) { - mutex_lock (&global_lock); - for (;;) - { - condition_wait (&more_packets, &global_lock); - net_bh (0); - } + etherport_bucket = ports_create_bucket (); + etherreadclass = ports_create_class (0, 0); + + cthread_detach (cthread_fork (ethernet_thread, 0)); } int ethernet_open (struct device *dev) { - if (ether_port != MACH_PORT_NULL) - return 0; - - etherreadclass = ports_create_class (0, 0); - errno = ports_create_port (etherreadclass, etherport_bucket, - sizeof (struct port_info), &readpt); - assert_perror (errno); - readptname = ports_get_right (readpt); - mach_port_insert_right (mach_task_self (), readptname, readptname, + error_t err; + device_t master_device; + struct ether_device *edev = (struct ether_device *) dev->priv; + + assert (edev->ether_port == MACH_PORT_NULL); + + err = ports_create_port (etherreadclass, etherport_bucket, + sizeof (struct port_info), &edev->readpt); + assert_perror (err); + edev->readptname = ports_get_right (edev->readpt); + mach_port_insert_right (mach_task_self (), edev->readptname, edev->readptname, MACH_MSG_TYPE_MAKE_SEND); - mach_port_set_qlimit (mach_task_self (), readptname, MACH_PORT_QLIMIT_MAX); + mach_port_set_qlimit (mach_task_self (), edev->readptname, MACH_PORT_QLIMIT_MAX); - device_open (master_device, D_WRITE | D_READ, ethername, ðer_port); + master_device = file_name_lookup (dev->name, O_READ | O_WRITE, 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", &edev->ether_port); + mach_port_deallocate (mach_task_self (), master_device); + if (err) + error (2, err, "device_open on %s", dev->name); + + err = device_set_filter (edev->ether_port, ports_get_right (edev->readpt), + MACH_MSG_TYPE_MAKE_SEND, 0, + bpf_ether_filter, bpf_ether_filter_len); + if (err) + error (2, err, "device_set_filter on %s", dev->name); + } + 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", dev->name); + error (2, err, "and cannot get device master port"); + } + err = device_open (master_device, D_WRITE | D_READ, dev->name, &edev->ether_port); + mach_port_deallocate (mach_task_self (), master_device); + if (err) + { + error (0, file_errno, "file_name_lookup %s", dev->name); + error (2, err, "device_open(%s)", dev->name); + } + + err = device_set_filter (edev->ether_port, ports_get_right (edev->readpt), + MACH_MSG_TYPE_MAKE_SEND, 0, + ether_filter, ether_filter_len); + if (err) + error (2, err, "device_set_filter on %s", dev->name); + } - device_set_filter (ether_port, ports_get_right (readpt), - MACH_MSG_TYPE_MAKE_SEND, 0, - ether_filter, ether_filter_len); - cthread_detach (cthread_fork (ethernet_thread, 0)); - cthread_detach (cthread_fork (input_work_thread, 0)); return 0; } @@ -166,76 +229,105 @@ ethernet_open (struct device *dev) int ethernet_xmit (struct sk_buff *skb, struct device *dev) { + error_t err; + struct ether_device *edev = (struct ether_device *) dev->priv; u_int count; - int err; - - err = device_write (ether_port, D_NOWAIT, 0, skb->data, skb->len, &count); - assert (err == 0); + + err = device_write (edev->ether_port, D_NOWAIT, 0, skb->data, skb->len, &count); + assert_perror (err); assert (count == skb->len); - dev_kfree_skb (skb, FREE_WRITE); + dev_kfree_skb (skb); return 0; } +/* Set device flags (e.g. promiscuous) */ +int +ethernet_change_flags (struct device *dev, short flags) +{ + error_t err = 0; +#ifdef NET_FLAGS + int status = flags; + struct ether_device *edev = (struct ether_device *) dev->priv; + err = device_set_status (edev->ether_port, NET_FLAGS, &status, 1); + if (err == D_INVALID_OPERATION) + /* Not supported, ignore. */ + err = 0; +#endif + return err; +} + void -setup_ethernet_device (char *name) +setup_ethernet_device (char *name, struct device **device) { struct net_status netstat; - u_int count; + size_t count; int net_address[2]; - int i; - - etherport_bucket = ports_create_bucket (); + error_t err; + struct ether_device *edev; + struct device *dev; + + edev = calloc (1, sizeof (struct ether_device)); + if (!edev) + error (2, ENOMEM, "%s", name); + edev->next = ether_dev; + ether_dev = edev; + + *device = dev = &edev->dev; + + dev->name = strdup (name); + /* Functions. These ones are the true "hardware layer" in Linux. */ + dev->open = 0; /* We set up before calling dev_open. */ + dev->stop = ethernet_stop; + dev->hard_start_xmit = ethernet_xmit; + dev->get_stats = ethernet_get_stats; + dev->set_multicast_list = ethernet_set_multi; + + /* These are the ones set by drivers/net/net_init.c::ether_setup. */ + dev->hard_header = eth_header; + dev->rebuild_header = eth_rebuild_header; + dev->hard_header_cache = eth_header_cache; + dev->header_cache_update = eth_header_cache_update; + dev->hard_header_parse = eth_header_parse; + /* We can't do these two (and we never try anyway). */ + /* dev->change_mtu = eth_change_mtu; */ + /* dev->set_mac_address = eth_mac_addr; */ - ethername = name; - - /* Interface buffers. */ - ether_dev.name = ethername; - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init (ðer_dev.buffs[i]); - - /* Functions */ - ether_dev.open = ethernet_open; - ether_dev.stop = ethernet_stop; - ether_dev.hard_start_xmit = ethernet_xmit; - ether_dev.hard_header = eth_header; - ether_dev.rebuild_header = eth_rebuild_header; - ether_dev.type_trans = eth_type_trans; - ether_dev.get_stats = ethernet_get_stats; - ether_dev.set_multicast_list = ethernet_set_multi; - /* Some more fields */ - ether_dev.type = ARPHRD_ETHER; - ether_dev.hard_header_len = sizeof (struct ethhdr); - ether_dev.addr_len = ETH_ALEN; - for (i = 0; i < 6; i++) - ether_dev.broadcast[i] = 0xff; - ether_dev.flags = IFF_BROADCAST | IFF_MULTICAST; - ether_dev.family = AF_INET; /* hmm. */ - ether_dev.pa_addr = ether_dev.pa_brdaddr = ether_dev.pa_mask = 0; - ether_dev.pa_alen = sizeof (unsigned long); - - ethernet_open (ðer_dev); + dev->priv = edev; /* For reverse lookup. */ + dev->type = ARPHRD_ETHER; + dev->hard_header_len = ETH_HLEN; + dev->addr_len = ETH_ALEN; + memset (dev->broadcast, 0xff, ETH_ALEN); + dev->flags = IFF_BROADCAST | IFF_MULTICAST; + dev_init_buffers (dev); + + ethernet_open (dev); /* Fetch hardware information */ count = NET_STATUS_COUNT; - device_get_status (ether_port, NET_STATUS, (dev_status_t) &netstat, &count); - ether_dev.mtu = netstat.max_packet_size - ether_dev.hard_header_len; + err = device_get_status (edev->ether_port, NET_STATUS, + (dev_status_t) &netstat, &count); + if (err) + error (2, err, "%s: Cannot get device status", name); + dev->mtu = netstat.max_packet_size - dev->hard_header_len; assert (netstat.header_format == HDR_ETHERNET); assert (netstat.header_size == ETH_HLEN); assert (netstat.address_size == ETH_ALEN); count = 2; assert (count * sizeof (int) >= ETH_ALEN); - device_get_status (ether_port, NET_ADDRESS, net_address, &count); + err = device_get_status (edev->ether_port, NET_ADDRESS, net_address, &count); + if (err) + error (2, err, "%s: Cannot get hardware Ethernet address", name); net_address[0] = ntohl (net_address[0]); net_address[1] = ntohl (net_address[1]); - bcopy (net_address, ether_dev.dev_addr, ETH_ALEN); + bcopy (net_address, dev->dev_addr, ETH_ALEN); - /* That should be enough. */ + /* That should be enough. */ - ether_dev.next = dev_base; - dev_base = ðer_dev; + /* This call adds the device to the `dev_base' chain, + initializes its `ifindex' member (which matters!), + and tells the protocol stacks about the device. */ + err = - register_netdevice (dev); + assert_perror (err); } - - - |