summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladislav Grishenko <themiron@mail.ru>2018-02-27 01:29:10 +0500
committerVladislav Grishenko <themiron@mail.ru>2018-02-27 15:28:43 +0500
commit8b24e82a67005ade50b92dfefa6413874a758132 (patch)
tree00843b1fc9161e092fde5295cc2c9cd42c83bfe1
parent0e7fea6d12beaeef80fa1cc666d8556d2ca1abee (diff)
downloadaccel-ppp-8b24e82a67005ade50b92dfefa6413874a758132.tar.gz
accel-ppp-8b24e82a67005ade50b92dfefa6413874a758132.zip
sstp: implement ipv6 & unix socket support
following bind option formats are valid: bind=x.x.x.x bind=2001:db8::1 bind=unix:/var/run/sstp.socket bind=unix:@sstp port option is meaningful for ipv4 and ipv6 only
-rw-r--r--accel-pppd/accel-ppp.conf.55
-rw-r--r--accel-pppd/ctrl/sstp/sstp.c192
2 files changed, 149 insertions, 48 deletions
diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5
index 9b58018a..fb38fea1 100644
--- a/accel-pppd/accel-ppp.conf.5
+++ b/accel-pppd/accel-ppp.conf.5
@@ -639,8 +639,9 @@ as a template, i.e l2tp%d => l2tp0.
.br
Configuration of SSTP module.
.TP
-.BI "bind=" x.x.x.x
-If this option is given then sstp server will bind to specified IP address.
+.BI "bind=" x.x.x.x|ipv6address|unix:pathname|unix:@abstract
+If this option is given then sstp server will bind to specified IP address
+or unix pathname/abstract socket.
.TP
.BI "port=" n
If this option is given then sstp server will bind to specified port.
diff --git a/accel-pppd/ctrl/sstp/sstp.c b/accel-pppd/ctrl/sstp/sstp.c
index 3ab2c2ae..11d57bae 100644
--- a/accel-pppd/ctrl/sstp/sstp.c
+++ b/accel-pppd/ctrl/sstp/sstp.c
@@ -11,8 +11,11 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
+#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <sys/ioctl.h>
+#include <sys/stat.h>
#include "linux_ppp.h"
#ifdef CRYPTO_OPENSSL
@@ -51,6 +54,8 @@
#define SHA256_DIGEST_LENGTH 32
#endif
+#define ADDRSTR_MAXLEN (sizeof("unix:") + sizeof(((struct sockaddr_un *)0)->sun_path))
+
enum {
STATE_INIT = 0,
STATE_STARTING,
@@ -58,6 +63,16 @@ enum {
STATE_FINISHED,
};
+struct sockaddr_t {
+ socklen_t len;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_un sun;
+ } u;
+} __attribute__((packed));
+
struct hash_t {
unsigned int len;
union {
@@ -125,6 +140,8 @@ static struct sstp_serv_t {
struct triton_context_t ctx;
struct triton_md_handler_t hnd;
+ struct sockaddr_t addr;
+
#ifdef CRYPTO_OPENSSL
SSL_CTX *ssl_ctx;
#endif
@@ -223,6 +240,52 @@ static int hex2bin(const char *src, uint8_t *dst, size_t size)
return n;
}
+static in_addr_t sockaddr_ipv4(struct sockaddr_t *addr)
+{
+ switch (addr->u.sa.sa_family) {
+ case AF_INET:
+ return addr->u.sin.sin_addr.s_addr;
+ case AF_INET6:
+ if (IN6_IS_ADDR_V4MAPPED(&addr->u.sin6.sin6_addr))
+ return addr->u.sin6.sin6_addr.s6_addr32[3];
+ /* fall through */
+ default:
+ return INADDR_ANY;
+ }
+}
+
+static int sockaddr_ntop(struct sockaddr_t *addr, char *dst, socklen_t size)
+{
+ char ipv6_buf[INET6_ADDRSTRLEN], *path, sign;
+
+ switch (addr->u.sa.sa_family) {
+ case AF_INET:
+ return snprintf(dst, size, "%s:%d",
+ inet_ntoa(addr->u.sin.sin_addr), ntohs(addr->u.sin.sin_port));
+ case AF_INET6:
+ if (IN6_IS_ADDR_V4MAPPED(&addr->u.sin6.sin6_addr)) {
+ inet_ntop(AF_INET, &addr->u.sin6.sin6_addr.s6_addr32[3],
+ ipv6_buf, sizeof(ipv6_buf));
+ } else {
+ inet_ntop(AF_INET6, &addr->u.sin6.sin6_addr,
+ ipv6_buf, sizeof(ipv6_buf));
+ }
+ return snprintf(dst, size, "%s:%d",
+ ipv6_buf, ntohs(addr->u.sin6.sin6_port));
+ case AF_UNIX:
+ if (addr->len <= offsetof(typeof(addr->u.sun), sun_path)) {
+ path = "NULL";
+ sign = path[0];
+ } else {
+ path = addr->u.sun.sun_path;
+ sign = path[0] ? : '@';
+ }
+ return snprintf(dst, size, "unix:%c%s", sign, path + 1);
+ }
+
+ return -1;
+}
+
/* buffer */
static inline void *buf_put(struct buffer_t *buf, int len)
@@ -1740,12 +1803,14 @@ error:
static int sstp_connect(struct triton_md_handler_t *h)
{
struct sstp_conn_t *conn;
- struct sockaddr_in addr;
- socklen_t size = sizeof(addr);
+ struct sockaddr_t addr;
+ char addr_buf[ADDRSTR_MAXLEN];
+ in_addr_t ip;
int sock, value;
while (1) {
- sock = accept(h->fd, (struct sockaddr *)&addr, &size);
+ addr.len = sizeof(addr.u);
+ sock = accept(h->fd, &addr.u.sa, &addr.len);
if (sock < 0) {
if (errno == EAGAIN)
return 0;
@@ -1758,38 +1823,42 @@ static int sstp_connect(struct triton_md_handler_t *h)
continue;
}
- if (triton_module_loaded("connlimit") && connlimit_check(cl_key_from_ipv4(addr.sin_addr.s_addr))) {
+ ip = sockaddr_ipv4(&addr);
+ if (ip && triton_module_loaded("connlimit") && connlimit_check(cl_key_from_ipv4(ip))) {
close(sock);
return 0;
}
-
- log_info2("sstp: new connection from %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+ sockaddr_ntop(&addr, addr_buf, sizeof(addr_buf));
+ log_info2("sstp: new connection from %s\n", addr_buf);
- if (iprange_client_check(addr.sin_addr.s_addr)) {
+ if (ip && iprange_client_check(addr.u.sin.sin_addr.s_addr)) {
log_warn("sstp: IP is out of client-ip-range, droping connection...\n");
close(sock);
continue;
}
- if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
+ value = fcntl(sock, F_GETFL);
+ if (value < 0 || fcntl(sock, F_SETFL, value | O_NONBLOCK) < 0) {
log_error("sstp: failed to set nonblocking mode: %s, closing connection...\n", strerror(errno));
close(sock);
continue;
}
- value = 65536;
- if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) {
- log_error("sstp: failed to set send buffer: %s, closing connection...\n", strerror(errno));
- close(sock);
- continue;
- }
+ if (addr.u.sa.sa_family != AF_UNIX) {
+ value = 65536;
+ if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) {
+ log_error("sstp: failed to set send buffer: %s, closing connection...\n", strerror(errno));
+ close(sock);
+ continue;
+ }
- value = 1;
- if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)) < 0) {
- log_error("sstp: failed to disable nagle: %s, closing connection...\n", strerror(errno));
- close(sock);
- continue;
+ value = 1;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)) < 0) {
+ log_error("sstp: failed to disable nagle: %s, closing connection...\n", strerror(errno));
+ close(sock);
+ continue;
+ }
}
conn = mempool_alloc(conn_pool);
@@ -1829,12 +1898,12 @@ static int sstp_connect(struct triton_md_handler_t *h)
conn->ctrl.name = "sstp";
conn->ctrl.ifname = "";
conn->ctrl.mppe = MPPE_UNSET;
- conn->ctrl.calling_station_id = _malloc(sizeof("255.255.255.255:65535"));
- conn->ctrl.called_station_id = _malloc(sizeof("255.255.255.255"));
- sprintf(conn->ctrl.calling_station_id, "%s:%d",
- inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
- getsockname(sock, &addr, &size);
- u_inet_ntoa(addr.sin_addr.s_addr, conn->ctrl.called_station_id);
+
+ conn->ctrl.calling_station_id = strdup(addr_buf);
+ addr.len = sizeof(addr.u);
+ getsockname(sock, &addr.u.sa, &addr.len);
+ sockaddr_ntop(&addr, addr_buf, sizeof(addr_buf));
+ conn->ctrl.called_station_id = strdup(addr_buf);
ppp_init(&conn->ppp);
conn->ppp.ses.ctrl = &conn->ctrl;
@@ -1867,6 +1936,8 @@ static void sstp_serv_close(struct triton_context_t *ctx)
serv->ssl_ctx = NULL;
#endif
+ if (serv->addr.u.sa.sa_family == AF_UNIX && serv->addr.u.sun.sun_path[0])
+ unlink(serv->addr.u.sun.sun_path);
}
#ifdef CRYPTO_OPENSSL
@@ -2001,6 +2072,7 @@ error:
static void load_config(void)
{
+ int ipmode;
char *opt;
opt = conf_get_opt("sstp", "verbose");
@@ -2054,7 +2126,9 @@ static void load_config(void)
conf_ip_pool = conf_get_opt("sstp", "ip-pool");
conf_ifname = conf_get_opt("sstp", "ifname");
- switch (iprange_check_activation()) {
+ ipmode = (serv.addr.u.sa.sa_family == AF_INET) ?
+ iprange_check_activation() : -1;
+ switch (ipmode) {
case IPRANGE_DISABLED:
log_warn("sstp: iprange module disabled, improper IP configuration of PPP interfaces may cause kernel soft lockup\n");
break;
@@ -2062,6 +2136,7 @@ static void load_config(void)
log_warn("sstp: no IP address range defined in section [%s], incoming sstp connections will be rejected\n",
IPRANGE_CONF_SECTION);
break;
+ case -1:
default:
/* Makes compiler happy */
break;
@@ -2076,40 +2151,65 @@ static struct sstp_serv_t serv = {
static void sstp_init(void)
{
- struct sockaddr_in addr;
+ struct sockaddr_t *addr = &serv.addr;
+ struct stat st;
+ int port, value;
char *opt;
- serv.hnd.fd = socket(PF_INET, SOCK_STREAM, 0);
+ opt = conf_get_opt("sstp", "port");
+ if (opt && atoi(opt) > 0)
+ port = atoi(opt);
+ else
+ port = SSTP_PORT;
+
+ opt = conf_get_opt("sstp", "bind");
+ if (opt && strncmp(opt, "unix:", sizeof("unix:") - 1) == 0) {
+ addr->len = sizeof(addr->u.sun);
+ addr->u.sun.sun_family = AF_UNIX;
+ snprintf(addr->u.sun.sun_path, sizeof(addr->u.sun.sun_path), "%s", opt + sizeof("unix:") - 1);
+ /* abstract socket support */
+ if (addr->u.sun.sun_path[0] == '@')
+ addr->u.sun.sun_path[0] = '\0';
+ } else if (opt && inet_pton(AF_INET6, opt, &addr->u.sin6.sin6_addr) > 0) {
+ addr->len = sizeof(addr->u.sin6);
+ addr->u.sin6.sin6_family = AF_INET6;
+ addr->u.sin6.sin6_port = htons(port);
+ } else {
+ addr->len = sizeof(addr->u.sin);
+ addr->u.sin.sin_family = AF_INET;
+ if (!opt || inet_pton(AF_INET, opt, &addr->u.sin.sin_addr) <= 0)
+ addr->u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr->u.sin.sin_port = htons(port);
+ }
+
+ serv.hnd.fd = socket(addr->u.sa.sa_family, SOCK_STREAM, 0);
if (serv.hnd.fd < 0) {
log_emerg("sstp: failed to create server socket: %s\n", strerror(errno));
return;
}
- fcntl(serv.hnd.fd, F_SETFD, fcntl(serv.hnd.fd, F_GETFD) | FD_CLOEXEC);
-
- addr.sin_family = AF_INET;
-
- opt = conf_get_opt("sstp", "bind");
- if (opt)
- addr.sin_addr.s_addr = inet_addr(opt);
- else
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- opt = conf_get_opt("sstp", "port");
- if (opt && atoi(opt) > 0)
- addr.sin_port = htons(atoi(opt));
- else
- addr.sin_port = htons(SSTP_PORT);
+ value = fcntl(serv.hnd.fd, F_GETFD);
+ if (value < 0 || fcntl(serv.hnd.fd, F_SETFD, value | FD_CLOEXEC) < 0) {
+ log_emerg("sstp: failed to set socket flags: %s\n", strerror(errno));
+ close(serv.hnd.fd);
+ return;
+ }
- setsockopt(serv.hnd.fd, SOL_SOCKET, SO_REUSEADDR, &serv.hnd.fd, 4);
+ if (addr->u.sa.sa_family == AF_UNIX) {
+ if (addr->u.sun.sun_path[0] &&
+ stat(addr->u.sun.sun_path, &st) == 0 && S_ISSOCK(st.st_mode)) {
+ unlink(addr->u.sun.sun_path);
+ }
+ } else
+ setsockopt(serv.hnd.fd, SOL_SOCKET, SO_REUSEADDR, &serv.hnd.fd, 4);
- if (bind(serv.hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
+ if (bind(serv.hnd.fd, &addr->u.sa, addr->len) < 0) {
log_emerg("sstp: failed to bind socket: %s\n", strerror(errno));
close(serv.hnd.fd);
return;
}
- if (listen(serv.hnd.fd, 100) < 0) {
+ if (listen(serv.hnd.fd, 10) < 0) {
log_emerg("sstp: failed to listen socket: %s\n", strerror(errno));
close(serv.hnd.fd);
return;