[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