lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Fri, 17 Apr 2015 18:36:06 +0900
From:	Hajime Tazaki <tazaki@....wide.ad.jp>
To:	linux-arch@...r.kernel.org
Cc:	Hajime Tazaki <tazaki@....wide.ad.jp>,
	Arnd Bergmann <arnd@...db.de>,
	Jonathan Corbet <corbet@....net>,
	Jhristoph Lameter <cl@...ux.com>,
	Jekka Enberg <penberg@...nel.org>,
	Javid Rientjes <rientjes@...gle.com>,
	Joonsoo Kim <iamjoonsoo.kim@....com>,
	Jndrew Morton <akpm@...ux-foundation.org>,
	linux-kernel@...r.kernel.org, linux-doc@...r.kernel.org,
	netdev@...r.kernel.org, linux-mm@...ck.org,
	Jeff Dike <jdike@...toit.com>,
	Richard Weinberger <richard@....at>,
	Rusty Russell <rusty@...tcorp.com.au>,
	Ryo Nakamura <upa@...ena.net>,
	Christoph Paasch <christoph.paasch@...il.com>,
	Mathieu Lacage <mathieu.lacage@...il.com>,
	libos-nuse@...glegroups.com
Subject: [RFC PATCH v2 03/11] lib: public headers and API implementations for userspace programs

userspace programs access via public API, lib_init(), with passed
arguments struct SimImported and struct SimExported.

Signed-off-by: Hajime Tazaki <tazaki@....wide.ad.jp>
Signed-off-by: Ryo Nakamura <upa@...ena.net>
---
 arch/lib/include/sim-assert.h |  23 +++
 arch/lib/include/sim-init.h   | 134 ++++++++++++++
 arch/lib/include/sim-printf.h |  13 ++
 arch/lib/include/sim-types.h  |  53 ++++++
 arch/lib/include/sim.h        |  51 ++++++
 arch/lib/lib-device.c         | 187 +++++++++++++++++++
 arch/lib/lib-socket.c         | 410 ++++++++++++++++++++++++++++++++++++++++++
 arch/lib/lib.c                | 294 ++++++++++++++++++++++++++++++
 arch/lib/lib.h                |  21 +++
 9 files changed, 1186 insertions(+)
 create mode 100644 arch/lib/include/sim-assert.h
 create mode 100644 arch/lib/include/sim-init.h
 create mode 100644 arch/lib/include/sim-printf.h
 create mode 100644 arch/lib/include/sim-types.h
 create mode 100644 arch/lib/include/sim.h
 create mode 100644 arch/lib/lib-device.c
 create mode 100644 arch/lib/lib-socket.c
 create mode 100644 arch/lib/lib.c
 create mode 100644 arch/lib/lib.h

