[Ns-developers] [NSC] [RFC PATCH]: add struct sockaddr mangling

Florian Westphal fw at strlen.de
Fri Oct 24 13:47:56 PDT 2008


Hi Sam,

this is an RFC patch that adds struct sockaddr* conversion
between network stack and host 'struct sockaddr' definition.

This is done by introducing an nsc-internal intermediate
type, struct nsc_sockaddr, to convert the stack definiton.

This (I hope) closes bug #320 in the nsnam tracker and makes
the API more extensible for the future.

This patch only converts linux 2.6.26 as an example.

diff -r b299f58f15a8 linux-2.6.26/SConscript
--- a/linux-2.6.26/SConscript	Mon Oct 20 18:44:56 2008 -0700
+++ b/linux-2.6.26/SConscript	Fri Oct 24 22:36:57 2008 +0200
@@ -120,7 +120,7 @@
     'lib/find_next_bit.c', 'lib/libcrc32c.c', 'lib/idr.c', # used by SCTP
     'lib/rbtree.c', 'lib/hexdump.c'])
 
-sim_sources = ['nsc/sim_support.cpp']
+sim_sources = ['nsc/sim_support.cpp', 'nsc/nsc_sockaddr.c']
 asm_sources = []
 
 if arch_i386:
diff -r b299f58f15a8 linux-2.6.26/nsc/nsc.h
--- a/linux-2.6.26/nsc/nsc.h	Mon Oct 20 18:44:56 2008 -0700
+++ b/linux-2.6.26/nsc/nsc.h	Fri Oct 24 22:36:57 2008 +0200
@@ -37,7 +37,8 @@
 void nsc_debugf(const char *, ...);
 void nsc_sodisconnect(void *so);
 void nsc_solisten(void *so, unsigned short port);
-int nsc_getsockpeername(void *so, void *name, size_t *namelen, int *port, int getpeername);
+struct nsc_sockaddr;
+int nsc_getsockpeername(void *so, struct nsc_sockaddr *s, int getpeername);
 int nsc_accept(void *so, void **a);
 int nsc_accept_sctp(void *so, void **a);
 int nsc_soreceive(void *so, void *buf, int *buflen);
diff -r b299f58f15a8 linux-2.6.26/nsc/nsc_sockaddr.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6.26/nsc/nsc_sockaddr.c	Fri Oct 24 22:36:57 2008 +0200
@@ -0,0 +1,63 @@
+#include <string.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "nsc.h"
+#include "nsc_sockaddr.h"
+
+static void nsc_convert_nscsockaddr_in(struct sockaddr_in *res, const struct nsc_sockaddr_in *from, size_t *len)
+{
+	*len = sizeof(*res);
+	res->sin_addr.s_addr = from->sin_addr;
+	res->sin_port = from->sin_port;
+}
+
+
+void nsc_convert_nscsockaddr(struct sockaddr *res, const struct nsc_sockaddr *from, size_t *len)
+{
+	unsigned long data[256/sizeof(unsigned long)]; // sockaddr storage... hopefully enough space.
+	size_t datalen = sizeof(data);
+	struct sockaddr *sa;
+	unsigned short af = from->sa_family;
+
+	sa = (struct sockaddr*) data;
+	sa->sa_family = af;
+
+	switch (af) {
+	case AF_INET:
+		nsc_convert_nscsockaddr_in((struct sockaddr_in*) data, (const struct nsc_sockaddr_in*) from, &datalen);
+		break;
+	default:
+		nsc_assert(0, "Unknown ->sa_family");
+	}
+
+	*len = datalen < *len ? datalen : *len;
+	if (*len)
+		memcpy(res, data, *len);
+}
+
+
+static void nsc_convert_sockaddr_in(struct nsc_sockaddr_in *res, const struct sockaddr_in *from)
+{
+	res->sin_addr = from->sin_addr.s_addr;
+	res->sin_port = from->sin_port;
+}
+
+
+void nsc_convert_sockaddr(struct nsc_sockaddr *res, const struct sockaddr *from)
+{
+	memset(res, 0, sizeof(*res));
+
+	res->sa_family = from->sa_family;
+	switch (res->sa_family) {
+	case AF_INET:
+		nsc_convert_sockaddr_in((struct nsc_sockaddr_in*) res, (const struct sockaddr_in*) from);
+		return;
+	default:
+		nsc_assert(0, "Unknown ->sa_family");
+	}
+}
+
+
+
diff -r b299f58f15a8 linux-2.6.26/nsc/sim_support.cpp
--- a/linux-2.6.26/nsc/sim_support.cpp	Mon Oct 20 18:44:56 2008 -0700
+++ b/linux-2.6.26/nsc/sim_support.cpp	Fri Oct 24 22:36:57 2008 +0200
@@ -31,6 +31,7 @@
 #include <map>
 
 #include "sim_interface.h"
