git » nmdb » commit 696db44

Implement SCTP on the server.

author Alberto Bertogli
2007-08-25 23:58:00 UTC
committer Alberto Bertogli
2007-08-25 23:58:00 UTC
parent a8c71c8a34044515ca03eab889831237ac82b17c

Implement SCTP on the server.

Pretty much copied from UDP. It lacks documentation, which will come in
a later patch.

Signed-off-by: Alberto Bertogli <albertito@gmail.com>

nmdb/Makefile +9 -1
nmdb/common.h +2 -0
nmdb/main.c +15 -0
nmdb/net-const.h +4 -0
nmdb/net.c +17 -1
nmdb/req.h +1 -0
nmdb/sctp-stub.c +18 -0
nmdb/sctp.c +258 -0
nmdb/sctp.h +10 -0

diff --git a/nmdb/Makefile b/nmdb/Makefile
index 4b1c90a..c3e103a 100644
--- a/nmdb/Makefile
+++ b/nmdb/Makefile
@@ -3,6 +3,7 @@
 ENABLE_TIPC = 1
 ENABLE_TCP = 1
 ENABLE_UDP = 1
+ENABLE_SCTP = 1
 
 # Backend to use, can be qdbm, bdb, or null
 BACKEND = qdbm
@@ -11,7 +12,8 @@ CFLAGS += -std=c99 -pedantic -Wall -O3
 ALL_CFLAGS = -D_XOPEN_SOURCE=500 $(CFLAGS)
 ALL_CFLAGS += -DENABLE_TIPC=$(ENABLE_TIPC) \
 		-DENABLE_TCP=$(ENABLE_TCP) \
-		-DENABLE_UDP=$(ENABLE_UDP)
+		-DENABLE_UDP=$(ENABLE_UDP) \
+		-DENABLE_SCTP=$(ENABLE_SCTP)
 
 ifdef DEBUG
 ALL_CFLAGS += -g
@@ -48,6 +50,12 @@ else
 	OBJS += udp-stub.o
 endif
 
+ifeq ($(ENABLE_SCTP), 1)
+	OBJS += sctp.o
+else
+	OBJS += sctp-stub.o
+endif
+
 # Use series of ifeq-endif instead of else-ifeq because otherwise the nesting
 # is a mess. Using "else ifeq ..." in the same line is only supported from
 # gmake 3.81, which is too new.