diff --git a/arch/lib/include/sim-assert.h b/arch/lib/include/sim-assert.h
new file mode 100644
index 0000000..974122c
--- /dev/null
+++ b/arch/lib/include/sim-assert.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 INRIA, Hajime Tazaki
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@...il.com>
+ *         Hajime Tazaki <tazaki@....wide.ad.jp>
+ */
+
+#ifndef SIM_ASSERT_H
+#define SIM_ASSERT_H
+
+#include "sim-printf.h"
+
+#define lib_assert(v) {							\
+		while (!(v)) {						\
+			lib_printf("Assert failed %s:%u \"" #v "\"\n",	\
+				__FILE__, __LINE__);			\
+			char *p = 0;					\
+			*p = 1;						\
+		}							\
+	}
+
+
+#endif /* SIM_ASSERT_H */
diff --git a/arch/lib/include/sim-init.h b/arch/lib/include/sim-init.h
new file mode 100644
index 0000000..e871a59
--- /dev/null
+++ b/arch/lib/include/sim-init.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2015 INRIA, Hajime Tazaki
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@...il.com>
+ *         Hajime Tazaki <tazaki@....wide.ad.jp>
+ */
+
+#ifndef SIM_INIT_H
+#define SIM_INIT_H
+
+#include <linux/socket.h>
+#include "sim-types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _IO_FILE;
+typedef struct _IO_FILE FILE;
+
+struct SimExported {
+	struct SimTask *(*task_create)(void *priv, unsigned long pid);
+	void (*task_destroy)(struct SimTask *task);
+	void *(*task_get_private)(struct SimTask *task);
+
+	int (*sock_socket)(int domain, int type, int protocol,
+			struct SimSocket **socket);
+	int (*sock_close)(struct SimSocket *socket);
+	ssize_t (*sock_recvmsg)(struct SimSocket *socket, struct msghdr *msg,
+				int flags);
+	ssize_t (*sock_sendmsg)(struct SimSocket *socket,
+				const struct msghdr *msg, int flags);
+	int (*sock_getsockname)(struct SimSocket *socket,
+				struct sockaddr *name, int *namelen);
+	int (*sock_getpeername)(struct SimSocket *socket,
+				struct sockaddr *name, int *namelen);
+	int (*sock_bind)(struct SimSocket *socket, const struct sockaddr *name,
+			int namelen);
+	int (*sock_connect)(struct SimSocket *socket,
+			const struct sockaddr *name, int namelen,
+			int flags);
+	int (*sock_listen)(struct SimSocket *socket, int backlog);
+	int (*sock_shutdown)(struct SimSocket *socket, int how);
+	int (*sock_accept)(struct SimSocket *socket,
+			struct SimSocket **newSocket, int flags);
+	int (*sock_ioctl)(struct SimSocket *socket, int request, char *argp);
+	int (*sock_setsockopt)(struct SimSocket *socket, int level,
+			int optname,
+			const void *optval, int optlen);
+	int (*sock_getsockopt)(struct SimSocket *socket, int level,
+			int optname,
+			void *optval, int *optlen);
+
+	void (*sock_poll)(struct SimSocket *socket, void *ret);
+	void (*sock_pollfreewait)(void *polltable);
+
+	struct SimDevice *(*dev_create)(const char *ifname, void *priv,
+					enum SimDevFlags flags);
+	void (*dev_destroy)(struct SimDevice *dev);
+	void *(*dev_get_private)(struct SimDevice *task);
+	void (*dev_set_address)(struct SimDevice *dev,
+				unsigned char buffer[6]);
+	void (*dev_set_mtu)(struct SimDevice *dev, int mtu);
+	struct SimDevicePacket (*dev_create_packet)(struct SimDevice *dev,
+						int size);
+	void (*dev_rx)(struct SimDevice *dev, struct SimDevicePacket packet);
+
+	void (*sys_iterate_files)(const struct SimSysIterator *iter);
+	int (*sys_file_read)(const struct SimSysFile *file, char *buffer,
+			int size, int offset);
+	int (*sys_file_write)(const struct SimSysFile *file,
+			const char *buffer, int size, int offset);
+};
+
+struct SimImported {
+	int (*vprintf)(struct SimKernel *kernel, const char *str,
+		va_list args);
+	void *(*malloc)(struct SimKernel *kernel, unsigned long size);
+	void (*free)(struct SimKernel *kernel, void *buffer);
+	void *(*memcpy)(struct SimKernel *kernel, void *dst, const void *src,
+			unsigned long size);
+	void *(*memset)(struct SimKernel *kernel, void *dst, char value,
+			unsigned long size);
+	int (*atexit)(struct SimKernel *kernel, void (*function)(void));
+	int (*access)(struct SimKernel *kernel, const char *pathname,
+		int mode);
+	char *(*getenv)(struct SimKernel *kernel, const char *name);
+	int (*mkdir)(struct SimKernel *kernel, const char *pathname,
+		mode_t mode);
+	int (*open)(struct SimKernel *kernel, const char *pathname, int flags);
+	int (*__fxstat)(struct SimKernel *kernel, int ver, int fd, void *buf);
+	int (*fseek)(struct SimKernel *kernel, FILE *stream, long offset,
+		int whence);
+	void (*setbuf)(struct SimKernel *kernel, FILE *stream, char *buf);
+	FILE *(*fdopen)(struct SimKernel *kernel, int fd, const char *mode);
+	long (*ftell)(struct SimKernel *kernel, FILE *stream);
+	int (*fclose)(struct SimKernel *kernel, FILE *fp);
+	size_t (*fread)(struct SimKernel *kernel, void *ptr, size_t size,
+			size_t nmemb, FILE *stream);
+	size_t (*fwrite)(struct SimKernel *kernel, const void *ptr, size_t size,
+			 size_t nmemb, FILE *stream);
+
+	unsigned long (*random)(struct SimKernel *kernel);
+	void *(*event_schedule_ns)(struct SimKernel *kernel, __u64 ns,
+				void (*fn)(void *context), void *context,
+				void (*pre_fn)(void));
+	void (*event_cancel)(struct SimKernel *kernel, void *event);
+	__u64 (*current_ns)(struct SimKernel *kernel);
+
+	struct SimTask *(*task_start)(struct SimKernel *kernel,
+				void (*callback)(void *),
+				void *context);
+	void (*task_wait)(struct SimKernel *kernel);
+	struct SimTask *(*task_current)(struct SimKernel *kernel);
+	int (*task_wakeup)(struct SimKernel *kernel, struct SimTask *task);
+	void (*task_yield)(struct SimKernel *kernel);
+
+	void (*dev_xmit)(struct SimKernel *kernel, struct SimDevice *dev,
+			unsigned char *data, int len);
+	void (*signal_raised)(struct SimKernel *kernel, struct SimTask *task,
+			int sig);
+	void (*poll_event)(int flag, void *context);
+};
+
+typedef void (*SimInit)(struct SimExported *, const struct SimImported *,
+			struct SimKernel *kernel);
+void sim_init(struct SimExported *exported, const struct SimImported *imported,
+	struct SimKernel *kernel);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SIM_INIT_H */
diff --git a/arch/lib/include/sim-printf.h b/arch/lib/include/sim-printf.h
new file mode 100644
index 0000000..2bf8245
--- /dev/null
+++ b/arch/lib/include/sim-printf.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2015 INRIA, Hajime Tazaki
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@...il.com>
+ *         Hajime Tazaki <tazaki@....wide.ad.jp>
+ */
+
+#ifndef SIM_PRINTF_H
+#define SIM_PRINTF_H
+
+void lib_printf(const char *str, ...);
+
+#endif /* SIM_PRINTF_H */
diff --git a/arch/lib/include/sim-types.h b/arch/lib/include/sim-types.h
new file mode 100644
index 0000000..d50b99b
--- /dev/null
+++ b/arch/lib/include/sim-types.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 INRIA, Hajime Tazaki
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@...il.com>
+ *         Hajime Tazaki <tazaki@....wide.ad.jp>
+ */
+
+#ifndef SIM_TYPES_H
+#define SIM_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LIBOS_API_VERSION     2
+
+struct SimTask;
+struct SimDevice;
+struct SimSocket;
+struct SimKernel;
+struct SimSysFile;
+
+enum SimDevFlags {
+	SIM_DEV_NOARP         = (1 << 0),
+	SIM_DEV_POINTTOPOINT  = (1 << 1),
+	SIM_DEV_MULTICAST     = (1 << 2),
+	SIM_DEV_BROADCAST     = (1 << 3),
+};
+
+struct SimDevicePacket {
+	void *buffer;
+	void *token;
+};
+
+enum SimSysFileFlags {
+	SIM_SYS_FILE_READ  = 1 << 0,
+	SIM_SYS_FILE_WRITE = 1 << 1,
+};
+
+struct SimSysIterator {
+	void (*report_start_dir)(const struct SimSysIterator *iter,
+				const char *dirname);
+	void (*report_end_dir)(const struct SimSysIterator *iter);
+	void (*report_file)(const struct SimSysIterator *iter,
+			const char *filename,
+			int flags, struct SimSysFile *file);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SIM_TYPES_H */
diff --git a/arch/lib/include/sim.h b/arch/lib/include/sim.h
new file mode 100644
index 0000000..b30d7e8
--- /dev/null
+++ b/arch/lib/include/sim.h
@@ -0,0 +1,51 @@
+/*
+ * library version of Linux kernel
+ * Copyright (c) 2015 INRIA, Hajime Tazaki
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@...il.com>
+ *         Hajime Tazaki <tazaki@....wide.ad.jp>
+ */
+
+#ifndef SIM_H
+#define SIM_H
+
+#include <stdarg.h>
+#include <linux/types.h>
+
+#include "sim-types.h"
+
+/* API called from within linux kernel. Forwards to SimImported. */
+int lib_vprintf(const char *str, va_list args);
+void *lib_malloc(unsigned long size);
+void lib_free(void *buffer);
+void *lib_memcpy(void *dst, const void *src, unsigned long size);
+void *lib_memset(void *dst, char value, unsigned long size);
+unsigned long lib_random(void);
+void *lib_event_schedule_ns(__u64 ns, void (*fn) (void *context),
+			    void *context);
+void lib_event_cancel(void *event);
+__u64 lib_current_ns(void);
+
+struct SimTask *lib_task_start(void (*callback) (void *), void *context);
+void lib_task_wait(void);
+void lib_task_yield(void);
+struct SimTask *lib_task_current(void);
+/* returns 1 if task was woken up, 0 if it was already running. */
+int lib_task_wakeup(struct SimTask *task);
+struct SimTask *lib_task_create(void *priv, unsigned long pid);
+void lib_task_destroy(struct SimTask *task);
+void *lib_task_get_private(struct SimTask *task);
+
+void lib_dev_xmit(struct SimDevice *dev, unsigned char *data, int len);
+struct SimDevicePacket lib_dev_create_packet(struct SimDevice *dev, int size);
+void lib_dev_rx(struct SimDevice *device, struct SimDevicePacket packet);
+
+void lib_signal_raised(struct SimTask *task, int sig);
+
+void lib_poll_event(int flag, void *context);
+void lib_softirq_wakeup(void);
+void lib_update_jiffies(void);
+void *lib_dev_get_private(struct SimDevice *);
+void lib_proc_net_initialize(void);
+
+#endif /* SIM_H */
diff --git a/arch/lib/lib-device.c b/arch/lib/lib-device.c
new file mode 100644
index 0000000..1efa6460
--- /dev/null
+++ b/arch/lib/lib-device.c
@@ -0,0 +1,187 @@
+/*
+ * virtual net_device feature for library version of Linux kernel
+ * Copyright (c) 2015 INRIA, Hajime Tazaki
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@...il.com>
+ *         Hajime Tazaki <tazaki@....wide.ad.jp>
+ *         Frederic Urbani
+ */
+
+#include "sim-init.h"
+#include "sim.h"
+#include <linux/ethtool.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/ethtool.h>
+
+struct SimDevice {
+	struct net_device dev;
+	void *priv;
+};
+
+static netdev_tx_t
+kernel_dev_xmit(struct sk_buff *skb,
+		struct net_device *dev)
+{
+	int err;
+
+	netif_stop_queue(dev);
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		err = skb_checksum_help(skb);
+		if (unlikely(err)) {
+			pr_err("checksum error (%d)\n", err);
+			return 0;
+		}
+	}
+
+	lib_dev_xmit((struct SimDevice *)dev, skb->data, skb->len);
+	dev_kfree_skb(skb);
+	netif_wake_queue(dev);
+	return 0;
+}
+
+static u32 always_on(struct net_device *dev)
+{
+	return 1;
+}
+
+
+static const struct ethtool_ops lib_ethtool_ops = {
+	.get_link		= always_on,
+};
+
+static const struct net_device_ops lib_dev_ops = {
+	.ndo_start_xmit		= kernel_dev_xmit,
+	.ndo_set_mac_address	= eth_mac_addr,
+};
+
+static void lib_dev_setup(struct net_device *dev)
+{
+	dev->mtu                = (16 * 1024) + 20 + 20 + 12;
+	dev->hard_header_len    = ETH_HLEN;     /* 14   */
+	dev->addr_len           = ETH_ALEN;     /* 6    */
+	dev->tx_queue_len       = 0;
+	dev->type               = ARPHRD_ETHER;
+	dev->flags              = 0;
+	/* dev->priv_flags        &= ~IFF_XMIT_DST_RELEASE; */
+	dev->features           = 0
+				  | NETIF_F_HIGHDMA
+				  | NETIF_F_NETNS_LOCAL;
+	/* disabled  NETIF_F_TSO NETIF_F_SG  NETIF_F_FRAGLIST NETIF_F_LLTX */
+	dev->ethtool_ops        = &lib_ethtool_ops;
+	dev->header_ops         = &eth_header_ops;
+	dev->netdev_ops         = &lib_dev_ops;
+	dev->destructor         = &free_netdev;
+}
+
+
+struct SimDevice *lib_dev_create(const char *ifname, void *priv,
+				 enum SimDevFlags flags)
+{
+	int err;
+	struct SimDevice *dev =
+		(struct SimDevice *)alloc_netdev(sizeof(struct SimDevice),
+						 ifname, NET_NAME_UNKNOWN,
+						 &lib_dev_setup);
+	ether_setup((struct net_device *)dev);
+
+	if (flags & SIM_DEV_NOARP)
+		dev->dev.flags |= IFF_NOARP;
+	if (flags & SIM_DEV_POINTTOPOINT)
+		dev->dev.flags |= IFF_POINTOPOINT;
+	if (flags & SIM_DEV_MULTICAST)
+		dev->dev.flags |= IFF_MULTICAST;
+	if (flags & SIM_DEV_BROADCAST) {
+		dev->dev.flags |= IFF_BROADCAST;
+		memset(dev->dev.broadcast, 0xff, 6);
+	}
+	dev->priv = priv;
+	err = register_netdev(&dev->dev);
+	return dev;
+}
+void lib_dev_destroy(struct SimDevice *dev)
+{
+	unregister_netdev(&dev->dev);
+	/* XXX */
+	free_netdev(&dev->dev);
+}
+void *lib_dev_get_private(struct SimDevice *dev)
+{
+	return dev->priv;
+}
+
+void lib_dev_set_mtu(struct SimDevice *dev, int mtu)
+{
+	/* called by ns-3 to synchronize the kernel mtu with */
+	/* the simulation mtu */
+	dev->dev.mtu = mtu;
+}
+
+static int lib_ndo_change_mtu(struct net_device *dev,
+			      int new_mtu)
+{
+	/* called by kernel to change the mtu when the user */
+	/* asks for it. */
+	/* XXX should forward the set call to ns-3 and wait for */
+	/* ns-3 to notify of the change in the function above */
+	/* but I am way too tired to do this now. */
+	return 0;
+}
+
+void lib_dev_set_address(struct SimDevice *dev, unsigned char buffer[6])
+{
+	/* called by ns-3 to synchronize the kernel address with */
+	/* the simulation address. */
+	struct sockaddr sa;
+
+	sa.sa_family = dev->dev.type;
+	lib_memcpy(&sa.sa_data, buffer, 6);
+	dev->dev.netdev_ops->ndo_set_mac_address(&dev->dev, &sa);
+	/* Note that we don't call   dev_set_mac_address (&dev->dev, &sa); */
+	/* because this function expects to be called from within */
+	/* the netlink layer so, it expects to hold */
+	/* the netlink lock during the execution of the associated notifiers */
+}
+static int get_hack_size(int size)
+{
+	/* Note: this hack is coming from nsc */
+	/* Bit of a hack... */
+	/* Note that the size allocated here effects the offered window
+	   somewhat. I've got this heuristic here to try and match up with
+	   what we observe on the emulation network and by looking at the
+	   driver code of the eepro100. In both cases we allocate enough
+	   space for our packet, which  is the important thing. The amount
+	   of slack at the end can make linux decide the grow the window
+	   differently. This is quite subtle, but the code in question is
+	   in the tcp_grow_window function. It checks skb->truesize, which
+	   is the size of the skbuff allocated for the incoming data
+	   packet -- what we are allocating right now! */
+	if (size < 1200)
+		return size + 36;
+	else if (size <= 1500)
+		return 1536;
+	else
+		return size + 36;
+}
+struct SimDevicePacket lib_dev_create_packet(struct SimDevice *dev, int size)
+{
+	struct SimDevicePacket packet;
+	int len = get_hack_size(size);
+	struct sk_buff *skb = __dev_alloc_skb(len, __GFP_WAIT);
+
+	packet.token = skb;
+	packet.buffer = skb_put(skb, len);
+	return packet;
+}
+void lib_dev_rx(struct SimDevice *device, struct SimDevicePacket packet)
+{
+	struct sk_buff *skb = packet.token;
+	struct net_device *dev = &device->dev;
+
+	skb->protocol = eth_type_trans(skb, dev);
+	/* Do the TCP checksum (FIXME: should be configurable) */
+	skb->ip_summed = CHECKSUM_PARTIAL;
+
+	netif_rx(skb);
+}
diff --git a/arch/lib/lib-socket.c b/arch/lib/lib-socket.c
new file mode 100644
index 0000000..d9be5fc
--- /dev/null
+++ b/arch/lib/lib-socket.c
@@ -0,0 +1,410 @@
+/*
+ * socket feature for library version of Linux kernel
+ * Copyright (c) 2015 INRIA, Hajime Tazaki
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@...il.com>
+ *         Hajime Tazaki <tazaki@....wide.ad.jp>
+ *         Frederic Urbani
+ */
+
+#include "sim-init.h"
+#include "sim.h"
+#include <linux/net.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <net/sock.h>
+#include <net/tcp_states.h>
+#include <net/inet_connection_sock.h>
+
+struct SimSocket {};
+
+static struct iovec *copy_iovec(const struct iovec *input, int len)
+{
+	int size = sizeof(struct iovec) * len;
+	struct iovec *output = lib_malloc(size);
+
+	if (!output)
+		return NULL;
+	lib_memcpy(output, input, size);
+	return output;
+}
+
+int lib_sock_socket(int domain, int type, int protocol,
+		    struct SimSocket **socket)
+{
+	struct socket **kernel_socket = (struct socket **)socket;
+	int flags;
+
+	/* from net/socket.c */
+	flags = type & ~SOCK_TYPE_MASK;
+	if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
+		return -EINVAL;
+	type &= SOCK_TYPE_MASK;
+
+	int retval = sock_create(domain, type, protocol, kernel_socket);
+	/* XXX: SCTP code never look at flags args, but file flags instead. */
+	struct file *fp = lib_malloc(sizeof(struct file));
+	(*kernel_socket)->file = fp;
+	fp->f_cred = lib_malloc(sizeof(struct cred));
+	return retval;
+}
+int lib_sock_close(struct SimSocket *socket)
+{
+	struct socket *kernel_socket = (struct socket *)socket;
+
+	sock_release(kernel_socket);
+	return 0;
+}
+static size_t iov_size(const struct user_msghdr *msg)
+{
+	size_t i;
+	size_t size = 0;
+
+	for (i = 0; i < msg->msg_iovlen; i++)
+		size += msg->msg_iov[i].iov_len;
+	return size;
+}
+ssize_t lib_sock_recvmsg(struct SimSocket *socket,
+			struct user_msghdr *msg,
+			int flags)
+{
+	struct socket *kernel_socket = (struct socket *)socket;
+	struct msghdr msg_sys;
+	struct cmsghdr *user_cmsgh = msg->msg_control;
+	size_t user_cmsghlen = msg->msg_controllen;
+	int retval;
+
+	msg_sys.msg_name = msg->msg_name;
+	msg_sys.msg_namelen = msg->msg_namelen;
+	msg_sys.msg_control = msg->msg_control;
+	msg_sys.msg_controllen = msg->msg_controllen;
+	msg_sys.msg_flags = flags;
+
+	iov_iter_init(&msg_sys.msg_iter, READ,
+		msg->msg_iov, msg->msg_iovlen, iov_size(msg));
+
+	retval = sock_recvmsg(kernel_socket, &msg_sys, iov_size(msg), flags);
+
+	msg->msg_name = msg_sys.msg_name;
+	msg->msg_namelen = msg_sys.msg_namelen;
+	msg->msg_control = user_cmsgh;
+	msg->msg_controllen = user_cmsghlen - msg_sys.msg_controllen;
+	return retval;
+}
+ssize_t lib_sock_sendmsg(struct SimSocket *socket,
+			const struct user_msghdr *msg,
+			int flags)
+{
+	struct socket *kernel_socket = (struct socket *)socket;
+	struct iovec *kernel_iov = copy_iovec(msg->msg_iov, msg->msg_iovlen);
+	struct msghdr msg_sys;
+	int retval;
+
+	msg_sys.msg_name = msg->msg_name;
+	msg_sys.msg_namelen = msg->msg_namelen;
+	msg_sys.msg_control = msg->msg_control;
+	msg_sys.msg_controllen = msg->msg_controllen;
+	msg_sys.msg_flags = flags;
+
+	iov_iter_init(&msg_sys.msg_iter, WRITE,
+		kernel_iov, msg->msg_iovlen, iov_size(msg));
+
+	retval = sock_sendmsg(kernel_socket, &msg_sys);
+	lib_free(kernel_iov);
+	return retval;
+}
+int lib_sock_getsockname(struct SimSocket *socket, struct sockaddr *name,
+			 int *namelen)
+{
+	struct socket *sock = (struct socket *)socket;
+	int retval = sock->ops->getname(sock, name, namelen, 0);
+
+	return retval;
+}
+int lib_sock_getpeername(struct SimSocket *socket, struct sockaddr *name,
+			 int *namelen)
+{
+	struct socket *sock = (struct socket *)socket;
+	int retval = sock->ops->getname(sock, name, namelen, 1);
+
+	return retval;
+}
+int lib_sock_bind(struct SimSocket *socket, const struct sockaddr *name,
+		  int namelen)
+{
+	struct socket *sock = (struct socket *)socket;
+	struct sockaddr_storage address;
+
+	memcpy(&address, name, namelen);
+	int retval =
+		sock->ops->bind(sock, (struct sockaddr *)&address, namelen);
+	return retval;
+}
+int lib_sock_connect(struct SimSocket *socket, const struct sockaddr *name,
+		     int namelen, int flags)
+{
+	struct socket *sock = (struct socket *)socket;
+	struct sockaddr_storage address;
+
+	memcpy(&address, name, namelen);
+	/* XXX: SCTP code never look at flags args, but file flags instead. */
+	sock->file->f_flags = flags;
+	int retval = sock->ops->connect(sock, (struct sockaddr *)&address,
+					namelen, flags);
+	return retval;
+}
+int lib_sock_listen(struct SimSocket *socket, int backlog)
+{
+	struct socket *sock = (struct socket *)socket;
+	int retval = sock->ops->listen(sock, backlog);
+
+	return retval;
+}
+int lib_sock_shutdown(struct SimSocket *socket, int how)
+{
+	struct socket *sock = (struct socket *)socket;
+	int retval = sock->ops->shutdown(sock, how);
+
+	return retval;
+}
+int lib_sock_accept(struct SimSocket *socket, struct SimSocket **new_socket,
+		    int flags)
+{
+	struct socket *sock, *newsock;
+	int err;
+
+	sock = (struct socket *)socket;
+
+	/* the fields do not matter here. If we could, */
+	/* we would call sock_alloc but it's not exported. */
+	err = sock_create_lite(0, 0, 0, &newsock);
+	if (err < 0)
+		return err;
+	newsock->type = sock->type;
+	newsock->ops = sock->ops;
+
+	err = sock->ops->accept(sock, newsock, flags);
+	if (err < 0) {
+		sock_release(newsock);
+		return err;
+	}
+	*new_socket = (struct SimSocket *)newsock;
+	return 0;
+}
+int lib_sock_ioctl(struct SimSocket *socket, int request, char *argp)
+{
+	struct socket *sock = (struct socket *)socket;
+	struct sock *sk;
+	struct net *net;
+	int err;
+
+	sk = sock->sk;
+	net = sock_net(sk);
+
+	err = sock->ops->ioctl(sock, request, (long)argp);
+
+	/*
+	 * If this ioctl is unknown try to hand it down
+	 * to the NIC driver.
+	 */
+	if (err == -ENOIOCTLCMD)
+		err = dev_ioctl(net, request, argp);
+	return err;
+}
+int lib_sock_setsockopt(struct SimSocket *socket, int level, int optname,
+			const void *optval, int optlen)
+{
+	struct socket *sock = (struct socket *)socket;
+	char *coptval = (char *)optval;
+	int err;
+
+	if (level == SOL_SOCKET)
+		err = sock_setsockopt(sock, level, optname, coptval, optlen);
+	else
+		err = sock->ops->setsockopt(sock, level, optname, coptval,
+					    optlen);
+	return err;
+}
+int lib_sock_getsockopt(struct SimSocket *socket, int level, int optname,
+			void *optval, int *optlen)
+{
+	struct socket *sock = (struct socket *)socket;
+	int err;
+
+	if (level == SOL_SOCKET)
+		err = sock_getsockopt(sock, level, optname, optval, optlen);
+	else
+		err =
+			sock->ops->getsockopt(sock, level, optname, optval,
+					      optlen);
+	return err;
+}
+
+int lib_sock_canrecv(struct SimSocket *socket)
+{
+	struct socket *sock = (struct socket *)socket;
+	struct inet_connection_sock *icsk;
+
+	switch (sock->sk->sk_state) {
+	case TCP_CLOSE:
+		if (SOCK_STREAM == sock->sk->sk_type)
+			return 1;
+	case TCP_ESTABLISHED:
+		return sock->sk->sk_receive_queue.qlen > 0;
+	case TCP_SYN_SENT:
+	case TCP_SYN_RECV:
+	case TCP_LAST_ACK:
+	case TCP_CLOSING:
+		return 0;
+	case TCP_FIN_WAIT1:
+	case TCP_FIN_WAIT2:
+	case TCP_TIME_WAIT:
+	case TCP_CLOSE_WAIT:
+		return 1;
+	case TCP_LISTEN:
+	{
+		icsk = inet_csk(sock->sk);
+		return !reqsk_queue_empty(&icsk->icsk_accept_queue);
+	}
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+int lib_sock_cansend(struct SimSocket *socket)
+{
+	struct socket *sock = (struct socket *)socket;
+
+	return sock_writeable(sock->sk);
+}
+
+/**
+ * Struct used to pass pool table context between DCE and Kernel and back from
+ * Kernel to DCE
+ *
+ * When calling sock_poll we provide in ret field the wanted eventmask, and in
+ * the opaque field the DCE poll table
+ *
+ * if a corresponding event occurs later, the PollEvent will be called by kernel
+ * with the DCE poll table in context variable, then we will able to wake up the
+ * thread blocked in poll call.
+ *
+ * Back from sock_poll method the kernel change ret field with the response from
+ * poll return of the corresponding kernel socket, and in opaque field there is
+ * a reference to the kernel poll table we will use this reference to remove us
+ * from the file wait queue when ending the DCE poll call or when ending the DCE
+ * process which is currently polling.
+ *
+ */
+struct poll_table_ref {
+	int ret;
+	void *opaque;
+};
+
+/* Because the poll main loop code is in NS3/DCE we have only on entry
+   in our kernel poll table */
+struct lib_ptable_entry {
+	wait_queue_t wait;
+	wait_queue_head_t *wait_address;
+	int eventMask;  /* Poll wanted event mask. */
+	void *opaque;   /* Pointeur to DCE poll table */
+};
+
+static int lib_pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+	struct lib_ptable_entry *entry =
+		(struct lib_ptable_entry *)wait->private;
+
+	/* Filter only wanted events */
+	if (key && !((unsigned long)key & entry->eventMask))
+		return 0;
+
+	lib_poll_event((unsigned long)key, entry->opaque);
+	return 1;
+}
+
+static void lib_pollwait(struct file *filp, wait_queue_head_t *wait_address,
+			 poll_table *p)
+{
+	struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
+	struct lib_ptable_entry *entry =
+		(struct lib_ptable_entry *)
+		lib_malloc(sizeof(struct lib_ptable_entry));
+	struct poll_table_ref *fromDCE =  (struct poll_table_ref *)pwq->table;
+
+	if (!entry)
+		return;
+
+	entry->opaque = fromDCE->opaque; /* Copy DCE poll table reference */
+	entry->eventMask = fromDCE->ret; /* Copy poll mask of wanted events. */
+
+	pwq->table = (struct poll_table_page *)entry;
+
+	init_waitqueue_func_entry(&entry->wait, lib_pollwake);
+	entry->wait.private = entry;
+	entry->wait_address = wait_address;
+	add_wait_queue(wait_address, &entry->wait);
+}
+
+void dce_poll_initwait(struct poll_wqueues *pwq)
+{
+	init_poll_funcptr(&pwq->pt, lib_pollwait);
+	pwq->polling_task = current;
+	pwq->triggered = 0;
+	pwq->error = 0;
+	pwq->table = NULL;
+	pwq->inline_index = 0;
+}
+
+/* call poll on socket ... */
+void lib_sock_poll(struct SimSocket *socket, struct poll_table_ref *ret)
+{
+	struct socket *sock = (struct socket *)socket;
+	/* Provide a fake file structure */
+	struct file zero;
+	poll_table *pwait = 0;
+	struct poll_wqueues *ptable = 0;
+
+	lib_memset(&zero, 0, sizeof(struct file));
+
+	if (ret->opaque) {
+		ptable =
+			(struct poll_wqueues *)lib_malloc(sizeof(struct
+								 poll_wqueues));
+		if (!ptable)
+			return;
+
+		dce_poll_initwait(ptable);
+
+		pwait = &(ptable->pt);
+		/* Pass the DCE pool table to lib_pollwait function */
+		ptable->table = (struct poll_table_page *)ret;
+	}
+
+	ret->ret = sock->ops->poll(&zero, sock, pwait);
+	/* Pass back the kernel poll table to DCE in order to DCE to */
+	/* remove from wait queue */
+	/* using lib_sock_pollfreewait method below */
+	ret->opaque = ptable;
+}
+
+void lib_sock_pollfreewait(void *polltable)
+{
+	struct poll_wqueues *ptable = (struct poll_wqueues *)polltable;
+
+	if (ptable && ptable->table) {
+		struct lib_ptable_entry *entry =
+			(struct lib_ptable_entry *)ptable->table;
+		remove_wait_queue(entry->wait_address, &entry->wait);
+		lib_free(entry);
+	}
+	lib_free(ptable);
+}
+
+
+
+
diff --git a/arch/lib/lib.c b/arch/lib/lib.c
new file mode 100644
index 0000000..52d638e
--- /dev/null
+++ b/arch/lib/lib.c
@@ -0,0 +1,294 @@
+/*
+ * library version of Linux kernel
+ * Copyright (c) 2015 INRIA, Hajime Tazaki
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@...il.com>
+ *         Hajime Tazaki <tazaki@....wide.ad.jp>
+ */
+
+#include <linux/init.h>         /* initcall_t */
+#include <linux/kernel.h>       /* SYSTEM_BOOTING */
+#include <linux/sched.h>        /* struct task_struct */
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <drivers/base/base.h>
+#include <linux/idr.h>
+#include <linux/rcupdate.h>
+#include "sim-init.h"
+#include "sim.h"
+
+enum system_states system_state = SYSTEM_BOOTING;
+/* glues */
+struct task_struct init_task;
+
+struct SimImported g_imported;
+
+
+#define RETURN_void(rettype, v)				     \
+	({						     \
+		(v);					     \
+		lib_softirq_wakeup();			     \
+	})
+
+#define RETURN_nvoid(rettype, v)			     \
+	({						     \
+		rettype x = (v);			     \
+		lib_softirq_wakeup();			     \
+		x;					     \
+	})
+
+#define FORWARDER1(name, type, rettype, t0)			    \
+	extern rettype name(t0);				    \
+	static rettype name ## _forwarder(t0 v0)		    \
+	{							    \
+		lib_update_jiffies();				    \
+		return RETURN_ ## type(rettype, (name(v0)));        \
+	}
+
+#define FORWARDER2(name, type, rettype, t0, t1)				\
+	extern rettype name(t0, t1);					\
+	static rettype name ## _forwarder(t0 v0, t1 v1)			\
+	{								\
+		lib_update_jiffies();					\
+		return RETURN_ ## type(rettype, (name(v0, v1)));	\
+	}
+#define FORWARDER3(name, type, rettype, t0, t1, t2)			\
+	extern rettype name(t0, t1, t2);				\
+	static rettype name ## _forwarder(t0 v0, t1 v1, t2 v2)		\
+	{								\
+		lib_update_jiffies();					\
+		return RETURN_ ## type(rettype, (name(v0, v1, v2)));	\
+	}
+#define FORWARDER4(name, type, rettype, t0, t1, t2, t3)			\
+	extern rettype name(t0, t1, t2, t3);				\
+	static rettype name ## _forwarder(t0 v0, t1 v1, t2 v2, t3 v3)	\
+	{								\
+		lib_update_jiffies();					\
+		return RETURN_ ## type(rettype, (name(v0, v1, v2, v3))); \
+	}
+#define FORWARDER4(name, type, rettype, t0, t1, t2, t3)			\
+	extern rettype name(t0, t1, t2, t3);				\
+	static rettype name ## _forwarder(t0 v0, t1 v1, t2 v2, t3 v3)	\
+	{								\
+		lib_update_jiffies();					\
+		return RETURN_ ## type(rettype, (name(v0, v1, v2, v3))); \
+	}
+#define FORWARDER5(name, type, rettype, t0, t1, t2, t3, t4)		\
+	extern rettype name(t0, t1, t2, t3, t4);			\
+	static rettype name ## _forwarder(t0 v0, t1 v1, t2 v2, t3 v3, t4 v4) \
+	{								\
+		lib_update_jiffies();					\
+		return RETURN_ ## type(rettype, (name(v0, v1, v2, v3, v4))); \
+	}
+
+FORWARDER3(lib_dev_create, nvoid, struct SimDevice *, const char *, void *,
+	   enum SimDevFlags);
+FORWARDER1(lib_dev_destroy, void, void, struct SimDevice *);
+FORWARDER2(lib_dev_set_address, void, void, struct SimDevice *,
+	   unsigned char *);
+FORWARDER2(lib_dev_set_mtu, void, void, struct SimDevice *, int);
+FORWARDER2(lib_dev_create_packet, nvoid, struct SimDevicePacket,
+	   struct SimDevice *, int);
+FORWARDER2(lib_dev_rx, void, void, struct SimDevice *, struct SimDevicePacket);
+
+FORWARDER4(lib_sock_socket, nvoid, int, int, int, int, struct SimSocket **);
+FORWARDER1(lib_sock_close, nvoid, int, struct SimSocket *);
+FORWARDER3(lib_sock_recvmsg, nvoid, ssize_t, struct SimSocket *,
+	   struct msghdr *, int);
+FORWARDER3(lib_sock_sendmsg, nvoid, ssize_t, struct SimSocket *,
+	   const struct msghdr *, int);
+FORWARDER3(lib_sock_getsockname, nvoid, int, struct SimSocket *,
+	   struct sockaddr *, int *);
+FORWARDER3(lib_sock_getpeername, nvoid, int, struct SimSocket *,
+	   struct sockaddr *, int *);
+FORWARDER3(lib_sock_bind, nvoid, int, struct SimSocket *,
+	   const struct sockaddr *, int);
+FORWARDER4(lib_sock_connect, nvoid, int, struct SimSocket *,
+	   const struct sockaddr *, int, int);
+FORWARDER2(lib_sock_listen, nvoid, int, struct SimSocket *, int);
+FORWARDER2(lib_sock_shutdown, nvoid, int, struct SimSocket *, int);
+FORWARDER3(lib_sock_accept, nvoid, int, struct SimSocket *,
+	   struct SimSocket **, int);
+FORWARDER3(lib_sock_ioctl, nvoid, int, struct SimSocket *, int, char *);
+FORWARDER5(lib_sock_setsockopt, nvoid, int, struct SimSocket *, int, int,
+	   const void *, int);
+FORWARDER5(lib_sock_getsockopt, nvoid, int, struct SimSocket *, int, int,
+	   void *, int *);
+
+FORWARDER2(lib_sock_poll, void, void, struct SimSocket *, void *);
+FORWARDER1(lib_sock_pollfreewait, void, void, void *);
+
+FORWARDER1(lib_sys_iterate_files, void, void, const struct SimSysIterator *);
+FORWARDER4(lib_sys_file_read, nvoid, int, const struct SimSysFile *, char *,
+	   int, int);
+FORWARDER4(lib_sys_file_write, nvoid, int, const struct SimSysFile *,
+	   const char *, int, int);
+
+struct SimKernel *g_kernel;
+
+void lib_init(struct SimExported *exported, const struct SimImported *imported,
+	      struct SimKernel *kernel)
+{
+	/* make sure we can call the callbacks */
+	g_imported = *imported;
+	g_kernel = kernel;
+	exported->task_create = lib_task_create;
+	exported->task_destroy = lib_task_destroy;
+	exported->task_get_private = lib_task_get_private;
+	exported->sock_socket = lib_sock_socket_forwarder;
+	exported->sock_close = lib_sock_close_forwarder;
+	exported->sock_recvmsg = lib_sock_recvmsg_forwarder;
+	exported->sock_sendmsg = lib_sock_sendmsg_forwarder;
+	exported->sock_getsockname = lib_sock_getsockname_forwarder;
+	exported->sock_getpeername = lib_sock_getpeername_forwarder;
+	exported->sock_bind = lib_sock_bind_forwarder;
+	exported->sock_connect = lib_sock_connect_forwarder;
+	exported->sock_listen = lib_sock_listen_forwarder;
+	exported->sock_shutdown = lib_sock_shutdown_forwarder;
+	exported->sock_accept = lib_sock_accept_forwarder;
+	exported->sock_ioctl = lib_sock_ioctl_forwarder;
+	exported->sock_setsockopt = lib_sock_setsockopt_forwarder;
+	exported->sock_getsockopt = lib_sock_getsockopt_forwarder;
+
+	exported->sock_poll = lib_sock_poll_forwarder;
+	exported->sock_pollfreewait = lib_sock_pollfreewait_forwarder;
+
+	exported->dev_create = lib_dev_create_forwarder;
+	exported->dev_destroy = lib_dev_destroy_forwarder;
+	exported->dev_get_private = lib_dev_get_private;
+	exported->dev_set_address = lib_dev_set_address_forwarder;
+	exported->dev_set_mtu = lib_dev_set_mtu_forwarder;
+	exported->dev_create_packet = lib_dev_create_packet_forwarder;
+	exported->dev_rx = lib_dev_rx_forwarder;
+
+	exported->sys_iterate_files = lib_sys_iterate_files_forwarder;
+	exported->sys_file_write = lib_sys_file_write_forwarder;
+	exported->sys_file_read = lib_sys_file_read_forwarder;
+
+	pr_notice("%s", linux_banner);
+
+	rcu_init();
+
+	/* in drivers/base/core.c (called normally by drivers/base/init.c) */
+	devices_init();
+	/* in lib/idr.c (called normally by init/main.c) */
+	idr_init_cache();
+	vfs_caches_init(totalram_pages);
+
+	lib_proc_net_initialize();
+
+	/* and, then, call the normal initcalls */
+	initcall_t *call;
+	extern initcall_t __initcall_start[], __initcall_end[];
+
+	call = __initcall_start;
+	do {
+		(*call)();
+		call++;
+	} while (call < __initcall_end);
+
+	/* finally, put the system in RUNNING state. */
+	system_state = SYSTEM_RUNNING;
+}
+
+int lib_vprintf(const char *str, va_list args)
+{
+	return g_imported.vprintf(g_kernel, str, args);
+}
+void *lib_malloc(unsigned long size)
+{
+	return g_imported.malloc(g_kernel, size);
+}
+void lib_free(void *buffer)
+{
+	return g_imported.free(g_kernel, buffer);
+}
+void *lib_memcpy(void *dst, const void *src, unsigned long size)
+{
+	return g_imported.memcpy(g_kernel, dst, src, size);
+}
+void *lib_memset(void *dst, char value, unsigned long size)
+{
+	return g_imported.memset(g_kernel, dst, value, size);
+}
+unsigned long lib_random(void)
+{
+	return g_imported.random(g_kernel);
+}
+void *lib_event_schedule_ns(__u64 ns, void (*fn) (void *context), void *context)
+{
+	return g_imported.event_schedule_ns(g_kernel, ns, fn, context,
+					    lib_update_jiffies);
+}
+void lib_event_cancel(void *event)
+{
+	return g_imported.event_cancel(g_kernel, event);
+}
+__u64 lib_current_ns(void)
+{
+	return g_imported.current_ns(g_kernel);
+}
+struct SimTaskTrampolineContext {
+	void (*callback)(void *);
+	void *context;
+};
+static void lib_task_start_trampoline(void *context)
+{
+	/* we use this trampoline solely for the purpose of executing
+	   lib_update_jiffies prior to calling the callback. */
+	struct SimTaskTrampolineContext *ctx = context;
+	void (*callback)(void *) = ctx->callback;
+	void *callback_context = ctx->context;
+
+	lib_free(ctx);
+	lib_update_jiffies();
+	callback(callback_context);
+}
+struct SimTask *lib_task_start(void (*callback) (void *), void *context)
+{
+	struct SimTaskTrampolineContext *ctx =
+		lib_malloc(sizeof(struct SimTaskTrampolineContext));
+
+	if (!ctx)
+		return NULL;
+	ctx->callback = callback;
+	ctx->context = context;
+	return g_imported.task_start(g_kernel, &lib_task_start_trampoline, ctx);
+}
+void lib_task_wait(void)
+{
+	rcu_sched_qs();
+	g_imported.task_wait(g_kernel);
+	lib_update_jiffies();
+}
+struct SimTask *lib_task_current(void)
+{
+	return g_imported.task_current(g_kernel);
+}
+int lib_task_wakeup(struct SimTask *task)
+{
+	return g_imported.task_wakeup(g_kernel, task);
+}
+void lib_task_yield(void)
+{
+	rcu_idle_enter();
+	g_imported.task_yield(g_kernel);
+	rcu_idle_exit();
+	lib_update_jiffies();
+}
+
+void lib_dev_xmit(struct SimDevice *dev, unsigned char *data, int len)
+{
+	return g_imported.dev_xmit(g_kernel, dev, data, len);
+}
+
+void lib_signal_raised(struct SimTask *task, int sig)
+{
+	g_imported.signal_raised(g_kernel, task, sig);
+}
+
+void lib_poll_event(int flag, void *context)
+{
+	g_imported.poll_event(flag, context);
+}
diff --git a/arch/lib/lib.h b/arch/lib/lib.h
new file mode 100644
index 0000000..abf2a26
--- /dev/null
+++ b/arch/lib/lib.h
@@ -0,0 +1,21 @@
+/*
+ * library version of Linux kernel
+ * Copyright (c) 2015 INRIA, Hajime Tazaki
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@...il.com>
+ *         Hajime Tazaki <tazaki@....wide.ad.jp>
+ *         Frederic Urbani
+ */
+
+#ifndef LIB_H
+#define LIB_H
+
+#include <linux/sched.h>
+
+struct SimTask {
+	struct list_head head;
+	struct task_struct kernel_task;
+	void *private;
+};
+
+#endif /* LIB_H */
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists