From c179ee88d91a84fc75dc4602cca500e8fa72ed66 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 27 Apr 2014 15:04:07 +0200 Subject: initial commit This patch bootstrap the new nft-sync software. Basically, this software aims to support two different setups: 1) Rule-set repository server. The software serves the nft rule-set to clients that request the ruleset. Basically from the system that acts as repository, you have to run: # nft-sync -c ../contrib/nft-sync.conf.server Then, from the client: # nft-sync -c ../contrib/nft-sync.conf.client --fetch Which displays the nft rule-set in the standard output, so you can inspect the nft rule-set. Alternatively, the client can also retrieve and apply the nft rule-set using the pull command instead: # nft-sync -c ../contrib/nft-sync.conf.client --pull [ Note that this command above does not work in this bootstrap yet ] 2) Rule-set synchronization: In case of primary-backup and multiprimary firewall configurations, the software makes sure that the firewall cluster is deploying the same filtering policy. In this case, you have to launch the process: # nft-sync -c ../contrib/nft-sync.conf --sync [ Note that this command above does not work in this bootstrap yet ] This bootstrap provides the basic infrastructure as a proof-of-concept. Many of the necessary features are still lacking: * Implement --sync and --pull commands. * Interaction with nft through libnftnl, which allows the software to retrieve the local nft rule-set, as well as to parse it and apply it. * SSL support, specifically the repository mode needs it to make sure nobody can steal your filtering policy from the network. * IPv6 support. * Allow to serve different rule-sets in the repository mode. And many others that will be added progressively. Signed-off-by: Pablo Neira Ayuso --- src/tcp.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 src/tcp.c (limited to 'src/tcp.c') diff --git a/src/tcp.c b/src/tcp.c new file mode 100644 index 0000000..ced350a --- /dev/null +++ b/src/tcp.c @@ -0,0 +1,295 @@ +/* + * (C) 2014 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "logging.h" + +/* + * TCP server side + */ + +struct tcp_server { + int fd; + union { + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; +}; + +#define TCP_SERVER_LISTEN 20 + +struct tcp_server *tcp_server_create(struct tcp_conf *conf) +{ + int ret, on = 1; + struct tcp_server *c; + socklen_t socklen = sizeof(int); + + c = calloc(1, sizeof(struct tcp_server)); + if (c == NULL) + return NULL; + + switch (conf->ipproto) { + case AF_INET: + c->addr.ipv4.sin_family = AF_INET; + c->addr.ipv4.sin_port = htons(conf->port); + c->addr.ipv4.sin_addr = conf->server.ipv4.inet_addr; + socklen = sizeof(struct sockaddr_in); + break; + + case AF_INET6: + c->addr.ipv6.sin6_family = AF_INET6; + c->addr.ipv6.sin6_port = htons(conf->port); + c->addr.ipv6.sin6_addr = conf->server.ipv6.inet_addr6; + c->addr.ipv6.sin6_scope_id = conf->server.ipv6.scope_id; + socklen = sizeof(struct sockaddr_in6); + break; + } + + c->fd = socket(conf->ipproto, SOCK_STREAM, 0); + if (c->fd < 0) + goto err1; + + ret = setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)); + if (ret < 0) + goto err2; + + ret = setsockopt(c->fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(int)); + if (ret < 0) + goto err2; + + ret = bind(c->fd, (struct sockaddr *) &c->addr, socklen); + if (ret < 0) + goto err2; + + ret = listen(c->fd, TCP_SERVER_LISTEN); + if (ret < 0) + goto err2; + + ret = fcntl(c->fd, F_SETFL, O_NONBLOCK); + if (ret < 0) + goto err2; + + return c; +err2: + close(c->fd); +err1: + free(c); + return NULL; +} + +void tcp_server_destroy(struct tcp_server *c) +{ + close(c->fd); + free(c); +} + +int tcp_server_get_fd(struct tcp_server *c) +{ + return c->fd; +} + +int tcp_server_accept(struct tcp_server *c, struct sockaddr_in *addr) +{ + int err, fd; + socklen_t socklen = sizeof(struct sockaddr_in); + + err = accept(c->fd, (struct sockaddr *)addr, &socklen); + if (err < 0 && errno != EAGAIN) + return -1; + + fd = err; + + err = fcntl(fd, F_SETFL, O_NONBLOCK); + if (err < 0) { + close(fd); + return -1; + } + + return fd; +} + +/* + * TCP client side + */ + +enum tcp_client_state { + TCP_DISCONNECTED = 0, + TCP_CONNECTING, + TCP_CONNECTED +}; + +struct tcp_client { + int fd; + enum tcp_client_state state; + union { + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; + socklen_t socklen; + struct nft_timer timer; + void *data; +}; + +#define TCP_CONNECT_TIMEOUT 1 + +static int tcp_client_init(struct tcp_client *c, struct tcp_conf *conf) +{ + int ret = 0; + + c->fd = socket(conf->ipproto, SOCK_STREAM, 0); + if (c->fd < 0) + return -1; + + switch (conf->ipproto) { + case AF_INET: + c->addr.ipv4.sin_family = AF_INET; + c->addr.ipv4.sin_port = htons(conf->port); + c->addr.ipv4.sin_addr = conf->client.inet_addr; + c->socklen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + c->addr.ipv6.sin6_family = AF_INET6; + c->addr.ipv6.sin6_port = htons(conf->port); + c->addr.ipv6.sin6_addr = conf->client.inet_addr6; + c->socklen = sizeof(struct sockaddr_in6); + break; + default: + ret = -1; + break; + } + + if (ret < 0) + goto err1; + + ret = fcntl(c->fd, F_SETFL, O_NONBLOCK); + if (ret < 0) + goto err1; + + ret = connect(c->fd, (struct sockaddr *)&c->addr, c->socklen); + if (ret < 0) { + switch (errno) { + case EINPROGRESS: + c->state = TCP_CONNECTING; + break; + default: /* ECONNREFUSED */ + c->state = TCP_DISCONNECTED; + goto err1; + } + } else { + /* very unlikely at this stage. */ + c->state = TCP_CONNECTED; + } + return 0; +err1: + close(c->fd); + return ret; +} + +int tcp_client_get_fd(struct tcp_client *c) +{ + return c->fd; +} + +struct tcp_client *tcp_client_create(struct tcp_conf *conf) +{ + struct tcp_client *c; + + c = calloc(1, sizeof(struct tcp_client)); + if (c == NULL) + return NULL; + + if (tcp_client_init(c, conf) < 0) { + free(c); + return NULL; + } + + return c; +} + +void tcp_client_destroy(struct tcp_client *c) +{ + close(c->fd); + free(c); +} + +ssize_t tcp_client_send(struct tcp_client *c, const void *data, int size) +{ + ssize_t ret = 0; + + switch (c->state) { + case TCP_DISCONNECTED: + ret = -1; + break; + case TCP_CONNECTING: + ret = connect(c->fd, (struct sockaddr *)&c->addr, c->socklen); + if (ret < 0) + return ret; + + c->state = TCP_CONNECTED; + /* fall through ... */ + case TCP_CONNECTED: + ret = send(c->fd, data, size, 0); + if (ret <= 0) { + /* errno == EPIPE || errno == ECONNRESET */ + c->state = TCP_DISCONNECTED; + return ret; + } + break; + } + return ret; +} + +ssize_t tcp_client_recv(struct tcp_client *c, void *data, int size) +{ + ssize_t ret = 0; + + switch (c->state) { + case TCP_DISCONNECTED: + ret = -1; + break; + case TCP_CONNECTING: + ret = connect(c->fd, (struct sockaddr *)&c->addr, c->socklen); + if (ret < 0) + return ret; + + c->state = TCP_CONNECTED; + /* fall through ... */ + case TCP_CONNECTED: + ret = recv(c->fd, data, size, 0); + if (ret <= 0) { + /* errno == ENOTCONN */ + c->state = TCP_DISCONNECTED; + return ret; + } + } + return ret; +} + +void tcp_client_set_data(struct tcp_client *c, void *data) +{ + c->data = data; +} + +void *tcp_client_get_data(struct tcp_client *c) +{ + return c->data; +} -- cgit v1.2.3