+#include "nsc_sockaddr.h"
 #include "nsc.h"
 #include "sysctl.h"
 #include "num_stacks.h"
@@ -129,23 +130,29 @@
 	    assert(ret < 0);
             return nsc_convert_syserr_to_nscerr(ret);
         }
-	virtual int getpeername(void *addrspace, size_t *addrspace_len, int *port)
+	virtual int getpeername(struct sockaddr *sa, size_t *salen)
 	{
 		int ret;
+		struct nsc_sockaddr nsc_sa;
 		set_stack_id(parent->stack_id);
-		ret = nsc_getsockpeername(so, addrspace, addrspace_len, port, 1);
+		ret = nsc_getsockpeername(so, &nsc_sa, 1);
 		set_stack_id(-1);
-		assert(ret <= 0);
+
+		if (ret == 0)
+			nsc_convert_nscsockaddr(sa, &nsc_sa, salen);
 		return nsc_convert_syserr_to_nscerr(ret);
 	}
 
-	virtual int getsockname(void *addrspace, size_t *addrspace_len, int *port)
+	virtual int getsockname(struct sockaddr *sa, size_t *salen)
 	{
 		int ret;
+		struct nsc_sockaddr nsc_sa;
 		set_stack_id(parent->stack_id);
-		ret = nsc_getsockpeername(so, addrspace, addrspace_len, port, 0);
+		ret = nsc_getsockpeername(so, &nsc_sa, 0);
 		set_stack_id(-1);
-		assert(ret <= 0);
+
+		if (ret == 0)
+			nsc_convert_nscsockaddr(sa, &nsc_sa, salen);
 		return nsc_convert_syserr_to_nscerr(ret);
 	}
 
diff -r b299f58f15a8 linux-2.6.26/nsc/support.c
--- a/linux-2.6.26/nsc/support.c	Mon Oct 20 18:44:56 2008 -0700
+++ b/linux-2.6.26/nsc/support.c	Fri Oct 24 22:36:57 2008 +0200
@@ -41,6 +41,7 @@
 
 #include "../nsc/nsc.h"
 #include "sim_errno.h"
+#include "nsc_sockaddr.h"
 
 extern void do_initcalls(void);
 extern void init_timervecs (void);
@@ -370,24 +371,16 @@
     return 0;
 }
 
-int nsc_getsockpeername(void *so, void *addrspace, size_t *buflen, int *port, int getpeername)
+int nsc_getsockpeername(void *so, struct nsc_sockaddr *sa, int getpeername)
 {
 	struct socket *sock = so;
 	struct sockaddr_storage ss;
 	int salen = sizeof(ss);
 
 	int ret = sock->ops->getname(sock, (struct sockaddr *) &ss, &salen, getpeername);
-	if (ret == 0) {
-		if (ss.ss_family == AF_INET) {
-			struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
-			size_t addrlen = min(*buflen, sizeof(sin->sin_addr));
-			memcpy(addrspace, &sin->sin_addr, addrlen);
-			*buflen = addrlen;
-			*port = sin->sin_port;
-		} else {
-			nsc_assert(0, "unsupported socket type for nsc_getsockpeername");
-		}
-	}
+	if (ret == 0)
+		nsc_convert_sockaddr(sa, (struct sockaddr *) &ss);
+	nsc_assert(ret == 0, "sock->ops->getname error");
 	return ret;
 }
 