diff --git a/nmdb/common.h b/nmdb/common.h
index 70ce08f..1a872c7 100644
--- a/nmdb/common.h
+++ b/nmdb/common.h
@@ -20,6 +20,8 @@ struct settings {
 	int tcp_port;
 	char *udp_addr;
 	int udp_port;
+	char *sctp_addr;
+	int sctp_port;
 	int numobjs;
 	int foreground;
 	int passive;
diff --git a/nmdb/main.c b/nmdb/main.c
index ad3e36d..5bb3cad 100644
--- a/nmdb/main.c
+++ b/nmdb/main.c
@@ -33,6 +33,8 @@ static void help(void) {
 	  "  -T addr	TCP listening address (all local addresses)\n"
 	  "  -u port	UDP listening port (26010)\n"
 	  "  -U addr	UDP listening address (all local addresses)\n"
+	  "  -s port	SCTP listening port (26010)\n"
+	  "  -S addr	SCTP listening address (all local addresses)\n"
 	  "  -c nobj	max. number of objects to be cached, in thousands (128)\n"
 	  "  -o	fname	log to the file 'fname'.\n"
 	  "  -f		don't fork and stay in the foreground\n"
@@ -55,6 +57,8 @@ static int load_settings(int argc, char **argv)
 	settings.tcp_port = -1;
 	settings.udp_addr = NULL;
 	settings.udp_port = -1;
+	settings.sctp_addr = NULL;
+	settings.sctp_port = -1;
 	settings.numobjs = -1;
 	settings.foreground = 0;
 	settings.passive = 0;
@@ -92,6 +96,13 @@ static int load_settings(int argc, char **argv)
 			settings.udp_addr = optarg;
 			break;
 
+		case 's':
+			settings.sctp_port = atoi(optarg);
+			break;
+		case 'S':
+			settings.sctp_addr = optarg;
+			break;
+
 		case 'c':
 			settings.numobjs = atoi(optarg) * 1024;
 			break;
@@ -130,6 +141,10 @@ static int load_settings(int argc, char **argv)
 		settings.udp_addr = UDP_SERVER_ADDR;
 	if (settings.udp_port == -1)
 		settings.udp_port = UDP_SERVER_PORT;
+	if (settings.sctp_addr == NULL)
+		settings.sctp_addr = SCTP_SERVER_ADDR;
+	if (settings.sctp_port == -1)
+		settings.sctp_port = SCTP_SERVER_PORT;
 	if (settings.numobjs == -1)
 		settings.numobjs = 128 * 1024;
 
diff --git a/nmdb/net-const.h b/nmdb/net-const.h
index bdaa29d..79d9553 100644
--- a/nmdb/net-const.h
+++ b/nmdb/net-const.h
@@ -19,6 +19,10 @@
 #define UDP_SERVER_ADDR "0.0.0.0"
 #define UDP_SERVER_PORT 26010
 
+/* SCTP default listen address and port. */
+#define SCTP_SERVER_ADDR "0.0.0.0"
+#define SCTP_SERVER_PORT 26010
+
 /* Protocol version, for checking in the network header. */
 #define PROTO_VER 1
 
diff --git a/nmdb/net.c b/nmdb/net.c
index 1145705..592d79d 100644
--- a/nmdb/net.c
+++ b/nmdb/net.c
@@ -12,6 +12,7 @@ typedef unsigned char u_char;
 #include "tipc.h"
 #include "tcp.h"
 #include "udp.h"
+#include "sctp.h"
 #include "net.h"
 #include "log.h"
 
@@ -33,7 +34,8 @@ void net_loop(void)
 	int tipc_fd = -1;
 	int tcp_fd = -1;
 	int udp_fd = -1;
-	struct event tipc_evt, tcp_evt, udp_evt,
+	int sctp_fd = -1;
+	struct event tipc_evt, tcp_evt, udp_evt, sctp_evt,
 		     sigterm_evt, sigint_evt, sigusr2_evt;
 
 	event_init();
@@ -77,6 +79,18 @@ void net_loop(void)
 		event_add(&udp_evt, NULL);
 	}
 
+	if (ENABLE_SCTP) {
+		sctp_fd = sctp_init();
+		if (sctp_fd < 0) {
+			errlog("Error initializing SCTP");
+			exit(1);
+		}
+
+		event_set(&sctp_evt, sctp_fd, EV_READ | EV_PERSIST, sctp_recv,
+				&sctp_evt);
+		event_add(&sctp_evt, NULL);
+	}
+
 	signal_set(&sigterm_evt, SIGTERM, exit_sighandler, &sigterm_evt);
 	signal_add(&sigterm_evt, NULL);
 	signal_set(&sigint_evt, SIGINT, exit_sighandler, &sigint_evt);
@@ -93,6 +107,8 @@ void net_loop(void)
 		event_del(&tcp_evt);
 	if (ENABLE_UDP)
 		event_del(&udp_evt);
+	if (ENABLE_SCTP)
+		event_del(&sctp_evt);
 
 	signal_del(&sigterm_evt);
 	signal_del(&sigint_evt);
diff --git a/nmdb/req.h b/nmdb/req.h
index 454f0f8..68ce4d8 100644
--- a/nmdb/req.h
+++ b/nmdb/req.h
@@ -11,6 +11,7 @@
 #define REQTYPE_TIPC 1
 #define REQTYPE_TCP 2
 #define REQTYPE_UDP 3
+#define REQTYPE_SCTP 4
 
 
 struct req_info {
diff --git a/nmdb/sctp-stub.c b/nmdb/sctp-stub.c
new file mode 100644
index 0000000..96221f5
--- /dev/null
+++ b/nmdb/sctp-stub.c
@@ -0,0 +1,18 @@
+
+/* SCTP stub file, used when SCTP is not compiled in. */
+
+int sctp_init(void)
+{
+	return -1;
+}
+
+void sctp_close(int fd)
+{
+	return;
+}
+
+void sctp_recv(int fd, short event, void *arg)
+{
+	return;
+}
+
diff --git a/nmdb/sctp.c b/nmdb/sctp.c
new file mode 100644
index 0000000..ef8064c
--- /dev/null
+++ b/nmdb/sctp.c
@@ -0,0 +1,258 @@
+
+#include <sys/types.h>		/* socket defines */
+#include <sys/socket.h>		/* socket functions */
+#include <stdlib.h>		/* malloc() */
+#include <stdint.h>		/* uint32_t and friends */
+#include <arpa/inet.h>		/* htonls() and friends */
+#include <netinet/in.h>		/* INET stuff */
+#include <netinet/sctp.h>	/* SCTP stuff */
+#include <string.h>		/* memcpy() */
+#include <unistd.h>		/* close() */
+
+#include "sctp.h"
+#include "common.h"
+#include "net-const.h"
+#include "req.h"
+#include "parse.h"
+#include "log.h"
+
+
+static void sctp_mini_reply(struct req_info *req, uint32_t reply);
+static void sctp_reply_err(struct req_info *req, uint32_t reply);
+static void sctp_reply_get(struct req_info *req, uint32_t reply,
+		unsigned char *val, size_t vsize);
+static void sctp_reply_set(struct req_info *req, uint32_t reply);
+static void sctp_reply_del(struct req_info *req, uint32_t reply);
+static void sctp_reply_cas(struct req_info *req, uint32_t reply);
+
+
+/*
+ * Miscelaneous helper functions
+ */
+
+static void rep_send_error(const struct req_info *req, const unsigned int code)
+{
+	int r, c;
+	unsigned char minibuf[3 * 4];
+
+	if (settings.passive)
+		return;
+
+	/* Network format: ID (4), REP_ERR (4), error code (4) */
+	r = htonl(REP_ERR);
+	c = htonl(code);
+	memcpy(minibuf, &(req->id), 4);
+	memcpy(minibuf + 4, &r, 4);
+	memcpy(minibuf + 8, &c, 4);
+
+	/* If this send fails, there's nothing to be done */
+	r = sendto(req->fd, minibuf, 3 * 4, 0, req->clisa, req->clilen);
+
+	if (r < 0) {
+		errlog("rep_send_error() failed");
+	}
+}
+
+
+static int rep_send(const struct req_info *req, const unsigned char *buf,
+		const size_t size)
+{
+	int rv;
+
+	if (settings.passive)
+		return 1;
+
+	rv = sendto(req->fd, buf, size, 0, req->clisa, req->clilen);
+	if (rv < 0) {
+		rep_send_error(req, ERR_SEND);
+		return 0;
+	}
+	return 1;
+}
+
+
+/* Send small replies, consisting in only a value. */
+static void sctp_mini_reply(struct req_info *req, uint32_t reply)
+{
+	/* We use a mini buffer to speedup the small replies, to avoid the
+	 * malloc() overhead. */
+	unsigned char minibuf[8];
+
+	if (settings.passive)
+		return;
+
+	reply = htonl(reply);
+	memcpy(minibuf, &(req->id), 4);
+	memcpy(minibuf + 4, &reply, 4);
+	rep_send(req, minibuf, 8);
+	return;
+}
+
+
+/* The sctp_reply_* functions are used by the db code to send the network
+ * replies. */
+
+void sctp_reply_err(struct req_info *req, uint32_t reply)
+{
+	rep_send_error(req, reply);
+}
+
+void sctp_reply_get(struct req_info *req, uint32_t reply,
+			unsigned char *val, size_t vsize)
+{
+	if (val == NULL) {
+		/* miss */
+		sctp_mini_reply(req, reply);
+	} else {
+		unsigned char *buf;
+		size_t bsize;
+		uint32_t t;
+
+		reply = htonl(reply);
+
+		/* The reply length is:
+		 * 4		id
+		 * 4		reply code
+		 * 4		vsize
+		 * vsize	val
+		 */
+		bsize = 4 + 4 + 4 + vsize;
+		buf = malloc(bsize);
+
+		t = htonl(vsize);
+
+		memcpy(buf, &(req->id), 4);
+		memcpy(buf + 4, &reply, 4);
+		memcpy(buf + 8, &t, 4);
+		memcpy(buf + 12, val, vsize);
+
+		rep_send(req, buf, bsize);
+		free(buf);
+	}
+	return;
+
+}
+
+
+void sctp_reply_set(struct req_info *req, uint32_t reply)
+{
+	sctp_mini_reply(req, reply);
+}
+
+
+void sctp_reply_del(struct req_info *req, uint32_t reply)
+{
+	sctp_mini_reply(req, reply);
+}
+
+void sctp_reply_cas(struct req_info *req, uint32_t reply)
+{
+	sctp_mini_reply(req, reply);
+}
+
+
+/*
+ * Main functions for receiving and parsing
+ */
+
+int sctp_init(void)
+{
+	int fd, rv;
+	struct sockaddr_in srvsa;
+	struct in_addr ia;
+
+	rv = inet_pton(AF_INET, settings.sctp_addr, &ia);
+	if (rv <= 0)
+		return -1;
+
+	srvsa.sin_family = AF_INET;
+	srvsa.sin_addr.s_addr = ia.s_addr;
+	srvsa.sin_port = htons(settings.sctp_port);
+
+	fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
+	if (fd < 0)
+		return -1;
+
+	rv = 1;
+	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rv, sizeof(rv)) < 0 ) {
+		errlog("Error in setsockopt():");
+		close(fd);
+		return -1;
+	}
+
+	rv = bind(fd, (struct sockaddr *) &srvsa, sizeof(srvsa));
+	if (rv < 0) {
+		close(fd);
+		return -1;
+	}
+
+	rv = listen(fd, 1024);
+	if (rv < 0) {
+		close(fd);
+		return -1;
+	}
+
+	/* Disable nagle algorithm, as we often handle small amounts of data
+	 * it can make I/O quite slow. */
+	rv = 1;
+	if (setsockopt(fd, IPPROTO_SCTP, SCTP_NODELAY, &rv, sizeof(rv)) < 0 ) {
+		close(fd);
+		return -1;
+	}
+
+
+	return fd;
+}
+
+
+void sctp_close(int fd)
+{
+	close(fd);
+}
+
+
+/* Static common buffer to avoid unnecessary allocations. See the comments on
+ * this same variable in tipc.c. */
+#define SBSIZE (68 * 1024)
+static unsigned char static_buf[SBSIZE];
+
+/* Called by libevent for each receive event */
+void sctp_recv(int fd, short event, void *arg)
+{
+	int rv;
+	struct req_info req;
+	struct sockaddr_in clisa;
+	socklen_t clilen;
+
+	clilen = sizeof(clisa);
+
+	rv = recvfrom(fd, static_buf, SBSIZE, 0, (struct sockaddr *) &clisa,
+			&clilen);
+	if (rv < 0) {
+		goto exit;
+	}
+
+	if (rv < 2) {
+		stats.net_broken_req++;
+		goto exit;
+	}
+
+	req.fd = fd;
+	req.type = REQTYPE_SCTP;
+	req.clisa = (struct sockaddr *) &clisa;
+	req.clilen = clilen;
+	req.mini_reply = sctp_mini_reply;
+	req.reply_err = sctp_reply_err;
+	req.reply_get = sctp_reply_get;
+	req.reply_set = sctp_reply_set;
+	req.reply_del = sctp_reply_del;
+	req.reply_cas = sctp_reply_cas;
+
+	/* parse the message */
+	parse_message(&req, static_buf, rv);
+
+exit:
+	return;
+}
+
+
diff --git a/nmdb/sctp.h b/nmdb/sctp.h
new file mode 100644
index 0000000..710fb18
--- /dev/null
+++ b/nmdb/sctp.h
@@ -0,0 +1,10 @@
+
+#ifndef _SCTP_H
+#define _SCTP_H
+
+int sctp_init(void);
+void sctp_close(int fd);
+void sctp_recv(int fd, short event, void *arg);
+
+#endif
+