#include #include #include #include #include #include #include #include #include #include #include "log.h" #include "radius_p.h" #include "mempool.h" #include "memdebug.h" static int make_socket(struct rad_req_t *req); static mempool_t req_pool; static struct rad_req_t *__rad_req_alloc(struct radius_pd_t *rpd, int code, const char *username, in_addr_t addr, int port, int prio) { struct rad_plugin_t *plugin; struct ppp_t *ppp = NULL; struct rad_req_t *req = mempool_alloc(req_pool); struct timespec ts; if (!req) { log_emerg("radius: out of memory\n"); return NULL; } if (rpd->ses->ctrl->ppp) ppp = container_of(rpd->ses, typeof(*ppp), ses); clock_gettime(CLOCK_MONOTONIC, &ts); memset(req, 0, sizeof(*req)); req->rpd = rpd; req->hnd.fd = -1; req->hnd.read = rad_req_read; req->ts = ts.tv_sec; req->type = code == CODE_ACCESS_REQUEST ? RAD_SERV_AUTH : RAD_SERV_ACCT; req->prio = prio; if (addr) req->serv = rad_server_get2(req->type, addr, port); else req->serv = rad_server_get(req->type); if (!req->serv) goto out_err; req->server_addr = req->serv->addr; req->server_port = req->serv->auth_port; while (1) { if (read(urandom_fd, req->RA, 16) != 16) { if (errno == EINTR) continue; log_ppp_error("radius:req:read urandom: %s\n", strerror(errno)); goto out_err; } break; } if (conf_verbose) req->log = log_ppp_info1; req->pack = rad_packet_alloc(code); if (!req->pack) goto out_err; if (code == CODE_ACCOUNTING_REQUEST && rpd->acct_username) username = rpd->acct_username; if (rad_packet_add_str(req->pack, NULL, "User-Name", username)) goto out_err; if (conf_nas_identifier) if (rad_packet_add_str(req->pack, NULL, "NAS-Identifier", conf_nas_identifier)) goto out_err; if (conf_nas_ip_address) if (rad_packet_add_ipaddr(req->pack, NULL, "NAS-IP-Address", conf_nas_ip_address)) goto out_err; if (rpd->ses->unit_idx != -1 && rad_packet_add_int(req->pack, NULL, "NAS-Port", rpd->ses->unit_idx)) goto out_err; if (conf_nas_port_id_in_req) { if (*rpd->ses->ifname && rad_packet_add_str(req->pack, NULL, "NAS-Port-Id", rpd->ses->ifname)) goto out_err; } if (req->rpd->ses->ctrl->type == CTRL_TYPE_IPOE) { if (rad_packet_add_val(req->pack, NULL, "NAS-Port-Type", "Ethernet")) goto out_err; } else { if (rad_packet_add_val(req->pack, NULL, "NAS-Port-Type", "Virtual")) goto out_err; if (rad_packet_add_val(req->pack, NULL, "Service-Type", "Framed-User")) goto out_err; if (rad_packet_add_val(req->pack, NULL, "Framed-Protocol", "PPP")) goto out_err; } if (rpd->ses->ctrl->calling_station_id) if (rad_packet_add_str(req->pack, NULL, "Calling-Station-Id", rpd->ses->ctrl->calling_station_id)) goto out_err; if (rpd->ses->ctrl->called_station_id) if (rad_packet_add_str(req->pack, NULL, "Called-Station-Id", rpd->ses->ctrl->called_station_id)) goto out_err; if (rpd->attr_class) if (rad_packet_add_octets(req->pack, NULL, "Class", rpd->attr_class, rpd->attr_class_len)) goto out_err; if (conf_attr_tunnel_type) if (rad_packet_add_str(req->pack, NULL, conf_attr_tunnel_type, rpd->ses->ctrl->name)) goto out_err; list_for_each_entry(plugin, &req->rpd->plugin_list, entry) { switch (code) { case CODE_ACCESS_REQUEST: if (plugin->send_access_request && plugin->send_access_request(plugin, req->pack)) goto out_err; break; case CODE_ACCOUNTING_REQUEST: if (plugin->send_accounting_request && plugin->send_accounting_request(plugin, req->pack)) goto out_err; break; } } return req; out_err: if (!req->serv) log_ppp_error("radius: no servers available\n"); else log_emerg("radius: out of memory\n"); rad_req_free(req); return NULL; } struct rad_req_t *rad_req_alloc(struct radius_pd_t *rpd, int code, const char *username, int prio) { return __rad_req_alloc(rpd, code, username, 0, 0, prio); } struct rad_req_t *rad_req_alloc2(struct radius_pd_t *rpd, int code, const char *username, in_addr_t addr, int port) { struct rad_req_t *req = __rad_req_alloc(rpd, code, username, addr, port, 0); if (!req) return NULL; if (code == CODE_ACCOUNTING_REQUEST) req->server_port = req->serv->acct_port; if (make_socket(req)) { rad_req_free(req); req = NULL; } return req; } struct rad_req_t *rad_req_alloc_empty() { struct rad_req_t *req = mempool_alloc(req_pool); if (!req) { log_emerg("radius: out of memory\n"); return NULL; } memset(req, 0, sizeof(*req)); req->hnd.fd = -1; return req; } int rad_req_acct_fill(struct rad_req_t *req) { struct ipv6db_addr_t *a; req->server_port = req->serv->acct_port; memset(req->RA, 0, sizeof(req->RA)); if (rad_packet_add_val(req->pack, NULL, "Acct-Status-Type", "Start")) return -1; if (rad_packet_add_val(req->pack, NULL, "Acct-Authentic", "RADIUS")) return -1; if (rad_packet_add_str(req->pack, NULL, "Acct-Session-Id", req->rpd->ses->sessionid)) return -1; if (rad_packet_add_int(req->pack, NULL, "Acct-Session-Time", 0)) return -1; if (rad_packet_add_int(req->pack, NULL, "Acct-Input-Octets", 0)) return -1; if (rad_packet_add_int(req->pack, NULL, "Acct-Output-Octets", 0)) return -1; if (rad_packet_add_int(req->pack, NULL, "Acct-Input-Packets", 0)) return -1; if (rad_packet_add_int(req->pack, NULL, "Acct-Output-Packets", 0)) return -1; if (rad_packet_add_int(req->pack, NULL, "Acct-Input-Gigawords", 0)) return -1; if (rad_packet_add_int(req->pack, NULL, "Acct-Output-Gigawords", 0)) return -1; if (conf_acct_delay_time) { if (rad_packet_add_int(req->pack, NULL, "Acct-Delay-Time", 0)) return -1; } if (req->rpd->ses->ipv4) { if (rad_packet_add_ipaddr(req->pack, NULL, "Framed-IP-Address", req->rpd->ses->ipv4->peer_addr)) return -1; } if (req->rpd->ses->ipv6) { if (rad_packet_add_ifid(req->pack, NULL, "Framed-Interface-Id", req->rpd->ses->ipv6->peer_intf_id)) return -1; list_for_each_entry(a, &req->rpd->ses->ipv6->addr_list, entry) { if (rad_packet_add_ipv6prefix(req->pack, NULL, "Framed-IPv6-Prefix", &a->addr, a->prefix_len)) return -1; } } if (req->rpd->ses->ipv6_dp) { list_for_each_entry(a, &req->rpd->ses->ipv6_dp->prefix_list, entry) { if (rad_packet_add_ipv6prefix(req->pack, NULL, "Delegated-IPv6-Prefix", &a->addr, a->prefix_len)) return -1; } req->rpd->ipv6_dp_sent = 1; } return 0; } void rad_req_free(struct rad_req_t *req) { assert(!req->active); assert(!req->entry.next); if (req->serv) rad_server_put(req->serv, req->type); if (req->hnd.tpd) triton_md_unregister_handler(&req->hnd, 1); else if (req->hnd.fd != -1) close(req->hnd.fd); if (req->timeout.tpd) triton_timer_del(&req->timeout); if (req->pack) rad_packet_free(req->pack); if (req->reply) rad_packet_free(req->reply); mempool_free(req); } static int make_socket(struct rad_req_t *req) { struct sockaddr_in addr; req->hnd.fd = socket(PF_INET, SOCK_DGRAM, 0); if (req->hnd.fd < 0) { log_ppp_error("radius:socket: %s\n", strerror(errno)); return -1; } fcntl(req->hnd.fd, F_SETFD, fcntl(req->hnd.fd, F_GETFD) | FD_CLOEXEC); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; if (conf_bind) { addr.sin_addr.s_addr = conf_bind; if (bind(req->hnd.fd, (struct sockaddr *) &addr, sizeof(addr))) { log_ppp_error("radius:bind: %s\n", strerror(errno)); goto out_err; } } addr.sin_addr.s_addr = req->server_addr; addr.sin_port = htons(req->server_port); if ( req->serv && !req->serv->bind_default && ( 0 > setsockopt(req->hnd.fd, SOL_SOCKET, SO_BINDTODEVICE, req->serv->bind_device, strlen(req->serv->bind_device)) ) ) { log_ppp_error("radius:setsockopt: %s\n", strerror(errno)); goto out_err; } if (connect(req->hnd.fd, (struct sockaddr *) &addr, sizeof(addr))) { log_ppp_error("radius:connect: %s\n", strerror(errno)); goto out_err; } if (fcntl(req->hnd.fd, F_SETFL, O_NONBLOCK)) { log_ppp_error("radius: failed to set nonblocking mode: %s\n", strerror(errno)); goto out_err; } return 0; out_err: if (req->hnd.tpd) triton_md_unregister_handler(&req->hnd, 1); else { close(req->hnd.fd); req->hnd.fd = -1; } return -1; } int __rad_req_send(struct rad_req_t *req, int async) { if (async == -1) { if (req->active) req->try = conf_max_try; if (rad_req_send(req)) req->sent(req, -1); return 0; } if (req->hnd.fd == -1 && make_socket(req)) return -2; if (req->before_send && req->before_send(req)) goto out_err; if (!req->pack->buf && rad_packet_build(req->pack, req->RA)) goto out_err; if (req->log) { req->log("send "); rad_packet_print(req->pack, req->serv, req->log); } if (req->sent) req->sent(req, 0); rad_packet_send(req->pack, req->hnd.fd, NULL); return 0; out_err: if (req->hnd.tpd) triton_md_unregister_handler(&req->hnd, 1); else { close(req->hnd.fd); req->hnd.fd = -1; } if (async && req->sent) req->sent(req, -1); return -1; } int rad_req_send(struct rad_req_t *req) { int r; req->send = __rad_req_send; if (req->try++ == conf_max_try) { if (req->active) rad_server_req_exit(req); log_ppp_warn("radius: server(%i) not responding\n", req->serv->id); if (rad_server_realloc(req)) { if (req->rpd) log_ppp_warn("radius: no available servers\n"); return -1; } req->try = 1; } if (!req->active) { while (1) { r = rad_server_req_enter(req); if (r >= 0) break; if (rad_server_realloc(req)) { if (req->rpd) log_ppp_warn("radius: no available servers\n"); return -1; } } } else r = __rad_req_send(req, 0); return r; } int rad_req_read(struct triton_md_handler_t *h) { struct rad_req_t *req = container_of(h, typeof(*req), hnd); struct rad_packet_t *pack; if (!req->rpd) log_switch(triton_context_self(), NULL); while (1) { if (rad_packet_recv(h->fd, &pack, NULL)) return 0; rad_server_reply(req->serv); if (pack->id == req->pack->id) break; rad_packet_free(pack); } req->reply = pack; if (req->active) rad_server_req_exit(req); else rad_server_req_cancel(req, 0); if (req->log) { req->log("recv "); rad_packet_print(req->reply, req->serv, req->log); } if (req->recv) req->recv(req); return 1; } static void req_init(void) { req_pool = mempool_create(sizeof(struct rad_req_t)); } DEFINE_INIT(50, req_init);