X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmain%2Fevent.c;h=95008a4c55dd7c299e8fd57d3e0d2a338114d134;hb=ab972f1f9b724fc0b71e6ca726078c92ad26bc6b;hp=41abddfccf54bc3a4c6be793355b20b0038f6b1b;hpb=489528ce86c3d20a71bd5a60f5f6eaa1c7bb2c1e;p=freeradius.git diff --git a/src/main/event.c b/src/main/event.c index 41abddf..95008a4 100644 --- a/src/main/event.c +++ b/src/main/event.c @@ -83,11 +83,6 @@ static pthread_t NO_SUCH_CHILD_PID; */ #define PTHREAD_MUTEX_LOCK(_x) #define PTHREAD_MUTEX_UNLOCK(_x) -int thread_pool_addrequest(REQUEST *request, RAD_REQUEST_FUNP fun) -{ - radius_handle_request(request, fun); - return 1; -} #endif /* @@ -114,7 +109,6 @@ static fr_packet_list_t *proxy_list = NULL; static void remove_from_proxy_hash(REQUEST *request); static void check_for_zombie_home_server(REQUEST *request); -static void remove_from_proxy_hash(REQUEST *request); #else #define remove_from_proxy_hash(foo) #endif @@ -157,6 +151,10 @@ static void remove_from_request_hash(REQUEST *request) fr_packet_list_yank(pl, request->packet); request->in_request_hash = FALSE; + /* + * FIXME: Move this to a "statistics" thread? + * Or (short term) add a mutex lock around it. + */ request_stats_final(request); #ifdef WITH_TCP @@ -203,7 +201,6 @@ static void ev_request_free(REQUEST **prequest) #ifdef WITH_PROXY static REQUEST *lookup_in_proxy_hash(RADIUS_PACKET *reply) { - int done = FALSE; RADIUS_PACKET **proxy_p; REQUEST *request; @@ -218,16 +215,8 @@ static REQUEST *lookup_in_proxy_hash(RADIUS_PACKET *reply) request = fr_packet2myptr(REQUEST, proxy, proxy_p); request->num_proxied_responses++; /* needs to be protected by lock */ - done = (request->num_proxied_requests == request->num_proxied_responses); PTHREAD_MUTEX_UNLOCK(&proxy_mutex); - - /* - * Catch the most common case of everything working - * correctly. - */ - if (done) remove_from_proxy_hash(request); - return request; } @@ -377,6 +366,8 @@ retry: rcode = fr_packet_list_id_alloc(proxy_list, request->home_server->proto, request->proxy, &proxy_listener); + request->num_proxied_requests = 1; + request->num_proxied_responses = 0; PTHREAD_MUTEX_UNLOCK(&proxy_mutex); if (!rcode) { @@ -408,7 +399,7 @@ retry: if (!fr_packet_list_insert(proxy_list, &request->proxy)) { fr_packet_list_id_free(proxy_list, request->proxy); PTHREAD_MUTEX_UNLOCK(&proxy_mutex); - radlog(L_PROXY, "Failed to insert entry into proxy list"); + radlog(L_PROXY, "Failed to insert entry into proxy list."); return 0; } @@ -467,12 +458,11 @@ static void wait_for_proxy_id_to_expire(void *ctx) #endif timercmp(&now, &request->when, >)) { if (request->packet) { - RDEBUG2("Cleaning up request %u ID %d with timestamp +%d", - request->number, request->packet->id, + RDEBUG2("Cleaning up request packet ID %d with timestamp +%d", + request->packet->id, (unsigned int) (request->timestamp - fr_start_time)); } else { - RDEBUG2("Cleaning up request %u with timestamp +%d", - request->number, + RDEBUG2("Cleaning up request with timestamp +%d", (unsigned int) (request->timestamp - fr_start_time)); } @@ -490,6 +480,7 @@ static void wait_for_child_to_die(void *ctx) REQUEST *request = ctx; rad_assert(request->magic == REQUEST_MAGIC); + remove_from_request_hash(request); /* * If it's still queued (waiting for a thread to pick it @@ -501,14 +492,15 @@ static void wait_for_child_to_die(void *ctx) (pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0))) { /* - * Cap delay at five minutes. + * Cap delay at max_request_time */ - if (request->delay < (USEC * 60 * 5)) { + if (request->delay < (USEC * request->root->max_request_time)) { request->delay += (request->delay >> 1); - radlog(L_INFO, "WARNING: Child is hung for request %u in component %s module %s.", - request->number, request->component, request->module); + radlog_request(L_INFO, 0, request, "WARNING: Child is hung in component %s module %s.", + request->component, request->module); } else { - RDEBUG2("Child is still stuck for request %u", + request->delay = USEC * request->root->max_request_time; + RDEBUG2("WARNING: Child is hung after \"max_request_time\" for request %u", request->number); } tv_add(&request->when, request->delay); @@ -517,8 +509,7 @@ static void wait_for_child_to_die(void *ctx) return; } - RDEBUG2("Child is finally responsive for request %u", request->number); - remove_from_request_hash(request); + RDEBUG2("Child is finally responsive"); #ifdef WITH_PROXY if (request->proxy) { @@ -548,8 +539,8 @@ static void cleanup_delay(void *ctx) } #endif - RDEBUG2("Cleaning up request %u ID %d with timestamp +%d", - request->number, request->packet->id, + RDEBUG2("Cleaning up request packet ID %d with timestamp +%d", + request->packet->id, (unsigned int) (request->timestamp - fr_start_time)); ev_request_free(&request); @@ -618,7 +609,7 @@ static void reject_delay(void *ctx) rad_assert(request->magic == REQUEST_MAGIC); rad_assert(request->child_state == REQUEST_REJECT_DELAY); - RDEBUG2("Sending delayed reject for request %u", request->number); + RDEBUG2("Sending delayed reject"); DEBUG_PACKET(request, request->reply, 1); @@ -686,6 +677,12 @@ static void no_response_to_ping(void *ctx) } +/* + * Note that we don't care what the value of the code field is. + * If the response has a valid (src ip/port, dst ip/port), id, + * and correctly signed Message-Authenticator, that's good + * enough. + */ static void received_response_to_ping(REQUEST *request) { home_server *home; @@ -819,7 +816,7 @@ static void ping_home_server(void *ctx) rad_assert(request->proxy_listener == NULL); if (!insert_into_proxy_hash(request)) { - radlog(L_PROXY, "Failed inserting status check %d into proxy hash. Discarding it.", + radlog(L_PROXY, "Failed to insert status check %d into proxy list. Discarding it.", request->number); ev_request_free(&request); return; @@ -930,9 +927,7 @@ static void proxy_fallback_handler(REQUEST *request) request->child_state = REQUEST_QUEUED; rad_assert(request->proxy != NULL); - if (!thread_pool_addrequest(request, virtual_server_handler)) { - request->child_state = REQUEST_DONE; - } + thread_pool_addrequest(request, virtual_server_handler); #ifdef HAVE_PTHREAD_H /* @@ -957,10 +952,10 @@ static int setup_post_proxy_fail(REQUEST *request) request->child_state = REQUEST_RUNNING; if (request->packet->code == PW_AUTHENTICATION_REQUEST) { - dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail-Authentication"); + dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-Authentication"); } else if (request->packet->code == PW_ACCOUNTING_REQUEST) { - dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail-Accounting"); + dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-Accounting"); #ifdef WITH_COA /* @@ -970,10 +965,10 @@ static int setup_post_proxy_fail(REQUEST *request) request->packet->code &= 0xff; /* restore it */ if (request->proxy->code == PW_COA_REQUEST) { - dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail-CoA"); + dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-CoA"); } else if (request->proxy->code == PW_DISCONNECT_REQUEST) { - dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail-Disconnect"); + dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-Disconnect"); } else { return 0; } @@ -983,16 +978,16 @@ static int setup_post_proxy_fail(REQUEST *request) return 0; } - if (!dval) dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail"); + if (!dval) dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail"); if (!dval) { - pairdelete(&request->config_items, PW_POST_PROXY_TYPE); + pairdelete(&request->config_items, PW_POST_PROXY_TYPE, 0); return 0; } - vp = pairfind(request->config_items, PW_POST_PROXY_TYPE); + vp = pairfind(request->config_items, PW_POST_PROXY_TYPE, 0); if (!vp) vp = radius_paircreate(request, &request->config_items, - PW_POST_PROXY_TYPE, PW_TYPE_INTEGER); + PW_POST_PROXY_TYPE, 0, PW_TYPE_INTEGER); vp->vp_integer = dval->value; rad_assert(request->proxy_reply == NULL); @@ -1098,8 +1093,8 @@ static void no_response_to_proxied_request(void *ctx) * well. */ if (home->no_response_fail) { - radlog(L_ERR, "Rejecting request %u (proxy Id %d) due to lack of any response from home server %s port %d", - request->number, request->proxy->id, + radlog_request(L_ERR, 0, request, "Rejecting request (proxy Id %d) due to lack of any response from home server %s port %d", + request->proxy->id, inet_ntop(request->proxy->dst_ipaddr.af, &request->proxy->dst_ipaddr.ipaddr, buffer, sizeof(buffer)), @@ -1147,33 +1142,53 @@ static void no_response_to_proxied_request(void *ctx) } #endif - if (home->state == HOME_STATE_IS_DEAD) { - rad_assert(home->ev != NULL); /* or it will never wake up */ + /* + * If it's not alive, don't try to make it a zombie. + */ + if (home->state != HOME_STATE_ALIVE) { + /* + * Don't check home->ev due to race conditions. + */ return; } /* - * Enable the zombie period when we notice that the home - * server hasn't responded. We do NOT back-date the start - * of the zombie period. + * We've received a real packet recently. Don't mark the + * server as zombie until we've received NO packets for a + * while. The "1/4" of zombie period was chosen rather + * arbitrarily. It's a balance between too short, which + * gives quick fail-over and fail-back, or too long, + * where the proxy still sends packets to an unresponsive + * home server. */ - if (home->state == HOME_STATE_ALIVE) { - home->state = HOME_STATE_ZOMBIE; - home->zombie_period_start = now; - fr_event_delete(el, &home->ev); - home->currently_outstanding = 0; - home->num_received_pings = 0; - - radlog(L_PROXY, "Marking home server %s port %d as zombie (it looks like it is dead).", - inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr, - buffer, sizeof(buffer)), - home->port); - - /* - * Start pinging the home server. - */ - ping_home_server(home); + if ((home->last_packet + ((home->zombie_period + 3) / 4)) >= now.tv_sec) { + return; } + + /* + * Enable the zombie period when we notice that the home + * server hasn't responded for a while. We back-date the + * zombie period to when we last received a response from + * the home server. + */ + home->state = HOME_STATE_ZOMBIE; + + home->zombie_period_start.tv_sec = home->last_packet; + home->zombie_period_start.tv_sec = USEC / 2; + + fr_event_delete(el, &home->ev); + home->currently_outstanding = 0; + home->num_received_pings = 0; + + radlog(L_PROXY, "Marking home server %s port %d as zombie (it looks like it is dead).", + inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr, + buffer, sizeof(buffer)), + home->port); + + /* + * Start pinging the home server. + */ + ping_home_server(home); } #endif @@ -1185,6 +1200,7 @@ static void wait_a_bit(void *ctx) rad_assert(request->magic == REQUEST_MAGIC); +#ifdef HAVE_PTHREAD_H /* * The socket was closed. Tell the request that * there is no point in continuing. @@ -1192,6 +1208,7 @@ static void wait_a_bit(void *ctx) if (request->listener->status != RAD_LISTEN_STATUS_KNOWN) { goto stop_processing; } +#endif #ifdef WITH_COA /* @@ -1213,6 +1230,26 @@ static void wait_a_bit(void *ctx) switch (request->child_state) { case REQUEST_QUEUED: case REQUEST_RUNNING: + /* + * If we're not thread-capable, OR we're capable, + * but have been told to run without threads, and + * the request is still running. This is usually + * because the request was proxied, and the home + * server didn't respond. + */ +#ifdef HAVE_PTHREAD_H + if (!have_children) +#endif + { + request->child_state = REQUEST_DONE; + goto done; + } + +#ifdef HAVE_PTHREAD_H + /* + * If we have threads, wait for the child thread + * to stop. + */ when = request->received; when.tv_sec += request->root->max_request_time; @@ -1229,24 +1266,18 @@ static void wait_a_bit(void *ctx) * Request still has more time. Continue * waiting. */ - if (timercmp(&now, &when, <) || - ((request->listener->type == RAD_LISTEN_DETAIL) && - (request->child_state == REQUEST_QUEUED))) { + if (timercmp(&now, &when, <)) { if (request->delay < (USEC / 10)) { request->delay = USEC / 10; } request->delay += request->delay >> 1; -#ifdef WITH_DETAIL /* - * Cap wait at some sane value for detail - * files. + * Cap delays at something reasonable. */ - if ((request->listener->type == RAD_LISTEN_DETAIL) && - (request->delay > (request->root->max_request_time * USEC))) { + if (request->delay > (request->root->max_request_time * USEC)) { request->delay = request->root->max_request_time * USEC; } -#endif request->when = now; tv_add(&request->when, request->delay); @@ -1255,7 +1286,8 @@ static void wait_a_bit(void *ctx) } stop_processing: -#if defined(HAVE_PTHREAD_H) + request->master_state = REQUEST_STOP_PROCESSING; + /* * A child thread MAY still be running on the * request. Ask the thread to stop working on @@ -1263,36 +1295,25 @@ static void wait_a_bit(void *ctx) */ if (have_children && (pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0)) { - request->master_state = REQUEST_STOP_PROCESSING; - - radlog(L_ERR, "WARNING: Unresponsive child for request %u, in module %s component %s", + radlog(L_ERR, "WARNING: Unresponsive child for request %u, in component %s module %s", request->number, - request->module ? request->module : "", - request->component ? request->component : ""); - - request->delay = USEC / 4; - tv_add(&request->when, request->delay); - callback = wait_for_child_to_die; - break; + request->component ? request->component : "", + request->module ? request->module : ""); + } + + request->delay = USEC; + tv_add(&request->when, request->delay); + callback = wait_for_child_to_die; + break; #endif /* - * Else no child thread is processing the - * request. We probably should have just marked - * the request as 'done' elsewhere, like in the - * post-proxy-fail handler. But doing that would - * involve checking for max_request_time in - * multiple places, so this may be simplest. - */ - request->child_state = REQUEST_DONE; - /* FALL-THROUGH */ - - /* * Mark the request as no longer running, * and clean it up. */ case REQUEST_DONE: + done: #ifdef HAVE_PTHREAD_H request->child_pid = NO_SUCH_CHILD_PID; #endif @@ -1314,7 +1335,6 @@ static void wait_a_bit(void *ctx) return; } #endif - request_stats_final(request); cleanup_delay(request); return; @@ -1323,7 +1343,6 @@ static void wait_a_bit(void *ctx) #ifdef HAVE_PTHREAD_H request->child_pid = NO_SUCH_CHILD_PID; #endif - request_stats_final(request); case REQUEST_PROXIED: rad_assert(request->next_callback != NULL); @@ -1350,7 +1369,7 @@ static void wait_a_bit(void *ctx) * mode, with no threads... */ if (!callback) { - RDEBUG("WARNING: Internal sanity check failed in event handler for request %u: Discarding the request!", request->number); + RDEBUG("WARNING: Internal sanity check failed in event handler: Discarding the request!"); ev_request_free(&request); return; } @@ -1386,7 +1405,7 @@ static int update_event_timestamp(RADIUS_PACKET *packet, time_t when) { VALUE_PAIR *vp; - vp = pairfind(packet->vps, PW_EVENT_TIMESTAMP); + vp = pairfind(packet->vps, PW_EVENT_TIMESTAMP, 0); if (!vp) return 0; vp->vp_date = when; @@ -1484,28 +1503,34 @@ static void retransmit_coa_request(void *ctx) * Id. */ RADIUS_PACKET old = *request->proxy; + home_server *home = request->home_server; + rad_listen_t *listener = request->proxy_listener; /* * Don't free the old Id on error. */ if (!insert_into_proxy_hash(request)) { - radlog(L_PROXY,"Failed re-inserting CoA request into proxy hash."); + radlog(L_PROXY,"Failed to insert retransmission of CoA request into proxy list."); return; } /* - * Now that we have a new Id, free the old one. + * Now that we have a new Id, free the old one + * and update the various statistics. */ PTHREAD_MUTEX_LOCK(&proxy_mutex); fr_packet_list_yank(proxy_list, &old); fr_packet_list_id_free(proxy_list, &old); + if (home) home->currently_outstanding--; +#ifdef WITH_TCP + if (listener) listener->count--; +#endif PTHREAD_MUTEX_UNLOCK(&proxy_mutex); - request->num_proxied_requests = 0; - request->num_proxied_responses = 0; + } else { /* FIXME: protect by a mutex? */ + request->num_proxied_requests++; } - request->num_proxied_requests++; request->num_coa_requests++; /* is NOT reset by code 3 lines above! */ request->proxy_listener->send(request->proxy_listener, @@ -1531,9 +1556,9 @@ static int originated_coa_request(REQUEST *request) /* * Check whether we want to originate one, or cancel one. */ - vp = pairfind(request->config_items, PW_SEND_COA_REQUEST); + vp = pairfind(request->config_items, PW_SEND_COA_REQUEST, 0); if (!vp && request->coa) { - vp = pairfind(request->coa->proxy->vps, PW_SEND_COA_REQUEST); + vp = pairfind(request->coa->proxy->vps, PW_SEND_COA_REQUEST, 0); } if (vp) { @@ -1552,18 +1577,18 @@ static int originated_coa_request(REQUEST *request) * src_ipaddr will be set up in proxy_encode. */ memset(&ipaddr, 0, sizeof(ipaddr)); - vp = pairfind(coa->proxy->vps, PW_PACKET_DST_IP_ADDRESS); + vp = pairfind(coa->proxy->vps, PW_PACKET_DST_IP_ADDRESS, 0); if (vp) { ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; } else if ((vp = pairfind(coa->proxy->vps, - PW_PACKET_DST_IPV6_ADDRESS)) != NULL) { + PW_PACKET_DST_IPV6_ADDRESS, 0)) != NULL) { ipaddr.af = AF_INET6; ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; } else if ((vp = pairfind(coa->proxy->vps, - PW_HOME_SERVER_POOL)) != NULL) { + PW_HOME_SERVER_POOL, 0)) != NULL) { coa->home_pool = home_pool_byname(vp->vp_strvalue, HOME_TYPE_COA); if (!coa->home_pool) { @@ -1604,7 +1629,7 @@ static int originated_coa_request(REQUEST *request) } else if (!coa->home_server) { int port = PW_COA_UDP_PORT; - vp = pairfind(coa->proxy->vps, PW_PACKET_DST_PORT); + vp = pairfind(coa->proxy->vps, PW_PACKET_DST_PORT, 0); if (vp) port = vp->vp_integer; coa->home_server = home_server_find(&ipaddr, port, IPPROTO_UDP); @@ -1616,7 +1641,7 @@ static int originated_coa_request(REQUEST *request) } } - vp = pairfind(coa->proxy->vps, PW_PACKET_TYPE); + vp = pairfind(coa->proxy->vps, PW_PACKET_TYPE, 0); if (vp) { switch (vp->vp_integer) { case PW_COA_REQUEST: @@ -1653,7 +1678,7 @@ static int originated_coa_request(REQUEST *request) /* * Call the pre-proxy routines. */ - vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE); + vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE, 0); if (vp) { RDEBUG2(" Found Pre-Proxy-Type %s", vp->vp_strvalue); pre_proxy_type = vp->vp_integer; @@ -1691,7 +1716,7 @@ static int originated_coa_request(REQUEST *request) coa->proxy->dst_port = coa->home_server->port; if (!insert_into_proxy_hash(coa)) { - radlog(L_PROXY, "Failed inserting CoA request into proxy hash."); + radlog(L_PROXY, "Failed to insert CoA request into proxy list."); goto fail; } @@ -1740,8 +1765,6 @@ static int originated_coa_request(REQUEST *request) * another thread may have picked it up. Don't * touch it! */ - request->num_proxied_requests = 1; - request->num_proxied_responses = 0; request->child_pid = NO_SUCH_CHILD_PID; update_event_timestamp(request->proxy, request->proxy_when.tv_sec); @@ -1772,7 +1795,7 @@ static int process_proxy_reply(REQUEST *request) * Run the packet through the post-proxy stage, * BEFORE playing games with the attributes. */ - vp = pairfind(request->config_items, PW_POST_PROXY_TYPE); + vp = pairfind(request->config_items, PW_POST_PROXY_TYPE, 0); if (vp) { RDEBUG2(" Found Post-Proxy-Type %s", vp->vp_strvalue); post_proxy_type = vp->vp_integer; @@ -1809,7 +1832,7 @@ static int process_proxy_reply(REQUEST *request) * the reply. These include Proxy-State * attributes from us and remote server. */ - pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE); + pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE, 0); /* * Add the attributes left in the proxy @@ -1858,9 +1881,9 @@ static int request_pre_handler(REQUEST *request) */ if (request->packet->dst_port == 0) { request->username = pairfind(request->packet->vps, - PW_USER_NAME); + PW_USER_NAME, 0); request->password = pairfind(request->packet->vps, - PW_USER_PASSWORD); + PW_USER_PASSWORD, 0); return 1; } @@ -1869,15 +1892,20 @@ static int request_pre_handler(REQUEST *request) * Put the decoded packet into it's proper place. */ if (request->proxy_reply != NULL) { + rcode = request->proxy_listener->decode(request->proxy_listener, request); + DEBUG_PACKET(request, request->proxy_reply, 0); + /* - * FIXME: For now, we can only proxy RADIUS packets. - * - * In order to proxy other packets, we need to - * somehow cache the "decode" function. + * Pro-actively remove it from the proxy hash. + * This is later than in 2.1.x, but it means that + * the replies are authenticated before being + * removed from the hash. */ - rcode = rad_decode(request->proxy_reply, request->proxy, - request->home_server->secret); - DEBUG_PACKET(request, request->proxy_reply, 0); + if ((rcode == 0) && + (request->num_proxied_requests <= request->num_proxied_responses)) { + remove_from_proxy_hash(request); + } + } else #endif if (request->packet->vps == NULL) { @@ -1913,14 +1941,14 @@ static int request_pre_handler(REQUEST *request) if (!request->username) { request->username = pairfind(request->packet->vps, - PW_USER_NAME); + PW_USER_NAME, 0); } #ifdef WITH_PROXY if (request->proxy) { return process_proxy_reply(request); -#endif } +#endif return 1; } @@ -1943,12 +1971,12 @@ static int proxy_request(REQUEST *request) #endif if (request->home_server->server) { - RDEBUG("ERROR: Cannot perform real proxying to a virtual server."); + RDEBUG("ERROR: Cannot proxy to a virtual server."); return 0; } if (!insert_into_proxy_hash(request)) { - radlog(L_PROXY, "Failed inserting request into proxy hash."); + radlog(L_PROXY, "Failed to insert request into proxy list."); return 0; } @@ -1969,8 +1997,7 @@ static int proxy_request(REQUEST *request) } request->next_callback = no_response_to_proxied_request; - RDEBUG2("Proxying request %u to home server %s port %d", - request->number, + RDEBUG2("Proxying request to home server %s port %d", inet_ntop(request->proxy->dst_ipaddr.af, &request->proxy->dst_ipaddr.ipaddr, buffer, sizeof(buffer)), @@ -1983,8 +2010,6 @@ static int proxy_request(REQUEST *request) * another thread may have picked it up. Don't * touch it! */ - request->num_proxied_requests = 1; - request->num_proxied_responses = 0; #ifdef HAVE_PTHREAD_H request->child_pid = NO_SUCH_CHILD_PID; #endif @@ -2099,11 +2124,11 @@ static int successfully_proxied_request(REQUEST *request) return 0; } - realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM); + realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM, 0); if (!realmpair || (realmpair->length == 0)) { int pool_type; - vp = pairfind(request->config_items, PW_HOME_SERVER_POOL); + vp = pairfind(request->config_items, PW_HOME_SERVER_POOL, 0); if (!vp) return 0; switch (request->packet->code) { @@ -2204,7 +2229,7 @@ found_pool: * requests. */ if (realm && (realm->striprealm == TRUE) && - (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) { + (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME, 0)) != NULL) { /* * If there's a Stripped-User-Name attribute in * the request, then use THAT as the User-Name @@ -2218,10 +2243,10 @@ found_pool: * from the vps list, and making the new * User-Name the head of the vps list. */ - vp = pairfind(request->proxy->vps, PW_USER_NAME); + vp = pairfind(request->proxy->vps, PW_USER_NAME, 0); if (!vp) { vp = radius_paircreate(request, NULL, - PW_USER_NAME, PW_TYPE_STRING); + PW_USER_NAME, 0, PW_TYPE_STRING); rad_assert(vp != NULL); /* handled by above function */ /* Insert at the START of the list */ vp->next = request->proxy->vps; @@ -2243,10 +2268,10 @@ found_pool: * anymore - we changed it. */ if ((request->packet->code == PW_AUTHENTICATION_REQUEST) && - pairfind(request->proxy->vps, PW_CHAP_PASSWORD) && - pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) { + pairfind(request->proxy->vps, PW_CHAP_PASSWORD, 0) && + pairfind(request->proxy->vps, PW_CHAP_CHALLENGE, 0) == NULL) { vp = radius_paircreate(request, &request->proxy->vps, - PW_CHAP_CHALLENGE, PW_TYPE_OCTETS); + PW_CHAP_CHALLENGE, 0, PW_TYPE_OCTETS); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } @@ -2256,7 +2281,7 @@ found_pool: * doesn't need it. */ vp = radius_paircreate(request, &request->proxy->vps, - PW_PROXY_STATE, PW_TYPE_OCTETS); + PW_PROXY_STATE, 0, PW_TYPE_OCTETS); snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%d", request->packet->id); vp->length = strlen(vp->vp_strvalue); @@ -2270,7 +2295,7 @@ found_pool: /* * Call the pre-proxy routines. */ - vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE); + vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE, 0); if (vp) { RDEBUG2(" Found Pre-Proxy-Type %s", vp->vp_strvalue); pre_proxy_type = vp->vp_integer; @@ -2326,7 +2351,7 @@ found_pool: } if (!proxy_request(request)) { - RDEBUG("ERROR: Failed to proxy request %u", request->number); + RDEBUG("ERROR: Failed to proxy request"); return -1; } @@ -2343,7 +2368,7 @@ static void request_post_handler(REQUEST *request) if ((request->master_state == REQUEST_STOP_PROCESSING) || (request->parent && (request->parent->master_state == REQUEST_STOP_PROCESSING))) { - RDEBUG2("request %u was cancelled.", request->number); + RDEBUG2("request was cancelled."); #ifdef HAVE_PTHREAD_H request->child_pid = NO_SUCH_CHILD_PID; #endif @@ -2375,7 +2400,7 @@ static void request_post_handler(REQUEST *request) */ if ((request->packet->code == PW_AUTHENTICATION_REQUEST) && (request->reply->code == 0) && - ((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) && + ((vp = pairfind(request->config_items, PW_AUTH_TYPE, 0)) != NULL) && (vp->vp_integer == PW_AUTHTYPE_REJECT)) { request->reply->code = PW_AUTHENTICATION_REJECT; } @@ -2437,7 +2462,7 @@ static void request_post_handler(REQUEST *request) /* * Copy Proxy-State from the request to the reply. */ - vp = paircopy2(request->packet->vps, PW_PROXY_STATE); + vp = paircopy2(request->packet->vps, PW_PROXY_STATE, 0); if (vp) pairadd(&request->reply->vps, vp); #endif @@ -2453,15 +2478,13 @@ static void request_post_handler(REQUEST *request) * Check if the lack of response is intentional. */ vp = pairfind(request->config_items, - PW_RESPONSE_PACKET_TYPE); + PW_RESPONSE_PACKET_TYPE, 0); if (!vp) { - RDEBUG2("There was no response configured: rejecting request %u", - request->number); + RDEBUG2("There was no response configured: rejecting request"); request->reply->code = PW_AUTHENTICATION_REJECT; } else if (vp->vp_integer == 256) { - RDEBUG2("Not responding to request %u", - request->number); + RDEBUG2("Not responding to request"); /* * Force cleanup after a long @@ -2484,7 +2507,7 @@ static void request_post_handler(REQUEST *request) * Post-Auth-Type = Reject */ if (request->reply->code == PW_AUTHENTICATION_REJECT) { - pairdelete(&request->config_items, PW_POST_AUTH_TYPE); + pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0); vp = radius_pairmake(request, &request->config_items, "Post-Auth-Type", "Reject", T_OP_SET); @@ -2500,8 +2523,7 @@ static void request_post_handler(REQUEST *request) when.tv_sec += request->root->reject_delay; if (timercmp(&when, &request->next_when, >)) { - RDEBUG2("Delaying reject of request %u for %d seconds", - request->number, + RDEBUG2("Delaying reject for %d seconds", request->root->reject_delay); request->next_when = when; request->next_callback = reject_delay; @@ -2552,8 +2574,11 @@ static void request_post_handler(REQUEST *request) * and it should re-send it. * If configured, encode, sign, and send. */ - if ((request->reply->code != 0) || - (request->listener->type == RAD_LISTEN_DETAIL)) { + if ((request->reply->code != 0) +#ifdef WITH_DETAIL + || (request->listener->type == RAD_LISTEN_DETAIL) +#endif + ) { DEBUG_PACKET(request, request->reply, 1); request->listener->send(request->listener, request); } @@ -2565,8 +2590,10 @@ static void request_post_handler(REQUEST *request) * if it wasn't proxied. */ if (!request->proxy && + (request->packet->code != PW_COA_REQUEST) && + (request->packet->code != PW_DISCONNECT_REQUEST) && (request->coa || - (pairfind(request->config_items, PW_SEND_COA_REQUEST) != NULL))) { + (pairfind(request->config_items, PW_SEND_COA_REQUEST, 0) != NULL))) { if (!originated_coa_request(request)) { RDEBUG2("Do CoA Fail handler here"); } @@ -2609,7 +2636,7 @@ static void request_post_handler(REQUEST *request) } #endif - RDEBUG2("Finished request %u.", request->number); + RDEBUG2("Finished request."); rad_assert(child_state >= 0); request->child_state = child_state; @@ -2620,12 +2647,101 @@ static void request_post_handler(REQUEST *request) } -static void received_retransmit(REQUEST *request, const RADCLIENT *client) -{ #ifdef WITH_PROXY - char buffer[128]; +static void rad_retransmit_packet(REQUEST *request) +{ + char buffer[256]; + +#ifdef WITH_TCP + if (request->home_server->proto == IPPROTO_TCP) { + DEBUG2("Suppressing duplicate proxied request to home server %s port %d proto TCP - ID: %d", + inet_ntop(request->proxy->dst_ipaddr.af, + &request->proxy->dst_ipaddr.ipaddr, + buffer, sizeof(buffer)), + request->proxy->dst_port, + request->proxy->id); + return; /* don't do anything else */ + } #endif + RDEBUG2("Sending duplicate proxied request to home server %s port %d - ID: %d", + inet_ntop(request->proxy->dst_ipaddr.af, + &request->proxy->dst_ipaddr.ipaddr, + buffer, sizeof(buffer)), + request->proxy->dst_port, + request->proxy->id); + request->num_proxied_requests++; + + DEBUG_PACKET(request, request->proxy, 1); + request->proxy_listener->send(request->proxy_listener, + request); +} + + +static int rad_retransmit(REQUEST *request) +{ + /* + * If we've just discovered that the home server + * is dead, OR the socket has been closed, look for + * another connection to a home server. + */ + if ((request->home_server->state == HOME_STATE_IS_DEAD) || + (request->proxy_listener->status != RAD_LISTEN_STATUS_KNOWN)) { + home_server *home; + + remove_from_proxy_hash(request); + + home = home_server_ldb(NULL, request->home_pool, request); + if (!home) { + RDEBUG2("ERROR: Failed to find live home server for request"); + no_home_servers: + /* + * Do post-request processing, + * and any insertion of necessary + * events. + */ + post_proxy_fail_handler(request); + return 1; + } + + request->proxy->code = request->packet->code; + + /* + * Free the old packet, to force re-encoding + */ + free(request->proxy->data); + request->proxy->data = NULL; + request->proxy->data_len = 0; + + /* + * This request failed over to a virtual + * server. Push it back onto the queue + * to be processed. + */ + if (request->home_server->server) { + proxy_fallback_handler(request); + return 1; + } + + /* + * Try to proxy the request. + */ + if (!proxy_request(request)) { + RDEBUG("ERROR: Failed to re-proxy request"); + goto no_home_servers; + } + return 1; + } /* else the home server is still alive */ + + rad_retransmit_packet(request); + + return 1; +} +#endif + +static void received_retransmit(REQUEST *request, const RADCLIENT *client) +{ + RAD_STATS_TYPE_INC(request->listener, total_dup_requests); RAD_STATS_CLIENT_INC(request->listener, client, total_dup_requests); @@ -2670,90 +2786,25 @@ static void received_retransmit(REQUEST *request, const RADCLIENT *client) check_for_zombie_home_server(request); /* - * If we've just discovered that the home server - * is dead, OR the socket has been closed, look for - * another connection to a home server. + * Home server is still alive, and the proxy + * socket is OK. Just re-send the packet. */ - if (((request->packet->dst_port != 0) && - (request->home_server->state == HOME_STATE_IS_DEAD)) || - (request->proxy_listener->status != RAD_LISTEN_STATUS_KNOWN)) { - home_server *home; - - remove_from_proxy_hash(request); - - home = home_server_ldb(NULL, request->home_pool, request); - if (!home) { - RDEBUG2("Failed to find live home server for request %u", request->number); - no_home_servers: - /* - * Do post-request processing, - * and any insertion of necessary - * events. - */ - post_proxy_fail_handler(request); - return; - } - - request->proxy->code = request->packet->code; - - /* - * Free the old packet, to force re-encoding - */ - free(request->proxy->data); - request->proxy->data = NULL; - request->proxy->data_len = 0; - - /* - * This request failed over to a virtual - * server. Push it back onto the queue - * to be processed. - */ - if (request->home_server->server) { - proxy_fallback_handler(request); - return; - } - - /* - * Try to proxy the request. - */ - if (!proxy_request(request)) { - RDEBUG("ERROR: Failed to re-proxy request %u", request->number); - goto no_home_servers; - } - - /* - * This code executes in the main server - * thread, so there's no need for locking. - */ - rad_assert(request->next_callback != NULL); - INSERT_EVENT(request->next_callback, request); - request->next_callback = NULL; - return; - } /* else the home server is still alive */ - -#ifdef WITH_TCP - if (request->home_server->proto == IPPROTO_TCP) { - DEBUG2("Suppressing duplicate proxied request to home server %s port %d proto TCP - ID: %d", - inet_ntop(request->proxy->dst_ipaddr.af, - &request->proxy->dst_ipaddr.ipaddr, - buffer, sizeof(buffer)), - request->proxy->dst_port, - request->proxy->id); + if ((request->home_server->state != HOME_STATE_IS_DEAD) && + (request->proxy_listener->status == RAD_LISTEN_STATUS_KNOWN)) { + rad_retransmit_packet(request); break; } -#endif - RDEBUG2("Sending duplicate proxied request to home server %s port %d - ID: %d", - inet_ntop(request->proxy->dst_ipaddr.af, - &request->proxy->dst_ipaddr.ipaddr, - buffer, sizeof(buffer)), - request->proxy->dst_port, - request->proxy->id); - request->num_proxied_requests++; + /* + * Otherwise, we need to fail over to another + * home server, and possibly run "post-proxy-type + * fail". Add an event waiting for the child to + * have a result. + */ + INSERT_EVENT(wait_a_bit, request); - DEBUG_PACKET(request, request->proxy, 1); - request->proxy_listener->send(request->proxy_listener, - request); + request->priority = RAD_LISTEN_PROXY; + thread_pool_addrequest(request, rad_retransmit); break; #endif @@ -2969,7 +3020,10 @@ int received_request(rad_listen_t *listener, /* * We may want to quench the new request. */ - if ((listener->type != RAD_LISTEN_DETAIL) && + if ( +#ifdef WITH_DETAIL + (listener->type != RAD_LISTEN_DETAIL) && +#endif !can_handle_new_request(packet, client, root)) { return 0; } @@ -3076,7 +3130,10 @@ REQUEST *received_proxy_response(RADIUS_PACKET *packet) REQUEST *request; /* - * Also removes from the proxy hash if responses == requests + * Lookup *without* removal. In versions prior to 2.2.0, + * this did lookup *and* removal. That method allowed + * attackers to spoof replies that caused entries to be + * removed from the proxy hash prior to validation. */ request = lookup_in_proxy_hash(packet); @@ -3090,53 +3147,80 @@ REQUEST *received_proxy_response(RADIUS_PACKET *packet) } /* - * We haven't replied to the NAS, but we have seen an - * earlier reply from the home server. Ignore this packet, - * as we're likely still processing the previous reply. + * There's a reply: discard it if it's a conflicting one. */ if (request->proxy_reply) { + /* + * ? The home server gave us a new proxy + * reply which doesn't match the old + * one. Delete it. + */ if (memcmp(request->proxy_reply->vector, packet->vector, - sizeof(request->proxy_reply->vector)) == 0) { - RDEBUG2("Discarding duplicate reply from host %s port %d - ID: %d for request %u", - inet_ntop(packet->src_ipaddr.af, - &packet->src_ipaddr.ipaddr, - buffer, sizeof(buffer)), - packet->src_port, packet->id, - request->number); - } else { - /* - * ? The home server gave us a new proxy - * reply which doesn't match the old - * one. Delete it. - */ + sizeof(request->proxy_reply->vector)) != 0) { RDEBUG2("Ignoring conflicting proxy reply"); - } + - /* assert that there's an event queued for request? */ + /* assert that there's an event queued for request? */ + return NULL; + } /* else it had previously passed verification */ + + /* + * Verify the packet before doing ANYTHING with + * it. This means we're doing more MD5 checks in + * the server core. However, we can fix that by + * moving to multiple threads listening on + * sockets. + * + * We do this AFTER looking the request up in the + * hash, and AFTER checking if we saw a previous + * request. This helps minimize the DoS effect + * of people attacking us with spoofed packets. + * + * FIXME: move the "read from proxy socket" code + * into one (or more) threads. Have it read from + * the socket, do the validation, and write a + * pointer to the packet into a pipe? Or queue it + * to the main server? + */ + } else if (rad_verify(packet, request->proxy, + request->home_server->secret) != 0) { + DEBUG("Ignoring spoofed proxy reply. Signature is invalid"); return NULL; } /* - * Verify the packet before doing ANYTHING with it. This - * means we're doing more MD5 checks in the server core. - * However, we can fix that by moving to multiple threads - * listening on sockets. - * - * We do this AFTER looking the request up in the hash, - * and AFTER vhecking if we saw a previous request. This - * helps minimize the DoS effect of people attacking us - * with spoofed packets. + * Check (again) if it's a duplicate reply. We do this + * after deleting the packet from the proxy hash. */ - if (rad_verify(packet, request->proxy, - request->home_server->secret) != 0) { - DEBUG("Ignoring spoofed proxy reply. Signature is invalid"); - return NULL; + if (request->proxy_reply) { + RDEBUG2("Discarding duplicate reply from host %s port %d - ID: %d", + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + buffer, sizeof(buffer)), + packet->src_port, packet->id); } gettimeofday(&now, NULL); /* + * "ping" packets have a different algorithm for marking + * a home server alive. They also skip all of the CoA, + * etc. checks. + */ + if (!request->packet) { + request->proxy_reply = packet; +#ifdef WITH_TCP + rad_assert(request->home_server != NULL); + if (request->home_server->proto != IPPROTO_TCP) +#endif + received_response_to_ping(request); + request->proxy_reply = NULL; /* caller will free it */ + ev_request_free(&request); + return NULL; + } + + /* * Maybe move this earlier in the decision process? * Having it here means that late or duplicate proxy * replies no longer get the home server marked as @@ -3146,10 +3230,11 @@ REQUEST *received_proxy_response(RADIUS_PACKET *packet) * receive a packet? Setting this here means that we * mark it alive on *any* packet, even if it's lost all * of the *other* packets in the last 10s. + * + * This behavior could be configurable. */ - if (request->proxy->code != PW_STATUS_SERVER) { - request->home_server->state = HOME_STATE_ALIVE; - } + request->home_server->state = HOME_STATE_ALIVE; + request->home_server->last_packet = now.tv_sec; #ifdef WITH_COA /* @@ -3198,7 +3283,7 @@ REQUEST *received_proxy_response(RADIUS_PACKET *packet) RDEBUG2("Ignoring proxy reply that arrived after we sent a reply to the NAS"); return NULL; } - + #ifdef WITH_STATS /* * The average includes our time to receive packets and @@ -3274,17 +3359,6 @@ REQUEST *received_proxy_response(RADIUS_PACKET *packet) } #endif - /* - * There's no incoming request, so it's a proxied packet - * we originated. - */ - if (!request->packet) { - received_response_to_ping(request); - request->proxy_reply = NULL; /* caller will free it */ - ev_request_free(&request); - return NULL; - } - request->child_state = REQUEST_QUEUED; request->when = now; request->delay = USEC; @@ -3496,7 +3570,7 @@ int event_new_fd(rad_listen_t *this) } #ifdef WITH_PROXY else { - int count = this->count; + int count; /* * Duplicate code @@ -3578,7 +3652,10 @@ int event_new_fd(rad_listen_t *this) */ if (this->status == RAD_LISTEN_STATUS_CLOSED) { int count = this->count; + +#ifdef WITH_DETAIL rad_assert(this->type != RAD_LISTEN_DETAIL); +#endif #ifdef WITH_PROXY /* @@ -3854,45 +3931,44 @@ static void event_socket_handler(fr_event_list_t *xel, UNUSED int fd, xel = xel; - if (listener->fd < 0) rad_panic("Socket was closed on us!"); + if ( +#ifdef WITH_DETAIL + (listener->type != RAD_LISTEN_DETAIL) && +#endif + (listener->fd < 0)) { + char buffer[256]; + + listener->print(listener, buffer, sizeof(buffer)); + radlog(L_ERR, "FATAL: Asked to read from closed socket: %s", + buffer); + + rad_panic("Socket was closed on us!"); + _exit(1); + } if (!listener->recv(listener, &fun, &request)) return; - if (!thread_pool_addrequest(request, fun)) { - request->child_state = REQUEST_DONE; - } -} + rad_assert(fun != NULL); + rad_assert(request != NULL); + thread_pool_addrequest(request, fun); +} +#ifdef WITH_DETAIL /* * This function is called periodically to see if this detail * file is available for reading. */ static void event_poll_detail(void *ctx) { - int rcode, delay; - RAD_REQUEST_FUNP fun; - REQUEST *request; + int delay; rad_listen_t *this = ctx; struct timeval when; listen_detail_t *detail = this->data; rad_assert(this->type == RAD_LISTEN_DETAIL); - /* - * Try to read something. - * - * FIXME: This does poll AND receive. - */ - rcode = this->recv(this, &fun, &request); - if (rcode != 0) { - rad_assert(fun != NULL); - rad_assert(request != NULL); - - if (!thread_pool_addrequest(request, fun)) { - request->child_state = REQUEST_DONE; - } - } + event_socket_handler(el, this->fd, this); fr_event_now(el, &now); when = now; @@ -3910,7 +3986,7 @@ static void event_poll_detail(void *ctx) exit(1); } } - +#endif static void event_status(struct timeval *wake) { @@ -4019,6 +4095,10 @@ int radius_event_init(CONF_SECTION *cs, int spawn_flag) if (check_config) { DEBUG("%s: #### Skipping IP addresses and Ports ####", mainconfig.name); + if (listen_init(cs, &head) < 0) { + fflush(NULL); + exit(1); + } return 1; }