diff --git a/tempesta.sh b/tempesta.sh index 14253d0c3a..8a2c127198 100755 --- a/tempesta.sh +++ b/tempesta.sh @@ -7,7 +7,7 @@ SSOCKET=sync_socket TDB=tempesta_db TFW=tempesta_fw -SCHED=tfw_sched_dummy +SCHED=tfw_sched_match TFW_ROOT=`pwd`/$TFW TFW_CACHE_SIZE=`expr 256 \* 1024` TFW_CACHE_PATH=$TFW_ROOT/cache @@ -45,7 +45,7 @@ start() cache_path="$TFW_CACHE_PATH" [ $? -ne 0 ] && error "cannot load tempesta module" - insmod $TFW_ROOT/sched/$SCHED.ko + insmod $TFW_ROOT/sched/match/$SCHED.ko [ $? -ne 0 ] && error "cannot load scheduler module" sysctl --load=tempesta.sysctl.conf diff --git a/tempesta_fw/if.c b/tempesta_fw/if.c index eb0676de15..bb44b1b441 100644 --- a/tempesta_fw/if.c +++ b/tempesta_fw/if.c @@ -20,9 +20,6 @@ * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include -#include -#include #include #include "tempesta.h" @@ -30,241 +27,6 @@ #include "lib.h" #include "log.h" -/* - * ------------------------------------------------------------------------ - * Helping routines - * ------------------------------------------------------------------------ - */ -/** - * Retrurn number of tokens in @str separated by space ([ \t]+). - * @str is null-terminated string. - */ -static int -tfw_str_tokens_count(const char *str) -{ - int n = 0; - - /* Eat empty string prefix. */ - while (*str == ' ' || *str == '\t') - ++str; - - while (*str) { - ++n; - /* Eat a word. */ - while (*str && *str != ' ' && *str != '\t') - ++str; - /* Eat all separators. */ - while (*str && (*str == ' ' || *str == '\t')) - ++str; - } - - return n; -} - -static int -tfw_inet_pton_ipv4(char **p, struct sockaddr_in *addr) -{ - int octet = -1, i = 0, port = 0; - unsigned char *a = (unsigned char *)&addr->sin_addr.s_addr; - - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = 0; - for ( ; **p && !isspace(**p); ++*p) { - if (isdigit(**p)) { - octet = (octet == -1) - ? **p - '0' - : octet * 10 + **p - '0'; - if ((!port && octet > 255) || octet > 0xFFFF) - return -EINVAL; - } - else if (octet >= 0 && ((**p == '.' && i < 4) - || (**p == ':' && i == 3))) - { - a[i++] = octet; - octet = -1; - port = **p == ':'; - } else - return -EINVAL; - } - if (octet >= 0) { - if (i == 3) { - /* Default port. */ - a[i] = octet; - addr->sin_port = htons(DEF_PORT); - return 0; - } - else if (i == 4) { - addr->sin_port = htons(octet); - return 0; - } - } - - return -EINVAL; -} - -static int -tfw_inet_pton_ipv6(char **p, struct sockaddr_in6 *addr) -{ -#define XD(x) ((x >= 'a') ? 10 + x - 'a' : x - '0') - - int words[9] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - int a, hole = -1, i = 0, port = -1, ipv4_mapped = 0; - - memset(addr, 0, sizeof(*addr)); - addr->sin6_family = AF_INET6; - - for ( ; **p && !isspace(**p); ++*p) { - if (i > 7 && !(i == 8 && port == 1)) - return -EINVAL; - if (**p == '[') { - port = 0; - } - else if (**p == ':') { - if (*(*p + 1) == ':') { - /* - * Leave current (if empty) or next (otherwise) - * word as a hole. - */ - ++*p; - hole = (words[i] != -1) ? ++i : i; - } else if (words[i] == -1) { - return -EINVAL; - } - /* Store port in the last word. */ - i = (port == 1) ? 8 : i + 1; - } - else if (**p == '.') { - ++i; - if (ipv4_mapped) - continue; - if (words[0] != -1 || words[1] != 0xFFFF - || words[2] == -1 || i != 3 || hole != 0) - return -EINVAL; - /* - * IPv4 mapped address. - * Recalculate the first 2 hexademical octets from to - * 1 decimal octet. - */ - addr->sin6_family = AF_INET; - words[0] = ((words[2] & 0xF000) >> 12) * 1000 - + ((words[2] & 0x0F00) >> 8) * 100 - + ((words[2] & 0x00F0) >> 4) * 10 - + (words[2] & 0x000F); - if (words[0] > 255) - return -EINVAL; - ipv4_mapped = 1; - i = 1; - words[1] = words[2] = -1; - } - else if (isxdigit(**p)) { - words[i] = words[i] == -1 ? 0 : words[i]; - if (ipv4_mapped || port == 1) { - if (!isdigit(**p)) - return -EINVAL; - words[i] = words[i] * 10 + **p - '0'; - if (port) { - if (words[i] > 0xFFFF) - return -EINVAL; - } - else if (ipv4_mapped && words[i] > 255) { - return -EINVAL; - } - } else { - words[i] = (words[i] << 4) | XD(tolower(**p)); - if (words[i] > 0xFFFF) - return -EINVAL; - } - } - else if (**p == ']') { - port = 1; - } - else - return -EINVAL; - } - - /* Some sanity checks. */ - if (!port || (port != -1 && words[8] <= 0) - || (ipv4_mapped && hole == -1) - || (ipv4_mapped && port == -1 && i != 3) - || (port == 1 && i != 8) - || (port == -1 && i < 7 && hole == -1)) - return -EINVAL; - - /* Copy parsed address. */ - if (ipv4_mapped) { - struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; - for (i = 0; i < 4; ++i) - addr4->sin_addr.s_addr |= words[i] << (3 - i) * 8; - } else { - for (i = a = 7; i >= 0 && a >= 0; ) { - if (words[i] == -1) { - if (i > hole) - --i; - else - if (a-- == i && i) - --i; - } else - addr->sin6_addr.s6_addr16[a--] - = htons(words[i--]); - } - } - - /* Set port. */ - if (port == -1) { - addr->sin6_port = htons(DEF_PORT); - return 0; - } - addr->sin6_port = htons(words[8]); - - return 0; -#undef XD -} - -/** - * Parse IPv4 and IPv6 addresses with optional port. - * See RFC5952. - * - * @p - string pointer, updated by the function. - * @addr - distination to write as a pointer to a union of sockaddr_in and - * sockaddr_in6. - */ -static int -tfw_inet_pton(char **p, void *addr) -{ - int mode = 0; - - /* Eat empty string prefix. */ - while (**p && isspace(**p)) - ++*p; - - /* Determine type of the address (IPv4/IPv6). */ - if (**p == '[' || isalpha(**p)) { - mode = 6; - } else { - char *p1 = *p; - while (*p1 && isdigit(*p1)) - p1++; - if (*p1 == ':') { - mode = 6; - } - else if (*p1 == '.') { - mode = 4; - } - else { - TFW_ERR("bad string: %s\n", *p); - return -EINVAL; - } - } - - if (mode == 4) - return tfw_inet_pton_ipv4(p, addr); - if (mode == 6) - return tfw_inet_pton_ipv6(p, addr); - - TFW_ERR("Can't parse address %s\n", *p); - return -EINVAL; -} - /* * ------------------------------------------------------------------------ * Sysctl and /proc interfaces diff --git a/tempesta_fw/lib.c b/tempesta_fw/lib.c index b772c7d376..21fa402658 100644 --- a/tempesta_fw/lib.c +++ b/tempesta_fw/lib.c @@ -20,16 +20,252 @@ * Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include +#include +#include +#include + #include "tempesta.h" #include "lib.h" #include "log.h" + +/** + * Retrurn number of tokens in @str separated by space ([ \t]+). + * @str is null-terminated string. + */ +int +tfw_str_tokens_count(const char *str) +{ + int n = 0; + + /* Eat empty string prefix. */ + while (*str == ' ' || *str == '\t') + ++str; + + while (*str) { + ++n; + /* Eat a word. */ + while (*str && *str != ' ' && *str != '\t') + ++str; + /* Eat all separators. */ + while (*str && (*str == ' ' || *str == '\t')) + ++str; + } + + return n; +} + +static int +tfw_inet_pton_ipv4(char **p, struct sockaddr_in *addr) +{ + int octet = -1, i = 0, port = 0; + unsigned char *a = (unsigned char *)&addr->sin_addr.s_addr; + + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = 0; + for ( ; **p && !isspace(**p); ++*p) { + if (isdigit(**p)) { + octet = (octet == -1) + ? **p - '0' + : octet * 10 + **p - '0'; + if ((!port && octet > 255) || octet > 0xFFFF) + return -EINVAL; + } + else if (octet >= 0 && ((**p == '.' && i < 4) + || (**p == ':' && i == 3))) + { + a[i++] = octet; + octet = -1; + port = **p == ':'; + } else + return -EINVAL; + } + if (octet >= 0) { + if (i == 3) { + /* Default port. */ + a[i] = octet; + addr->sin_port = htons(DEF_PORT); + return 0; + } + else if (i == 4) { + addr->sin_port = htons(octet); + return 0; + } + } + + return -EINVAL; +} + +static int +tfw_inet_pton_ipv6(char **p, struct sockaddr_in6 *addr) +{ +#define XD(x) ((x >= 'a') ? 10 + x - 'a' : x - '0') + + int words[9] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + int a, hole = -1, i = 0, port = -1, ipv4_mapped = 0; + + memset(addr, 0, sizeof(*addr)); + addr->sin6_family = AF_INET6; + + for ( ; **p && !isspace(**p); ++*p) { + if (i > 7 && !(i == 8 && port == 1)) + return -EINVAL; + if (**p == '[') { + port = 0; + } + else if (**p == ':') { + if (*(*p + 1) == ':') { + /* + * Leave current (if empty) or next (otherwise) + * word as a hole. + */ + ++*p; + hole = (words[i] != -1) ? ++i : i; + } else if (words[i] == -1) { + return -EINVAL; + } + /* Store port in the last word. */ + i = (port == 1) ? 8 : i + 1; + } + else if (**p == '.') { + ++i; + if (ipv4_mapped) + continue; + if (words[0] != -1 || words[1] != 0xFFFF + || words[2] == -1 || i != 3 || hole != 0) + return -EINVAL; + /* + * IPv4 mapped address. + * Recalculate the first 2 hexademical octets from to + * 1 decimal octet. + */ + addr->sin6_family = AF_INET; + words[0] = ((words[2] & 0xF000) >> 12) * 1000 + + ((words[2] & 0x0F00) >> 8) * 100 + + ((words[2] & 0x00F0) >> 4) * 10 + + (words[2] & 0x000F); + if (words[0] > 255) + return -EINVAL; + ipv4_mapped = 1; + i = 1; + words[1] = words[2] = -1; + } + else if (isxdigit(**p)) { + words[i] = words[i] == -1 ? 0 : words[i]; + if (ipv4_mapped || port == 1) { + if (!isdigit(**p)) + return -EINVAL; + words[i] = words[i] * 10 + **p - '0'; + if (port) { + if (words[i] > 0xFFFF) + return -EINVAL; + } + else if (ipv4_mapped && words[i] > 255) { + return -EINVAL; + } + } else { + words[i] = (words[i] << 4) | XD(tolower(**p)); + if (words[i] > 0xFFFF) + return -EINVAL; + } + } + else if (**p == ']') { + port = 1; + } + else + return -EINVAL; + } + + /* Some sanity checks. */ + if (!port || (port != -1 && words[8] <= 0) + || (ipv4_mapped && hole == -1) + || (ipv4_mapped && port == -1 && i != 3) + || (port == 1 && i != 8) + || (port == -1 && i < 7 && hole == -1)) + return -EINVAL; + + /* Copy parsed address. */ + if (ipv4_mapped) { + struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; + for (i = 0; i < 4; ++i) + addr4->sin_addr.s_addr |= words[i] << (3 - i) * 8; + } else { + for (i = a = 7; i >= 0 && a >= 0; ) { + if (words[i] == -1) { + if (i > hole) + --i; + else + if (a-- == i && i) + --i; + } else + addr->sin6_addr.s6_addr16[a--] + = htons(words[i--]); + } + } + + /* Set port. */ + if (port == -1) { + addr->sin6_port = htons(DEF_PORT); + return 0; + } + addr->sin6_port = htons(words[8]); + + return 0; +#undef XD +} + +/** + * Parse IPv4 and IPv6 addresses with optional port. + * See RFC5952. + * + * @p - string pointer, updated by the function. + * @addr - distination to write as a pointer to a union of sockaddr_in and + * sockaddr_in6. + */ +int +tfw_inet_pton(char **p, void *addr) +{ + int mode = 0; + + /* Eat empty string prefix. */ + while (**p && isspace(**p)) + ++*p; + + /* Determine type of the address (IPv4/IPv6). */ + if (**p == '[' || isalpha(**p)) { + mode = 6; + } else { + char *p1 = *p; + while (*p1 && isdigit(*p1)) + p1++; + if (*p1 == ':') { + mode = 6; + } + else if (*p1 == '.') { + mode = 4; + } + else { + TFW_ERR("bad string: %s\n", *p); + return -EINVAL; + } + } + + if (mode == 4) + return tfw_inet_pton_ipv4(p, addr); + if (mode == 6) + return tfw_inet_pton_ipv6(p, addr); + + TFW_ERR("Can't parse address %s\n", *p); + return -EINVAL; +} +EXPORT_SYMBOL(tfw_inet_pton); + /** * Print UPv4/IPv6 address in network byte order to @buf. * @buf must be MAX_ADDR_LEN bytes in size. */ int -tfw_inet_ntop(void *addr, char *buf) +tfw_inet_ntop(const void *addr, char *buf) { unsigned short family = *(unsigned short *)addr; @@ -60,6 +296,7 @@ tfw_inet_ntop(void *addr, char *buf) return 0; } +EXPORT_SYMBOL(tfw_inet_ntop); static bool tfw_addr_eq_inet(const struct sockaddr_in *a, const struct sockaddr_in *b) @@ -114,3 +351,4 @@ tfw_addr_eq(const void *addr1, const void *addr2) return false; } } +EXPORT_SYMBOL(tfw_addr_eq); diff --git a/tempesta_fw/lib.h b/tempesta_fw/lib.h index 6004cae9b8..6d4b627cfe 100644 --- a/tempesta_fw/lib.h +++ b/tempesta_fw/lib.h @@ -22,7 +22,26 @@ #include "str.h" -int tfw_inet_ntop(void *addr, char *buf); +#ifdef DEBUG +#define DEBUG_IS_DEFINED 1 +#else +#define DEBUG_IS_DEFINED 0 +#endif + +#define IF_DEBUG if (DEBUG_IS_DEFINED) + +#ifndef packedenum +#define packedenum enum __attribute__((packed)) +#endif + +#ifndef STRINGIFY +#define _STRINGIFY(x) #x +#define STRINGIFY(x) _STRINGIFY(x) +#endif + +int tfw_str_tokens_count(const char *str); +int tfw_inet_pton(char **p, void *addr); +int tfw_inet_ntop(const void *addr, char *buf); bool tfw_addr_eq(const void *addr1, const void *addr2); #endif /* __TFW_LIB_H__ */ diff --git a/tempesta_fw/pool.c b/tempesta_fw/pool.c index e35b70c977..994e7ec724 100644 --- a/tempesta_fw/pool.c +++ b/tempesta_fw/pool.c @@ -47,6 +47,7 @@ __tfw_pool_new(size_t n) return p; } +EXPORT_SYMBOL(__tfw_pool_new); void * tfw_pool_alloc(TfwPool *p, size_t n) @@ -61,6 +62,7 @@ tfw_pool_alloc(TfwPool *p, size_t n) return a; } +EXPORT_SYMBOL(tfw_pool_alloc); void * tfw_pool_realloc(TfwPool *p, void *ptr, size_t old_n, size_t new_n) @@ -82,9 +84,11 @@ tfw_pool_realloc(TfwPool *p, void *ptr, size_t old_n, size_t new_n) return a; } +EXPORT_SYMBOL(tfw_pool_realloc); void tfw_pool_free(TfwPool *p) { free_pages((unsigned long)p, p->order); } +EXPORT_SYMBOL(tfw_pool_free); diff --git a/tempesta_fw/sched.h b/tempesta_fw/sched.h index 9a376f707e..7d58c4942b 100644 --- a/tempesta_fw/sched.h +++ b/tempesta_fw/sched.h @@ -35,6 +35,15 @@ * the functionality to different module type? */ + +/** + * The maximum number of servers that may be added to any scheduler. + * + * Schedulers are allowed to reject tfw_sched_add_srv() calls if the count of + * servers reaches this number. + */ +#define TFW_SCHED_MAX_SERVERS 64 + typedef struct { const char *name; diff --git a/tempesta_fw/sched/Makefile b/tempesta_fw/sched/Makefile index 93fdfbcc25..9fe451b9d0 100644 --- a/tempesta_fw/sched/Makefile +++ b/tempesta_fw/sched/Makefile @@ -16,7 +16,5 @@ # this program; if not, write to the Free Software Foundation, Inc., 59 # Temple Place - Suite 330, Boston, MA 02111-1307, USA. -EXTRA_CFLAGS = -I$(src)/../../tempesta_db -I$(src)/../../sync_socket -DDEBUG -Wextra -Wno-unused-parameter - -obj-m = tfw_sched_rr.o tfw_sched_dummy.o tfw_sched_match.o +obj-m = dummy/ rr/ match/ diff --git a/tempesta_fw/sched/dummy/Makefile b/tempesta_fw/sched/dummy/Makefile new file mode 100644 index 0000000000..4133544a21 --- /dev/null +++ b/tempesta_fw/sched/dummy/Makefile @@ -0,0 +1,21 @@ +# Tempesta FW +# +# Copyright (C) 2012-2014 NatSys Lab. (info@natsys-lab.com). +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, +# or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +EXTRA_CFLAGS = -DDEBUG -I$(src)/../../ -I$(src)/../../../tempesta_db -I$(src)/../../../sync_socket + +obj-m = tfw_sched_dummy.o diff --git a/tempesta_fw/sched/tfw_sched_dummy.c b/tempesta_fw/sched/dummy/tfw_sched_dummy.c similarity index 97% rename from tempesta_fw/sched/tfw_sched_dummy.c rename to tempesta_fw/sched/dummy/tfw_sched_dummy.c index 6e2a5f98cf..7a662ae6a7 100644 --- a/tempesta_fw/sched/tfw_sched_dummy.c +++ b/tempesta_fw/sched/dummy/tfw_sched_dummy.c @@ -19,8 +19,8 @@ */ #include -#include "../log.h" -#include "../sched.h" +#include "log.h" +#include "sched.h" MODULE_AUTHOR(TFW_AUTHOR); MODULE_DESCRIPTION("Tempesta dummy scheduler"); diff --git a/tempesta_fw/sched/match/Makefile b/tempesta_fw/sched/match/Makefile new file mode 100644 index 0000000000..ec683f1233 --- /dev/null +++ b/tempesta_fw/sched/match/Makefile @@ -0,0 +1,22 @@ +# Tempesta FW +# +# Copyright (C) 2012-2014 NatSys Lab. (info@natsys-lab.com). +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, +# or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +EXTRA_CFLAGS = -DDEBUG -I$(src)/../../ -I$(src)/../../../tempesta_db -I$(src)/../../../sync_socket + +obj-m = tfw_sched_match.o +tfw_sched_match-objs += sysctl.o sched_match.o diff --git a/tempesta_fw/sched/match/sched_match.c b/tempesta_fw/sched/match/sched_match.c new file mode 100644 index 0000000000..3bec9de405 --- /dev/null +++ b/tempesta_fw/sched/match/sched_match.c @@ -0,0 +1,565 @@ +/** + * Tempesta FW + * + * Copyright (C) 2012-2014 NatSys Lab. (info@natsys-lab.com). + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "sched.h" +#include "server.h" +#include "http.h" + +#include "tfw_sched_match.h" + +MODULE_AUTHOR(TFW_AUTHOR); +MODULE_DESCRIPTION("Tempesta request-matching scheduler"); +MODULE_VERSION("0.0.1"); +MODULE_LICENSE("GPL"); + + +/* How does the scheduler work: + * + * There is a global matching table that contains rules of the following format: + * { subject, operator, pattern, servers_list } + * For example: + * { SUBJ_HOST, OP_EQUAL, "example.com", { TfwServer, TfwServer, TfwServer } } + * { SUBJ_URI, OP_PREFIX, "/foo/bar", { TfwServer } } + * { SUBJ_URI, OP_PREFIX, "/", { TfwServer, TfwServer } } + * + * The 'subject' determines a field of a HTTP request (uri, host, header, etc). + * The 'operator' determines how to compare the field with the 'pattern'. + * Each incoming HTTP request is sequentially matched against each entry in the + * table until. If a matching entry is found, then the algorithm stops and + * returns a server from the list. + * + */ + +typedef struct { + u16 counter; + u8 srv_max; + u8 srv_n; + TfwServer *srv[0]; +} SrvList; + +#define SIZE_OF_SRV_LIST(n) (sizeof(SrvList) + sizeof(TfwServer) * (n)) + + +typedef struct { + subj_t subj; + op_t op; + short len; + const char *pattern; + SrvList *servers; +} MatchTblEntry; + +typedef struct { + int count; + MatchTblEntry *entries[RULE_MAX_COUNT]; + TfwPool *pool; +} MatchTbl; + + +/** + * The table is not updated in-place. + * Instead, each time something is modified, a new table is built and the + * pointer is replaced via RCU. + */ +static MatchTbl *match_tbl = NULL; +static SrvList *added_servers = NULL; +static RuleTbl rule_tbl; + +DEFINE_SPINLOCK(sched_match_write_lock); + + +static void +dbg_print_entry(const char *msg, const MatchTblEntry *e, bool print_servers) +{ + DBG("%s: subj=%d op=%d pattern='%.*s'\n", + msg, e->subj, e->op, e->len, e->pattern); + + if (print_servers) { + int i; + char buf[32]; + const SrvList *s = e->servers; + + DBG("Servers (total %d):\n", s->srv_n); + + for (i = 0; i < s->srv_n; ++i) { + tfw_server_snprint(s->srv[i], buf, sizeof(buf)); + DBG(" #%d - %s\n", i, buf); + } + } +} + + +static int +srv_list_find(const SrvList *list, const TfwServer *srv) +{ + int i; + + BUG_ON(!list || !srv); + + for (i = 0; i < list->srv_n; ++i) { + if (list->srv[i] == srv) + return i; + } + + return -1; +} + +static int +srv_list_add(SrvList *list, TfwServer *srv) +{ + BUG_ON(!list || !srv); + + if (srv_list_find(list, srv) > 0) { + ERR("The server is already present in the list\n"); + return -EEXIST; + } else if (list->srv_n >= list->srv_max) { + ERR("No space left in the servers list\n"); + return -ENOMEM; + } + + list->srv[list->srv_n] = srv; + ++list->srv_n; + + return 0; +} + +static int +srv_list_del(SrvList *list, TfwServer *srv) +{ + int idx; + + BUG_ON(!list || !srv); + + idx = srv_list_find(list, srv); + + if (idx < 0) { + ERR("Server is not found in the list\n"); + return -ENOENT; + } + + list->srv[idx] = list->srv[list->srv_n - 1]; + list->srv[list->srv_n] = NULL; + --list->srv_n; + + return 0; +} + +static TfwServer * +srv_list_get_rr(SrvList *list) +{ + unsigned int n; + TfwServer *srv; + + BUG_ON(!list); + + do { + n = list->srv_n; + if (!n) { + ERR("The servers list is empty\n"); + return NULL; + } + + srv = list->srv[list->counter++ % n]; + } while (!srv); + + return srv; +} + +static TfwServer * +srv_list_get_by_addr(const SrvList *list, const TfwAddr *addr) +{ + int i; + int ret; + TfwServer *curr_srv; + TfwAddr curr_addr; + + BUG_ON(!list || !addr); + + for (i = 0; i < list->srv_n; ++i) { + curr_srv = list->srv[i]; + + ret = tfw_server_get_addr(curr_srv, &curr_addr); + if (ret) { + ERR("Can't obtain address of the server: %p\n", curr_srv); + return NULL; + } + + if (tfw_addr_eq(addr, &curr_addr)) { + return curr_srv; + } + } + + return NULL; +} + + +/** + * Merge a table of rules and a list of servers into a single matching table. + * + * The function copies elements from @rule_tbl into @tbl and resolves IP + * addresses to TfwServer objects from the given @all_servers list. + * + * @rule_tbl Contains a set of rules and corresponding IP addresses. + * @all_servers Contains a list of all known servers. + * + * @tbl The output table to be filled. + * Not allocated automatically, upon the call it must be pre-allocated + * using tfw_pool_new(). + * + * Also, the function uses @tbl->pool to allocate new elements, so you are + * responsible for freeing them, even if the function returns an error. + */ +int +fill_match_tbl(const RuleTbl *rule_tbl, const SrvList *all_servers, MatchTbl *tbl) +{ + int rule_idx, addr_idx, pattern_len; + char *pattern; + const Rule *rule; + MatchTblEntry *entry; + SrvList *servers; + + for (rule_idx = 0; rule_idx < rule_tbl->rules_n; ++rule_idx) { + rule = &rule_tbl->rules[rule_idx]; + pattern_len = strnlen(rule->pattern, sizeof(rule->pattern)); + + entry = tfw_pool_alloc(tbl->pool, sizeof(*entry)); + pattern = tfw_pool_alloc(tbl->pool, pattern_len); + servers = tfw_pool_alloc(tbl->pool, SIZE_OF_SRV_LIST(rule->addrs_n)); + if (!entry || !pattern || !servers) { + ERR("Can't allocate memory\n"); + return -1; + } + + servers->srv_max = rule->addrs_n; + + for (addr_idx = 0; addr_idx < rule->addrs_n; ++addr_idx) { + const TfwAddr *addr = &rule->addrs[addr_idx]; + TfwServer *srv = srv_list_get_by_addr(all_servers, addr); + if (srv) { + srv_list_add(servers, srv); + } else { + TFW_ERR_ADDR("No server found for addr", addr); + } + } + + memcpy(pattern, rule->pattern, pattern_len); + entry->subj = rule->subj; + entry->op = rule->op; + entry->pattern = pattern; + entry->len = pattern_len; + entry->servers = servers; + + tbl->entries[tbl->count++] = entry; + } + + return 0; +} + + +/** + * The function does two things: + * 1. Builds new MatchTbl from a set of Rules (stored in 'rule_tbl') and a set + * of online servers (stored in 'added_servers'). + * 2. Replace the global 'match_tbl' with a fresh one using the RCU mechanism. + * + * It should be called when rules or servers are changed. + */ +static int +refresh_match_tbl(void) +{ + int ret; + MatchTbl *old_tbl, *new_tbl; + + new_tbl = tfw_pool_new(MatchTbl, TFW_POOL_ZERO); + if (!new_tbl) { + ERR("Can't create a new matching tabe\n"); + return -1; + } + + spin_lock_bh(&sched_match_write_lock); + ret = fill_match_tbl(&rule_tbl, added_servers, new_tbl); + spin_unlock_bh(&sched_match_write_lock); + + if (ret) { + ERR("Can't fill a new matching table\n"); + tfw_pool_free(new_tbl->pool); + return ret; + } + + DBG("Replacing the matching table\n"); + + old_tbl = match_tbl; + rcu_assign_pointer(match_tbl, new_tbl); + synchronize_rcu(); + + if (old_tbl) { + DBG("Freeing old matching table\n"); + tfw_pool_free(old_tbl->pool); + } + + return 0; +} + +/** + * Evaluate an "expression" of a form: (subject [operator] pattern). + * + * The function maps a given @op to a C function that compares strings, + * passes arguments @str and @cstr to it and returns the result of comparison. + */ +static bool +apply_str_op(op_t op, const TfwStr *str, const char *cstr, int cstr_len) +{ + + static const typeof(&tfw_str_eq_cstr_ci) fns[] = { + [OP_EQUAL] = tfw_str_eq_cstr_ci, + [OP_PREFIX] = tfw_str_startswith_cstr_ci, + }; + + BUG_ON(op >= ARRAY_SIZE(fns)); + BUG_ON(!fns[op]); + + return fns[op](str, cstr, cstr_len); +} + + +static bool +match_uri(const TfwHttpReq *r, const MatchTblEntry *e) +{ + return apply_str_op(e->op, &r->uri, e->pattern, e->len); +} + +static bool +match_host(const TfwHttpReq *r, const MatchTblEntry *e) +{ + const TfwStr *host = &r->host; + + if (!host) + host = &r->h_tbl->tbl[TFW_HTTP_HDR_HOST].field; + + return apply_str_op(e->op, host, e->pattern, e->len); +} + +static bool +match_any_header(const TfwHttpReq *r, const MatchTblEntry *e) +{ + int i; + TfwStr *hdr; + TfwHttpHdrTbl *tbl = r->h_tbl; + + for (i = 0; i < tbl->size; ++i) { + hdr = &tbl->tbl[i].field; + if (apply_str_op(e->op, hdr, e->pattern, e->len)) + return true; + + } + + return false; +} + +static bool +match(const TfwHttpReq *req, const MatchTblEntry *entry) +{ + static const typeof(&match_uri) fns[] = { + [SUBJ_HOST] = match_host, + [SUBJ_URI] = match_uri, + [SUBJ_HEADER] = match_any_header, + }; + subj_t subj = entry->subj; + + BUG_ON(subj >= ARRAY_SIZE(fns)); + BUG_ON(!fns[subj]); + + return fns[subj](req, entry); +} + + +static TfwServer * +do_matches(const TfwHttpReq *req, const MatchTbl *tbl) +{ + int i; + MatchTblEntry *entry; + TfwServer *srv; + + DBG("Matching request: %p against %d rules\n", req, tbl->count); + + for (i = 0; i < tbl->count; ++i) { + entry = tbl->entries[i]; + + if (match(req, entry)) { + dbg_print_entry("Match", entry, true); + + srv = srv_list_get_rr(entry->servers); + if (srv) + return srv; + } else { + dbg_print_entry("No match", entry, false); + } + } + + return NULL; +} + +TfwServer * +tfw_sched_match_get_srv(TfwMsg *msg) +{ + TfwServer *srv = NULL; + MatchTbl *tbl = NULL; + + if (!added_servers->srv_n) { + ERR("The scheduler's server list is empty\n"); + return NULL; + } + + rcu_read_lock(); + tbl = rcu_dereference(match_tbl); + if (!tbl) { + ERR("The scheduler's matchig table is empty\n"); + } else { + srv = do_matches((TfwHttpReq *)msg, tbl); + } + rcu_read_unlock(); + + if (!srv) + ERR("A matching server is not found\n"); + + return srv; +} + + +int +tfw_sched_match_add_srv(TfwServer *srv) +{ + int ret = 0; + + DBG("Adding server: %p\n", srv); + + spin_lock_bh(&sched_match_write_lock); + ret = srv_list_add(added_servers, srv); + spin_unlock_bh(&sched_match_write_lock); + + if (ret) { + ERR("Can't add the server to the scheduler: %p\n", srv); + return -1; + } + + ret = refresh_match_tbl(); + if (ret) { + ERR("Can't re-build the matching table\n"); + } + + return ret; +} + +int +tfw_sched_match_del_srv(TfwServer *srv) +{ + int ret = 0; + + DBG("Deleting server: %p\n", srv); + + spin_lock_bh(&sched_match_write_lock); + ret = srv_list_del(added_servers, srv); + spin_unlock_bh(&sched_match_write_lock); + + if (ret) { + ERR("Can't delete the server from the scheduler: %p\n", srv); + return -1; + } + + ret = refresh_match_tbl(); + if (ret) { + ERR("Can't refresh the matching table\n"); + } + + return ret; +} + + +int +apply_new_rules(const RuleTbl *tbl) +{ + int ret; + + DBG("Applying new matching rules\n"); + + spin_lock_bh(&sched_match_write_lock); + memcpy(&rule_tbl, tbl, sizeof(rule_tbl)); + spin_unlock_bh(&sched_match_write_lock); + + ret = refresh_match_tbl(); + if (ret) { + ERR("Can't refresh the matching table\n"); + } + + return ret; +} + + +extern int sysctl_register(void); +extern void sysctl_unregister(void); + +int +tfw_sched_match_init(void) +{ + int ret; + + static TfwScheduler tfw_sched_rr_mod = { + .name = "match", + .get_srv = tfw_sched_match_get_srv, + .add_srv = tfw_sched_match_add_srv, + .del_srv = tfw_sched_match_del_srv + }; + + LOG("init\n"); + + added_servers = kzalloc(SIZE_OF_SRV_LIST(TFW_SCHED_MAX_SERVERS), GFP_KERNEL); + if (!added_servers) { + ERR("Can't allocate servers list\n"); + return -ENOMEM; + } + added_servers->srv_max = TFW_SCHED_MAX_SERVERS; + + + ret = sysctl_register(); + if (ret) { + ERR("Can't register the sysctl table\n"); + return ret; + } + + ret = tfw_sched_register(&tfw_sched_rr_mod); + if (ret) { + ERR("Can't register the scheduler module\n"); + } + + return ret; +} +module_init(tfw_sched_match_init); + +void +tfw_sched_match_exit(void) +{ + tfw_sched_unregister(); + sysctl_unregister(); + kfree(added_servers); +} +module_exit(tfw_sched_match_exit); + + diff --git a/tempesta_fw/sched/match/sysctl.c b/tempesta_fw/sched/match/sysctl.c new file mode 100644 index 0000000000..d9c3b7d733 --- /dev/null +++ b/tempesta_fw/sched/match/sysctl.c @@ -0,0 +1,429 @@ +/** + * Tempesta FW + * + * Copyright (C) 2012-2014 NatSys Lab. (info@natsys-lab.com). + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "tfw_sched_match.h" + +#define RULES_TEXT_BUF_SIZE 1024 +#define IP_ADDR_TEXT_BUF_SIZE 64 + + + +/* + * The following code is a sysctl interface with a primitive descend parser + * for the configuration. The code is a bit awkward, it is a subject to change + * in the near future. + */ + +typedef enum { + TOKEN_NA = 0, + TOKEN_LBRACE, + TOKEN_RBRACE, + TOKEN_STR, +} token_t; + +static const char *token_str_tbl[] = { + [TOKEN_NA] = STRINGIFY(TOKEN_NA), + [TOKEN_LBRACE] = STRINGIFY(TOKEN_LBRACE), + [TOKEN_RBRACE] = STRINGIFY(TOKEN_RBRACE), + [TOKEN_STR] = STRINGIFY(TOKEN_STR), +}; + +const char * +token_str(token_t t) +{ + BUG_ON(t >= ARRAY_SIZE(token_str_tbl)); + return token_str_tbl[t]; +} + + +typedef struct { + token_t token; + int len; + const char *lexeme; + const char *pos; + Rule *rule; + RuleTbl *tbl; +} ParserState; + + +#define PARSER_ERR(s, ...) \ +do { \ + ERR("Parser error: " __VA_ARGS__); \ + ERR("lexeme: %.*s position: %.80s\n", s->len, s->lexeme, s->pos); \ +} while (0) + + +token_t get_token(ParserState *s) +{ + static const token_t single_char_tokens[] = { + [0 ... 255] = TOKEN_NA, + ['{'] = TOKEN_LBRACE, + ['}'] = TOKEN_RBRACE, + }; + const char *p; + + s->token = TOKEN_NA; + s->lexeme = NULL; + s->len = 0; + s->pos = skip_spaces(s->pos); + + if (!s->pos[0]) + goto out; + s->lexeme = s->pos; + + s->token = single_char_tokens[(u8)s->pos[0]]; + if (s->token) { + s->pos++; + s->len = 1; + goto out; + } + + if (s->lexeme[0] == '"') { + for (p = s->pos + 1; *p; ++p) { + if (*p == '"' && *(p - 1) != '\\') { + break; + } + } + if (*p == '"') { + s->lexeme++; + s->len = (p - s->lexeme); + s->pos = ++p; + s->token = TOKEN_STR; + } else { + PARSER_ERR(s, "unterminated quote"); + } + } else { + for (p = s->pos + 1; *p && !isspace(*p); ++p); + s->len = (p - s->pos); + s->pos = p; + s->token = TOKEN_STR; + } + +out: + return s->token; +} + +static token_t +peek_token(ParserState *s) +{ + ParserState old_state = *s; + token_t t = get_token(s); + *s = old_state; + + return t; +} + +/* + * The following functions are trying to mimic something like this: + * input ::= rules + * rules ::= rule rules + * rule ::= subj + * | op + * | pattern + * | LBRACE + * | servers + * | RBRACE + * servers ::= server servers + * server ::= STR + * subj ::= STR + * op ::= STR + * pattern ::= STR + */ + +#define EXPECT(token, s, action_if_unexpected) \ +do { \ + token_t _t = peek_token(s); \ + if (_t != token) { \ + PARSER_ERR(s, "Unexpected token: %s (expected %s)\n", \ + token_str(_t), token_str(token)); \ + action_if_unexpected; \ + } \ +} while (0) + +#define EXPECT_EITHER(t1, t2, s, action_if_unexpected) \ +({ \ + token_t _t = peek_token(s); \ + if (_t != t1 && _t != t2) { \ + PARSER_ERR(s, "Unexpected token: %s (expected: %s or %s)\n", \ + token_str(_t) token_str(t1), token_str(t2)); \ + action_if_unexpected; \ + } \ + _t; \ +}) + +#define IDX_BY_STR(array, str, maxlen) \ +({ \ + int _found_idx = 0; \ + int _i; \ + for (_i = 0; _i < ARRAY_SIZE(array); ++_i) { \ + if (!strncmp(str, array[_i], maxlen)) { \ + _found_idx = _i; \ + break; \ + } \ + } \ + _found_idx; \ +}) + +static int +subj(ParserState *s) +{ + static const char *subj_str_tbl[] = { + [SUBJ_NA] = STRINGIFY(SUBJ_NA), + [SUBJ_HOST] = "host", + [SUBJ_URI] = "uri", + [SUBJ_HEADER] = "header", + }; + subj_t subj; + + EXPECT(TOKEN_STR, s, return -1); + get_token(s); + + subj = IDX_BY_STR(subj_str_tbl, s->lexeme, s->len); + if (!subj) { + PARSER_ERR(s, "invalid subject"); + return -1; + } + + s->rule->subj = subj; + + return 0; +} + +static int +op(ParserState *s) +{ + static const char *op_str_tbl[] = { + [OP_NA] = STRINGIFY(OP_NA), + [OP_EQUAL] = "=", + [OP_PREFIX] = "^", + }; + op_t op; + + EXPECT(TOKEN_STR, s, return -1); + get_token(s); + + op = IDX_BY_STR(op_str_tbl, s->lexeme, s->len); + if (!op) { + PARSER_ERR(s, "invalid operator"); + return -1; + } + + s->rule->op = op; + + return 0; +} + +static int +pattern(ParserState *s) +{ + EXPECT(TOKEN_STR, s, return -1); + get_token(s); + + if (s->len >= sizeof(s->rule->pattern)) { + PARSER_ERR(s, "too long pattern: %.*s", s->len, s->lexeme); + return -1; + } + + memcpy(s->rule->pattern, s->lexeme, s->len); + s->rule->pattern[s->len] = '\0'; + + return 0; +} + +static int +server(ParserState *s) +{ + int ret = 0; + TfwAddr *addr; + char *pos; + char buf[IP_ADDR_TEXT_BUF_SIZE + 1]; + + EXPECT(TOKEN_STR, s, return -1); + get_token(s); + + if (s->rule->addrs_n >= ARRAY_SIZE(s->rule->addrs)) { + PARSER_ERR(s, "max number of addresses per rule reached"); + return -1; + } + + if (s->len >= sizeof(buf)) { + PARSER_ERR(s, "too long address: %.*s", s->len, s->lexeme); + return -1; + } + + memcpy(buf, s->lexeme, s->len); + buf[s->len] = '\0'; + pos = buf; + addr = &s->rule->addrs[s->rule->addrs_n++]; + ret = tfw_inet_pton(&pos, addr); + + if (ret) { + PARSER_ERR(s, "invalid address"); + } + + return ret; +} + +static int +servers(ParserState *s) +{ + EXPECT(TOKEN_LBRACE, s, return -1); + get_token(s); + + while (1) { + token_t t = peek_token(s); + if (t == TOKEN_RBRACE) { + get_token(s); + return 0; + } else { + EXPECT(TOKEN_STR, s, return -1); + if (server(s)) + return -1; + } + } + + return 0; +} + +static int +rule(ParserState *s) +{ + return subj(s) || op(s) || pattern(s) || servers(s); +} + +static int +rules(ParserState *s) +{ + int ret; + + while (peek_token(s)) { + if (s->tbl->rules_n >= ARRAY_SIZE(s->tbl->rules)) { + PARSER_ERR(s, "max number of rules reached"); + return -1; + } + + s->rule = &s->tbl->rules[s->tbl->rules_n++]; + + ret = rule(s); + if (ret) + return ret; + } + + return 0; +} + +int run_parser(const char *input, RuleTbl *tbl) +{ + ParserState s; + + memset(&s, 0, sizeof(s)); + s.pos = input; + s.tbl = tbl; + + return rules(&s); +} + +static int +sysctl_handle_rules(ctl_table *ctl, int write, void __user *user_buf, + size_t *lenp, loff_t *ppos) +{ + int ret = 0; + int len = 0; + char *buf = NULL; + RuleTbl *tbl = NULL; + + if (write) { + buf = kzalloc(ctl->maxlen + 1, GFP_KERNEL); + tbl = kzalloc(sizeof(*tbl), GFP_KERNEL); + if (!buf || !tbl) { + ERR("Can't allocate memory\n"); + ret = -ENOMEM; + goto out; + } + + len = min((size_t)ctl->maxlen, *lenp); + ret = copy_from_user(buf, user_buf, len); + if (ret) { + ERR("Can't copy data from user-space\n"); + goto out; + } + + ret = run_parser(buf, tbl); + if (ret) { + ERR("Can't parse input data\n"); + ret = -EINVAL; + goto out; + } + + ret = apply_new_rules(tbl); + if (ret) { + ERR("Can't apply new matching rules\n"); + goto out; + } + } + + ret = proc_dostring(ctl, write, user_buf, lenp, ppos); + if (!ret) + goto out; + +out: + kfree(buf); + kfree(tbl); + + return ret; +} + + + +static char ctl_data[RULES_TEXT_BUF_SIZE]; + +static ctl_table sched_match_tbl[] = { + { + .procname = "sched_match", + .data = ctl_data, + .maxlen = RULES_TEXT_BUF_SIZE, + .mode = 0644, + .proc_handler = sysctl_handle_rules + }, + {} +}; + +static struct ctl_table_header *sched_match_ctl; + + +int +sysctl_register(void) +{ + sched_match_ctl = register_net_sysctl(&init_net, "net/tempesta", + sched_match_tbl); + + return sched_match_ctl ? 0 : 1; +} + +void +sysctl_unregister(void) +{ + unregister_net_sysctl_table(sched_match_ctl); +} diff --git a/tempesta_fw/sched/match/tfw_sched_match.h b/tempesta_fw/sched/match/tfw_sched_match.h new file mode 100644 index 0000000000..d5eb613152 --- /dev/null +++ b/tempesta_fw/sched/match/tfw_sched_match.h @@ -0,0 +1,66 @@ +/** + * Tempesta FW + * + * Copyright (C) 2012-2014 NatSys Lab. (info@natsys-lab.com). + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef __TFW_SCHED_MATCH_H__ +#define __TFW_SCHED_MATCH_H__ + +#include "tempesta.h" +#include "lib.h" + +#define LOG_BANNER "tfw_sched_match: " +#define ERR(...) TFW_ERR(LOG_BANNER __VA_ARGS__) +#define LOG(...) TFW_LOG(LOG_BANNER __VA_ARGS__) +#define DBG(...) TFW_DBG(LOG_BANNER __VA_ARGS__) + +#define RULE_MAX_COUNT 64 +#define RULE_PATTERN_SIZE 256 +#define RULE_ADDR_COUNT 16 + +typedef packedenum { + SUBJ_NA = 0, + SUBJ_HOST, + SUBJ_URI, + SUBJ_HEADER +} subj_t; + +typedef packedenum { + OP_NA = 0, + OP_EQUAL, + OP_PREFIX, +} op_t; + +typedef struct { + subj_t subj; + op_t op; + size_t addrs_n; + char pattern[RULE_PATTERN_SIZE]; + TfwAddr addrs[RULE_ADDR_COUNT]; +} Rule; + +typedef struct { + size_t rules_n; + Rule rules[RULE_MAX_COUNT]; +} RuleTbl; + +int apply_new_rules(const RuleTbl *tbl); + +op_t op_from_str(const char *str, size_t maxlen); +subj_t subj_from_str(const char *str, size_t maxlen); + +#endif /* __TFW_SCHED_MATCH_H__ */ diff --git a/tempesta_fw/sched/rr/Makefile b/tempesta_fw/sched/rr/Makefile new file mode 100644 index 0000000000..d9dee645dc --- /dev/null +++ b/tempesta_fw/sched/rr/Makefile @@ -0,0 +1,21 @@ +# Tempesta FW +# +# Copyright (C) 2012-2014 NatSys Lab. (info@natsys-lab.com). +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, +# or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 +# Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +EXTRA_CFLAGS = -DDEBUG -I$(src)/../../ -I$(src)/../../../tempesta_db -I$(src)/../../../sync_socket + +obj-m = tfw_sched_rr.o diff --git a/tempesta_fw/sched/tfw_sched_rr.c b/tempesta_fw/sched/rr/tfw_sched_rr.c similarity index 98% rename from tempesta_fw/sched/tfw_sched_rr.c rename to tempesta_fw/sched/rr/tfw_sched_rr.c index d8f79b3512..f88df53edb 100644 --- a/tempesta_fw/sched/tfw_sched_rr.c +++ b/tempesta_fw/sched/rr/tfw_sched_rr.c @@ -20,9 +20,9 @@ #include #include -#include "../debugfs.h" -#include "../lib.h" -#include "../sched.h" +#include "debugfs.h" +#include "lib.h" +#include "sched.h" MODULE_AUTHOR(TFW_AUTHOR); MODULE_DESCRIPTION("Tempesta round-robin scheduler"); @@ -37,7 +37,7 @@ MODULE_LICENSE("GPL"); * The memory for servers list is allocated statically, so this is a maximum * number of servers that may be added for scheduling in this module. */ -#define RR_MAX_SERVERS_N 64 +#define RR_MAX_SERVERS_N TFW_SCHED_MAX_SERVERS /** * The structure represents a list of servers read in a round-robin fashion. diff --git a/tempesta_fw/server.c b/tempesta_fw/server.c index 646ab0f0f1..e8e911c432 100644 --- a/tempesta_fw/server.c +++ b/tempesta_fw/server.c @@ -75,7 +75,21 @@ tfw_create_server(struct sock *s) return srv; } -int tfw_server_snprint(const TfwServer *srv, char *buf, size_t buf_size) +int +tfw_server_get_addr(const TfwServer *srv, TfwAddr *addr) +{ + int ret = 0; + int len = sizeof(*addr); + + memset(addr, 0, len); + ret = kernel_getpeername(srv->sock->sk_socket, &addr->addr, &len); + + return ret; +} +EXPORT_SYMBOL(tfw_server_get_addr); + +int +tfw_server_snprint(const TfwServer *srv, char *buf, size_t buf_size) { TfwAddr addr; int len = sizeof(addr); @@ -83,8 +97,7 @@ int tfw_server_snprint(const TfwServer *srv, char *buf, size_t buf_size) BUG_ON(!srv || !buf || !buf_size); - memset(&addr, 0, sizeof(addr)); - kernel_getpeername(srv->sock->sk_socket, &addr.addr, &len); + tfw_server_get_addr(srv, &addr); tfw_inet_ntop(&addr, addr_str_buf); len = snprintf(buf, buf_size, "srv %p: %s", srv, addr_str_buf); diff --git a/tempesta_fw/server.h b/tempesta_fw/server.h index 6f4d0ecdae..6fae1addec 100644 --- a/tempesta_fw/server.h +++ b/tempesta_fw/server.h @@ -21,6 +21,7 @@ #define __TFW_SERVER_H__ #include +#include "tempesta.h" #define TFW_MAX_SERVER_STR_SIZE 100 @@ -34,6 +35,7 @@ typedef struct { TfwServer *tfw_create_server(struct sock *s); void tfw_destroy_server(struct sock *s); +int tfw_server_get_addr(const TfwServer *srv, TfwAddr *addr); int tfw_server_snprint(const TfwServer *srv, char *buf, size_t buf_size); int tfw_server_init(void); diff --git a/tempesta_fw/str.c b/tempesta_fw/str.c index f4a4ea7a8c..044594590e 100644 --- a/tempesta_fw/str.c +++ b/tempesta_fw/str.c @@ -156,12 +156,14 @@ tfw_str_eq_cstr(const TfwStr *str, const char *cstr, int cstr_len) { return str_eq_cstr(str, cstr, cstr_len, false); } +EXPORT_SYMBOL(tfw_str_eq_cstr); bool tfw_str_eq_cstr_ci(const TfwStr *str, const char *cstr, int cstr_len) { return str_eq_cstr(str, cstr, cstr_len, true); } +EXPORT_SYMBOL(tfw_str_eq_cstr_ci); bool tfw_str_startswith_cstr_ci(const TfwStr *str, const char *cstr, int cstr_len) @@ -190,4 +192,5 @@ tfw_str_startswith_cstr_ci(const TfwStr *str, const char *cstr, int cstr_len) return true; } +EXPORT_SYMBOL(tfw_str_startswith_cstr_ci);