diff -r b299f58f15a8 sim/nsc_sockaddr.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sim/nsc_sockaddr.h	Fri Oct 24 22:36:57 2008 +0200
@@ -0,0 +1,53 @@
+#ifndef NSC_SIM_SOCKADDR_H_
+#define NSC_SIM_SOCKADDR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+/*
+ * meant for internal nsc use only.
+ * can't include libc headers here, this header is used from -nostdinc code, too.
+ */
+
+#define NSC_SA_COMMON_DEF(prefix)	unsigned short prefix ## _family
+#define NSC_SA_COMMON_SIZEOF	(sizeof (unsigned short))
+
+// nsc_sockaddr must be large enough to hold _ALL_ nsc_sockaddr* types.
+struct nsc_sockaddr {
+  NSC_SA_COMMON_DEF(sa);
+  char sa_data[128 - NSC_SA_COMMON_SIZEOF];
+};
+
+struct nsc_sockaddr_in {
+  NSC_SA_COMMON_DEF(sin);
+  unsigned int sin_port;
+  unsigned int sin_addr;
+  unsigned char pad[sizeof(struct nsc_sockaddr)
+	  - NSC_SA_COMMON_SIZEOF
+	  - sizeof(unsigned int) - sizeof(unsigned int)];
+};
+
+#undef NSC_SA_COMMON_SIZEOF
+#undef NSC_SA_COMMON_DEF
+
+// convert a nsc_sockaddr_* struct to a matching host struct sockaddr* type.
+// *res_len is updated to hold the size of the actual sockaddr, ie. sizeof(sockaddr_in) for AF_INET.
+void nsc_convert_nscsockaddr(struct sockaddr *res, const struct nsc_sockaddr *from, size_t *res_len);
+
+// covert a _NSC_ kernel sockaddr* representation to the nsc_* aequivalent.
+// this is required because the host operating system and the kernel network stack
+// that is being simulated by NSC may have different sockaddr representations.
+//
+// nsc_sockaddr is an intermediate format to handle converting between
+// host operating system and simulated network stack.
+//
+// unlinke struct sockaddr, struct nsc_sockaddr is guaranteed to be large enough to hold
+// the result, so the *len argument is not needed.
+void nsc_convert_sockaddr(struct nsc_sockaddr *res, const struct sockaddr *from);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif 
+#endif
+
+
diff -r b299f58f15a8 sim/sim_interface.h
--- a/sim/sim_interface.h	Mon Oct 20 18:44:56 2008 -0700
+++ b/sim/sim_interface.h	Fri Oct 24 22:36:57 2008 +0200
@@ -149,21 +149,10 @@
     virtual bool is_connected() = 0;
     virtual bool is_listening() = 0;
 
-    /*
-     * addrspace points to a byte buffer of *addrs_len bytes.
-     * It is filled with a (network order) byte representation of the ip address,
-     * addrs_len is updated to reflect the length (i.e. 4 or eventually 16 for ipv6).
-     * *port is updated with the TCP/SCTP etc. port number.
-     *
-     * The reason that this doesn't use a *struct sockaddr is that the BSDs
-     * have an sa_len member in there, Linux does not, so the struct layout is
-     * different depending on the actual NSC stack and the real host operating
-     * system.
-     */
-    virtual int getpeername(void *addrspace, size_t *addrs_len, int *port) {
+    virtual int getpeername(struct sockaddr *sa, size_t *salen) {
 	    return -1;
     }
-    virtual int getsockname(void *addrspace, size_t *addrs_len, int *port) {
+    virtual int getsockname(struct sockaddr *sa, size_t *salen) {
 	    return -1;
     }
     /* Optional functions used to get and set variables for this TCP


More information about the Ns-developers mailing list