if (a->server && !b->server) return -1;
if (!a->server && b->server) return +1;
+
if (a->server && b->server) {
int rcode = a->type - b->type;
if (rcode != 0) return rcode;
}
#endif
-#ifdef WITH_PROXY
-#ifndef WITH_TCP
-#define home_server_free free
-#else
-static void home_server_free(void *data)
-{
- int i;
- home_server *home = data;
-
- if (home->proto == IPPROTO_TCP) {
- for (i = 0; i < home->max_connections; i++) {
- if (!home->listeners[i]) continue;
-
- listen_free(&home->listeners[i]);
- }
- }
-
- free(home);
-}
-
-#endif /* WITH_TCP */
-#endif /* WITH_PROXY */
-
void realms_free(void)
{
#ifdef WITH_PROXY
#ifdef WITH_PROXY
-#ifdef WITH_TCP
-static CONF_PARSER tcp_config[] = {
+static CONF_PARSER limit_config[] = {
{ "max_connections", PW_TYPE_INTEGER,
offsetof(home_server, max_connections), NULL, "16" },
{ NULL, -1, 0, NULL, NULL } /* end the list */
};
-#endif
static struct in_addr hs_ip4addr;
static struct in6_addr hs_ip6addr;
offsetof(home_server, coa_mrd), 0, Stringify(30) },
#endif
-#ifdef WITH_TCP
- { "limit", PW_TYPE_SUBSECTION, 0, NULL, (const void *) tcp_config },
-#endif
+ { "limit", PW_TYPE_SUBSECTION, 0, NULL, (const void *) limit_config },
{ NULL, -1, 0, NULL, NULL } /* end the list */
};
+static void null_free(UNUSED void *data)
+{
+}
+
static int home_server_add(realm_config_t *rc, CONF_SECTION *cs, int pool_type)
{
const char *name2;
home_server *home;
int dual = FALSE;
CONF_PAIR *cp;
-#ifdef WITH_TCP
- CONF_SECTION *limit;
- int max_connections;
-#endif
free(hs_virtual_server); /* used only for printing during parsing */
hs_virtual_server = NULL;
return 0;
}
-#ifdef WITH_TCP
- max_connections = 16;
- cp = NULL;
- limit = cf_subsection_find_next(cs, NULL, "limit");
- if (limit) cp = cf_pair_find(limit, "max_connections");
- if (cp) {
- const char *value = cf_pair_value(cp);
-
- if (value) max_connections = atoi(value);
-
- if ((max_connections > 1024) ||
- (max_connections == 0)) max_connections = 1024;
-
- /*
- * Set max_connections to 1 for non-TCP sockets.
- */
- cp = cf_pair_find(cs, "proto");
- if (!cp ||
- (((value = cf_pair_value(cp)) != NULL) &&
- (strcmp(value, "tcp") != 0))) {
- max_connections = 1;
- }
- }
-#endif
-
- home = rad_malloc(sizeof(*home)
-#ifdef WITH_TCP
- + (sizeof(home->listeners[0]) * max_connections)
-#endif
- );
- memset(home, 0, sizeof(*home)
-#ifdef WITH_TCP
- + (sizeof(home->listeners[0]) * max_connections)
-#endif
- );
+ home = rad_malloc(sizeof(*home));
+ memset(home, 0, sizeof(*home));
home->name = name2;
home->cs = cs;
+ /*
+ * For zombie period calculations. We want to count
+ * zombies from the time when the server starts, instead
+ * of from 1970.
+ */
+ home->last_packet = time(NULL);
+
+ /*
+ * Authentication servers have a default "no_response_fail = 0".
+ * Accounting servers have a default "no_response_fail = 1".
+ *
+ * This is because authentication packets are retried, so
+ * they can fail over to another home server. Accounting
+ * packets are not retried, so they cannot fail over, and
+ * instead should be rejected immediately.
+ */
+ home->no_response_fail = 2;
+
memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
if (cf_section_parse(cs, home, home_server_config) < 0) {
if (strcasecmp(hs_type, "auth") == 0) {
home->type = HOME_TYPE_AUTH;
+ if (home->no_response_fail == 2) home->no_response_fail = 0;
if (pool_type != home->type) {
mismatch:
cf_log_err(cf_sectiontoitem(cs),
- "Server pool cannot include home server %s of type \"%s\"",
+ "Home server %s of unexpected type \"%s\"",
name2, hs_type);
goto error;
}
} else if (strcasecmp(hs_type, "acct") == 0) {
home->type = HOME_TYPE_ACCT;
+ if (home->no_response_fail == 2) home->no_response_fail = 1;
if (pool_type != home->type) goto mismatch;
} else if (strcasecmp(hs_type, "auth+acct") == 0) {
}
}
-#ifdef WITH_TCP
home->proto = IPPROTO_UDP;
+#ifdef WITH_TCP
if (hs_proto) {
if (strcmp(hs_proto, "udp") == 0) {
free(hs_proto);
goto error;
}
}
-
- home->listeners[0] = NULL;
-
- /*
- * We need SOME value. Set it high enough that no one
- * will run into it.
- */
-
- if (home->proto == IPPROTO_TCP) {
- home->max_connections = max_connections;
-
- /*
- * FIXME: Parse "min_connections", too, and set
- * those up.
- */
- memset(home->listeners, 0,
- home->max_connections * sizeof(home->listeners[0]));
- }
#endif
- if ((home->ipaddr.af != AF_UNSPEC) && /* could be virtual server */
+ if (!home->server &&
rbtree_finddata(home_servers_byaddr, home)) {
cf_log_err(cf_sectiontoitem(cs), "Duplicate home server");
goto error;
}
/*
- * Look up the name using the *same* address family as
- * for the home server.
+ * If the home is a virtual server, don't look up source IP.
*/
- if (hs_srcipaddr && (home->ipaddr.af != AF_UNSPEC)) {
- if (ip_hton(hs_srcipaddr, home->ipaddr.af, &home->src_ipaddr) < 0) {
- cf_log_err(cf_sectiontoitem(cs), "Failed parsing src_ipaddr");
- goto error;
+ if (!home->server) {
+ rad_assert(home->ipaddr.af != AF_UNSPEC);
+
+ /*
+ * Otherwise look up the source IP using the same
+ * address family as the destination IP.
+ */
+ if (hs_srcipaddr) {
+ if (ip_hton(hs_srcipaddr, home->ipaddr.af, &home->src_ipaddr) < 0) {
+ cf_log_err(cf_sectiontoitem(cs), "Failed parsing src_ipaddr");
+ goto error;
+ }
+
+ } else {
+ /*
+ * Source isn't specified: Source is
+ * the correct address family, but all zeros.
+ */
+ home->src_ipaddr.af = home->ipaddr.af;
}
}
+
free(hs_srcipaddr);
hs_srcipaddr = NULL;
goto error;
}
- if ((home->ipaddr.af != AF_UNSPEC) && /* could be virtual server */
+ if (!home->server &&
!rbtree_insert(home_servers_byaddr, home)) {
rbtree_deletebydata(home_servers_byname, home);
cf_log_err(cf_sectiontoitem(cs),
if (home->coa_mrd > 60 ) home->coa_mrd = 60;
#endif
+ if (home->max_connections > 1024) home->max_connections = 1024;
+
#ifdef WITH_TCP
+ /*
+ * UDP sockets can't be connection limited.
+ */
+ if (home->proto != IPPROTO_TCP) home->max_connections = 0;
+#endif
+
if ((home->idle_timeout > 0) && (home->idle_timeout < 5))
home->idle_timeout = 5;
if ((home->lifetime > 0) && (home->lifetime < 5))
home->lifetime = 5;
if ((home->lifetime > 0) && (home->idle_timeout > home->lifetime))
home->idle_timeout = 0;
-#endif
if (dual) {
home_server *home2 = rad_malloc(sizeof(*home2));
home2->ping_user_password = NULL;
home2->cs = cs;
+ if (home->no_response_fail == 2) home->no_response_fail = 0;
+ if (home2->no_response_fail == 2) home2->no_response_fail = 1;
+
if (!rbtree_insert(home_servers_byname, home2)) {
cf_log_err(cf_sectiontoitem(cs),
"Internal error %d adding home server %s.",
return 0;
}
- if ((home->ipaddr.af != AF_UNSPEC) &&
+ if (!home->server &&
!rbtree_insert(home_servers_byaddr, home2)) {
rbtree_deletebydata(home_servers_byname, home2);
cf_log_err(cf_sectiontoitem(cs),
return 0;
}
-#ifdef WITH_TCP
- home2->proto = home->proto;
-#endif
-
#ifdef WITH_STATS
home2->number = home_server_max_number++;
if (!rbtree_insert(home_servers_bynumber, home2)) {
rbtree_deletebydata(home_servers_byname, home2);
- if (home2->ipaddr.af != AF_UNSPEC) {
+ if (!home2->server) {
rbtree_deletebydata(home_servers_byname, home2);
}
cf_log_err(cf_sectiontoitem(cs),
#endif
}
+ /*
+ * Mark it as already processed
+ */
+ cf_data_add(cs, "home_server", null_free, null_free);
+
return 1;
}
home->type = type;
home->secret = secret;
home->cs = cs;
+ home->proto = IPPROTO_UDP;
p = strchr(name, ':');
if (!p) {
free(q);
return 0;
}
+ home->src_ipaddr.af = home->ipaddr.af;
} else {
home->ipaddr.af = AF_UNSPEC;
home->server = server;
#ifdef HAVE_REGEX_H
if (name2[0] == '~') {
+ int rcode;
regex_t reg;
/*
* Include substring matches.
*/
- if (regcomp(®, name2 + 1,
- REG_EXTENDED | REG_NOSUB | REG_ICASE) != 0) {
+ rcode = regcomp(®, name2 + 1,
+ REG_EXTENDED | REG_NOSUB | REG_ICASE);
+ if (rcode != 0) {
+ char buffer[256];
+
+ regerror(rcode, ®, buffer, sizeof(buffer));
+
cf_log_err(cf_sectiontoitem(cs),
- "Invalid regex in realm \"%s\"", name2);
+ "Invalid regex \"%s\": %s",
+ name2 + 1, buffer);
goto error;
}
regfree(®);
}
#ifdef WITH_PROXY
- home_servers_byaddr = rbtree_create(home_server_addr_cmp, home_server_free, 0);
+ home_servers_byaddr = rbtree_create(home_server_addr_cmp, free, 0);
if (!home_servers_byaddr) {
realms_free();
return 0;
return 0;
}
}
+
+ /*
+ * CoA home servers aren't tied to realms.
+ */
+ for (cs = cf_subsection_find_next(config, NULL, "home_server");
+ cs != NULL;
+ cs = cf_subsection_find_next(config, cs, "home_server")) {
+ /*
+ * Server was already loaded.
+ */
+ if (cf_data_find(cs, "home_server")) continue;
+
+ if (!home_server_add(rc, cs, HOME_TYPE_COA)) {
+ return 0;
+ }
+ }
#endif
break;
case HOME_POOL_KEYED_BALANCE:
- if ((vp = pairfind(request->config_items, PW_LOAD_BALANCE_KEY)) != NULL) {
+ if ((vp = pairfind(request->config_items, PW_LOAD_BALANCE_KEY, 0)) != NULL) {
hash = fr_hash(vp->vp_strvalue, vp->length);
start = hash % pool->num_home_servers;
break;
/*
* We've found the first "live" one. Use that.
*/
- if (pool->type == HOME_POOL_FAIL_OVER) {
+ if (pool->type != HOME_POOL_LOAD_BALANCE) {
found = home;
break;
}
* the 'hints' file.
*/
request->proxy->vps = paircopy(request->packet->vps);
-
- /*
- * Set the source IP address for proxying.
- */
- request->proxy->src_ipaddr = found->src_ipaddr;
}
/*
* Update the various fields as appropriate.
*/
+ request->proxy->src_ipaddr = found->src_ipaddr;
+ request->proxy->src_port = 0;
request->proxy->dst_ipaddr = found->ipaddr;
request->proxy->dst_port = found->port;
request->home_server = found;
*/
if (found->message_authenticator &&
(request->packet->code == PW_AUTHENTICATION_REQUEST) &&
- !pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR)) {
+ !pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0)) {
radius_pairmake(request, &request->proxy->vps,
"Message-Authenticator", "0x00",
T_OP_SET);
}
-home_server *home_server_find(fr_ipaddr_t *ipaddr, int port)
+home_server *home_server_find(fr_ipaddr_t *ipaddr, int port, int proto)
{
home_server myhome;
memset(&myhome, 0, sizeof(myhome));
myhome.ipaddr = *ipaddr;
myhome.port = port;
+#ifdef WITH_TCP
+ myhome.proto = proto;
+#else
+ myhome.proto = IPPROTO_UDP;
+#endif
myhome.server = NULL; /* we're not called for internal proxying */
return rbtree_finddata(home_servers_byaddr, &myhome);
}
#endif
-
-#ifdef WITH_PROXY
-static int home_server_create_callback(void *ctx, void *data)
-{
- rad_listen_t *head = ctx;
- home_server *home = data;
- rad_listen_t *this;
-
- /*
- * If there WAS a src address defined, ensure that a
- * proxy listener has been defined.
- */
- if (home->src_ipaddr.af != AF_UNSPEC) {
- this = proxy_new_listener(&home->src_ipaddr, TRUE);
-
- /*
- * Failed to create it: Die
- */
- if (!this) return 1;
-
- this->next = head->next;
- head->next = this;
- }
-
- return 0;
-}
-
-/*
- * Taking a void* here solves some header issues.
- */
-int home_server_create_listeners(void *ctx)
-{
- rad_listen_t *head = ctx;
-
- if (!home_servers_byaddr) return 0;
-
- rad_assert(head != NULL);
-
- /*
- * Add the listeners to the TAIL of the list.
- */
- while (head->next) head = head->next;
-
- if (rbtree_walk(home_servers_byaddr, InOrder,
- home_server_create_callback, head) != 0) {
- return -1;
- }
-
- return 0;
-}
-#endif