author | Alberto Bertogli
<albertito@gmail.com> 2007-08-25 23:58:00 UTC |
committer | Alberto Bertogli
<albertito@gmail.com> 2007-08-25 23:58:00 UTC |
parent | a8c71c8a34044515ca03eab889831237ac82b17c |
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 +