From c1cf00d86a4a3f5de170f968077f5b1e80b6b482 Mon Sep 17 00:00:00 2001
From: Dimitri Staessens <dimitri.staessens@ugent.be>
Date: Mon, 22 Jan 2018 10:25:54 +0100
Subject: [PATCH] qcommon: Add Ouroboros support (experimental)

This adds support for the Ouroboros IPC subsystem. This patch was
written by

Dimitri Staessens <dimitri.staessens@ugent.be>
Sander Vrijders   <sander.vrijders@ugent.be>
Addy Bombeke      <addy.bombeke@ugent.be>
---
 Makefile                     |   6 ++
 code/client/cl_main.c        |   6 +-
 code/qcommon/net_chan.c      |   6 ++
 code/qcommon/net_ip.c        |  27 +++++++-
 code/qcommon/net_ouroboros.c | 144 +++++++++++++++++++++++++++++++++++++++++++
 code/qcommon/net_ouroboros.h |  46 ++++++++++++++
 code/qcommon/qcommon.h       |   4 +-
 7 files changed, 234 insertions(+), 5 deletions(-)
 create mode 100644 code/qcommon/net_ouroboros.c
 create mode 100644 code/qcommon/net_ouroboros.h

diff --git a/Makefile b/Makefile
index 4aa9f519..12da9fde 100644
--- a/Makefile
+++ b/Makefile
@@ -1645,6 +1645,7 @@ Q3OBJ = \
   $(B)/client/msg.o \
   $(B)/client/net_chan.o \
   $(B)/client/net_ip.o \
+  $(B)/client/net_ouroboros.o \
   $(B)/client/huffman.o \
   \
   $(B)/client/snd_adpcm.o \
@@ -2209,6 +2210,7 @@ Q3DOBJ = \
   $(B)/ded/msg.o \
   $(B)/ded/net_chan.o \
   $(B)/ded/net_ip.o \
+  $(B)/ded/net_ouroboros.o \
   $(B)/ded/huffman.o \
   \
   $(B)/ded/q_math.o \
@@ -2943,3 +2945,7 @@ endif
 ifneq ($(findstring clean, $(MAKECMDGOALS)),)
 .NOTPARALLEL:
 endif
