summaryrefslogtreecommitdiff
path: root/eth-multiplexer/vdev.c
diff options
context:
space:
mode:
authorZheng Da <zhengda1936@gmail.com>2016-11-02 18:06:53 +0100
committerJustus Winter <justus@gnupg.org>2016-11-04 15:40:53 +0100
commit25d614aa11e52370c200e677099746a619ea76db (patch)
tree46bbcbf80c1264131264b6d3ab334d856a17ffdc /eth-multiplexer/vdev.c
parent50e14fce11f2bebb4faad220f8f610a55f4110c5 (diff)
eth-multiplexer: Merge the eth-multiplexer.
* Makefile (prog-subdirs): Add the new program. * NEWS: Update. * eth-multiplexer/ChangeLog: New file. * eth-multiplexer/Makefile: Likewise. * eth-multiplexer/README: Likewise. * eth-multiplexer/demuxer.c: Likewise. * eth-multiplexer/dev_stat.c: Likewise. * eth-multiplexer/device_impl.c: Likewise. * eth-multiplexer/ethernet.c: Likewise. * eth-multiplexer/ethernet.h: Likewise. * eth-multiplexer/mig-decls.h: Likewise. * eth-multiplexer/mig-mutate.h: Likewise. * eth-multiplexer/multiplexer.c: Likewise. * eth-multiplexer/netfs_impl.c: Likewise. * eth-multiplexer/netfs_impl.h: Likewise. * eth-multiplexer/notify_impl.c: Likewise. * eth-multiplexer/test.c: Likewise. * eth-multiplexer/util.h: Likewise. * eth-multiplexer/vdev.c: Likewise. * eth-multiplexer/vdev.h: Likewise. The eth-multiplexer has been written by Zheng Da. This merges his work into the main repository.
Diffstat (limited to 'eth-multiplexer/vdev.c')
-rw-r--r--eth-multiplexer/vdev.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/eth-multiplexer/vdev.c b/eth-multiplexer/vdev.c
new file mode 100644
index 00000000..7620cd0f
--- /dev/null
+++ b/eth-multiplexer/vdev.c
@@ -0,0 +1,309 @@
+/*
+ Copyright (C) 2008 Free Software Foundation, Inc.
+ Written by Zheng Da.
+
+ 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 this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* This file implement the virtual network interface */
+
+#include <string.h>
+#include <stdio.h>
+#include <netinet/ip.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <error.h>
+
+#include <pthread.h>
+
+#include "vdev.h"
+#include "queue.h"
+#include "bpf_impl.h"
+#include "util.h"
+
+#define ETH_HLEN sizeof (struct ethhdr)
+
+static struct vether_device *dev_head;
+static int dev_num;
+
+/* This lock is only used to protected the virtual device list.
+ * TODO every device structure should has its own lock to protect itself. */
+static pthread_mutex_t dev_list_lock = PTHREAD_MUTEX_INITIALIZER;
+
+mach_msg_type_t header_type =
+{
+ MACH_MSG_TYPE_BYTE,
+ 8,
+ NET_HDW_HDR_MAX,
+ TRUE,
+ FALSE,
+ FALSE,
+ 0
+};
+
+mach_msg_type_t packet_type =
+{
+ MACH_MSG_TYPE_BYTE, /* name */
+ 8, /* size */
+ 0, /* number */
+ TRUE, /* inline */
+ FALSE, /* longform */
+ FALSE /* deallocate */
+};
+
+int
+get_dev_num ()
+{
+ return dev_num;
+}
+
+struct vether_device *
+lookup_dev_by_name (char *name)
+{
+ struct vether_device *vdev;
+ pthread_mutex_lock (&dev_list_lock);
+ for (vdev = dev_head; vdev; vdev = vdev->next)
+ {
+ if (strncmp (vdev->name, name, IFNAMSIZ) == 0)
+ break;
+ }
+ pthread_mutex_unlock (&dev_list_lock);
+ return vdev;
+}
+
+int
+foreach_dev_do (int (func) (struct vether_device *))
+{
+ struct vether_device *vdev;
+ int rval = 0;
+ pthread_mutex_lock (&dev_list_lock);
+ for (vdev = dev_head; vdev; vdev = vdev->next)
+ {
+ pthread_mutex_unlock (&dev_list_lock);
+ /* func() can stop the loop by returning <> 0 */
+ rval = func (vdev);
+ pthread_mutex_lock (&dev_list_lock);
+ if (rval)
+ break;
+ }
+ pthread_mutex_unlock (&dev_list_lock);
+ return rval;
+}
+
+/* Remove all filters with the dead name. */
+int
+remove_dead_port_from_dev (mach_port_t dead_port)
+{
+ struct vether_device *vdev;
+ pthread_mutex_lock (&dev_list_lock);
+ for (vdev = dev_head; vdev; vdev = vdev->next)
+ {
+ remove_dead_filter (&vdev->port_list,
+ &vdev->port_list.if_rcv_port_list, dead_port);
+ remove_dead_filter (&vdev->port_list,
+ &vdev->port_list.if_snd_port_list, dead_port);
+ }
+ pthread_mutex_unlock (&dev_list_lock);
+ return 0;
+}
+
+/* Add a new virtual interface to the multiplexer. */
+struct vether_device *
+add_vdev (char *name, int size,
+ struct port_class *class, struct port_bucket *bucket)
+{
+ error_t err;
+ struct vether_device *vdev;
+
+ if (size < sizeof (*vdev))
+ size = sizeof (*vdev);
+ err = ports_create_port (class, bucket, size, &vdev);
+ if (err)
+ return NULL;
+
+ vdev->dev_port = ports_get_right (vdev);
+ ports_port_deref (vdev);
+ strncpy (vdev->name, name, IFNAMSIZ);
+ vdev->if_header_size = ETH_HLEN;
+ vdev->if_mtu = ETH_MTU;
+ vdev->if_header_format = HDR_ETHERNET;
+ vdev->if_address_size = ETH_ALEN;
+ vdev->if_flags = 0;
+ vdev->if_address[0] = 0x52;
+ vdev->if_address[1] = 0x54;
+ *(int *)(vdev->if_address + 2) = random ();
+
+ queue_init (&vdev->port_list.if_rcv_port_list);
+ queue_init (&vdev->port_list.if_snd_port_list);
+
+ pthread_mutex_lock (&dev_list_lock);
+ vdev->next = dev_head;
+ dev_head = vdev;
+ vdev->pprev = &dev_head;
+ if (vdev->next)
+ vdev->next->pprev = &vdev->next;
+ dev_num++;
+ pthread_mutex_unlock (&dev_list_lock);
+
+ debug ("initialize the virtual device\n");
+ return vdev;
+}
+
+void
+destroy_vdev (void *port)
+{
+ struct vether_device *vdev = (struct vether_device *)port;
+
+ debug ("device %s is going to be destroyed\n", vdev->name);
+ /* Delete it from the virtual device list */
+ pthread_mutex_lock (&dev_list_lock);
+ *vdev->pprev = vdev->next;
+ if (vdev->next)
+ vdev->next->pprev = vdev->pprev;
+ dev_num--;
+ pthread_mutex_unlock (&dev_list_lock);
+
+ /* TODO Delete all filters in the interface,
+ * there shouldn't be any filters left */
+ destroy_filters (&vdev->port_list);
+}
+
+/* Test if there are devices existing in the list */
+int
+has_vdev ()
+{
+ return dev_head != NULL;
+}
+
+/* Broadcast the packet to all virtual interfaces
+ * except the one the packet is from */
+int
+broadcast_pack (char *data, int datalen, struct vether_device *from_vdev)
+{
+ int internal_deliver_pack (struct vether_device *vdev)
+ {
+ if (from_vdev == vdev)
+ return 0;
+ return deliver_pack (data, datalen, vdev);
+ }
+
+ return foreach_dev_do (internal_deliver_pack);
+}
+
+/* Create a message, and deliver it. */
+int
+deliver_pack (char *data, int datalen, struct vether_device *vdev)
+{
+ struct net_rcv_msg msg;
+ int pack_size;
+ struct ethhdr *header;
+ struct packet_header *packet;
+
+ pack_size = datalen - sizeof (struct ethhdr);
+ /* remember message sizes must be rounded up */
+ msg.msg_hdr.msgh_size = (((mach_msg_size_t) (sizeof(struct net_rcv_msg)
+ - NET_RCV_MAX + pack_size)) + 3) & ~3;
+
+ header = (struct ethhdr *) msg.header;
+ packet = (struct packet_header *) msg.packet;
+ msg.header_type = header_type;
+ memcpy (header, data, sizeof (struct ethhdr));
+ msg.packet_type = packet_type;
+ memcpy (packet + 1, data + sizeof (struct ethhdr), pack_size);
+ packet->type = header->h_proto;
+ packet->length = pack_size + sizeof (struct packet_header);
+ msg.packet_type.msgt_number = packet->length;
+
+ return deliver_msg (&msg, vdev);
+}
+
+/* Broadcast the message to all virtual interfaces. */
+int
+broadcast_msg (struct net_rcv_msg *msg)
+{
+ int rval = 0;
+ mach_msg_header_t header;
+
+ int internal_deliver_msg (struct vether_device *vdev)
+ {
+ return deliver_msg (msg, vdev);
+ }
+
+ /* Save the message header because deliver_msg will change it. */
+ header = msg->msg_hdr;
+ rval = foreach_dev_do (internal_deliver_msg);
+ msg->msg_hdr = header;
+ return rval;
+}
+
+/*
+ * Deliver the message to all right pfinet servers that
+ * connects to the virtual network interface.
+ */
+int
+deliver_msg(struct net_rcv_msg *msg, struct vether_device *vdev)
+{
+ mach_msg_return_t err;
+ queue_head_t *if_port_list;
+ net_rcv_port_t infp, nextfp;
+
+ msg->msg_hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0);
+ /* remember message sizes must be rounded up */
+ msg->msg_hdr.msgh_local_port = MACH_PORT_NULL;
+ msg->msg_hdr.msgh_kind = MACH_MSGH_KIND_NORMAL;
+ msg->msg_hdr.msgh_id = NET_RCV_MSG_ID;
+
+ if_port_list = &vdev->port_list.if_rcv_port_list;
+ FILTER_ITERATE (if_port_list, infp, nextfp, &infp->input)
+ {
+ mach_port_t dest;
+ net_hash_entry_t entp, *hash_headp;
+ int ret_count;
+
+ entp = (net_hash_entry_t) 0;
+ ret_count = bpf_do_filter (infp,
+ msg->packet + sizeof (struct packet_header),
+ msg->net_rcv_msg_packet_count, msg->header,
+ sizeof (struct ethhdr), &hash_headp, &entp);
+ if (entp == (net_hash_entry_t) 0)
+ dest = infp->rcv_port;
+ else
+ dest = entp->rcv_port;
+
+ if (ret_count)
+ {
+ debug ("before delivering the packet\n");
+ msg->msg_hdr.msgh_remote_port = dest;
+ err = mach_msg ((mach_msg_header_t *)msg,
+ MACH_SEND_MSG|MACH_SEND_TIMEOUT,
+ msg->msg_hdr.msgh_size, 0, MACH_PORT_NULL,
+ 0, MACH_PORT_NULL);
+ if (err != MACH_MSG_SUCCESS)
+ {
+ mach_port_deallocate(mach_task_self (),
+ ((mach_msg_header_t *)msg)->msgh_remote_port);
+ error (0, err, "mach_msg");
+ return -1;
+ }
+ debug ("after delivering the packet\n");
+ }
+ }
+ FILTER_ITERATE_END
+
+ return 0;
+}
+