RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/radius_snmp.h>
#include <freeradius-devel/modules.h>
#include <freeradius-devel/rad_assert.h>
#include <freeradius-devel/vqp.h>
#include <freeradius-devel/vmps.h>
#include <freeradius-devel/detail.h>
+#ifdef WITH_UDPFROMTO
+#include <freeradius-devel/udpfromto.h>
+#endif
+
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
*/
fr_ipaddr_t ipaddr;
int port;
+ const char *interface;
RADCLIENT_LIST *clients;
} listen_socket_t;
+static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type);
+
/*
* Find a per-socket client.
*/
RADCLIENT *client_listener_find(const rad_listen_t *listener,
- const fr_ipaddr_t *ipaddr)
+ const fr_ipaddr_t *ipaddr, int src_port)
{
#ifdef WITH_DYNAMIC_CLIENTS
int rcode;
- time_t now;
- listen_socket_t *sock;
REQUEST *request;
- RADCLIENT *client, *created;
+ RADCLIENT *created;
#endif
+ time_t now;
+ RADCLIENT *client;
RADCLIENT_LIST *clients;
rad_assert(listener != NULL);
rad_assert(ipaddr != NULL);
- rad_assert((listener->type == RAD_LISTEN_AUTH) ||
- (listener->type == RAD_LISTEN_ACCT) ||
- (listener->type == RAD_LISTEN_VQP) ||
- (listener->type == RAD_LISTEN_DHCP));
-
clients = ((listen_socket_t *)listener->data)->clients;
/*
*/
rad_assert(clients != NULL);
-#ifdef WITH_DYNAMIC_CLIENTS
client = client_find(clients, ipaddr);
- if (!client) return NULL;
+ if (!client) {
+ static time_t last_printed = 0;
+ char name[256], buffer[128];
+
+#ifdef WITH_DYNAMIC_CLIENTS
+ unknown: /* used only for dynamic clients */
+#endif
+
+ /*
+ * DoS attack quenching, but only in debug mode.
+ * If they're running in debug mode, show them
+ * every packet.
+ */
+ if (debug_flag == 0) {
+ now = time(NULL);
+ if (last_printed == now) return NULL;
+
+ last_printed = now;
+ }
+
+ listener->print(listener, name, sizeof(name));
+
+ radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d",
+ name, inet_ntop(ipaddr->af, &ipaddr->ipaddr,
+ buffer, sizeof(buffer)),
+ src_port);
+ return NULL;
+ }
+
+#ifndef WITH_DYNAMIC_CLIENTS
+ return client; /* return the found client. */
+#else
/*
* No server defined, and it's not dynamic. Return it.
/*
* It's a dynamically generated client, check it.
*/
- if (client->dynamic) {
+ if (client->dynamic && (src_port != 0)) {
/*
* Lives forever. Return it.
*/
if ((client->created + client->lifetime) > now) return client;
/*
- * It's dead, Jim. Delete it.
+ * This really puts them onto a queue for later
+ * deletion.
*/
client_delete(clients, client);
/*
* WTF?
*/
- if (!client) return NULL;
- if (!client->client_server) return NULL;
+ if (!client) goto unknown;
+ if (!client->client_server) goto unknown;
/*
* At this point, 'client' is the enclosing
* can be defined.
*/
rad_assert(client->dynamic == 0);
- } else {
+
+ } else if (!client->dynamic && client->rate_limit) {
/*
* The IP is unknown, so we've found an enclosing
* network. Enable DoS protection. We only
* allow one new client per second. Known
* clients aren't subject to this restriction.
*/
- if (now == client->last_new_client) return NULL;
+ if (now == client->last_new_client) goto unknown;
}
client->last_new_client = now;
request = request_alloc();
- if (!request) return NULL;
+ if (!request) goto unknown;
request->listener = listener;
request->client = client;
- request->packet = rad_alloc(0);
- if (!request->packet) {
+ request->packet = rad_recv(listener->fd, 0x02); /* MSG_PEEK */
+ if (!request->packet) { /* badly formed, etc */
request_free(&request);
- return NULL;
+ goto unknown;
}
- request->reply = rad_alloc(0);
+ request->reply = rad_alloc_reply(request->packet);
if (!request->reply) {
request_free(&request);
- return NULL;
+ goto unknown;
}
request->packet->timestamp = request->timestamp;
request->number = 0;
*
* and create the RADCLIENT structure from that.
*/
-
- sock = listener->data;
- request->packet->sockfd = listener->fd;
- request->packet->src_ipaddr = *ipaddr;
- request->packet->src_port = 0; /* who cares... */
- request->packet->dst_ipaddr = sock->ipaddr;
- request->packet->dst_port = sock->port;
-
- request->reply->sockfd = request->packet->sockfd;
- request->reply->dst_ipaddr = request->packet->src_ipaddr;
- request->reply->src_ipaddr = request->packet->dst_ipaddr;
- request->reply->dst_port = request->packet->src_port;
- request->reply->src_port = request->packet->dst_port;
- request->reply->id = request->packet->id;
- request->reply->code = 0; /* UNKNOWN code */
-
-
DEBUG("server %s {", request->server);
rcode = module_authorize(0, request);
if (rcode != RLM_MODULE_OK) {
request_free(&request);
- return NULL;
+ goto unknown;
}
- created = client_create(clients, request);
+ /*
+ * If the client was updated by rlm_dynamic_clients,
+ * don't create the client from attribute-value pairs.
+ */
+ if (request->client == client) {
+ created = client_create(clients, request);
+ } else {
+ created = request->client;
+ /*
+ * This frees the client if it isn't valid.
+ */
+ if (!client_validate(clients, client, created)) goto unknown;
+ }
request_free(&request);
- return created; /* may be NULL */
-#else
- return client_find(clients, ipaddr);
+ if (!created) goto unknown;
+
+ return created;
#endif
}
DICT_VALUE *dval;
switch (request->listener->type) {
+#ifdef WITH_STATS
+ case RAD_LISTEN_NONE:
+#endif
case RAD_LISTEN_AUTH:
dval = dict_valbyname(PW_AUTZ_TYPE, "Status-Server");
if (dval) {
break;
#endif
+#ifdef WITH_COA
+ /*
+ * This is a vendor extension. Suggested by Glen
+ * Zorn in IETF 72, and rejected by the rest of
+ * the WG. We like it, so it goes in here.
+ */
+ case RAD_LISTEN_COA:
+ dval = dict_valbyname(PW_RECV_COA_TYPE, "Status-Server");
+ if (dval) {
+ rcode = module_recv_coa(dval->value, request);
+ } else {
+ rcode = RLM_MODULE_OK;
+ }
+
+ switch (rcode) {
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ request->reply->code = PW_COA_ACK;
+ break;
+
+ default:
+ request->reply->code = 0; /* don't reply */
+ break;
+ }
+ break;
+#endif
+
default:
return 0;
}
+#ifdef WITH_STATS
+ /*
+ * Full statistics are available only on a statistics
+ * socket.
+ */
+ if (request->listener->type == RAD_LISTEN_NONE) {
+ request_stats_reply(request);
+ }
+#endif
+
return 0;
}
-static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
+static int socket_print(const rad_listen_t *this, char *buffer, size_t bufsize)
{
+ size_t len;
listen_socket_t *sock = this->data;
const char *name;
- char ip_buf[256];
-
- if ((sock->ipaddr.af == AF_INET) &&
- (sock->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
- strcpy(ip_buf, "*");
- } else {
- ip_ntoh(&sock->ipaddr, ip_buf, sizeof(ip_buf));
- }
switch (this->type) {
+#ifdef WITH_STATS
+ case RAD_LISTEN_NONE: /* what a hack... */
+ name = "status";
+ break;
+#endif
+
case RAD_LISTEN_AUTH:
name = "authentication";
break;
break;
#endif
+#ifdef WITH_COA
+ case RAD_LISTEN_COA:
+ name = "coa";
+ break;
+#endif
+
default:
name = "??";
break;
}
- if (!this->server) {
- return snprintf(buffer, bufsize, "%s address %s port %d",
- name, ip_buf, sock->port);
+#define FORWARD len = strlen(buffer); if (len >= (bufsize + 1)) return 0;buffer += len;bufsize -= len
+#define ADDSTRING(_x) strlcpy(buffer, _x, bufsize);FORWARD
+
+ ADDSTRING(name);
+
+ if (sock->interface) {
+ ADDSTRING(" interface ");
+ ADDSTRING(sock->interface);
+ }
+
+ ADDSTRING(" address ");
+
+ if ((sock->ipaddr.af == AF_INET) &&
+ (sock->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
+ strlcpy(buffer, "*", bufsize);
+ } else {
+ ip_ntoh(&sock->ipaddr, buffer, bufsize);
+ }
+ FORWARD;
+
+ ADDSTRING(" port ");
+ snprintf(buffer, bufsize, "%d", sock->port);
+ FORWARD;
+
+ if (this->server) {
+ ADDSTRING(" as server ");
+ ADDSTRING(this->server);
}
- return snprintf(buffer, bufsize, "%s address %s port %d as server %s",
- name, ip_buf, sock->port, this->server);
+#undef ADDSTRING
+#undef FORWARD
+
+ return 1;
}
+extern int check_config; /* radiusd.c */
+
/*
* Parse an authentication or accounting socket.
&listen_port, "0");
if (rcode < 0) return -1;
- if ((listen_port < 0) || (listen_port > 65500)) {
+ if ((listen_port < 0) || (listen_port > 65535)) {
cf_log_err(cf_sectiontoitem(cs),
"Invalid value for \"port\"");
return -1;
sock->ipaddr = ipaddr;
sock->port = listen_port;
- /*
- * And bind it to the port.
- */
- if (listen_bind(this) < 0) {
- char buffer[128];
- cf_log_err(cf_sectiontoitem(cs),
- "Error binding to port for %s port %d",
- ip_ntoh(&sock->ipaddr, buffer, sizeof(buffer)),
- sock->port);
- return -1;
+ if (check_config) {
+ if (home_server_find(&sock->ipaddr, sock->port)) {
+ char buffer[128];
+
+ DEBUG("ERROR: We have been asked to listen on %s port %d, which is also listed as a home server. This can create a proxy loop.",
+ ip_ntoh(&sock->ipaddr, buffer, sizeof(buffer)),
+ sock->port);
+ return -1;
+ }
+
+ return 0; /* don't do anything */
}
/*
* else don't.
*/
if (cf_pair_find(cs, "interface")) {
-#ifndef SO_BINDTODEVICE
- cf_log_err(cf_sectiontoitem(cs),
- "System does not support binding to interfaces. Delete this line from the configuration file.");
- return -1;
-#else
const char *value;
CONF_PAIR *cp = cf_pair_find(cs, "interface");
- struct ifreq ifreq;
rad_assert(cp != NULL);
value = cf_pair_value(cp);
"No interface name given");
return -1;
}
+ sock->interface = value;
+ }
- strcpy(ifreq.ifr_name, value);
-
- if (setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE,
- (char *)&ifreq, sizeof(ifreq)) < 0) {
- cf_log_err(cf_sectiontoitem(cs),
- "Failed binding to interface %s: %s",
- value, strerror(errno));
- return -1;
- } /* else it worked. */
-#endif
+ /*
+ * And bind it to the port.
+ */
+ if (listen_bind(this) < 0) {
+ char buffer[128];
+ cf_log_err(cf_sectiontoitem(cs),
+ "Error binding to port for %s port %d",
+ ip_ntoh(&sock->ipaddr, buffer, sizeof(buffer)),
+ sock->port);
+ return -1;
}
#ifdef WITH_PROXY
sock->clients = clients_parse_section(client_cs);
if (!sock->clients) {
cf_log_err(cf_sectiontoitem(cs),
- "Failed to find any clients for this listen section");
+ "Failed to load clients for this listen section");
return -1;
}
}
#endif
-
+#ifdef WITH_STATS
/*
* Check if an incoming request is "ok"
*
* It takes packets, not requests. It sees if the packet looks
* OK. If so, it does a number of sanity checks on it.
*/
-static int auth_socket_recv(rad_listen_t *listener,
+static int stats_socket_recv(rad_listen_t *listener,
RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
{
ssize_t rcode;
int code, src_port;
RADIUS_PACKET *packet;
- RAD_REQUEST_FUNP fun = NULL;
- char buffer[128];
RADCLIENT *client;
fr_ipaddr_t src_ipaddr;
rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
if (rcode < 0) return 0;
- RAD_SNMP_TYPE_INC(listener, total_requests);
+ RAD_STATS_TYPE_INC(listener, total_requests);
if (rcode < 20) { /* AUTH_HDR_LEN */
- RAD_SNMP_TYPE_INC(listener, total_malformed_requests);
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
return 0;
}
if ((client = client_listener_find(listener,
- &src_ipaddr)) == NULL) {
+ &src_ipaddr, src_port)) == NULL) {
rad_recv_discard(listener->fd);
- RAD_SNMP_TYPE_INC(listener, total_invalid_requests);
+ RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+ return 0;
+ }
- if (debug_flag > 0) {
- char name[1024];
+ /*
+ * We only understand Status-Server on this socket.
+ */
+ if (code != PW_STATUS_SERVER) {
+ DEBUG("Ignoring packet code %d sent to Status-Server port",
+ code);
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_unknown_types);
+ RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+ return 0;
+ }
- listener->print(listener, name, sizeof(name));
+ /*
+ * Now that we've sanity checked everything, receive the
+ * packet.
+ */
+ packet = rad_recv(listener->fd, 1); /* require message authenticator */
+ if (!packet) {
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ DEBUG("%s", fr_strerror());
+ return 0;
+ }
- /*
- * This is debugging rather than logging, so that
- * DoS attacks don't affect us.
- */
- DEBUG("Ignoring request to %s from unknown client %s port %d",
- name,
- inet_ntop(src_ipaddr.af, &src_ipaddr.ipaddr,
- buffer, sizeof(buffer)), src_port);
- }
+ if (!received_request(listener, packet, prequest, client)) {
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+ rad_free(&packet);
+ return 0;
+ }
+
+ *pfun = rad_status_server;
+ return 1;
+}
+#endif
+
+
+/*
+ * Check if an incoming request is "ok"
+ *
+ * It takes packets, not requests. It sees if the packet looks
+ * OK. If so, it does a number of sanity checks on it.
+ */
+static int auth_socket_recv(rad_listen_t *listener,
+ RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+ ssize_t rcode;
+ int code, src_port;
+ RADIUS_PACKET *packet;
+ RAD_REQUEST_FUNP fun = NULL;
+ RADCLIENT *client;
+ fr_ipaddr_t src_ipaddr;
+
+ rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
+ if (rcode < 0) return 0;
+
+ RAD_STATS_TYPE_INC(listener, total_requests);
+
+ if (rcode < 20) { /* AUTH_HDR_LEN */
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ return 0;
+ }
+ if ((client = client_listener_find(listener,
+ &src_ipaddr, src_port)) == NULL) {
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_invalid_requests);
return 0;
}
*/
switch(code) {
case PW_AUTHENTICATION_REQUEST:
- RAD_SNMP_CLIENT_INC(listener, client, requests);
+ RAD_STATS_CLIENT_INC(listener, client, total_requests);
fun = rad_authenticate;
break;
case PW_STATUS_SERVER:
if (!mainconfig.status_server) {
rad_recv_discard(listener->fd);
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
- RAD_SNMP_CLIENT_INC(listener, client, packets_dropped);
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
return 0;
}
default:
rad_recv_discard(listener->fd);
- RAD_SNMP_INC(rad_snmp.auth.total_unknown_types);
- RAD_SNMP_CLIENT_INC(listener, client, unknown_types);
+ RAD_STATS_INC(radius_auth_stats.total_unknown_types);
+ RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED",
code, client->shortname, src_port);
*/
packet = rad_recv(listener->fd, client->message_authenticator);
if (!packet) {
- RAD_SNMP_TYPE_INC(listener, total_malformed_requests);
- DEBUG("%s", librad_errstr);
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ DEBUG("%s", fr_strerror());
return 0;
}
if (!received_request(listener, packet, prequest, client)) {
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
- RAD_SNMP_CLIENT_INC(listener, client, packets_dropped);
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
rad_free(&packet);
return 0;
}
int code, src_port;
RADIUS_PACKET *packet;
RAD_REQUEST_FUNP fun = NULL;
- char buffer[128];
RADCLIENT *client;
fr_ipaddr_t src_ipaddr;
rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
if (rcode < 0) return 0;
- RAD_SNMP_TYPE_INC(listener, total_requests);
+ RAD_STATS_TYPE_INC(listener, total_requests);
if (rcode < 20) { /* AUTH_HDR_LEN */
- RAD_SNMP_TYPE_INC(listener, total_malformed_requests);
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
return 0;
}
if ((client = client_listener_find(listener,
- &src_ipaddr)) == NULL) {
+ &src_ipaddr, src_port)) == NULL) {
rad_recv_discard(listener->fd);
- RAD_SNMP_TYPE_INC(listener, total_invalid_requests);
+ RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+ return 0;
+ }
+
+ /*
+ * Some sanity checks, based on the packet code.
+ */
+ switch(code) {
+ case PW_ACCOUNTING_REQUEST:
+ RAD_STATS_CLIENT_INC(listener, client, total_requests);
+ fun = rad_accounting;
+ break;
+
+ case PW_STATUS_SERVER:
+ if (!mainconfig.status_server) {
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+
+ DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
+ return 0;
+ }
+ fun = rad_status_server;
+ break;
+
+ default:
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_unknown_types);
+ RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+
+ DEBUG("Invalid packet code %d sent to a accounting port from client %s port %d : IGNORED",
+ code, client->shortname, src_port);
+ return 0;
+ } /* switch over packet types */
+
+ /*
+ * Now that we've sanity checked everything, receive the
+ * packet.
+ */
+ packet = rad_recv(listener->fd, 0);
+ if (!packet) {
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ radlog(L_ERR, "%s", fr_strerror());
+ return 0;
+ }
+
+ /*
+ * There can be no duplicate accounting packets.
+ */
+ if (!received_request(listener, packet, prequest, client)) {
+ RAD_STATS_TYPE_INC(listener, total_packets_dropped);
+ RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+ rad_free(&packet);
+ return 0;
+ }
+
+ *pfun = fun;
+ return 1;
+}
+#endif
+
+
+#ifdef WITH_COA
+/*
+ * For now, all CoA requests are *only* originated, and not
+ * proxied. So all of the necessary work is done in the
+ * post-proxy section, which is automatically handled by event.c.
+ * As a result, we don't have to do anything here.
+ */
+static int rad_coa_reply(REQUEST *request)
+{
+ VALUE_PAIR *s1, *s2;
+
+ /*
+ * Inform the user about RFC requirements.
+ */
+ s1 = pairfind(request->proxy->vps, PW_STATE);
+ if (s1) {
+ s2 = pairfind(request->proxy_reply->vps, PW_STATE);
+
+ if (!s2) {
+ DEBUG("WARNING: Client was sent State in CoA, and did not respond with State.");
+
+ } else if ((s1->length != s2->length) ||
+ (memcmp(s1->vp_octets, s2->vp_octets,
+ s1->length) != 0)) {
+ DEBUG("WARNING: Client was sent State in CoA, and did not respond with the same State.");
+ }
+ }
+
+ return RLM_MODULE_OK;
+}
+
+/*
+ * Receive a CoA packet.
+ */
+static int rad_coa_recv(REQUEST *request)
+{
+ int rcode = RLM_MODULE_OK;
+ int ack, nak;
+ VALUE_PAIR *vp;
+
+ /*
+ * Get the correct response
+ */
+ switch (request->packet->code) {
+ case PW_COA_REQUEST:
+ ack = PW_COA_ACK;
+ nak = PW_COA_NAK;
+ break;
+
+ case PW_DISCONNECT_REQUEST:
+ ack = PW_DISCONNECT_ACK;
+ nak = PW_DISCONNECT_NAK;
+ break;
+
+ default: /* shouldn't happen */
+ return RLM_MODULE_FAIL;
+ }
+
+#ifdef WITH_PROXY
+#define WAS_PROXIED (request->proxy)
+#else
+#define WAS_PROXIED (0)
+#endif
+
+ if (!WAS_PROXIED) {
+ /*
+ * RFC 5176 Section 3.3. If we have a CoA-Request
+ * with Service-Type = Authorize-Only, it MUST
+ * have a State attribute in it.
+ */
+ vp = pairfind(request->packet->vps, PW_SERVICE_TYPE);
+ if (request->packet->code == PW_COA_REQUEST) {
+ if (vp && (vp->vp_integer == 17)) {
+ vp = pairfind(request->packet->vps, PW_STATE);
+ if (!vp || (vp->length == 0)) {
+ RDEBUG("ERROR: CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute");
+ request->reply->code = PW_COA_NAK;
+ return RLM_MODULE_FAIL;
+ }
+ }
+ } else if (vp) {
+ /*
+ * RFC 5176, Section 3.2.
+ */
+ RDEBUG("ERROR: Disconnect-Request MUST NOT contain a Service-Type attribute");
+ request->reply->code = PW_DISCONNECT_NAK;
+ return RLM_MODULE_FAIL;
+ }
+ rcode = module_recv_coa(0, request);
+ switch (rcode) {
+ case RLM_MODULE_FAIL:
+ case RLM_MODULE_INVALID:
+ case RLM_MODULE_REJECT:
+ case RLM_MODULE_USERLOCK:
+ default:
+ request->reply->code = nak;
+ break;
+
+ case RLM_MODULE_HANDLED:
+ return rcode;
+
+ case RLM_MODULE_NOOP:
+ case RLM_MODULE_NOTFOUND:
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ request->reply->code = ack;
+ break;
+ }
+ } else {
/*
- * This is debugging rather than logging, so that
- * DoS attacks don't affect us.
+ * Start the reply code with the proxy reply
+ * code.
*/
+ request->reply->code = request->proxy_reply->code;
+ }
+
+ /*
+ * Copy State from the request to the reply.
+ * See RFC 5176 Section 3.3.
+ */
+ vp = paircopy2(request->packet->vps, PW_STATE);
+ if (vp) pairadd(&request->reply->vps, vp);
+
+ /*
+ * We may want to over-ride the reply.
+ */
+ rcode = module_send_coa(0, request);
+ switch (rcode) {
+ /*
+ * We need to send CoA-NAK back if Service-Type
+ * is Authorize-Only. Rely on the user's policy
+ * to do that. We're not a real NAS, so this
+ * restriction doesn't (ahem) apply to us.
+ */
+ case RLM_MODULE_FAIL:
+ case RLM_MODULE_INVALID:
+ case RLM_MODULE_REJECT:
+ case RLM_MODULE_USERLOCK:
+ default:
+ /*
+ * Over-ride an ACK with a NAK
+ */
+ request->reply->code = nak;
+ break;
+
+ case RLM_MODULE_HANDLED:
+ return rcode;
+
+ case RLM_MODULE_NOOP:
+ case RLM_MODULE_NOTFOUND:
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ /*
+ * Do NOT over-ride a previously set value.
+ * Otherwise an "ok" here will re-write a
+ * NAK to an ACK.
+ */
+ if (request->reply->code == 0) {
+ request->reply->code = ack;
+ }
+ break;
+
+ }
+
+ return RLM_MODULE_OK;
+}
+
+
+/*
+ * Check if an incoming request is "ok"
+ *
+ * It takes packets, not requests. It sees if the packet looks
+ * OK. If so, it does a number of sanity checks on it.
+ */
+static int coa_socket_recv(rad_listen_t *listener,
+ RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+ ssize_t rcode;
+ int code, src_port;
+ RADIUS_PACKET *packet;
+ RAD_REQUEST_FUNP fun = NULL;
+ char buffer[128];
+ RADCLIENT *client;
+ fr_ipaddr_t src_ipaddr;
+
+ rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
+ if (rcode < 0) return 0;
+
+ RAD_STATS_TYPE_INC(listener, total_requests);
+
+ if (rcode < 20) { /* AUTH_HDR_LEN */
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ return 0;
+ }
+
+ if ((client = client_listener_find(listener,
+ &src_ipaddr, src_port)) == NULL) {
+ rad_recv_discard(listener->fd);
+ RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+
if (debug_flag > 0) {
char name[1024];
listener->print(listener, name, sizeof(name));
+ /*
+ * This is debugging rather than logging, so that
+ * DoS attacks don't affect us.
+ */
DEBUG("Ignoring request to %s from unknown client %s port %d",
name,
inet_ntop(src_ipaddr.af, &src_ipaddr.ipaddr,
return 0;
}
- /*
- * Some sanity checks, based on the packet code.
- */
- switch(code) {
- case PW_ACCOUNTING_REQUEST:
- RAD_SNMP_CLIENT_INC(listener, client, requests);
- fun = rad_accounting;
- break;
-
- case PW_STATUS_SERVER:
- if (!mainconfig.status_server) {
- rad_recv_discard(listener->fd);
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
- RAD_SNMP_CLIENT_INC(listener, client, unknown_types);
-
- DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
- return 0;
- }
- fun = rad_status_server;
+ /*
+ * Some sanity checks, based on the packet code.
+ */
+ switch(code) {
+ case PW_COA_REQUEST:
+ case PW_DISCONNECT_REQUEST:
+ fun = rad_coa_recv;
break;
default:
rad_recv_discard(listener->fd);
- RAD_SNMP_TYPE_INC(listener, total_unknown_types);
- RAD_SNMP_CLIENT_INC(listener, client, unknown_types);
-
- DEBUG("Invalid packet code %d sent to a accounting port from client %s port %d : IGNORED",
+ DEBUG("Invalid packet code %d sent to coa port from client %s port %d : IGNORED",
code, client->shortname, src_port);
return 0;
+ break;
} /* switch over packet types */
/*
* Now that we've sanity checked everything, receive the
* packet.
*/
- packet = rad_recv(listener->fd, 0);
+ packet = rad_recv(listener->fd, client->message_authenticator);
if (!packet) {
- RAD_SNMP_TYPE_INC(listener, total_malformed_requests);
- radlog(L_ERR, "%s", librad_errstr);
+ RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+ DEBUG("%s", fr_strerror());
return 0;
}
- /*
- * There can be no duplicate accounting packets.
- */
if (!received_request(listener, packet, prequest, client)) {
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
- RAD_SNMP_CLIENT_INC(listener, client, packets_dropped);
rad_free(&packet);
return 0;
}
packet = rad_recv(listener->fd, 0);
if (!packet) {
- radlog(L_ERR, "%s", librad_errstr);
+ radlog(L_ERR, "%s", fr_strerror());
return 0;
}
break;
#endif
+#ifdef WITH_COA
+ case PW_DISCONNECT_ACK:
+ case PW_DISCONNECT_NAK:
+ case PW_COA_ACK:
+ case PW_COA_NAK:
+ fun = rad_coa_reply;
+ break;
+#endif
+
default:
/*
* FIXME: Update MIB for packet types?
request = received_proxy_response(packet);
if (!request) {
+ rad_free(&packet);
return 0;
}
+#ifdef WITH_COA
+ /*
+ * Distinguish proxied CoA requests from ones we
+ * originate.
+ */
+ if ((fun == rad_coa_reply) &&
+ (request->packet->code == request->proxy->code)) {
+ fun = rad_coa_recv;
+ }
+#endif
+
rad_assert(fun != NULL);
*pfun = fun;
*prequest = request;
static int proxy_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
{
- if (rad_verify(request->proxy_reply, request->proxy,
- request->home_server->secret) < 0) {
- return -1;
- }
+ /*
+ * rad_verify is run in event.c, received_proxy_response()
+ */
return rad_decode(request->proxy_reply, request->proxy,
request->home_server->secret);
}
#endif
-#ifdef WITH_SNMP
-static int radius_snmp_recv(rad_listen_t *listener,
- UNUSED RAD_REQUEST_FUNP *pfun,
- UNUSED REQUEST **prequest)
-{
- if ((rad_snmp.smux_fd >= 0) &&
- (rad_snmp.smux_event == SMUX_READ)) {
- smux_read();
- }
+#include "dhcpd.c"
- /*
- * If we've got to re-connect, then do so now,
- * before calling select again.
- */
- if (rad_snmp.smux_event == SMUX_CONNECT) {
- smux_connect();
- }
+#include "command.c"
+static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = {
+#ifdef WITH_STATS
+ { common_socket_parse, NULL,
+ stats_socket_recv, auth_socket_send,
+ socket_print, client_socket_encode, client_socket_decode },
+#else
/*
- * Reset this every time, as the smux connect may have
- * opened a new socket.
+ * This always gets defined.
*/
- listener->fd = rad_snmp.smux_fd;
-
- return 0;
-}
-
-
-static int radius_snmp_print(UNUSED rad_listen_t *this, char *buffer, size_t bufsize)
-{
- return snprintf(buffer, bufsize, "SMUX with OID .1.3.6.1.4.1.11344.1.1.1");
-}
-
-#endif
-
-#include "dhcpd.c"
-
-static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = {
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL}, /* RAD_LISTEN_NONE */
+#endif
#ifdef WITH_PROXY
/* proxying */
{ common_socket_parse, NULL,
proxy_socket_recv, proxy_socket_send,
socket_print, proxy_socket_encode, proxy_socket_decode },
-#else
- { NULL, NULL, NULL, NULL, NULL, NULL, NULL},
#endif
/* authentication */
{ common_socket_parse, NULL,
acct_socket_recv, acct_socket_send,
socket_print, client_socket_encode, client_socket_decode},
-#else
- { NULL, NULL, NULL, NULL, NULL, NULL, NULL},
#endif
#ifdef WITH_DETAIL
{ detail_parse, detail_free,
detail_recv, detail_send,
detail_print, detail_encode, detail_decode },
-#else
- { NULL, NULL, NULL, NULL, NULL, NULL, NULL},
#endif
#ifdef WITH_VMPS
{ common_socket_parse, NULL,
vqp_socket_recv, vqp_socket_send,
socket_print, vqp_socket_encode, vqp_socket_decode },
-#else
- { NULL, NULL, NULL, NULL, NULL, NULL, NULL},
#endif
#ifdef WITH_DHCP
{ dhcp_socket_parse, NULL,
dhcp_socket_recv, dhcp_socket_send,
socket_print, dhcp_socket_encode, dhcp_socket_decode },
-#else
- { NULL, NULL, NULL, NULL, NULL, NULL, NULL},
#endif
- { NULL, NULL, NULL, NULL, NULL, NULL, NULL} /* RAD_LISTEN_SNMP */
+#ifdef WITH_COMMAND_SOCKET
+ /* TCP command socket */
+ { command_socket_parse, command_socket_free,
+ command_domain_accept, command_domain_send,
+ command_socket_print, command_socket_encode, command_socket_decode },
+#endif
+
+#ifdef WITH_COA
+ /* Change of Authorization */
+ { common_socket_parse, NULL,
+ coa_socket_recv, auth_socket_send, /* CoA packets are same as auth */
+ socket_print, client_socket_encode, client_socket_decode },
+#endif
+
};
*/
static int listen_bind(rad_listen_t *this)
{
+ int rcode;
+ struct sockaddr_storage salocal;
+ socklen_t salen;
listen_socket_t *sock = this->data;
/*
break;
#endif
+#ifdef WITH_COA
+ case RAD_LISTEN_COA:
+ svp = getservbyname ("radius-dynauth", "udp");
+ if (svp != NULL) {
+ sock->port = ntohs(svp->s_port);
+ } else {
+ sock->port = PW_COA_UDP_PORT;
+ }
+ break;
+#endif
+
default:
- radlog(L_ERR, "ERROR: Non-fatal internal sanity check failed in bind.");
+ DEBUG("WARNING: Internal sanity check failed in binding to socket. Ignoring problem.");
return -1;
}
}
- this->fd = fr_socket(&sock->ipaddr, sock->port);
+ /*
+ * Copy fr_socket() here, as we may need to bind to a device.
+ */
+ this->fd = socket(sock->ipaddr.af, SOCK_DGRAM, 0);
if (this->fd < 0) {
- radlog(L_ERR, "ERROR: Failed to open socket: %s",
- librad_errstr);
+ radlog(L_ERR, "Failed opening socket: %s", strerror(errno));
+ return -1;
+ }
+
+ /*
+ * Bind to a device BEFORE touching IP addresses.
+ */
+ if (sock->interface) {
+#ifdef SO_BINDTODEVICE
+ struct ifreq ifreq;
+ strcpy(ifreq.ifr_name, sock->interface);
+
+ fr_suid_up();
+ rcode = setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE,
+ (char *)&ifreq, sizeof(ifreq));
+ fr_suid_down();
+ if (rcode < 0) {
+ close(this->fd);
+ radlog(L_ERR, "Failed binding to interface %s: %s",
+ sock->interface, strerror(errno));
+ return -1;
+ } /* else it worked. */
+#else
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+#ifdef HAVE_NET_IF_H
+ /*
+ * Odds are that any system supporting "bind to
+ * device" also supports IPv6, so this next bit
+ * isn't necessary. But it's here for
+ * completeness.
+ *
+ * If we're doing IPv6, and the scope hasn't yet
+ * been defined, set the scope to the scope of
+ * the interface.
+ */
+ if (sock->ipaddr.af == AF_INET6) {
+ if (sock->ipaddr.scope == 0) {
+ sock->ipaddr.scope = if_nametoindex(sock->interface);
+ if (sock->ipaddr.scope == 0) {
+ close(this->fd);
+ radlog(L_ERR, "Failed finding interface %s: %s",
+ sock->interface, strerror(errno));
+ return -1;
+ }
+ } /* else scope was defined: we're OK. */
+ } else
+#endif
+#endif
+ /*
+ * IPv4: no link local addresses,
+ * and no bind to device.
+ */
+ {
+ close(this->fd);
+ radlog(L_ERR, "Failed binding to interface %s: \"bind to device\" is unsupported", sock->interface);
+ return -1;
+ }
+#endif
+ }
+
+#ifdef WITH_UDPFROMTO
+ /*
+ * Initialize udpfromto for all sockets.
+ */
+ if (udpfromto_init(this->fd) != 0) {
+ close(this->fd);
+ return -1;
+ }
+#endif
+
+ /*
+ * Set up sockaddr stuff.
+ */
+ if (!fr_ipaddr2sockaddr(&sock->ipaddr, sock->port, &salocal, &salen)) {
+ close(this->fd);
return -1;
}
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+ if (sock->ipaddr.af == AF_INET6) {
+ /*
+ * Listening on '::' does NOT get you IPv4 to
+ * IPv6 mapping. You've got to listen on an IPv4
+ * address, too. This makes the rest of the server
+ * design a little simpler.
+ */
+#ifdef IPV6_V6ONLY
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&sock->ipaddr.ipaddr.ip6addr)) {
+ int on = 1;
+
+ setsockopt(this->fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *)&on, sizeof(on));
+ }
+#endif /* IPV6_V6ONLY */
+ }
+#endif /* HAVE_STRUCT_SOCKADDR_IN6 */
+
+
+ if (sock->ipaddr.af == AF_INET) {
+ UNUSED int flag;
+
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+ /*
+ * Disable PMTU discovery. On Linux, this
+ * also makes sure that the "don't fragment"
+ * flag is zero.
+ */
+ flag = IP_PMTUDISC_DONT;
+ setsockopt(this->fd, IPPROTO_IP, IP_MTU_DISCOVER,
+ &flag, sizeof(flag));
+#endif
+
+#if defined(IP_DONTFRAG)
+ /*
+ * Ensure that the "don't fragment" flag is zero.
+ */
+ flag = 0;
+ setsockopt(this->fd, IPPROTO_IP, IP_DONTFRAG,
+ &flag, sizeof(flag));
+#endif
+ }
/*
+ * May be binding to priviledged ports.
+ */
+ fr_suid_up();
+ rcode = bind(this->fd, (struct sockaddr *) &salocal, salen);
+ fr_suid_down();
+ if (rcode < 0) {
+ char buffer[256];
+ close(this->fd);
+
+ this->print(this, buffer, sizeof(buffer));
+ radlog(L_ERR, "Failed binding to %s: %s\n",
+ buffer, strerror(errno));
+ return -1;
+ }
+
+ /*
* FreeBSD jail issues. We bind to 0.0.0.0, but the
* kernel instead binds us to a 1.2.3.4. If this
* happens, notice, and remember our real IP.
this->decode = master_listen[this->type].decode;
switch (type) {
+#ifdef WITH_STATS
+ case RAD_LISTEN_NONE:
+#endif
case RAD_LISTEN_AUTH:
+#ifdef WITH_ACCOUNTING
case RAD_LISTEN_ACCT:
+#endif
+#ifdef WITH_PROXY
case RAD_LISTEN_PROXY:
+#endif
+#ifdef WITH_VMPS
case RAD_LISTEN_VQP:
- case RAD_LISTEN_DHCP:
+#endif
+#ifdef WITH_COA
+ case RAD_LISTEN_COA:
+#endif
this->data = rad_malloc(sizeof(listen_socket_t));
memset(this->data, 0, sizeof(listen_socket_t));
break;
+#ifdef WITH_DHCP
+ case RAD_LISTEN_DHCP:
+ this->data = rad_malloc(sizeof(dhcp_socket_t));
+ memset(this->data, 0, sizeof(dhcp_socket_t));
+ break;
+#endif
+
#ifdef WITH_DETAIL
case RAD_LISTEN_DETAIL:
this->data = NULL;
break;
#endif
+#ifdef WITH_COMMAND_SOCKET
+ case RAD_LISTEN_COMMAND:
+ this->data = rad_malloc(sizeof(fr_command_socket_t));
+ memset(this->data, 0, sizeof(fr_command_socket_t));
+ break;
+#endif
+
default:
rad_assert("Unsupported option!" == NULL);
break;
/*
* Externally visible function for creating a new proxy LISTENER.
*
- * For now, don't take ipaddr or port.
- *
* Not thread-safe, but all calls to it are protected by the
- * proxy mutex in request_list.c
+ * proxy mutex in event.c
*/
-rad_listen_t *proxy_new_listener()
+rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists)
{
- int last_proxy_port, port;
rad_listen_t *this, *tmp, **last;
listen_socket_t *sock, *old;
- this = listen_alloc(RAD_LISTEN_PROXY);
-
/*
* Find an existing proxy socket to copy.
- *
- * FIXME: Make it per-realm, or per-home server!
*/
- last_proxy_port = 0;
old = NULL;
last = &mainconfig.listen;
for (tmp = mainconfig.listen; tmp != NULL; tmp = tmp->next) {
- if (tmp->type == RAD_LISTEN_PROXY) {
- sock = tmp->data;
- if (sock->port > last_proxy_port) {
- last_proxy_port = sock->port + 1;
- }
- if (!old) old = sock;
+ /*
+ * Not proxy, ignore it.
+ */
+ if (tmp->type != RAD_LISTEN_PROXY) goto next;
+
+ sock = tmp->data;
+
+ /*
+ * If we were asked to copy a specific one, do
+ * so. If we're just finding one that already
+ * exists, return a pointer to it. Otherwise,
+ * create ANOTHER one with the same IP address.
+ */
+ if ((ipaddr->af != AF_UNSPEC) &&
+ (fr_ipaddr_cmp(&sock->ipaddr, ipaddr) != 0)) {
+ if (exists) return tmp;
+ goto next;
}
+
+ if (!old) old = sock;
+ next:
last = &(tmp->next);
}
+ this = listen_alloc(RAD_LISTEN_PROXY);
+ sock = this->data;
+
if (!old) {
- listen_free(&this);
- return NULL; /* This is a serious error. */
+ /*
+ * The socket MUST already exist if we're binding
+ * to an address while proxying.
+ *
+ * If we're initializing the server, it's OK for the
+ * socket to NOT exist.
+ */
+ if (!exists) {
+ DEBUG("WARNING: No previous template for proxy socket. Source IP address may be chosen by the OS");
+ }
+
+ if (ipaddr->af != AF_UNSPEC) {
+ sock->ipaddr = *ipaddr;
+ } else {
+ memset(&sock->ipaddr, 0, sizeof(sock->ipaddr));
+ sock->ipaddr.af = AF_INET; /* Oh well */
+ }
+ } else {
+ sock->ipaddr = old->ipaddr;
}
- /*
- * FIXME: find a new IP address to listen on?
- *
- * This could likely be done in the "home server"
- * configuration, to have per-home-server source IP's.
- */
- sock = this->data;
- memcpy(&sock->ipaddr, &old->ipaddr, sizeof(sock->ipaddr));
+ sock->port = 0;
- /*
- * Keep going until we find an unused port.
- */
- for (port = last_proxy_port; port < 64000; port++) {
- sock->port = port;
- if (listen_bind(this) == 0) {
- /*
- * Add the new listener to the list of
- * listeners.
- */
- *last = this;
- return this;
- }
+ if (listen_bind(this) >= 0) {
+ /*
+ * Add the new listener to the list of
+ * listeners.
+ */
+ *last = this;
+ return this;
}
+ DEBUG("Failed binding to new proxy socket");
listen_free(&this);
return NULL;
}
#endif
static const FR_NAME_NUMBER listen_compare[] = {
+#ifdef WITH_STATS
+ { "status", RAD_LISTEN_NONE },
+#endif
{ "auth", RAD_LISTEN_AUTH },
#ifdef WITH_ACCOUNTING
{ "acct", RAD_LISTEN_ACCT },
#endif
#ifdef WITH_DHCP
{ "dhcp", RAD_LISTEN_DHCP },
-#else
- { "dhcp", RAD_LISTEN_NONE },
+#endif
+#ifdef WITH_COMMAND_SOCKET
+ { "control", RAD_LISTEN_COMMAND },
+#endif
+#ifdef WITH_COA
+ { "coa", RAD_LISTEN_COA },
#endif
{ NULL, 0 },
};
return NULL;
}
- type = fr_str2int(listen_compare, listen_type,
- RAD_LISTEN_NONE);
- if (type == RAD_LISTEN_NONE) {
+ type = fr_str2int(listen_compare, listen_type, -1);
+ if (type < 0) {
cf_log_err(cf_sectiontoitem(cs),
"Invalid type \"%s\" in listen section.",
listen_type);
{
int override = FALSE;
int rcode;
- CONF_SECTION *cs;
+ CONF_SECTION *cs = NULL;
rad_listen_t **last;
rad_listen_t *this;
fr_ipaddr_t server_ipaddr;
listen_socket_t *sock;
server_ipaddr.af = AF_INET;
- radlog(L_INFO, "WARNING: The directive 'bind_adress' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
+ radlog(L_INFO, "WARNING: The directive 'bind_address' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
bind_it:
#ifdef WITH_VMPS
if (!sock->clients) {
cf_log_err(cf_sectiontoitem(config),
"Failed to find any clients for this listen section");
+ listen_free(&this);
return -1;
}
if (listen_bind(this) < 0) {
- listen_free(&this);
listen_free(head);
radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->port);
+ listen_free(&this);
return -1;
}
auth_port = sock->port; /* may have been updated in listen_bind */
* Otherwise, don't do anything.
*/
do_proxy:
+ /*
+ * No sockets to receive packets, this is an error.
+ * proxying is pointless.
+ */
+ if (!*head) {
+ radlog(L_ERR, "The server is not configured to listen on any ports. Cannot start.");
+ return -1;
+ }
+
#ifdef WITH_PROXY
if (mainconfig.proxy_requests == TRUE) {
int port = -1;
listen_socket_t *sock = NULL;
- /*
- * No sockets to receive packets, therefore
- * proxying is pointless.
- */
- if (!*head) return -1;
-
- if (defined_proxy) goto do_snmp;
+ if (defined_proxy) goto check_home_servers;
/*
* Find the first authentication port,
for (this = *head; this != NULL; this = this->next) {
if (this->type == RAD_LISTEN_AUTH) {
sock = this->data;
+
+ /*
+ * We shouldn't proxy on loopback.
+ */
+ if ((sock->ipaddr.af == AF_INET) &&
+ (sock->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_LOOPBACK))) continue;
+
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+ if ((sock->ipaddr.af == AF_INET6) &&
+ (IN6_IS_ADDR_LINKLOCAL(&sock->ipaddr.ipaddr.ip6addr))) continue;
+#endif
+
if (server_ipaddr.af == AF_UNSPEC) {
server_ipaddr = sock->ipaddr;
}
port = sock->port + 2; /* skip acct port */
break;
}
+#ifdef WITH_VMPS
if (this->type == RAD_LISTEN_VQP) {
sock = this->data;
if (server_ipaddr.af == AF_UNSPEC) {
port = sock->port + 1;
break;
}
+#endif
}
if (port < 0) port = 1024 + (fr_rand() & 0x1ff);
radlog(L_ERR, "Failed to open socket for proxying");
return -1;
}
- }
-
- do_snmp: /* used only in proxy code. */
-#endif
-
-#ifdef WITH_SNMP
- if (radius_snmp_init(config)) {
- this = rad_malloc(sizeof(*this));
- memset(this, 0, sizeof(*this));
-
- this->type = RAD_LISTEN_SNMP;
- this->fd = rad_snmp.smux_fd;
-
- this->recv = radius_snmp_recv;
- this->print = radius_snmp_print;
-
- *last = this;
- last = &(this->next);
+
+ /*
+ * Create *additional* proxy listeners, based
+ * on their src_ipaddr.
+ */
+ check_home_servers:
+ if (home_server_create_listeners() != 0) return -1;
}
#endif
*head = NULL;
}
+
+#ifdef WITH_STATS
+RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr,
+ int port)
+{
+ rad_listen_t *this;
+
+ for (this = mainconfig.listen; this != NULL; this = this->next) {
+ listen_socket_t *sock;
+
+ if ((this->type != RAD_LISTEN_AUTH) &&
+ (this->type != RAD_LISTEN_ACCT)) continue;
+
+ sock = this->data;
+
+ if ((sock->port == port) &&
+ (fr_ipaddr_cmp(ipaddr, &sock->ipaddr) == 0)) {
+ return sock->clients;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port)
+{
+ rad_listen_t *this;
+
+ for (this = mainconfig.listen; this != NULL; this = this->next) {
+ listen_socket_t *sock;
+
+ /*
+ * FIXME: For TCP, ignore the *secondary*
+ * listeners associated with the main socket.
+ */
+ if ((this->type != RAD_LISTEN_AUTH) &&
+ (this->type != RAD_LISTEN_ACCT)) continue;
+
+ sock = this->data;
+
+ if ((sock->port == port) &&
+ (fr_ipaddr_cmp(ipaddr, &sock->ipaddr) == 0)) {
+ return this;
+ }
+
+ if ((sock->port == port) &&
+ ((sock->ipaddr.af == AF_INET) &&
+ (sock->ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY))) {
+ return this;
+ }
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+ if ((sock->port == port) &&
+ (sock->ipaddr.af == AF_INET6) &&
+ (IN6_IS_ADDR_UNSPECIFIED(&sock->ipaddr.ipaddr.ip6addr))) {
+ return this;
+ }
+#endif
+ }
+
+ return NULL;
+}