+
+#Ouroboros support
+LIBS+="-louroboros-dev"
+LIBS+="-lpthread"
diff --git a/code/client/cl_main.c b/code/client/cl_main.c
index 82d0edfb..33a85f83 100644
--- a/code/client/cl_main.c
+++ b/code/client/cl_main.c
@@ -1697,7 +1697,7 @@ void CL_Connect_f( void ) {
 	netadrtype_t family = NA_UNSPEC;
 
 	if ( argc != 2 && argc != 3 ) {
-		Com_Printf( "usage: connect [-4|-6] server\n");
+		Com_Printf( "usage: connect [-4|-6|-O] server\n");
 		return;	
 	}
 	
@@ -1709,8 +1709,10 @@ void CL_Connect_f( void ) {
 			family = NA_IP;
 		else if(!strcmp(Cmd_Argv(1), "-6"))
 			family = NA_IP6;
+		else if(!strcmp(Cmd_Argv(1), "-O"))
+			family = NA_OUROBOROS;
 		else
-			Com_Printf( "warning: only -4 or -6 as address type understood.\n");
+			Com_Printf( "warning: only -4, -6 or -O as address type understood.\n");
 		
 		Q_strncpyz( server, Cmd_Argv(2), sizeof( server ) );
 	}
diff --git a/code/qcommon/net_chan.c b/code/qcommon/net_chan.c
index 9e7d9b87..b0103cc3 100644
--- a/code/qcommon/net_chan.c
+++ b/code/qcommon/net_chan.c
@@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 #include "q_shared.h"
 #include "qcommon.h"
+#include "net_ouroboros.h"
 
 /*
 
@@ -638,6 +639,11 @@ int NET_StringToAdr( const char *s, netadr_t *a, netadrtype_t family )
 		return 1;
 	}
 
+	if(family == NA_OUROBOROS) {
+		OUROBOROS_Resolve(s, a);
+		return 1;
+	}
+
 	Q_strncpyz( base, s, sizeof( base ) );
 	
 	if(*base == '[' || Q_CountChar(base, ':') > 1)
diff --git a/code/qcommon/net_ip.c b/code/qcommon/net_ip.c
index bcccda20..1bcce311 100644
--- a/code/qcommon/net_ip.c
+++ b/code/qcommon/net_ip.c
@@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 #include "../qcommon/q_shared.h"
 #include "../qcommon/qcommon.h"
+#include "net_ouroboros.h"
 
 #ifdef _WIN32
 #	include <winsock2.h>
@@ -392,6 +393,9 @@ qboolean NET_CompareBaseAdrMask(netadr_t a, netadr_t b, int netmask)
 	byte cmpmask, *addra, *addrb;
 	int curbyte;
 	
+	if (a.type == NA_OUROBOROS && b.type == NA_OUROBOROS)
+		return a.fd == b.fd ? qtrue : qfalse;
+
 	if (a.type != b.type)
 		return qfalse;
 
@@ -495,6 +499,9 @@ qboolean	NET_CompareAdr (netadr_t a, netadr_t b)
 	if(!NET_CompareBaseAdr(a, b))
 		return qfalse;
 	
+	if (a.type == NA_OUROBOROS && b.type == NA_OUROBOROS)
+		return a.fd == b.fd ? qtrue :qfalse;
+
 	if (a.type == NA_IP || a.type == NA_IP6)
 	{
 		if (a.port == b.port)
@@ -627,6 +634,9 @@ qboolean NET_GetPacket(netadr_t *net_from, msg_t *net_message, fd_set *fdr)
 		}
 	}
 	
+	ret = OUROBOROS_Recvfrom(net_message, net_from);
+	if (ret > 0)
+		return qtrue;
 	
 	return qfalse;
 }
@@ -644,6 +654,11 @@ void Sys_SendPacket( int length, const void *data, netadr_t to ) {
 	int				ret = SOCKET_ERROR;
 	struct sockaddr_storage	addr;
 
+	if (to.type == NA_OUROBOROS) {
+		OUROBOROS_Sendto(length, data, &to);
+		return;
+	}
+
 	if( to.type != NA_BROADCAST && to.type != NA_IP && to.type != NA_IP6 && to.type != NA_MULTICAST6)
 	{
 		Com_Error( ERR_FATAL, "Sys_SendPacket: bad address type" );
@@ -1585,8 +1600,14 @@ void NET_Init( void ) {
 #endif
 
 	NET_Config( qtrue );
-	
+
 	Cmd_AddCommand ("net_restart", NET_Restart_f);
+
+#ifdef	DEDICATED
+	OUROBOROS_Init(1);
+#else
+	OUROBOROS_Init(0);
+#endif
 }
 
 
@@ -1602,6 +1623,8 @@ void NET_Shutdown( void ) {
 
 	NET_Config( qfalse );
 
+	OUROBOROS_Fini();
+
 #ifdef _WIN32
 	WSACleanup();
 	winsockInitialized = qfalse;
@@ -1694,7 +1717,7 @@ void NET_Sleep(int msec)
 
 	if(retval == SOCKET_ERROR)
 		Com_Printf("Warning: select() syscall failed: %s\n", NET_ErrorString());
-	else if(retval > 0)
+	else
 		NET_Event(&fdr);
 }
 
diff --git a/code/qcommon/net_ouroboros.c b/code/qcommon/net_ouroboros.c
new file mode 100644
index 00000000..ee98a2e0
--- /dev/null
+++ b/code/qcommon/net_ouroboros.c
@@ -0,0 +1,144 @@
+/*
+ *  Ouroboros calls for ioq3
+ *
+ *    Addy Bombeke      <addy.bombeke@ugent.be>
+ *    Sander Vrijders   <sander.vrijders@intec.ugent.be>
+ *    Dimitri Staessens <dimitri.staessens@intec.ugent.be>
+ *
+ *    Copyright (C) 2015 - 2018
+ *
+ *  Modified from software originally written as part of the MSc
+ *  thesis in electrical engineering,
+ *
+ *  "Comparing RINA to TCP/IP for latency-constrained applications",
+ *
+ *  at Ghent University, Academic Year 2014-2015
+ *
+ *  This program 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 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "net_ouroboros.h"
+
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <ouroboros/dev.h>
+#include <ouroboros/fqueue.h>
+
+fset_t * flows;
+fqueue_t * fq;
+
+void OUROBOROS_Resolve(const char * s, netadr_t * a)
+{
+        int fd;
+
+        a->type = NA_OUROBOROS;
+
+        fd = flow_alloc(s, NULL, NULL);
+        if (fd < 0) {
+                printf("Failed to allocate flow.\n");
+                return;
+        }
+
+        fset_add(flows, fd);
+
+        a->fd = fd;
+}
+
+void OUROBOROS_Sendto(int length, const void * data, netadr_t * to)
+{
+        flow_write(to->fd, (void *) data, length);
+}
+
+int OUROBOROS_Recvfrom(msg_t * msg, netadr_t * from)
+{
+        int             fd;
+        ssize_t         count = 0;
+        struct timespec timeout = {0, 0};
+
+        fevent(flows, fq, &timeout);
+
+        if ((fd = fqueue_next(fq)) >= 0) {
+                count = flow_read(fd, msg->data, msg->maxsize);
+                if (count < 0)
+                        return 0;
+
+                if (count > msg->maxsize) {
+                        printf("Oversized packet received.\n");
+                        return 0;
+                }
+
+                from->type = NA_OUROBOROS;
+                from->fd = fd;
+                msg->cursize = count;
+                msg->readcount = 0;
+
+                return count;
+        }
+
+        return 0;
+}
+
+static void * OUROBOROS_Server_Accept(void * o)
+{
+        int fd;
+
+        (void) o;
+
+        for (;;) {
+                fd = flow_accept(NULL, NULL);
+                if (fd < 0) {
+                        printf("Failed to accept flow.\n");
+                        continue;
+                }
+
+                fset_add(flows, fd);
+        }
+
+        return (void *) 0;
+}
+
+void OUROBOROS_Init(int server)
+{
+        pthread_t accept_thread;
+
+        flows = fset_create();
+        if (flows == NULL) {
+                printf("Failed to create flow set.\n");
+                return;
+        }
+
+        fq = fqueue_create();
+        if (fq == NULL) {
+                printf("Failed to create flow queue.\n");
+                return;
+        }
+
+        if (server) {
+                pthread_create(&accept_thread,
+                               NULL,
+                               OUROBOROS_Server_Accept,
+                               NULL);
+                pthread_detach(accept_thread);
+        }
+}
+
+void OUROBOROS_Fini(void)
+{
+        fqueue_destroy(fq);
+        fset_destroy(flows);
+}
diff --git a/code/qcommon/net_ouroboros.h b/code/qcommon/net_ouroboros.h
new file mode 100644
index 00000000..5f85987e
--- /dev/null
+++ b/code/qcommon/net_ouroboros.h
@@ -0,0 +1,46 @@
+/*
+ *  Ouroboros calls for ioq3
+ *
+ *    Addy Bombeke      <addy.bombeke@ugent.be>
+ *    Sander Vrijders   <sander.vrijders@intec.ugent.be>
+ *    Dimitri Staessens <dimitri.staessens@intec.ugent.be>
+ *
+ *    Copyright (C) 2015 - 2018
+ *
+ *  Modified from software originally written as part of the MSc
+ *  thesis in electrical engineering,
+ *
+ *  "Comparing RINA to TCP/IP for latency-constrained applications",
+ *
+ *  at Ghent University, Academic Year 2014-2015
+ *
+ *  This program 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 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef NET_OUROBOROS_H
+#define NET_OUROBOROS_H
+
+#include "../qcommon/q_shared.h"
+#include "../qcommon/qcommon.h"
+
+void OUROBOROS_Init(int server);
+void OUROBOROS_Fini(void);
+
+void OUROBOROS_Resolve(const char * s, netadr_t * a);
+
+int  OUROBOROS_Recvfrom(msg_t * msg, netadr_t * from);
+void OUROBOROS_Sendto(int length, const void * data, netadr_t * to);
+
+#endif /*NET_OUROBOROS_H */
diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h
index 4471198c..eff5472f 100644
--- a/code/qcommon/qcommon.h
+++ b/code/qcommon/qcommon.h
@@ -147,7 +147,8 @@ typedef enum {
 	NA_IP,
 	NA_IP6,
 	NA_MULTICAST6,
-	NA_UNSPEC
+	NA_UNSPEC,
+	NA_OUROBOROS
 } netadrtype_t;
 
 typedef enum {
@@ -164,6 +165,7 @@ typedef struct {
 
 	unsigned short	port;
 	unsigned long	scope_id;	// Needed for IPv6 link-local addresses
+	int             fd;       // Needed for Ouroboros support
 } netadr_t;
 
 void		NET_Init( void );
-- 
2.16.0

