diff --git a/src/jconf.c b/src/jconf.c index f9690826..43716a36 100644 --- a/src/jconf.c +++ b/src/jconf.c @@ -109,6 +109,41 @@ parse_addr(const char *str_in, ss_addr_t *addr) free(str); } +static int +parse_dscp(char *str) +{ + size_t str_len = strlen(str); + + // Pre-defined values (EF, CSx, AFxy) + if (str_len == 2 && strcasecmp(str, "EF") == 0) { + return DSCP_EF; + } + + if (str_len == DSCP_CS_LEN && strncasecmp(str, "CS", 2) == 0) { + if (str[2] >= '0' && str[2] <= '7') { + // CSx = 8x + return (str[2] - '0') << 3; + } + } + + if (str_len == DSCP_AF_LEN && strncasecmp(str, "AF", 2) == 0) { + if (str[2] >= '1' && str[2] <= '4' && str[3] >= '1' && str[3] <= '3') { + // AFxy = 8x + 2y + return ((str[2] - '0') << 3) | ((str[3] - '0') << 1); + } + } + + // Manual hexadecimal mode (0xYZ) + char *endptr; + int dscp = (int)strtol(str, &endptr, 0); + if (*endptr == '\0' && dscp >= DSCP_MIN && dscp <= DSCP_MAX) { + return dscp; + } + + LOGE("Invalid DSCP value (%s)", str); + return DSCP_DEFAULT; +} + jconf_t * read_jconf(const char *file) { @@ -230,6 +265,23 @@ read_jconf(const char *file) conf.nofile = value->u.integer; } else if (strcmp(name, "nameserver") == 0) { conf.nameserver = to_string(value); + } else if (strcmp(name, "dscp") == 0) { + if (value->type == json_object) { + for (j = 0; j < value->u.object.length; j++) { + if (j >= MAX_DSCP_NUM) { + break; + } + json_value *v = value->u.object.values[j].value; + if (v->type == json_string) { + int dscp = parse_dscp(to_string(v)); + char *port = ss_strndup(value->u.object.values[j].name, + value->u.object.values[j].name_length); + conf.dscp[j].port = port; + conf.dscp[j].dscp = dscp; + conf.dscp_num = j + 1; + } + } + } } else if (strcmp(name, "tunnel_address") == 0) { conf.tunnel_address = to_string(value); } else if (strcmp(name, "mode") == 0) { diff --git a/src/jconf.h b/src/jconf.h index 891f6168..314d8236 100644 --- a/src/jconf.h +++ b/src/jconf.h @@ -24,12 +24,22 @@ #define MAX_PORT_NUM 1024 #define MAX_REMOTE_NUM 10 +#define MAX_DSCP_NUM 64 #define MAX_CONF_SIZE 128 * 1024 #define MAX_DNS_NUM 4 #define MAX_CONNECT_TIMEOUT 10 #define MAX_REQUEST_TIMEOUT 60 #define MIN_UDP_TIMEOUT 10 +#define DSCP_EF 0x2E +#define DSCP_MIN 0x0 +#define DSCP_MAX 0x3F +#define DSCP_DEFAULT 0x0 +#define DSCP_MIN_LEN 2 +#define DSCP_MAX_LEN 4 +#define DSCP_CS_LEN 3 +#define DSCP_AF_LEN 4 + #define TCP_ONLY 0 #define TCP_AND_UDP 1 #define UDP_ONLY 3 @@ -44,6 +54,11 @@ typedef struct { char *password; } ss_port_password_t; +typedef struct { + char *port; + int dscp; +} ss_dscp_t; + typedef struct { int remote_num; ss_addr_t remote_addr[MAX_REMOTE_NUM]; @@ -63,6 +78,8 @@ typedef struct { int reuse_port; int nofile; char *nameserver; + int dscp_num; + ss_dscp_t dscp[MAX_DSCP_NUM]; char *tunnel_address; int mode; int mtu; diff --git a/src/redir.c b/src/redir.c index 2a97d324..2a25cd35 100644 --- a/src/redir.c +++ b/src/redir.c @@ -786,6 +786,12 @@ accept_cb(EV_P_ ev_io *w, int revents) // Set non blocking setnonblocking(remotefd); + if (listener->tos >= 0) { + if (setsockopt(remotefd, IPPROTO_IP, IP_TOS, &listener->tos, sizeof(listener->tos)) != 0) { + ERROR("setsockopt IP_TOS"); + } + } + // Enable MPTCP if (listener->mptcp > 1) { int err = setsockopt(remotefd, SOL_TCP, listener->mptcp, &opt, sizeof(opt)); @@ -883,6 +889,9 @@ main(int argc, char **argv) ss_addr_t remote_addr[MAX_REMOTE_NUM]; char *remote_port = NULL; + int dscp_num = 0; + ss_dscp_t * dscp = NULL; + static struct option long_options[] = { { "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN }, { "mtu", required_argument, NULL, GETOPT_VAL_MTU }, @@ -1061,6 +1070,8 @@ main(int argc, char **argv) nofile = conf->nofile; } #endif + dscp_num = conf->dscp_num; + dscp = conf->dscp; } if (remote_num == 0 || remote_port == NULL || local_port == NULL @@ -1187,44 +1198,59 @@ main(int argc, char **argv) struct ev_loop *loop = EV_DEFAULT; - if (mode != UDP_ONLY) { - // Setup socket - int listenfd; - listenfd = create_and_bind(local_addr, local_port); - if (listenfd == -1) { - FATAL("bind() error"); - } - if (listen(listenfd, SOMAXCONN) == -1) { - FATAL("listen() error"); - } - setnonblocking(listenfd); + listen_ctx_t* listen_ctx_current = &listen_ctx; + do { + if (mode != UDP_ONLY) { + // Setup socket + int listenfd; + listenfd = create_and_bind(local_addr, local_port); + if (listenfd == -1) { + FATAL("bind() error"); + } + if (listen(listenfd, SOMAXCONN) == -1) { + FATAL("listen() error"); + } + setnonblocking(listenfd); - listen_ctx.fd = listenfd; + listen_ctx_current->fd = listenfd; - ev_io_init(&listen_ctx.io, accept_cb, listenfd, EV_READ); - ev_io_start(loop, &listen_ctx.io); - } + ev_io_init(&listen_ctx_current->io, accept_cb, listenfd, EV_READ); + ev_io_start(loop, &listen_ctx_current->io); + } - // Setup UDP - if (mode != TCP_ONLY) { - LOGI("UDP relay enabled"); - char *host = remote_addr[0].host; - char *port = remote_addr[0].port == NULL ? remote_port : remote_addr[0].port; - struct sockaddr_storage *storage = ss_malloc(sizeof(struct sockaddr_storage)); - memset(storage, 0, sizeof(struct sockaddr_storage)); - if (get_sockaddr(host, port, storage, 1, ipv6first) == -1) { - FATAL("failed to resolve the provided hostname"); + // Setup UDP + if (mode != TCP_ONLY) { + LOGI("UDP relay enabled"); + char *host = remote_addr[0].host; + char *port = remote_addr[0].port == NULL ? remote_port : remote_addr[0].port; + struct sockaddr_storage *storage = ss_malloc(sizeof(struct sockaddr_storage)); + memset(storage, 0, sizeof(struct sockaddr_storage)); + if (get_sockaddr(host, port, storage, 1, ipv6first) == -1) { + FATAL("failed to resolve the provided hostname"); + } + struct sockaddr *addr = (struct sockaddr *)storage; + init_udprelay(local_addr, local_port, addr, + get_sockaddr_len(addr), mtu, crypto, listen_ctx_current->timeout, NULL); } - struct sockaddr *addr = (struct sockaddr *)storage; - init_udprelay(local_addr, local_port, addr, - get_sockaddr_len(addr), mtu, crypto, listen_ctx.timeout, NULL); - } - if (mode == UDP_ONLY) { - LOGI("TCP relay disabled"); - } + if (mode == UDP_ONLY) { + LOGI("TCP relay disabled"); + } - LOGI("listening at %s:%s", local_addr, local_port); + if(listen_ctx_current->tos) { + LOGI("listening at %s:%s (TOS/DSCP 0x%x)", local_addr, local_port, listen_ctx_current->tos); + } else { + LOGI("listening at %s:%s", local_addr, local_port); + } + + // Handle additionals TOS/DSCP listening ports + if (dscp_num > 0) { + listen_ctx_current = (listen_ctx_t*) malloc(sizeof(listen_ctx_t)); + listen_ctx_current = memcpy(listen_ctx_current, &listen_ctx, sizeof(listen_ctx_t)); + local_port = dscp[dscp_num-1].port; + listen_ctx_current->tos = dscp[dscp_num-1].dscp; + } + } while (dscp_num-- > 0); // setuid if (user != NULL && !run_as(user)) { diff --git a/src/redir.h b/src/redir.h index 697b5405..d9d59177 100644 --- a/src/redir.h +++ b/src/redir.h @@ -37,6 +37,7 @@ typedef struct listen_ctx { int timeout; int fd; int mptcp; + int tos; struct sockaddr **remote_addr; } listen_ctx_t;