diff options
Diffstat (limited to 'accel-pppd/ctrl')
26 files changed, 1886 insertions, 681 deletions
diff --git a/accel-pppd/ctrl/ipoe/arp.c b/accel-pppd/ctrl/ipoe/arp.c index ef9d3859..a6ca5b13 100644 --- a/accel-pppd/ctrl/ipoe/arp.c +++ b/accel-pppd/ctrl/ipoe/arp.c @@ -13,10 +13,8 @@ #include <netinet/ip.h> #include <sys/socket.h> #include <sys/ioctl.h> -#ifdef HAVE_GOOD_IFARP -#include <linux/if_arp.h> -#endif -#include <linux/if_packet.h> +#include <net/if_arp.h> +#include <netpacket/packet.h> #include "list.h" #include "triton.h" diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.c b/accel-pppd/ctrl/ipoe/dhcpv4.c index 466dfee6..2bd59437 100644 --- a/accel-pppd/ctrl/ipoe/dhcpv4.c +++ b/accel-pppd/ctrl/ipoe/dhcpv4.c @@ -24,15 +24,12 @@ #include "ipdb.h" #include "radius.h" #include "dhcp_attr_defs.h" +#include "utils.h" #include "dhcpv4.h" #define BUF_SIZE 4096 -#ifndef max -#define max(x,y) ((x) > (y) ? (x) : (y)) -#endif - struct dhcpv4_relay_ctx { struct list_head entry; struct triton_context_t *ctx; @@ -161,7 +158,7 @@ struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifna goto out_err; } - if (bind(sock, &addr, sizeof(addr))) { + if (bind(sock, (struct sockaddr*)&addr, sizeof(addr))) { log_error("bind: %s\n", strerror(errno)); goto out_err; } @@ -349,6 +346,12 @@ static int dhcpv4_parse_packet(struct dhcpv4_packet *pack, int len) list_add_tail(&opt->entry, &pack->options); + } + + if (dhcpv4_check_options(pack)) + return -1; + + list_for_each_entry(opt, &pack->options, entry) { if (opt->type == 53) pack->msg_type = opt->data[0]; else if (opt->type == 82) @@ -356,17 +359,14 @@ static int dhcpv4_parse_packet(struct dhcpv4_packet *pack, int len) else if (opt->type == 62) pack->client_id = opt; else if (opt->type == 50) - pack->request_ip = *(uint32_t *)opt->data; + memcpy(&pack->request_ip, opt->data, sizeof(pack->request_ip)); else if (opt->type == 54) - pack->server_id = *(uint32_t *)opt->data; + memcpy(&pack->server_id, opt->data, sizeof(pack->server_id)); } if (pack->msg_type == 0 || pack->msg_type > 8) return -1; - if (dhcpv4_check_options(pack)) - return -1; - pack->ptr = ptr; /*if (conf_verbose) { @@ -933,6 +933,7 @@ void dhcpv4_send_notify(struct dhcpv4_serv *serv, struct dhcpv4_packet *req, uns { struct dhcpv4_packet *pack = dhcpv4_packet_alloc(); uint8_t opt[8 + ETH_ALEN]; + uint32_t value; if (!pack) { log_emerg("out of memory\n"); @@ -946,8 +947,10 @@ void dhcpv4_send_notify(struct dhcpv4_serv *serv, struct dhcpv4_packet *req, uns pack->hdr->siaddr = 0; pack->hdr->giaddr = 0; - *(uint32_t *)opt = htonl(ACCEL_PPP_MAGIC); - *(uint32_t *)(opt + 4) = htonl(weight); + value = htonl(ACCEL_PPP_MAGIC); + memcpy(opt, &value, sizeof(value)); + value = htonl(weight); + memcpy(opt + sizeof(value), &value, sizeof(value)); memcpy(opt + 8, serv->hwaddr, ETH_ALEN); dhcpv4_packet_add_opt_u8(pack, 53, DHCPDISCOVER); @@ -1012,12 +1015,12 @@ struct dhcpv4_relay *dhcpv4_relay_create(const char *_addr, in_addr_t giaddr, st if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &f, sizeof(f))) log_error("dhcpv4: setsockopt(SO_REUSEADDR): %s\n", strerror(errno)); - if (bind(sock, &laddr, sizeof(laddr))) { + if (bind(sock, (struct sockaddr*)&laddr, sizeof(laddr))) { log_error("dhcpv4: relay: %s: bind: %s\n", _addr, strerror(errno)); goto out_err_unlock; } - if (connect(sock, &raddr, sizeof(raddr))) { + if (connect(sock, (struct sockaddr*)&raddr, sizeof(raddr))) { log_error("dhcpv4: relay: %s: connect: %s\n", _addr, strerror(errno)); goto out_err_unlock; } @@ -1100,8 +1103,8 @@ int dhcpv4_relay_send(struct dhcpv4_relay *relay, struct dhcpv4_packet *request, if (server_id) { opt = dhcpv4_packet_find_opt(request, 54); if (opt) { - _server_id = *(uint32_t *)opt->data; - *(uint32_t *)opt->data = server_id; + memcpy(&_server_id, opt->data, sizeof(_server_id)); + memcpy(opt->data, &server_id, sizeof(server_id)); } } @@ -1123,7 +1126,7 @@ int dhcpv4_relay_send(struct dhcpv4_relay *relay, struct dhcpv4_packet *request, request->hdr->giaddr = giaddr; if (opt) - *(uint32_t *)opt->data = _server_id; + memcpy(opt->data, &_server_id, sizeof(_server_id)); if (n != len) return -1; diff --git a/accel-pppd/ctrl/ipoe/dhcpv4_options.c b/accel-pppd/ctrl/ipoe/dhcpv4_options.c index b5f2b3bf..fe6c2a5f 100644 --- a/accel-pppd/ctrl/ipoe/dhcpv4_options.c +++ b/accel-pppd/ctrl/ipoe/dhcpv4_options.c @@ -46,7 +46,7 @@ static struct known_option options[] = { { 26, 2, 2, 2, "MTU", print_int }, { 28, 4, 4, 4, "Broadcast", print_ip }, { 33, 8, 255, 8, "Route", print_route }, - { 42, 4, 4, 4, "NTP", print_ip }, + { 42, 4, 255, 4, "NTP", print_ip }, { 43, 1, 255, 1, "Vendor-Specific", print_hex }, { 50, 4, 4, 4, "Request-IP", print_ip }, { 51, 4, 4, 4, "Lease-Time", print_uint }, @@ -113,18 +113,28 @@ void dhcpv4_print_options(struct dhcpv4_packet *pack, void (*print)(const char * static void print_int(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...)) { - if (opt->len == 2) - print("%i", ntohs(*(int16_t *)(opt->data))); - else - print("%i", ntohl(*(int32_t *)(opt->data))); + if (opt->len == 2) { + int16_t val; + memcpy(&val, opt->data, sizeof(val)); + print("%i", ntohs(val)); + } else { + int32_t val; + memcpy(&val, opt->data, sizeof(val)); + print("%i", ntohl(val)); + } } static void print_uint(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...)) { - if (opt->len == 2) - print("%u", ntohs(*(uint16_t *)(opt->data))); - else - print("%u", ntohl(*(uint32_t *)(opt->data))); + if (opt->len == 2) { + uint16_t val; + memcpy(&val, opt->data, sizeof(val)); + print("%u", ntohs(val)); + } else { + uint32_t val; + memcpy(&val, opt->data, sizeof(val)); + print("%u", ntohl(val)); + } } static void print_ip(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...)) @@ -133,7 +143,8 @@ static void print_ip(const struct dhcpv4_option *opt, int elem_size, void (*prin uint32_t ip; for (i = 0; i < n; i++) { - ip = ntohl(*(uint32_t *)(opt->data + i*elem_size)); + memcpy(&ip, opt->data + i*elem_size, sizeof(ip)); + ip = ntohl(ip); if (i) print(","); @@ -170,8 +181,10 @@ static void print_route(const struct dhcpv4_option *opt, int elem_size, void (*p uint32_t ip, gw; for (i = 0; i < n; i++) { - ip = ntohl(*(uint32_t *)(opt->data + i*8)); - gw = ntohl(*(uint32_t *)(opt->data + i*8 + 4)); + memcpy(&ip, opt->data + i*8, sizeof(ip)); + memcpy(&gw, opt->data + i*8 + 4, sizeof(gw)); + ip = ntohl(ip); + gw = ntohl(gw); if (i) print(","); @@ -192,7 +205,10 @@ static void print_message_type(const struct dhcpv4_option *opt, int elem_size, v { const char *msg_name[] = {"", "Discover", "Offer", "Request", "Decline", "Ack", "Nak", "Release", "Inform"}; - print("%s", msg_name[opt->data[0]]); + if (opt->data[0] < sizeof(msg_name) / sizeof(msg_name[0])) + print("%s", msg_name[opt->data[0]]); + else + print("%u", opt->data[0]); } static void print_request_list(const struct dhcpv4_option *opt, int elem_size, void (*print)(const char *fmt, ...)) @@ -219,14 +235,19 @@ static void print_relay_agent(const struct dhcpv4_option *opt, int elem_size, vo int type, len; while (ptr < endptr) { + if (endptr - ptr < 2) { + print("invalid"); + return; + } + if (ptr != opt->data) print(" "); type = *ptr++; len = *ptr++; - /*if (ptr + len > endptr) { + if (endptr - ptr < len) { print(" invalid"); return; - }*/ + } if (type == 1) print("{Agent-Circuit-ID "); else if (type == 2) @@ -252,7 +273,9 @@ static void print_classless_route(const struct dhcpv4_option *opt, int elem_size { const uint8_t *ptr = opt->data; const uint8_t *endptr = ptr + opt->len; - int mask, i, mask1 = 0; + unsigned int prefix_len, i; + int mask; + uint32_t mask1; uint32_t ip; uint32_t gw; @@ -261,20 +284,23 @@ static void print_classless_route(const struct dhcpv4_option *opt, int elem_size print(","); mask = *ptr++; - ip = ntohl(*(uint32_t *)ptr); - for (i = 0; i < mask; i++) - mask1 |= (1 << (32 - i)); + if (mask > 32) + return; + + prefix_len = (mask + 7) / 8; + if ((size_t)(endptr - ptr) < prefix_len + sizeof(gw)) + return; + + ip = 0; + for (i = 0; i < prefix_len; i++) + ip |= (uint32_t)ptr[i] << (24 - i * 8); + mask1 = mask ? UINT32_MAX << (32 - mask) : 0; ip &= mask1; - if (mask <= 8) - ptr++; - else if (mask <= 16) - ptr += 2; - else if (mask <= 24) - ptr += 3; - else - ptr += 4; - gw = ntohl(*(uint32_t *)ptr); - ptr += 4; + ptr += prefix_len; + + memcpy(&gw, ptr, sizeof(gw)); + gw = ntohl(gw); + ptr += sizeof(gw); print("%i.%i.%i.%i/%i via %i.%i.%i.%i", (ip >> 24) & 0xff, diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c index 95ff8568..e7b1320f 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.c +++ b/accel-pppd/ctrl/ipoe/ipoe.c @@ -14,13 +14,9 @@ #include <netinet/ip.h> #include <sys/socket.h> #include <sys/ioctl.h> -#include <linux/if.h> -#ifdef HAVE_GOOD_IFARP -#include <linux/if_arp.h> -#endif -#include <linux/route.h> - -#include <pcre.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/route.h> #include "events.h" #include "list.h" @@ -56,7 +52,7 @@ #define SESSION_TERMINATED "Session was terminated" struct iplink_arg { - pcre *re; + pcre2_code *re; const char *opt; long *arg1; }; @@ -184,9 +180,7 @@ static int conf_check_mac_change; static int conf_soft_terminate; static int conf_calling_sid = SID_MAC; -static unsigned int stat_starting; -static unsigned int stat_active; -static unsigned int stat_delayed_offer; +static struct ipoe_stat_t ipoe_stat; static mempool_t ses_pool; static mempool_t disc_item_pool; @@ -229,6 +223,39 @@ static struct ipoe_session *ipoe_session_create_up(struct ipoe_serv *serv, struc static void __terminate(struct ap_session *ses); static void ipoe_ipv6_disable(struct ipoe_serv *serv); +void __export ipoe_stat_get(struct ipoe_stat_t *stat) +{ + stat->starting = __atomic_load_n(&ipoe_stat.starting, __ATOMIC_RELAXED); + stat->active = __atomic_load_n(&ipoe_stat.active, __ATOMIC_RELAXED); + stat->delayed_offer = __atomic_load_n(&ipoe_stat.delayed_offer, __ATOMIC_RELAXED); +} + +unsigned int __export ipoe_stat_starting(void) +{ + return __atomic_load_n(&ipoe_stat.starting, __ATOMIC_RELAXED); +} + +unsigned int __export ipoe_stat_active(void) +{ + return __atomic_load_n(&ipoe_stat.active, __ATOMIC_RELAXED); +} + +static void ipoe_stat_inc(unsigned int *stat) +{ + __atomic_add_fetch(stat, 1, __ATOMIC_RELAXED); +} + +static void ipoe_stat_dec(unsigned int *stat) +{ + __atomic_sub_fetch(stat, 1, __ATOMIC_RELAXED); +} + +static void ipoe_stat_move(unsigned int *from, unsigned int *to) +{ + ipoe_stat_dec(from); + ipoe_stat_inc(to); +} + static void ipoe_ctx_switch(struct triton_context_t *ctx, void *arg) { if (arg) { @@ -745,7 +772,7 @@ static void ipoe_session_start(struct ipoe_session *ses) } } - __sync_add_and_fetch(&stat_starting, 1); + ipoe_stat_inc(&ipoe_stat.starting); assert(!ses->ses.username); @@ -756,6 +783,10 @@ static void ipoe_session_start(struct ipoe_session *ses) return; } + /* take ownership now so the string is freed by ipoe_session_free() + * even if the session terminates before auth_result() consumes it */ + ses->username = username; + ses->ses.unit_idx = ses->serv->ifindex; triton_event_fire(EV_CTRL_STARTING, &ses->ses); @@ -766,9 +797,9 @@ static void ipoe_session_start(struct ipoe_session *ses) if (ses->serv->opt_shared && ipoe_create_interface(ses)) return; - if (conf_noauth) + if (conf_noauth) { r = PWDB_SUCCESS; - else { + } else { #ifdef RADIUS if (radius_loaded) { ses->radius.send_access_request = ipoe_rad_send_auth_request; @@ -785,7 +816,6 @@ static void ipoe_session_start(struct ipoe_session *ses) } else pass = username; - ses->username = username; r = pwdb_check(&ses->ses, (pwdb_callback)auth_result, ses, username, PPP_PAP, pass); if (r == PWDB_WAIT) @@ -1023,9 +1053,9 @@ static void __ipoe_session_activate(struct ipoe_session *ses) in_addr_t gw; iproute_get(ses->router, &gw, NULL); if (gw) - iproute_add(0, ses->siaddr, ses->yiaddr, gw, conf_proto, 32); + iproute_add(0, ses->siaddr, ses->yiaddr, gw, conf_proto, 32, NULL); else - iproute_add(0, ses->siaddr, ses->router, gw, conf_proto, 32); + iproute_add(0, ses->siaddr, ses->router, gw, conf_proto, 32, NULL); }*/ if (serv->opt_mode == MODE_L3) @@ -1061,17 +1091,16 @@ static void __ipoe_session_activate(struct ipoe_session *ses) } } - __sync_sub_and_fetch(&stat_starting, 1); - __sync_add_and_fetch(&stat_active, 1); + ipoe_stat_move(&ipoe_stat.starting, &ipoe_stat.active); ses->started = 1; ap_session_activate(&ses->ses); if (ses->ifindex == -1 && !serv->opt_ifcfg) { if (!serv->opt_ip_unnumbered) - iproute_add(serv->ifindex, ses->router, ses->yiaddr, 0, conf_proto, ses->mask, 0); + iproute_add(serv->ifindex, ses->router, ses->yiaddr, 0, conf_proto, ses->mask, 0, NULL); else - iproute_add(serv->ifindex, serv->opt_src ?: ses->router, ses->yiaddr, 0, conf_proto, 32, 0); + iproute_add(serv->ifindex, serv->opt_src ?: ses->router, ses->yiaddr, 0, conf_proto, 32, 0, NULL); } if (ses->l4_redirect) @@ -1172,7 +1201,7 @@ static void ipoe_session_started(struct ap_session *s) if (ses->ses.ipv4->peer_addr != ses->yiaddr) //ipaddr_add_peer(ses->ses.ifindex, ses->router, ses->yiaddr); // breaks quagga - iproute_add(ses->ses.ifindex, ses->router, ses->yiaddr, 0, conf_proto, 32, 0); + iproute_add(ses->ses.ifindex, ses->router, ses->yiaddr, 0, conf_proto, 32, 0, NULL); if (ses->ifindex != -1 && ses->xid) { ses->dhcpv4 = dhcpv4_create(ses->ctrl.ctx, ses->ses.ifname, ""); @@ -1187,9 +1216,9 @@ static void ipoe_session_started(struct ap_session *s) static void ipoe_session_free(struct ipoe_session *ses) { if (ses->started) - __sync_sub_and_fetch(&stat_active, 1); + ipoe_stat_dec(&ipoe_stat.active); else - __sync_sub_and_fetch(&stat_starting, 1); + ipoe_stat_dec(&ipoe_stat.starting); if (ses->timer.tpd) triton_timer_del(&ses->timer); @@ -1215,6 +1244,9 @@ static void ipoe_session_free(struct ipoe_session *ses) if (ses->l4_redirect_ipset) _free(ses->l4_redirect_ipset); + if (ses->username) + _free(ses->username); + triton_context_unregister(&ses->ctx); if (ses->data) @@ -1256,9 +1288,9 @@ static void ipoe_session_finished(struct ap_session *s) } else if (ses->started) { if (!serv->opt_ifcfg) { if (!serv->opt_ip_unnumbered) - iproute_del(serv->ifindex, ses->router, ses->yiaddr, 0, conf_proto, ses->mask, 0); + iproute_del(serv->ifindex, ses->router, ses->yiaddr, 0, conf_proto, ses->mask, 0, NULL); else - iproute_del(serv->ifindex, serv->opt_src ?: ses->router, ses->yiaddr, 0, conf_proto, 32, 0); + iproute_del(serv->ifindex, serv->opt_src ?: ses->router, ses->yiaddr, 0, conf_proto, 32, 0, NULL); } } @@ -1365,10 +1397,10 @@ static struct ipoe_session *ipoe_session_create_dhcpv4(struct ipoe_serv *serv, s if (ap_shutdown) return NULL; - if (conf_max_starting && ap_session_stat.starting >= conf_max_starting) + if (conf_max_starting && ap_session_stat_starting() >= conf_max_starting) return NULL; - if (conf_max_sessions && ap_session_stat.active + ap_session_stat.starting >= conf_max_sessions) + if (conf_max_sessions && ap_session_stat_active() + ap_session_stat_starting() >= conf_max_sessions) return NULL; ses = ipoe_session_alloc(serv->ifname); @@ -1635,7 +1667,7 @@ static void ipoe_serv_disc_timer(struct triton_timer_t *t) list_del(&d->entry); mempool_free(d); - __sync_sub_and_fetch(&stat_delayed_offer, 1); + ipoe_stat_dec(&ipoe_stat.delayed_offer); } while (!list_empty(&serv->arp_list)) { @@ -1654,7 +1686,7 @@ static void ipoe_serv_disc_timer(struct triton_timer_t *t) list_del(&d->entry); mempool_free(d); - __sync_sub_and_fetch(&stat_delayed_offer, 1); + ipoe_stat_dec(&ipoe_stat.delayed_offer); } if (list_empty(&serv->disc_list) && list_empty(&serv->arp_list)) @@ -1675,7 +1707,7 @@ static void ipoe_serv_add_disc_arp(struct ipoe_serv *serv, struct _arphdr *arph, if (!d) return; - __sync_add_and_fetch(&stat_delayed_offer, 1); + ipoe_stat_inc(&ipoe_stat.delayed_offer); memcpy(&d->arph, arph, sizeof(*arph)); clock_gettime(CLOCK_MONOTONIC, &d->ts); @@ -1695,7 +1727,7 @@ static void ipoe_serv_add_disc(struct ipoe_serv *serv, struct dhcpv4_packet *pac if (!d) return; - __sync_add_and_fetch(&stat_delayed_offer, 1); + ipoe_stat_inc(&ipoe_stat.delayed_offer); dhcpv4_packet_ref(pack); d->pack = pack; @@ -1724,7 +1756,7 @@ static int ipoe_serv_check_disc(struct ipoe_serv *serv, struct dhcpv4_packet *pa dhcpv4_packet_free(d->pack); mempool_free(d); - __sync_sub_and_fetch(&stat_delayed_offer, 1); + ipoe_stat_dec(&ipoe_stat.delayed_offer); return 1; } @@ -1803,6 +1835,7 @@ static int check_notify(struct ipoe_serv *serv, struct dhcpv4_packet *pack) struct dhcpv4_option *opt = dhcpv4_packet_find_opt(pack, 43); struct ipoe_session *ses; unsigned int w; + uint32_t value; if (!opt) return 0; @@ -1810,10 +1843,12 @@ static int check_notify(struct ipoe_serv *serv, struct dhcpv4_packet *pack) if (opt->len != 8 + ETH_ALEN) return 0; - if (*(uint32_t *)opt->data != htonl(ACCEL_PPP_MAGIC)) + memcpy(&value, opt->data, sizeof(value)); + if (value != htonl(ACCEL_PPP_MAGIC)) return 0; - w = htonl(*(uint32_t *)(opt->data + 4)); + memcpy(&value, opt->data + sizeof(value), sizeof(value)); + w = ntohl(value); list_for_each_entry(ses, &serv->sessions, entry) { if (ses->xid == pack->hdr->xid && memcmp(pack->hdr->chaddr, ses->hwaddr, ETH_ALEN) == 0) { @@ -1872,7 +1907,7 @@ static void __ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet if (!ses) goto out; - ses->weight = weight = serv->opt_weight >= 0 ? serv->sess_cnt * serv->opt_weight : (stat_active + 1) * conf_weight; + ses->weight = weight = serv->opt_weight >= 0 ? serv->sess_cnt * serv->opt_weight : (ipoe_stat_active() + 1) * conf_weight; } else { if (ses->terminate) { triton_context_call(ses->ctrl.ctx, (triton_event_func)ipoe_session_terminated, ses); @@ -1997,12 +2032,14 @@ static void ipoe_ses_recv_dhcpv4_relay(struct dhcpv4_packet *pack) { struct ipoe_session *ses = container_of(triton_context_self(), typeof(*ses), ctx); struct dhcpv4_option *opt; + uint32_t value; if (ses->dhcpv4_relay_reply) dhcpv4_packet_free(ses->dhcpv4_relay_reply); if (!ses->dhcpv4_request) { ses->dhcpv4_relay_reply = NULL; + dhcpv4_packet_free(pack); return; } @@ -2014,24 +2051,32 @@ static void ipoe_ses_recv_dhcpv4_relay(struct dhcpv4_packet *pack) } opt = dhcpv4_packet_find_opt(pack, 51); - if (opt) - ses->lease_time = ntohl(*(uint32_t *)opt->data); + if (opt) { + memcpy(&value, opt->data, sizeof(value)); + ses->lease_time = ntohl(value); + } opt = dhcpv4_packet_find_opt(pack, 58); - if (opt) - ses->renew_time = ntohl(*(uint32_t *)opt->data); + if (opt) { + memcpy(&value, opt->data, sizeof(value)); + ses->renew_time = ntohl(value); + } opt = dhcpv4_packet_find_opt(pack, 59); - if (opt) - ses->rebind_time = ntohl(*(uint32_t *)opt->data); + if (opt) { + memcpy(&value, opt->data, sizeof(value)); + ses->rebind_time = ntohl(value); + } opt = dhcpv4_packet_find_opt(pack, 1); - if (opt) - ses->mask = parse_dhcpv4_mask(ntohl(*(uint32_t *)opt->data)); + if (opt) { + memcpy(&value, opt->data, sizeof(value)); + ses->mask = parse_dhcpv4_mask(ntohl(value)); + } opt = dhcpv4_packet_find_opt(pack, 3); if (opt) - ses->router = *(uint32_t *)opt->data; + memcpy(&ses->router, opt->data, sizeof(ses->router)); if (pack->msg_type == DHCPOFFER) { if (ses->ses.state == AP_STATE_STARTING) { @@ -2110,10 +2155,10 @@ static struct ipoe_session *ipoe_session_create_up(struct ipoe_serv *serv, struc if (ap_shutdown) return NULL; - if (conf_max_starting && ap_session_stat.starting >= conf_max_starting) + if (conf_max_starting && ap_session_stat_starting() >= conf_max_starting) return NULL; - if (conf_max_sessions && ap_session_stat.active + ap_session_stat.starting >= conf_max_sessions) + if (conf_max_sessions && ap_session_stat_active() + ap_session_stat_starting() >= conf_max_sessions) return NULL; if (connlimit_loaded && connlimit_check(serv->opt_shared ? cl_key_from_ipv4(saddr) : serv->ifindex)) @@ -2330,7 +2375,7 @@ void ipoe_serv_recv_arp(struct ipoe_serv *serv, struct _arphdr *arph) list_del(&d->entry); mempool_free(d); - __sync_sub_and_fetch(&stat_delayed_offer, 1); + ipoe_stat_dec(&ipoe_stat.delayed_offer); break; } @@ -2376,7 +2421,7 @@ static void ev_radius_access_accept(struct ev_radius_t *ev) ses->siaddr = attr->val.ipaddr; break; case DHCP_Router_Address: - ses->router = *(in_addr_t *)attr->raw; + memcpy(&ses->router, attr->raw, sizeof(ses->router)); break; case DHCP_Subnet_Mask: ses->mask = ipaddr_to_prefix(attr->val.ipaddr); @@ -2627,14 +2672,14 @@ static void ipoe_serv_release(struct ipoe_serv *serv) list_del(&d->entry); dhcpv4_packet_free(d->pack); mempool_free(d); - __sync_sub_and_fetch(&stat_delayed_offer, 1); + ipoe_stat_dec(&ipoe_stat.delayed_offer); } while (!list_empty(&serv->arp_list)) { struct arp_item *d = list_entry(serv->arp_list.next, typeof(*d), entry); list_del(&d->entry); mempool_free(d); - __sync_sub_and_fetch(&stat_delayed_offer, 1); + ipoe_stat_dec(&ipoe_stat.delayed_offer); } while (!list_empty(&serv->req_list)) { @@ -2707,10 +2752,14 @@ static void l4_redirect_ctx_close(struct triton_context_t *ctx) static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt, void *client) { + struct ipoe_stat_t stat; + + ipoe_stat_get(&stat); + cli_send(client, "ipoe:\r\n"); - cli_sendv(client," starting: %u\r\n", stat_starting); - cli_sendv(client," active: %u\r\n", stat_active); - cli_sendv(client," delayed: %u\r\n", stat_delayed_offer); + cli_sendv(client," starting: %u\r\n", stat.starting); + cli_sendv(client," active: %u\r\n", stat.active); + cli_sendv(client," delayed: %u\r\n", stat.delayed_offer); return CLI_CMD_OK; } @@ -2728,12 +2777,6 @@ static void print_session_type(struct ap_session *s, char *buf) *buf = 0; } -void __export ipoe_get_stat(unsigned int **starting, unsigned int **active) -{ - *starting = &stat_starting; - *active = &stat_active; -} - static void __terminate(struct ap_session *ses) { ap_session_terminate(ses, TERM_NAS_REQUEST, 1); @@ -2771,9 +2814,10 @@ struct ipoe_serv *ipoe_find_serv(const char *ifname) static int get_offer_delay() { struct delay *r, *prev = NULL; + unsigned int active = ipoe_stat_active(); list_for_each_entry(r, &conf_offer_delay, entry) { - if (!prev || stat_active >= r->conn_cnt) { + if (!prev || active >= r->conn_cnt) { prev = r; continue; } @@ -2804,10 +2848,10 @@ void ipoe_vlan_mon_notify(int ifindex, int vid, int vlan_ifindex) struct ifreq ifr; char *ptr; int len, r, svid; - pcre *re = NULL; - const char *pcre_err; + pcre2_code *re = NULL; + int pcre_err; char *pattern; - int pcre_offset; + PCRE2_SIZE pcre_offset; char ifname[IFNAMSIZ]; if (!sect) @@ -2905,15 +2949,17 @@ void ipoe_vlan_mon_notify(int ifindex, int vid, int vlan_ifindex) memcpy(pattern, opt->val + 3, ptr - (opt->val + 3)); pattern[ptr - (opt->val + 3)] = 0; - re = pcre_compile2(pattern, 0, NULL, &pcre_err, &pcre_offset, NULL); + re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, &pcre_err, &pcre_offset, NULL); _free(pattern); if (!re) continue; - r = pcre_exec(re, NULL, ifname, len, 0, 0, NULL, 0); - pcre_free(re); + pcre2_match_data *match_data = pcre2_match_data_create(0, NULL); + r = pcre2_match(re, (PCRE2_SPTR)ifname, len, 0, 0, match_data, NULL); + pcre2_match_data_free(match_data); + pcre2_code_free(re); if (r < 0) continue; @@ -2943,32 +2989,38 @@ static void ipoe_serv_timeout(struct triton_timer_t *t) static void ipoe_ipv6_enable(struct ipoe_serv *serv) { struct ifreq ifr; + uint32_t addr; strcpy(ifr.ifr_name, serv->ifname); ifr.ifr_hwaddr.sa_family = AF_UNSPEC; ifr.ifr_hwaddr.sa_data[0] = 0x33; ifr.ifr_hwaddr.sa_data[1] = 0x33; - *(uint32_t *)(ifr.ifr_hwaddr.sa_data + 2) = htonl(0x02); + addr = htonl(0x02); + memcpy(ifr.ifr_hwaddr.sa_data + 2, &addr, sizeof(addr)); ioctl(sock_fd, SIOCADDMULTI, &ifr); - *(uint32_t *)(ifr.ifr_hwaddr.sa_data + 2) = htonl(0x010002); + addr = htonl(0x010002); + memcpy(ifr.ifr_hwaddr.sa_data + 2, &addr, sizeof(addr)); ioctl(sock_fd, SIOCADDMULTI, &ifr); } static void ipoe_ipv6_disable(struct ipoe_serv *serv) { struct ifreq ifr; + uint32_t addr; strcpy(ifr.ifr_name, serv->ifname); ifr.ifr_hwaddr.sa_family = AF_UNSPEC; ifr.ifr_hwaddr.sa_data[0] = 0x33; ifr.ifr_hwaddr.sa_data[1] = 0x33; - *(uint32_t *)(ifr.ifr_hwaddr.sa_data + 2) = htonl(0x02); + addr = htonl(0x02); + memcpy(ifr.ifr_hwaddr.sa_data + 2, &addr, sizeof(addr)); ioctl(sock_fd, SIOCDELMULTI, &ifr); - *(uint32_t *)(ifr.ifr_hwaddr.sa_data + 2) = htonl(0x010002); + addr = htonl(0x010002); + memcpy(ifr.ifr_hwaddr.sa_data + 2, &addr, sizeof(addr)); ioctl(sock_fd, SIOCDELMULTI, &ifr); } @@ -3109,12 +3161,12 @@ static void add_interface(const char *ifname, int ifindex, const char *opt, int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (connect(sock, &addr, sizeof(addr))) { + if (connect(sock, (struct sockaddr*)&addr, sizeof(addr))) { log_error("dhcpv4: relay: %s: connect: %s\n", opt_relay, strerror(errno)); goto out_err; } - getsockname(sock, &addr, &len); + getsockname(sock, (struct sockaddr*)&addr, &len); opt_giaddr = addr.sin_addr.s_addr; close(sock); @@ -3358,8 +3410,12 @@ static void load_interface(const char *opt) static int __load_interface_re(int index, int flags, const char *name, int iflink, int vid, struct iplink_arg *arg) { - if (pcre_exec(arg->re, NULL, name, strlen(name), 0, 0, NULL, 0) < 0) + pcre2_match_data *match_data = pcre2_match_data_create(0, NULL); + if (pcre2_match(arg->re, (PCRE2_SPTR)name, strlen(name), 0, 0, match_data, NULL) < 0) { + pcre2_match_data_free(match_data); return 0; + } + pcre2_match_data_free(match_data); add_interface(name, index, arg->opt, iflink, vid, 0); @@ -3368,11 +3424,11 @@ static int __load_interface_re(int index, int flags, const char *name, int iflin static void load_interface_re(const char *opt) { - pcre *re = NULL; - const char *pcre_err; + pcre2_code *re = NULL; + int pcre_err; char *pattern; const char *ptr; - int pcre_offset; + PCRE2_SIZE pcre_offset; struct iplink_arg arg; struct ipoe_serv *serv; @@ -3382,10 +3438,12 @@ static void load_interface_re(const char *opt) memcpy(pattern, opt + 3, ptr - (opt + 3)); pattern[ptr - (opt + 3)] = 0; - re = pcre_compile2(pattern, 0, NULL, &pcre_err, &pcre_offset, NULL); + re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, &pcre_err, &pcre_offset, NULL); if (!re) { - log_error("ipoe: '%s': %s at %i\r\n", pattern, pcre_err, pcre_offset); + PCRE2_UCHAR err_msg[64]; + pcre2_get_error_message(pcre_err, err_msg, sizeof(err_msg)); + log_error("ipoe: '%s': %s at %i\r\n", pattern, err_msg, (int)pcre_offset); return; } @@ -3398,11 +3456,13 @@ static void load_interface_re(const char *opt) if (serv->active) continue; - if (pcre_exec(re, NULL, serv->ifname, strlen(serv->ifname), 0, 0, NULL, 0) >= 0) + pcre2_match_data *match_data = pcre2_match_data_create(0, NULL); + if (pcre2_match(re, (PCRE2_SPTR)serv->ifname, strlen(serv->ifname), 0, 0, match_data, NULL) >= 0) add_interface(serv->ifname, serv->ifindex, opt, 0, 0, 0); + pcre2_match_data_free(match_data); } - pcre_free(re); + pcre2_code_free(re); _free(pattern); } @@ -3474,7 +3534,7 @@ static void load_gw_addr(struct conf_sect_t *sect) continue; } - a->mask1 = ((1 << a->mask) - 1) << (32 - a->mask); + a->mask1 = (int)(((1u << a->mask) - 1u) << (32 - a->mask)); list_add_tail(&a->entry, &conf_gw_addr); } } @@ -3538,19 +3598,6 @@ static void load_radius_attrs(void) } #endif -static void strip(char *str) -{ - char *ptr = str; - char *endptr = strchr(str, 0); - while (1) { - ptr = strchr(ptr, ' '); - if (ptr) - memmove(ptr, ptr + 1, endptr - ptr - 1); - else - break; - } -} - int parse_offer_delay(const char *str) { char *str1; @@ -3567,7 +3614,7 @@ int parse_offer_delay(const char *str) return 0; str1 = _strdup(str); - strip(str1); + u_strstrip(str1, ' '); ptr1 = str1; @@ -3583,17 +3630,23 @@ int parse_offer_delay(const char *str) memset(r, 0, sizeof(*r)); r->delay = strtol(ptr1, &endptr, 10); - if (*endptr) + if (*endptr) { + _free(r); goto out_err; + } if (list_empty(&conf_offer_delay)) r->conn_cnt = 0; else { - if (!ptr3) + if (!ptr3) { + _free(r); goto out_err; + } r->conn_cnt = strtol(ptr3 + 1, &endptr, 10); - if (*endptr) + if (*endptr) { + _free(r); goto out_err; + } } list_add_tail(&r->entry, &conf_offer_delay); @@ -3608,6 +3661,11 @@ int parse_offer_delay(const char *str) return 0; out_err: + while (!list_empty(&conf_offer_delay)) { + r = list_entry(conf_offer_delay.next, typeof(*r), entry); + list_del(&r->entry); + _free(r); + } _free(str1); log_error("ipoe: failed to parse offer-delay\n"); return -1; @@ -3670,8 +3728,12 @@ static int __load_vlan_mon_re(int index, int flags, const char *name, int iflink long mask1[4096/8/sizeof(long)]; struct ipoe_serv *serv; - if (pcre_exec(arg->re, NULL, name, strlen(name), 0, 0, NULL, 0) < 0) + pcre2_match_data *match_data = pcre2_match_data_create(0, NULL); + if (pcre2_match(arg->re, (PCRE2_SPTR)name, strlen(name), 0, 0, match_data, NULL) < 0) { + pcre2_match_data_free(match_data); return 0; + } + pcre2_match_data_free(match_data); if (!(flags & IFF_UP)) { memset(&ifr, 0, sizeof(ifr)); @@ -3701,11 +3763,11 @@ static int __load_vlan_mon_re(int index, int flags, const char *name, int iflink static void load_vlan_mon_re(const char *opt, long *mask, int len) { - pcre *re = NULL; - const char *pcre_err; + pcre2_code *re = NULL; + int pcre_err; char *pattern; const char *ptr; - int pcre_offset; + PCRE2_SIZE pcre_offset; struct iplink_arg arg; for (ptr = opt; *ptr && *ptr != ','; ptr++); @@ -3714,10 +3776,12 @@ static void load_vlan_mon_re(const char *opt, long *mask, int len) memcpy(pattern, opt + 3, ptr - (opt + 3)); pattern[ptr - (opt + 3)] = 0; - re = pcre_compile2(pattern, 0, NULL, &pcre_err, &pcre_offset, NULL); + re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, &pcre_err, &pcre_offset, NULL); if (!re) { - log_error("ipoe: '%s': %s at %i\r\n", pattern, pcre_err, pcre_offset); + PCRE2_UCHAR err_msg[64]; + pcre2_get_error_message(pcre_err, err_msg, sizeof(err_msg)); + log_error("ipoe: '%s': %s at %i\r\n", pattern, err_msg, (int)pcre_offset); return; } @@ -3727,7 +3791,7 @@ static void load_vlan_mon_re(const char *opt, long *mask, int len) iplink_list((iplink_list_func)__load_vlan_mon_re, &arg); - pcre_free(re); + pcre2_code_free(re); _free(pattern); } @@ -3768,6 +3832,7 @@ static void parse_local_net(const char *opt) char str[17]; in_addr_t addr; int mask; + unsigned long val; char *endptr; struct local_net *n; @@ -3778,9 +3843,10 @@ static void parse_local_net(const char *opt) addr = inet_addr(str); if (addr == INADDR_NONE) goto out_err; - mask = strtoul(ptr + 1, &endptr, 10); - if (mask > 32) + val = strtoul(ptr + 1, &endptr, 10); + if (*endptr || val > 32) goto out_err; + mask = val; } else { addr = inet_addr(opt); if (addr == INADDR_NONE) @@ -3788,7 +3854,7 @@ static void parse_local_net(const char *opt) mask = 24; } - mask = htonl(mask ? ~0 << (32 - mask) : 0); + mask = htonl(mask ? UINT32_MAX << (32 - mask) : 0); addr = addr & mask; list_for_each_entry(n, &local_nets, entry) { diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h index 116602be..2bd3a14f 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.h +++ b/accel-pppd/ctrl/ipoe/ipoe.h @@ -3,7 +3,7 @@ #include <stdint.h> #include <pthread.h> -#include <linux/if.h> +#include <net/if.h> #include "triton.h" #include "ap_session.h" @@ -130,6 +130,17 @@ struct ipoe_session_info { uint32_t peer_addr; }; +struct ipoe_stat_t +{ + unsigned int starting; + unsigned int active; + unsigned int delayed_offer; +}; + +void ipoe_stat_get(struct ipoe_stat_t *stat); +unsigned int ipoe_stat_starting(void); +unsigned int ipoe_stat_active(void); + int ipoe_ipv6_nd_start(struct ipoe_serv *serv); #ifdef USE_LUA @@ -153,7 +164,8 @@ void ipoe_nl_delete_interfaces(void); int ipoe_nl_create(); void ipoe_nl_delete(int ifindex); int ipoe_nl_modify(int ifindex, uint32_t peer_addr, uint32_t addr, uint32_t gw, int link_ifindex, uint8_t *hwaddr); -void ipoe_nl_get_sessions(struct list_head *list); +int ipoe_nl_get_sessions(struct list_head *list); +int ipoe_nl_flush_sessions(void); int ipoe_nl_add_exclude(uint32_t addr, int mask); void ipoe_nl_del_exclude(uint32_t addr); int ipoe_nl_add_net(uint32_t addr, int mask); diff --git a/accel-pppd/ctrl/ipoe/ipoe_netlink.c b/accel-pppd/ctrl/ipoe/ipoe_netlink.c index e7080e92..df4dec67 100644 --- a/accel-pppd/ctrl/ipoe/ipoe_netlink.c +++ b/accel-pppd/ctrl/ipoe/ipoe_netlink.c @@ -10,7 +10,7 @@ #include <net/ethernet.h> #include <netinet/ip.h> #include <arpa/inet.h> -#include <linux/if.h> +#include <net/if.h> #include <linux/genetlink.h> #include "triton.h" @@ -46,6 +46,8 @@ int ipoe_nl_add_exclude(uint32_t addr, int mask) return -1; } + memset(&req, 0, sizeof(req)); + nlh = &req.n; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -82,6 +84,8 @@ void ipoe_nl_del_exclude(uint32_t addr) return; } + memset(&req, 0, sizeof(req)); + nlh = &req.n; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -114,6 +118,8 @@ int ipoe_nl_add_net(uint32_t addr, int mask) return -1; } + memset(&req, 0, sizeof(req)); + nlh = &req.n; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -150,6 +156,8 @@ void ipoe_nl_del_net(uint32_t addr) return; } + memset(&req, 0, sizeof(req)); + nlh = &req.n; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -181,6 +189,8 @@ void ipoe_nl_add_interface(int ifindex, uint8_t mode) return; } + memset(&req, 0, sizeof(req)); + nlh = &req.n; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -213,6 +223,8 @@ void ipoe_nl_del_interface(int ifindex) return; } + memset(&req, 0, sizeof(req)); + nlh = &req.n; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -253,6 +265,8 @@ int ipoe_nl_create() return -1; } + memset(&req, 0, sizeof(req)); + nlh = &req.n; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -317,6 +331,8 @@ int ipoe_nl_modify(int ifindex, uint32_t peer_addr, uint32_t addr, uint32_t gw, return -1; } + memset(&req, 0, sizeof(req)); + nlh = &req.n; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -394,17 +410,25 @@ static int dump_session(const struct sockaddr_nl *addr, struct nlmsghdr *n, void return 0; } -void ipoe_nl_get_sessions(struct list_head *list) +int ipoe_nl_get_sessions(struct list_head *list) { + struct rtnl_handle rth; struct nlmsghdr *nlh; struct genlmsghdr *ghdr; struct { struct nlmsghdr n; char buf[1024]; } req; + int ret; - if (rth.fd == -1) - return; + /* a private socket, so that the dump does not have to compete with + * the packet notifications delivered to the multicast one */ + if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) { + log_error("ipoe: cannot open generic netlink socket\n"); + return -1; + } + + memset(&req, 0, sizeof(req)); nlh = &req.n; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); @@ -416,11 +440,51 @@ void ipoe_nl_get_sessions(struct list_head *list) ghdr->cmd = IPOE_CMD_GET; if (rtnl_send(&rth, (char *)nlh, nlh->nlmsg_len) < 0) { - log_emerg("ipoe: failed to send dump request: %s\n", strerror(errno)); - return; + log_error("ipoe: failed to send dump request: %s\n", strerror(errno)); + rtnl_close(&rth); + return -1; } - rtnl_dump_filter(&rth, dump_session, list, NULL, NULL); + ret = rtnl_dump_filter(&rth, dump_session, list, NULL, NULL); + + rtnl_close(&rth); + + return ret; +} + +int ipoe_nl_flush_sessions(void) +{ + struct rtnl_handle rth; + struct nlmsghdr *nlh; + struct genlmsghdr *ghdr; + struct { + struct nlmsghdr n; + char buf[128]; + } req; + int ret = 0; + + if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) { + log_error("ipoe: cannot open generic netlink socket\n"); + return -EIO; + } + + memset(&req, 0, sizeof(req)); + + nlh = &req.n; + nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_type = ipoe_genl_id; + + ghdr = NLMSG_DATA(&req.n); + ghdr->cmd = IPOE_CMD_FLUSH; + + errno = 0; + if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0) + ret = errno ? -errno : -EIO; + + rtnl_close(&rth); + + return ret; } void ipoe_nl_delete(int ifindex) @@ -438,6 +502,8 @@ void ipoe_nl_delete(int ifindex) return; } + memset(&req, 0, sizeof(req)); + nlh = &req.n; nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -459,7 +525,10 @@ static void delete_sessions() struct ipoe_session_info *info; LIST_HEAD(ds_list); - ipoe_nl_get_sessions(&ds_list); + + if (ipoe_nl_get_sessions(&ds_list)) + log_error("ipoe: failed to enumerate sessions left by a previous" + " instance, some of them are not removed\n"); while (!list_empty(&ds_list)) { info = list_entry(ds_list.next, typeof(*info), entry); @@ -469,6 +538,24 @@ static void delete_sessions() } } +static void flush_sessions() +{ + int r = ipoe_nl_flush_sessions(); + + if (!r) + return; + + if (r == -EOPNOTSUPP) { + log_warn("ipoe: loaded ipoe module does not support IPOE_CMD_FLUSH," + " removing sessions one by one, reload the module to fix\n"); + delete_sessions(); + return; + } + + log_error("ipoe: failed to remove sessions left by a previous instance:" + " %s\n", strerror(-r)); +} + static void ipoe_up_handler(const struct sockaddr_nl *addr, struct nlmsghdr *h) { struct rtattr *tb[PKT_ATTR_MAX + 1]; @@ -628,23 +715,32 @@ static void init(void) log_warn("failed to load ipoe module\n"); mcg_id = genl_resolve_mcg(IPOE_GENL_NAME, IPOE_GENL_MCG_PKT, &ipoe_genl_id); + + if (!ipoe_genl_id) { + log_error("ipoe: cannot resolve netlink family, state left by a" + " previous instance is not removed\n"); + return; + } + + /* Drop everything a previous instance may have left in the kernel. + * The interfaces go first: while their rx handlers are still attached + * the module keeps reporting unclassified packets, and once we join + * the multicast group that traffic competes with our own replies. */ + ipoe_nl_delete_interfaces(); + flush_sessions(); + ipoe_nl_del_exclude(0); + ipoe_nl_del_net(0); + if (mcg_id == -1) { log_warn("ipoe: unclassified packet handling is disabled\n"); - rth.fd = -1; return; } if (rtnl_open_byproto(&rth, 1 << (mcg_id - 1), NETLINK_GENERIC)) { log_error("ipoe: cannot open generic netlink socket\n"); - rth.fd = -1; return; } - delete_sessions(); - ipoe_nl_del_exclude(0); - ipoe_nl_del_net(0); - ipoe_nl_delete_interfaces(); - fcntl(rth.fd, F_SETFL, O_NONBLOCK); fcntl(rth.fd, F_SETFD, fcntl(rth.fd, F_GETFD) | FD_CLOEXEC); diff --git a/accel-pppd/ctrl/l2tp/CMakeLists.txt b/accel-pppd/ctrl/l2tp/CMakeLists.txt index 66dd3fc6..80cf453b 100644 --- a/accel-pppd/ctrl/l2tp/CMakeLists.txt +++ b/accel-pppd/ctrl/l2tp/CMakeLists.txt @@ -8,10 +8,10 @@ ADD_LIBRARY(l2tp SHARED packet.c # netlink.c ) +TARGET_LINK_LIBRARIES(l2tp ${crypto_lib}) #TARGET_LINK_LIBRARIES(l2tp nl nl-genl) INSTALL(TARGETS l2tp LIBRARY DESTINATION lib${LIB_SUFFIX}/accel-ppp) FILE(GLOB dict "${CMAKE_CURRENT_SOURCE_DIR}/dict/*") INSTALL(FILES ${dict} DESTINATION share/accel-ppp/l2tp) - diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index bb1d1699..cf0c502c 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.c +++ b/accel-pppd/ctrl/l2tp/l2tp.c @@ -16,6 +16,8 @@ #include <linux/if_ether.h> #include <linux/if_pppox.h> +#include <openssl/md5.h> + #include "triton.h" #include "mempool.h" #include "log.h" @@ -24,7 +26,6 @@ #include "utils.h" #include "iprange.h" #include "cli.h" -#include "crypto.h" #include "connlimit.h" @@ -99,17 +100,22 @@ static const char *conf_ipv6_pool; static const char *conf_dpv6_pool; static const char *conf_ifname; -static unsigned int stat_conn_starting; -static unsigned int stat_conn_active; -static unsigned int stat_conn_finishing; +struct l2tp_stat_t +{ + unsigned int conn_starting; + unsigned int conn_active; + unsigned int conn_finishing; -static unsigned int stat_sess_starting; -static unsigned int stat_sess_active; -static unsigned int stat_sess_finishing; + unsigned int sess_starting; + unsigned int sess_active; + unsigned int sess_finishing; -static unsigned int stat_active; -static unsigned int stat_starting; -static unsigned int stat_finishing; + unsigned int data_starting; + unsigned int data_active; + unsigned int data_finishing; +}; + +static struct l2tp_stat_t l2tp_stat; struct l2tp_serv_t { @@ -123,6 +129,11 @@ struct l2tp_sess_t struct l2tp_conn_t *paren_conn; uint16_t sid; uint16_t peer_sid; +/* We will keep l2tp attributes Calling-Number/Called-Number and their length while the session exists */ + char *calling_num; + int calling_num_len; + char *called_num; + int called_num_len; unsigned int ref_count; int state1; @@ -199,6 +210,45 @@ static void l2tp_session_free(struct l2tp_sess_t *sess); static void l2tp_tunnel_free(struct l2tp_conn_t *conn); static void apses_stop(void *data); +static void l2tp_stat_inc(unsigned int *stat) +{ + __atomic_add_fetch(stat, 1, __ATOMIC_RELAXED); +} + +static void l2tp_stat_dec(unsigned int *stat) +{ + __atomic_sub_fetch(stat, 1, __ATOMIC_RELAXED); +} + +static void l2tp_stat_move(unsigned int *from, unsigned int *to) +{ + l2tp_stat_dec(from); + l2tp_stat_inc(to); +} + +static void l2tp_stat_get(struct l2tp_stat_t *stat) +{ + stat->conn_starting = __atomic_load_n(&l2tp_stat.conn_starting, __ATOMIC_RELAXED); + stat->conn_active = __atomic_load_n(&l2tp_stat.conn_active, __ATOMIC_RELAXED); + stat->conn_finishing = __atomic_load_n(&l2tp_stat.conn_finishing, __ATOMIC_RELAXED); + stat->sess_starting = __atomic_load_n(&l2tp_stat.sess_starting, __ATOMIC_RELAXED); + stat->sess_active = __atomic_load_n(&l2tp_stat.sess_active, __ATOMIC_RELAXED); + stat->sess_finishing = __atomic_load_n(&l2tp_stat.sess_finishing, __ATOMIC_RELAXED); + stat->data_starting = __atomic_load_n(&l2tp_stat.data_starting, __ATOMIC_RELAXED); + stat->data_active = __atomic_load_n(&l2tp_stat.data_active, __ATOMIC_RELAXED); + stat->data_finishing = __atomic_load_n(&l2tp_stat.data_finishing, __ATOMIC_RELAXED); +} + +unsigned int __export l2tp_stat_starting(void) +{ + return __atomic_load_n(&l2tp_stat.data_starting, __ATOMIC_RELAXED); +} + +unsigned int __export l2tp_stat_active(void) +{ + return __atomic_load_n(&l2tp_stat.data_active, __ATOMIC_RELAXED); +} + #define log_tunnel(log_func, conn, fmt, ...) \ do { \ @@ -848,16 +898,17 @@ out_err: return -1; } +static void l2tp_session_free_ptr(void *ptr) +{ + l2tp_session_free((struct l2tp_sess_t *) ptr); +} + static void l2tp_tunnel_free_sessions(struct l2tp_conn_t *conn) { void *sessions = conn->sessions; conn->sessions = NULL; -#ifdef HAVE_FREE_FN_T - tdestroy(sessions, (__free_fn_t)l2tp_session_free); -#else - tdestroy(sessions, (void(*)(void *))l2tp_session_free); -#endif + tdestroy(sessions, l2tp_session_free_ptr); /* Let l2tp_session_free() handle the session counter and * the reference held by the tunnel. */ @@ -870,12 +921,10 @@ static int l2tp_tunnel_disconnect(struct l2tp_conn_t *conn, case STATE_INIT: case STATE_WAIT_SCCRP: case STATE_WAIT_SCCCN: - __sync_sub_and_fetch(&stat_conn_starting, 1); - __sync_add_and_fetch(&stat_conn_finishing, 1); + l2tp_stat_move(&l2tp_stat.conn_starting, &l2tp_stat.conn_finishing); break; case STATE_ESTB: - __sync_sub_and_fetch(&stat_conn_active, 1); - __sync_add_and_fetch(&stat_conn_finishing, 1); + l2tp_stat_move(&l2tp_stat.conn_active, &l2tp_stat.conn_finishing); break; case STATE_FIN: case STATE_FIN_WAIT: @@ -955,7 +1004,7 @@ static void __tunnel_destroy(struct l2tp_conn_t *conn) mempool_free(conn); - __sync_sub_and_fetch(&stat_conn_finishing, 1); + l2tp_stat_dec(&l2tp_stat.conn_finishing); } static void tunnel_put(struct l2tp_conn_t *conn) @@ -983,12 +1032,16 @@ static void __session_destroy(struct l2tp_sess_t *sess) _free(sess->ctrl.calling_station_id); if (sess->ctrl.called_station_id) _free(sess->ctrl.called_station_id); + if (sess->calling_num) + _free(sess->calling_num); + if (sess->called_num) + _free(sess->called_num); log_session(log_info2, sess, "session destroyed\n"); mempool_free(sess); - __sync_sub_and_fetch(&stat_sess_finishing, 1); + l2tp_stat_dec(&l2tp_stat.sess_finishing); /* Now that the session is fully destroyed, * drop the reference to the tunnel. @@ -1021,15 +1074,13 @@ static void l2tp_session_free(struct l2tp_sess_t *sess) case STATE_WAIT_OCCN: log_session(log_info2, sess, "deleting session\n"); - __sync_sub_and_fetch(&stat_sess_starting, 1); - __sync_add_and_fetch(&stat_sess_finishing, 1); + l2tp_stat_move(&l2tp_stat.sess_starting, &l2tp_stat.sess_finishing); break; case STATE_ESTB: log_session(log_info2, sess, "deleting session\n"); triton_event_fire(EV_CTRL_FINISHED, &sess->ppp.ses); - __sync_sub_and_fetch(&stat_sess_active, 1); - __sync_add_and_fetch(&stat_sess_finishing, 1); + l2tp_stat_move(&l2tp_stat.sess_active, &l2tp_stat.sess_finishing); pthread_mutex_lock(&sess->apses_lock); if (sess->apses_ctx.tpd) @@ -1124,12 +1175,10 @@ static void l2tp_tunnel_free(struct l2tp_conn_t *conn) case STATE_INIT: case STATE_WAIT_SCCRP: case STATE_WAIT_SCCCN: - __sync_sub_and_fetch(&stat_conn_starting, 1); - __sync_add_and_fetch(&stat_conn_finishing, 1); + l2tp_stat_move(&l2tp_stat.conn_starting, &l2tp_stat.conn_finishing); break; case STATE_ESTB: - __sync_sub_and_fetch(&stat_conn_active, 1); - __sync_add_and_fetch(&stat_conn_finishing, 1); + l2tp_stat_move(&l2tp_stat.conn_active, &l2tp_stat.conn_finishing); break; case STATE_FIN: case STATE_FIN_WAIT: @@ -1252,7 +1301,7 @@ static void __apses_destroy(void *data) log_ppp_info2("session destroyed\n"); - __sync_sub_and_fetch(&stat_finishing, 1); + l2tp_stat_dec(&l2tp_stat.data_finishing); /* Drop reference to the L2TP session */ session_put(sess); @@ -1267,12 +1316,10 @@ static void apses_finished(struct ap_session *apses) switch (sess->apses_state) { case APSTATE_STARTING: - __sync_sub_and_fetch(&stat_starting, 1); - __sync_add_and_fetch(&stat_finishing, 1); + l2tp_stat_move(&l2tp_stat.data_starting, &l2tp_stat.data_finishing); break; case APSTATE_STARTED: - __sync_sub_and_fetch(&stat_active, 1); - __sync_add_and_fetch(&stat_finishing, 1); + l2tp_stat_move(&l2tp_stat.data_active, &l2tp_stat.data_finishing); break; case APSTATE_FINISHING: break; @@ -1313,12 +1360,10 @@ static void apses_stop(void *data) switch (sess->apses_state) { case APSTATE_INIT: case APSTATE_STARTING: - __sync_sub_and_fetch(&stat_starting, 1); - __sync_add_and_fetch(&stat_finishing, 1); + l2tp_stat_move(&l2tp_stat.data_starting, &l2tp_stat.data_finishing); break; case APSTATE_STARTED: - __sync_sub_and_fetch(&stat_active, 1); - __sync_add_and_fetch(&stat_finishing, 1); + l2tp_stat_move(&l2tp_stat.data_active, &l2tp_stat.data_finishing); break; case APSTATE_FINISHING: break; @@ -1377,8 +1422,7 @@ static void apses_started(struct ap_session *apses) return; } - __sync_sub_and_fetch(&stat_starting, 1); - __sync_add_and_fetch(&stat_active, 1); + l2tp_stat_move(&l2tp_stat.data_starting, &l2tp_stat.data_active); sess->apses_state = APSTATE_STARTED; log_ppp_info1("session started over l2tp session %hu-%hu, %hu-%hu\n", @@ -1507,7 +1551,7 @@ static struct l2tp_sess_t *l2tp_tunnel_alloc_session(struct l2tp_conn_t *conn) tunnel_hold(conn); session_hold(sess); - __sync_add_and_fetch(&stat_sess_starting, 1); + l2tp_stat_inc(&l2tp_stat.sess_starting); return sess; } @@ -1613,7 +1657,7 @@ static struct l2tp_conn_t *l2tp_tunnel_alloc(const struct sockaddr_in *peer, strerror(errno)); goto err_conn_fd; } - if (bind(conn->hnd.fd, host, sizeof(*host))) { + if (bind(conn->hnd.fd, (struct sockaddr*)host, sizeof(*host))) { log_error("l2tp: impossible to allocate new tunnel:" " bind() failed: %s\n", strerror(errno)); goto err_conn_fd; @@ -1646,7 +1690,7 @@ static struct l2tp_conn_t *l2tp_tunnel_alloc(const struct sockaddr_in *peer, goto err_conn_fd; } - if (getsockname(conn->hnd.fd, &conn->host_addr, &hostaddrlen) < 0) { + if (getsockname(conn->hnd.fd, (struct sockaddr*)&conn->host_addr, &hostaddrlen) < 0) { log_error("l2tp: impossible to allocate new tunnel:" " getsockname() failed: %s\n", strerror(errno)); goto err_conn_fd; @@ -1726,7 +1770,7 @@ static struct l2tp_conn_t *l2tp_tunnel_alloc(const struct sockaddr_in *peer, conn->peer_rcv_wnd_sz = DEFAULT_PEER_RECV_WINDOW_SIZE; tunnel_hold(conn); - __sync_add_and_fetch(&stat_conn_starting, 1); + l2tp_stat_inc(&l2tp_stat.conn_starting); return conn; @@ -1747,7 +1791,7 @@ static inline int l2tp_tunnel_update_peerport(struct l2tp_conn_t *conn, int res; conn->peer_addr.sin_port = port_nbo; - res = connect(conn->hnd.fd, &conn->peer_addr, sizeof(conn->peer_addr)); + res = connect(conn->hnd.fd, (struct sockaddr*)&conn->peer_addr, sizeof(conn->peer_addr)); if (res < 0) { log_tunnel(log_error, conn, "impossible to update peer port from %hu to %hu:" @@ -1775,25 +1819,52 @@ static int l2tp_session_start_data_channel(struct l2tp_sess_t *sess) sess->ctrl.max_mtu = conf_ppp_max_mtu; sess->ctrl.mppe = conf_mppe; - sess->ctrl.calling_station_id = _malloc(17); - if (sess->ctrl.calling_station_id == NULL) { - log_session(log_error, sess, - "impossible to start data channel:" - " allocation of calling station ID failed\n"); - goto err; + /* If l2tp calling number avp exists, we use it, otherwise we use lac ip */ + if (sess->calling_num != NULL) { + sess->ctrl.calling_station_id = _malloc(sess->calling_num_len+1); + if (sess->ctrl.calling_station_id == NULL) { + log_session(log_error, sess, + "impossible to start data channel:" + " allocation of calling station ID failed\n"); + goto err; + }else { + strcpy(sess->ctrl.calling_station_id, sess->calling_num); + } + } else { + sess->ctrl.calling_station_id = _malloc(17); + if (sess->ctrl.calling_station_id == NULL) { + log_session(log_error, sess, + "impossible to start data channel:" + " allocation of calling station ID failed\n"); + goto err; + } else { + u_inet_ntoa(sess->paren_conn->peer_addr.sin_addr.s_addr, + sess->ctrl.calling_station_id); + } } - u_inet_ntoa(sess->paren_conn->peer_addr.sin_addr.s_addr, - sess->ctrl.calling_station_id); - - sess->ctrl.called_station_id = _malloc(17); - if (sess->ctrl.called_station_id == NULL) { - log_session(log_error, sess, - "impossible to start data channel:" - " allocation of called station ID failed\n"); - goto err; + /* If l2tp called number avp exists, we use it, otherwise we use my ip */ + if (sess->called_num != NULL) { + sess->ctrl.called_station_id = _malloc(sess->called_num_len+1); + if (sess->ctrl.called_station_id == NULL) { + log_session(log_error, sess, + "impossible to start data channel:" + " allocation of called station ID failed\n"); + goto err; + } else { + strcpy(sess->ctrl.called_station_id, sess->called_num); + } + } else { + sess->ctrl.called_station_id = _malloc(17); + if (sess->ctrl.called_station_id == NULL) { + log_session(log_error, sess, + "impossible to start data channel:" + " allocation of called station ID failed\n"); + goto err; + } else { + u_inet_ntoa(sess->paren_conn->host_addr.sin_addr.s_addr, + sess->ctrl.called_station_id); + } } - u_inet_ntoa(sess->paren_conn->host_addr.sin_addr.s_addr, - sess->ctrl.called_station_id); if (conf_ip_pool) { sess->ppp.ses.ipv4_pool_name = _strdup(conf_ip_pool); @@ -1844,7 +1915,7 @@ static int l2tp_session_start_data_channel(struct l2tp_sess_t *sess) goto err_put_ctx; } - __sync_add_and_fetch(&stat_starting, 1); + l2tp_stat_inc(&l2tp_stat.data_starting); return 0; @@ -1974,8 +2045,7 @@ static int l2tp_session_connect(struct l2tp_sess_t *sess) } triton_event_fire(EV_CTRL_STARTED, &sess->ppp.ses); - __sync_sub_and_fetch(&stat_sess_starting, 1); - __sync_add_and_fetch(&stat_sess_active, 1); + l2tp_stat_move(&l2tp_stat.sess_starting, &l2tp_stat.sess_active); sess->state1 = STATE_ESTB; if (l2tp_session_start_data_channel(sess) < 0) { @@ -2053,8 +2123,7 @@ static int l2tp_tunnel_connect(struct l2tp_conn_t *conn) close(tunnel_fd); - __sync_sub_and_fetch(&stat_conn_starting, 1); - __sync_add_and_fetch(&stat_conn_active, 1); + l2tp_stat_move(&l2tp_stat.conn_starting, &l2tp_stat.conn_active); conn->state = STATE_ESTB; return 0; @@ -2684,12 +2753,10 @@ static void l2tp_tunnel_finwait(struct l2tp_conn_t *conn) switch (conn->state) { case STATE_WAIT_SCCRP: case STATE_WAIT_SCCCN: - __sync_sub_and_fetch(&stat_conn_starting, 1); - __sync_add_and_fetch(&stat_conn_finishing, 1); + l2tp_stat_move(&l2tp_stat.conn_starting, &l2tp_stat.conn_finishing); break; case STATE_ESTB: - __sync_sub_and_fetch(&stat_conn_active, 1); - __sync_add_and_fetch(&stat_conn_finishing, 1); + l2tp_stat_move(&l2tp_stat.conn_active, &l2tp_stat.conn_finishing); break; case STATE_FIN: break; @@ -2769,10 +2836,10 @@ static int l2tp_recv_SCCRQ(const struct l2tp_serv_t *serv, return 0; } - if (conf_max_starting && ap_session_stat.starting >= conf_max_starting) + if (conf_max_starting && ap_session_stat_starting() >= conf_max_starting) return 0; - if (conf_max_sessions && ap_session_stat.active + ap_session_stat.starting >= conf_max_sessions) + if (conf_max_sessions && ap_session_stat_active() + ap_session_stat_starting() >= conf_max_sessions) return 0; if (triton_module_loaded("connlimit") @@ -3299,6 +3366,10 @@ static int l2tp_recv_ICRQ(struct l2tp_conn_t *conn, uint16_t sid = 0; uint16_t res = 0; uint16_t err = 0; + uint8_t calling[L2TP_AVP_LEN_MASK] = {0}; + uint8_t called[L2TP_AVP_LEN_MASK] = {0}; + int n = 0; + int m = 0; if (conn->state != STATE_ESTB && conn->lns_mode) { log_tunnel(log_warn, conn, "discarding unexpected ICRQ\n"); @@ -3311,10 +3382,10 @@ static int l2tp_recv_ICRQ(struct l2tp_conn_t *conn, return 0; } - if (conf_max_starting && ap_session_stat.starting >= conf_max_starting) + if (conf_max_starting && ap_session_stat_starting() >= conf_max_starting) return 0; - if (conf_max_sessions && ap_session_stat.active + ap_session_stat.starting >= conf_max_sessions) + if (conf_max_sessions && ap_session_stat_active() + ap_session_stat_starting() >= conf_max_sessions) return 0; if (triton_module_loaded("connlimit") @@ -3336,7 +3407,17 @@ static int l2tp_recv_ICRQ(struct l2tp_conn_t *conn, case Call_Serial_Number: case Bearer_Type: case Calling_Number: + /* Save Calling-Number L2TP attribute locally */ + if (attr->attr->id == Calling_Number) { + n = attr->length; + memcpy(calling,attr->val.octets,n); + } case Called_Number: + /* Save Called-Number L2TP attribute locally */ + if (attr->attr->id == Called_Number) { + m = attr->length; + memcpy(called,attr->val.octets,m); + } case Sub_Address: case Physical_Channel_ID: break; @@ -3375,6 +3456,30 @@ static int l2tp_recv_ICRQ(struct l2tp_conn_t *conn, sess->peer_sid = peer_sid; sid = sess->sid; + /* Allocate memory for Calling-Number if exists, and put it to l2tp_sess_t structure */ + if (n > 0) { + sess->calling_num = _malloc(n+1); + if (sess->calling_num == NULL) { + log_tunnel(log_warn, conn, "can't allocate memory for Calling Number attribute. Will use LAC IP instead\n"); + }else{ + memcpy(sess->calling_num, calling, n); + sess->calling_num[n] = '\0'; + sess->calling_num_len = n; + } + } + + /* Allocate memory for Called-Number if exists, and put it to l2tp_sess_t structure */ + if (m > 1) { + sess->called_num = _malloc(m+1); + if (sess->called_num == NULL) { + log_tunnel(log_warn, conn, "can't allocate memory for Called Number attribute. Will use my IP instead\n"); + } else { + memcpy(sess->called_num, called, m); + sess->called_num[m] = '\0'; + sess->called_num_len = m; + } + } + if (unknown_attr) { log_tunnel(log_error, conn, "impossible to handle ICRQ:" " unknown mandatory attribute type %i," @@ -3394,8 +3499,8 @@ static int l2tp_recv_ICRQ(struct l2tp_conn_t *conn, goto out_reject; } - log_tunnel(log_info1, conn, "new session %hu-%hu created following" - " reception of ICRQ\n", sid, peer_sid); + log_tunnel(log_info1, conn, "new session %hu-%hu with calling num %s len %d, called num %s len %d created following" + " reception of ICRQ\n", sid, peer_sid, sess->calling_num, sess->calling_num_len, sess->called_num, sess->called_num_len); return 0; @@ -3617,10 +3722,10 @@ static int l2tp_recv_OCRQ(struct l2tp_conn_t *conn, return 0; } - if (conf_max_starting && ap_session_stat.starting >= conf_max_starting) + if (conf_max_starting && ap_session_stat_starting() >= conf_max_starting) return 0; - if (conf_max_sessions && ap_session_stat.active + ap_session_stat.starting >= conf_max_sessions) + if (conf_max_sessions && ap_session_stat_active() + ap_session_stat_starting() >= conf_max_sessions) return 0; if (triton_module_loaded("connlimit") @@ -4637,21 +4742,25 @@ err_fd: static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt, void *client) { + struct l2tp_stat_t stat; + + l2tp_stat_get(&stat); + cli_send(client, "l2tp:\r\n"); cli_send(client, " tunnels:\r\n"); - cli_sendv(client, " starting: %u\r\n", stat_conn_starting); - cli_sendv(client, " active: %u\r\n", stat_conn_active); - cli_sendv(client, " finishing: %u\r\n", stat_conn_finishing); + cli_sendv(client, " starting: %u\r\n", stat.conn_starting); + cli_sendv(client, " active: %u\r\n", stat.conn_active); + cli_sendv(client, " finishing: %u\r\n", stat.conn_finishing); cli_send(client, " sessions (control channels):\r\n"); - cli_sendv(client, " starting: %u\r\n", stat_sess_starting); - cli_sendv(client, " active: %u\r\n", stat_sess_active); - cli_sendv(client, " finishing: %u\r\n", stat_sess_finishing); + cli_sendv(client, " starting: %u\r\n", stat.sess_starting); + cli_sendv(client, " active: %u\r\n", stat.sess_active); + cli_sendv(client, " finishing: %u\r\n", stat.sess_finishing); cli_send(client, " sessions (data channels):\r\n"); - cli_sendv(client, " starting: %u\r\n", stat_starting); - cli_sendv(client, " active: %u\r\n", stat_active); - cli_sendv(client, " finishing: %u\r\n", stat_finishing); + cli_sendv(client, " starting: %u\r\n", stat.data_starting); + cli_sendv(client, " active: %u\r\n", stat.data_active); + cli_sendv(client, " finishing: %u\r\n", stat.data_finishing); return CLI_CMD_OK; } @@ -4857,12 +4966,6 @@ static void l2tp_create_session_help(char * const *fields, int fields_cnt, " - place new call in tunnel <tid>\r\n"); } -void __export l2tp_get_stat(unsigned int **starting, unsigned int **active) -{ - *starting = &stat_starting; - *active = &stat_active; -} - static void load_config(void) { const char *opt; diff --git a/accel-pppd/ctrl/l2tp/l2tp.h b/accel-pppd/ctrl/l2tp/l2tp.h index 76de867f..2f113a25 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.h +++ b/accel-pppd/ctrl/l2tp/l2tp.h @@ -77,6 +77,9 @@ struct l2tp_packet_t extern int conf_verbose; extern int conf_avp_permissive; +unsigned int l2tp_stat_starting(void); +unsigned int l2tp_stat_active(void); + static inline int l2tp_packet_is_ZLB(const struct l2tp_packet_t *pack) { return list_empty(&pack->attrs); diff --git a/accel-pppd/ctrl/l2tp/packet.c b/accel-pppd/ctrl/l2tp/packet.c index 97e205f3..f134666d 100644 --- a/accel-pppd/ctrl/l2tp/packet.c +++ b/accel-pppd/ctrl/l2tp/packet.c @@ -8,7 +8,8 @@ #include <fcntl.h> #include <arpa/inet.h> -#include "crypto.h" +#include <openssl/md5.h> + #include "triton.h" #include "log.h" #include "mempool.h" @@ -112,42 +113,26 @@ void l2tp_packet_free(struct l2tp_packet_t *pack) static void memxor(uint8_t *dst, const uint8_t *src, size_t sz) { - const uintmax_t *umax_src = (const uintmax_t *)src; - uintmax_t *umax_dst = (uintmax_t *)dst; - size_t left = sz % sizeof(uintmax_t); size_t indx; - for (indx = 0; indx < sz / sizeof(uintmax_t); ++indx) - umax_dst[indx] ^= umax_src[indx]; - - src += sz - left; - dst += sz - left; - while (left) { - if (left >= sizeof(uint32_t)) { - *(uint32_t *)dst ^= *(uint32_t *)src; - src += sizeof(uint32_t); - dst += sizeof(uint32_t); - left -= sizeof(uint32_t); - } else if (left >= sizeof(uint16_t)) { - *(uint16_t *)dst ^= *(uint16_t *)src; - src += sizeof(uint16_t); - dst += sizeof(uint16_t); - left -= sizeof(uint16_t); - } else { - *dst ^= *src; - src += sizeof(uint8_t); - dst += sizeof(uint8_t); - left -= sizeof(uint8_t); - } - } + for (indx = 0; indx < sz; ++indx) + dst[indx] ^= src[indx]; } /* * Decipher hidden AVPs, keeping the Hidden AVP Subformat (i.e. the attribute * value is prefixed by 2 bytes indicating its length in network byte order). + * + * On success the deciphered original attribute length is stored into + * *orig_attr_len, already validated against the size of the received AVP. + * Callers must never re-read that length from the AVP body themselves: it is + * the output of the cipher, so a peer using a mismatching secret (or an + * attacker blindly injecting hidden AVPs) makes it an essentially random + * 16 bits value. */ static int decode_avp(struct l2tp_avp_t *avp, const struct l2tp_attr_t *RV, - const char *secret, size_t secret_len) + const char *secret, size_t secret_len, + uint16_t *orig_attr_len_out) { MD5_CTX md5_ctx; uint8_t md5[MD5_DIGEST_LENGTH]; @@ -161,7 +146,7 @@ static int decode_avp(struct l2tp_avp_t *avp, const struct l2tp_attr_t *RV, uint16_t last_block_len; avp_len = avp->flags & L2TP_AVP_LEN_MASK; - if (avp_len < sizeof(struct l2tp_avp_t) + 2) { + if (avp_len < sizeof(struct l2tp_avp_t) + sizeof(uint16_t)) { /* Hidden AVPs must contain at least two bytes for storing original attribute length */ log_warn("l2tp: incorrect hidden avp received (type %hu):" @@ -179,20 +164,22 @@ static int decode_avp(struct l2tp_avp_t *avp, const struct l2tp_attr_t *RV, MD5_Final(p1, &md5_ctx); if (attr_len <= MD5_DIGEST_LENGTH) { + /* The whole attribute fits in the first block: it is fully + deciphered, nothing more to do but to check its length */ memxor(avp->val, p1, attr_len); - return 0; + goto out; } memxor(p1, avp->val, MD5_DIGEST_LENGTH); - orig_attr_len = ntohs(*(uint16_t *)p1); + orig_attr_len = u_read_be16(p1); - if (orig_attr_len <= MD5_DIGEST_LENGTH - 2) { + if (orig_attr_len <= MD5_DIGEST_LENGTH - sizeof(uint16_t)) { /* Enough bytes decoded already, no need to decode padding */ memcpy(avp->val, p1, MD5_DIGEST_LENGTH); - return 0; + goto out; } - if (orig_attr_len > attr_len - 2) { + if (orig_attr_len > attr_len - sizeof(uint16_t)) { log_warn("l2tp: incorrect hidden avp received (type %hu):" " original attribute length too big (ciphered" " attribute length: %hu bytes, advertised original" @@ -203,7 +190,7 @@ static int decode_avp(struct l2tp_avp_t *avp, const struct l2tp_attr_t *RV, /* Decode remaining blocks. Start from the last block as preceding blocks must be kept hidden for computing MD5s */ - bytes_left = orig_attr_len + 2 - MD5_DIGEST_LENGTH; + bytes_left = orig_attr_len + sizeof(uint16_t) - MD5_DIGEST_LENGTH; last_block_len = bytes_left % MD5_DIGEST_LENGTH; blocks_left = bytes_left / MD5_DIGEST_LENGTH; if (last_block_len) { @@ -227,6 +214,23 @@ static int decode_avp(struct l2tp_avp_t *avp, const struct l2tp_attr_t *RV, } memcpy(avp->val, p1, MD5_DIGEST_LENGTH); +out: + /* The length prefix comes out of the cipher, so it is only as + trustworthy as the peer's knowledge of the shared secret. Bound it + against the room actually available in the received AVP before + letting it drive any read of the attribute value */ + orig_attr_len = u_read_be16(avp->val); + if (orig_attr_len > attr_len - sizeof(uint16_t)) { + log_warn("l2tp: incorrect hidden avp received (type %hu):" + " deciphered attribute length too big (ciphered" + " attribute length: %hu bytes, deciphered original" + " attribute length: %hu bytes), wrong secret?\n", + ntohs(avp->type), attr_len, orig_attr_len); + return -1; + } + + *orig_attr_len_out = orig_attr_len; + return 0; } @@ -240,6 +244,7 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info, struct sockaddr_in addr; socklen_t addr_len; uint16_t orig_avp_len; + uint16_t orig_attr_len; void *orig_avp_val; uint8_t *buf, *ptr; int n, length; @@ -280,7 +285,7 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info, ptr = (uint8_t *)(hdr + 1); addr_len = sizeof(addr); - n = recvfrom(fd, buf, L2TP_MAX_PACKET_SIZE, 0, &addr, &addr_len); + n = recvfrom(fd, buf, L2TP_MAX_PACKET_SIZE, 0, (struct sockaddr*)&addr, &addr_len); if (n < 0) { mempool_free(buf); if (errno == EAGAIN) { @@ -419,10 +424,11 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info, ntohs(avp->type)); goto out_err; } - if (decode_avp(avp, RV, secret, secret_len) < 0) + if (decode_avp(avp, RV, secret, secret_len, + &orig_attr_len) < 0) goto out_err; - orig_avp_len = ntohs(*(uint16_t *)avp->val) + sizeof(*avp); + orig_avp_len = orig_attr_len + sizeof(*avp); orig_avp_val = avp->val + sizeof(uint16_t); } else { orig_avp_len = avp_len; @@ -444,17 +450,17 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info, case ATTR_TYPE_INT16: if (orig_avp_len != sizeof(*avp) + 2) goto out_err_len; - attr->val.uint16 = ntohs(*(uint16_t *)orig_avp_val); + attr->val.uint16 = u_read_be16(orig_avp_val); break; case ATTR_TYPE_INT32: if (orig_avp_len != sizeof(*avp) + 4) goto out_err_len; - attr->val.uint32 = ntohl(*(uint32_t *)orig_avp_val); + attr->val.uint32 = u_read_be32(orig_avp_val); break; case ATTR_TYPE_INT64: if (orig_avp_len != sizeof(*avp) + 8) goto out_err_len; - attr->val.uint64 = be64toh(*(uint64_t *)orig_avp_val); + attr->val.uint64 = u_read_be64(orig_avp_val); break; case ATTR_TYPE_OCTETS: attr->val.octets = _malloc(attr->length); @@ -531,13 +537,13 @@ int l2tp_packet_send(int sock, struct l2tp_packet_t *pack) else switch (attr->attr->type) { case ATTR_TYPE_INT16: - *(int16_t *)avp->val = htons(attr->val.int16); + u_write_be16(avp->val, attr->val.int16); break; case ATTR_TYPE_INT32: - *(int32_t *)avp->val = htonl(attr->val.int32); + u_write_be32(avp->val, attr->val.int32); break; case ATTR_TYPE_INT64: - *(uint64_t *)avp->val = htobe64(attr->val.uint64); + u_write_be64(avp->val, attr->val.uint64); break; case ATTR_TYPE_STRING: case ATTR_TYPE_OCTETS: @@ -552,7 +558,7 @@ int l2tp_packet_send(int sock, struct l2tp_packet_t *pack) memcpy(buf, &pack->hdr, sizeof(pack->hdr)); hdr->flags = htons(pack->hdr.flags); - n = sendto(sock, buf, len, 0, &pack->addr, sizeof(pack->addr)); + n = sendto(sock, buf, len, 0, (struct sockaddr*)&pack->addr, sizeof(pack->addr)); mempool_free(buf); if (n < 0) { diff --git a/accel-pppd/ctrl/l2tp/packet_test.c b/accel-pppd/ctrl/l2tp/packet_test.c new file mode 100644 index 00000000..a6c9a182 --- /dev/null +++ b/accel-pppd/ctrl/l2tp/packet_test.c @@ -0,0 +1,491 @@ +/* + * Standalone regression test for the L2TP control message parser. + * + * Not part of the cmake build. Compile and run with: + * gcc -O1 -g -Wall -fno-strict-aliasing -D_GNU_SOURCE \ + * -fsanitize=address,undefined -fno-sanitize-recover=all \ + * -I accel-pppd -I accel-pppd/include -I accel-pppd/ctrl/l2tp \ + * -o /tmp/l2tp_packet_test \ + * accel-pppd/ctrl/l2tp/packet_test.c accel-pppd/ctrl/l2tp/packet.c \ + * -lcrypto && /tmp/l2tp_packet_test + * + * The interesting part is the hidden AVP subformat: the 2 bytes length prefix + * of a hidden AVP is an *output of the cipher*, so a peer using a different + * secret -- or an attacker injecting hidden AVPs blindly -- turns it into an + * essentially random 16 bits value. It must never be trusted to bound a read + * of the attribute value. + * + * The test drives the real parser through a real UDP socket: + * - hand-crafted packets exercise the hidden AVP length checks, including + * the single block (attribute <= 16 bytes) cipher path which the accel-ppp + * encoder itself never produces (it always pads by >= 16 bytes); + * - l2tp_packet_send()/l2tp_recv() round trips exercise the multi block + * cipher path and the unaligned AVP accessors. + * + * Everything packet.c needs besides libcrypto is stubbed below. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <sys/socket.h> + +#include <openssl/md5.h> + +#include "triton.h" +#include "log.h" +#include "mempool.h" +#include "l2tp.h" +#include "attr_defs.h" + +static int failures; +#define CHECK(cond) do { if (!(cond)) { \ + fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); failures++; } } while (0) + +/* ------------------------------------------------------------------ stubs */ + +int conf_verbose = 1; +int conf_avp_permissive = 0; + +/* A dictionary just big enough for the attributes used here. Types are the + ones accel-ppp's own dictionary uses, except Tie_Breaker which is turned + into an INT64 to get coverage of the 64 bits accessor. */ +static struct l2tp_dict_attr_t dict[] = { + { .name = "Message-Type", .id = Message_Type, .type = ATTR_TYPE_INT16, .M = 1, .H = 0 }, + { .name = "Tie-Breaker", .id = Tie_Breaker, .type = ATTR_TYPE_INT64, .M = 0, .H = -1 }, + { .name = "Host-Name", .id = Host_Name, .type = ATTR_TYPE_STRING, .M = 1, .H = -1 }, + { .name = "Assigned-Tunnel-Id", .id = Assigned_Tunnel_ID, .type = ATTR_TYPE_INT16, .M = 1, .H = -1 }, + { .name = "Call-Serial-Number", .id = Call_Serial_Number, .type = ATTR_TYPE_INT32, .M = 1, .H = -1 }, + { .name = "Random-Vector", .id = Random_Vector, .type = ATTR_TYPE_OCTETS, .M = 1, .H = 0 }, +}; + +struct l2tp_dict_attr_t *l2tp_dict_find_attr_by_id(int id) +{ + size_t indx; + + for (indx = 0; indx < sizeof(dict) / sizeof(dict[0]); ++indx) + if (dict[indx].id == id) + return &dict[indx]; + + return NULL; +} + +const struct l2tp_dict_value_t *l2tp_dict_find_value(const struct l2tp_dict_attr_t *attr, + l2tp_value_t val) +{ + return NULL; +} + +/* Size carrying mempool: allocations stay exactly as large as the pool's + object size, so that ASan traps any read past the end of a packet buffer */ +mempool_t *mempool_create(int size) +{ + int *pool = malloc(sizeof(int)); + + *pool = size; + + return (mempool_t *)pool; +} + +void *mempool_alloc(mempool_t *pool) +{ + return malloc(*(int *)pool); +} + +void mempool_free(void *ptr) +{ + free(ptr); +} + +void triton_register_init(int order, void (*func)(void)) +{ + func(); +} + +int u_randbuf(void *buf, size_t buf_len, int *err) +{ + uint8_t *u8_buf = buf; + size_t indx; + + /* Deterministic on purpose: reproducible failures beat real entropy */ + for (indx = 0; indx < buf_len; ++indx) + u8_buf[indx] = (uint8_t)(indx * 7 + 0x5a); + + return 0; +} + +#define DEFINE_LOG_STUB(name) \ + void name(const char *fmt, ...) {} +DEFINE_LOG_STUB(log_emerg) +DEFINE_LOG_STUB(log_error) +DEFINE_LOG_STUB(log_warn) +DEFINE_LOG_STUB(log_ppp_debug) + +/* -------------------------------------------------------- packet building */ + +struct pktbuf { + uint8_t data[2048]; + size_t len; +}; + +static void pkt_init(struct pktbuf *pkt) +{ + struct l2tp_hdr_t hdr; + + memset(&hdr, 0, sizeof(hdr)); + hdr.flags = htons(L2TP_FLAG_T | L2TP_FLAG_L | L2TP_FLAG_S | 2); + + memset(pkt, 0, sizeof(*pkt)); + memcpy(pkt->data, &hdr, sizeof(hdr)); + pkt->len = sizeof(hdr); +} + +/* Append an AVP and return a pointer to its value */ +static uint8_t *pkt_add_avp(struct pktbuf *pkt, uint16_t extra_flags, + uint16_t type, const void *val, size_t val_len) +{ + struct l2tp_avp_t avp; + uint8_t *ptr = pkt->data + pkt->len; + + memset(&avp, 0, sizeof(avp)); + avp.flags = htons(extra_flags | ((sizeof(avp) + val_len) & L2TP_AVP_LEN_MASK)); + avp.type = htons(type); + + memcpy(ptr, &avp, sizeof(avp)); + if (val_len) + memcpy(ptr + sizeof(avp), val, val_len); + pkt->len += sizeof(avp) + val_len; + + return ptr + sizeof(avp); +} + +static void pkt_finish(struct pktbuf *pkt) +{ + uint16_t length = htons(pkt->len); + + memcpy(pkt->data + offsetof(struct l2tp_hdr_t, length), + &length, sizeof(length)); +} + +/* + * Cipher a hidden AVP whose cleartext (length prefix included) is at most one + * MD5 block long, i.e. the path that never validated the length prefix. + */ +static void hide_single_block(uint8_t *val, size_t val_len, uint16_t type, + const char *secret, size_t secret_len, + const uint8_t *rv, size_t rv_len) +{ + uint8_t md5[MD5_DIGEST_LENGTH]; + uint16_t attr_type = htons(type); + MD5_CTX md5_ctx; + size_t indx; + + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, &attr_type, sizeof(attr_type)); + MD5_Update(&md5_ctx, secret, secret_len); + MD5_Update(&md5_ctx, rv, rv_len); + MD5_Final(md5, &md5_ctx); + + for (indx = 0; indx < val_len && indx < MD5_DIGEST_LENGTH; ++indx) + val[indx] ^= md5[indx]; +} + +/* --------------------------------------------------------------- plumbing */ + +static const char secret[] = "s3cr3t"; +static int sock = -1; +static struct sockaddr_in sock_addr; + +static void loopback_socket(void) +{ + socklen_t addr_len = sizeof(sock_addr); + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + perror("socket"); + exit(1); + } + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0 + || getsockname(sock, (struct sockaddr *)&sock_addr, &addr_len) < 0) { + perror("bind"); + exit(1); + } +} + +/* Feed raw bytes to the parser, NULL means "packet rejected" */ +static struct l2tp_packet_t *parse(const struct pktbuf *pkt) +{ + struct l2tp_packet_t *pack = NULL; + + if (sendto(sock, pkt->data, pkt->len, 0, + (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { + perror("sendto"); + exit(1); + } + + CHECK(l2tp_recv(sock, &pack, NULL, secret, sizeof(secret) - 1) == 0); + + return pack; +} + +static const struct l2tp_attr_t *find_attr(const struct l2tp_packet_t *pack, int id) +{ + const struct l2tp_attr_t *attr; + + list_for_each_entry(attr, &pack->attrs, entry) + if (attr->attr->id == id) + return attr; + + return NULL; +} + +/* ------------------------------------------------------------------ tests */ + +/* + * A hidden AVP small enough to be ciphered in a single block: its deciphered + * length prefix used to be taken at face value, so anything up to 65535 was + * handed to the memcpy() feeding attr->val, reading way past the packet + * buffer. The parser must accept a prefix only if the attribute value it + * announces really fits in the received AVP. + */ +static void test_hidden_avp_length_prefix(void) +{ + static const struct { + const char *name; + size_t attr_len; /* ciphered attribute length */ + uint16_t declared; /* deciphered length prefix */ + int accept; + } cases[] = { + { "lies about 64K", 16, 0xffff, 0 }, + { "lies, minimal avp", 2, 0xffff, 0 }, + { "off by one", 16, 15, 0 }, + { "one byte too big", 4, 3, 0 }, + { "fits exactly", 16, 14, 1 }, + { "fits", 16, 4, 1 }, + { "empty value", 2, 0, 1 }, + { "no length prefix", 1, 0, 0 }, + }; + static const uint8_t rv[16] = { + 0xf3, 0x1a, 0x00, 0xff, 0x42, 0x7c, 0x91, 0x08, + 0x5d, 0xe6, 0x33, 0xb0, 0x14, 0xaa, 0x69, 0xc2, + }; + uint8_t value[MD5_DIGEST_LENGTH]; + struct l2tp_packet_t *pack; + const struct l2tp_attr_t *attr; + struct pktbuf pkt; + uint16_t declared; + size_t indx, i; + + for (indx = 0; indx < sizeof(cases) / sizeof(cases[0]); ++indx) { + pkt_init(&pkt); + pkt_add_avp(&pkt, L2TP_AVP_FLAG_M, Random_Vector, rv, sizeof(rv)); + + /* Cleartext: 2 bytes length prefix, then the value, then padding. + The value is a recognizable pattern so that a short read shows + up as wrong content rather than as a lucky pass. */ + memset(value, 0, sizeof(value)); + declared = htons(cases[indx].declared); + memcpy(value, &declared, cases[indx].attr_len < sizeof(declared) + ? cases[indx].attr_len : sizeof(declared)); + for (i = sizeof(declared); i < cases[indx].attr_len; ++i) + value[i] = 'a' + (i % 26); + + hide_single_block(value, cases[indx].attr_len, Host_Name, + secret, sizeof(secret) - 1, rv, sizeof(rv)); + pkt_add_avp(&pkt, L2TP_AVP_FLAG_M | L2TP_AVP_FLAG_H, Host_Name, + value, cases[indx].attr_len); + pkt_finish(&pkt); + + pack = parse(&pkt); + if (!cases[indx].accept) { + if (pack) { + fprintf(stderr, "FAIL %s:%d: hidden avp accepted" + " (%s)\n", __FILE__, __LINE__, + cases[indx].name); + failures++; + l2tp_packet_free(pack); + } + continue; + } + + if (!pack) { + fprintf(stderr, "FAIL %s:%d: hidden avp rejected (%s)\n", + __FILE__, __LINE__, cases[indx].name); + failures++; + continue; + } + + attr = find_attr(pack, Host_Name); + CHECK(attr != NULL); + if (attr) { + CHECK(attr->length == cases[indx].declared); + for (i = 0; i < cases[indx].declared; ++i) + CHECK((uint8_t)attr->val.string[i] == + 'a' + ((i + sizeof(declared)) % 26)); + CHECK(attr->val.string[cases[indx].declared] == '\0'); + } + l2tp_packet_free(pack); + } +} + +/* + * A hidden AVP is rejected outright when no Random Vector was received, or + * when its length cannot even hold the length prefix. + */ +static void test_hidden_avp_prerequisites(void) +{ + static const uint8_t value[16] = { 0 }; + struct l2tp_packet_t *pack; + struct pktbuf pkt; + + pkt_init(&pkt); + pkt_add_avp(&pkt, L2TP_AVP_FLAG_M | L2TP_AVP_FLAG_H, Host_Name, + value, sizeof(value)); + pkt_finish(&pkt); + pack = parse(&pkt); + CHECK(pack == NULL); + if (pack) + l2tp_packet_free(pack); + + /* Random Vector present, but the hidden AVP carries no value at all */ + pkt_init(&pkt); + pkt_add_avp(&pkt, L2TP_AVP_FLAG_M, Random_Vector, value, sizeof(value)); + pkt_add_avp(&pkt, L2TP_AVP_FLAG_M | L2TP_AVP_FLAG_H, Host_Name, NULL, 0); + pkt_finish(&pkt); + pack = parse(&pkt); + CHECK(pack == NULL); + if (pack) + l2tp_packet_free(pack); +} + +/* + * Round trip through the real encoder. With hide_avps set every attribute but + * Message-Type and Random-Vector goes through the multi block cipher, since + * encode_attr() always appends at least 16 bytes of padding. + */ +static void test_roundtrip(int hide_avps) +{ + static const char host_name[] = "accel-ppp regression test host name"; + struct l2tp_packet_t *pack; + const struct l2tp_attr_t *attr; + int ret; + + pack = l2tp_packet_alloc(2, Message_Type_Hello, &sock_addr, hide_avps, + secret, sizeof(secret) - 1); + CHECK(pack != NULL); + if (!pack) + return; + + /* Odd length string first: everything after it sits on an odd offset, + so the integer accessors below run unaligned */ + CHECK(l2tp_packet_add_string(pack, Host_Name, host_name, 1) == 0); + CHECK(l2tp_packet_add_int16(pack, Assigned_Tunnel_ID, 0x1234, 1) == 0); + CHECK(l2tp_packet_add_int32(pack, Call_Serial_Number, 0x89abcdef, 1) == 0); + CHECK(l2tp_packet_add_int64(pack, Tie_Breaker, 0x0123456789abcdefULL, 0) == 0); + + ret = l2tp_packet_send(sock, pack); + CHECK(ret == 0); + l2tp_packet_free(pack); + if (ret < 0) + return; + + pack = NULL; + CHECK(l2tp_recv(sock, &pack, NULL, secret, sizeof(secret) - 1) == 0); + CHECK(pack != NULL); + if (!pack) + return; + + attr = find_attr(pack, Message_Type); + CHECK(attr && attr->val.uint16 == Message_Type_Hello); + attr = find_attr(pack, Host_Name); + CHECK(attr && attr->length == (int)strlen(host_name)); + CHECK(attr && strcmp(attr->val.string, host_name) == 0); + attr = find_attr(pack, Assigned_Tunnel_ID); + CHECK(attr && attr->val.uint16 == 0x1234); + attr = find_attr(pack, Call_Serial_Number); + CHECK(attr && attr->val.uint32 == 0x89abcdef); + attr = find_attr(pack, Tie_Breaker); + CHECK(attr && attr->val.uint64 == 0x0123456789abcdefULL); + + l2tp_packet_free(pack); +} + +/* + * A hidden AVP deciphered with the wrong secret yields a random length + * prefix. Whatever it is, the parser must not read outside the AVP. + */ +static void test_wrong_secret(void) +{ + struct l2tp_packet_t *pack; + struct pktbuf pkt; + uint8_t buf[1024]; + size_t len, indx; + int ret; + + pack = l2tp_packet_alloc(2, Message_Type_Hello, &sock_addr, 1, + secret, sizeof(secret) - 1); + CHECK(pack != NULL); + if (!pack) + return; + + CHECK(l2tp_packet_add_string(pack, Host_Name, "hidden", 1) == 0); + CHECK(l2tp_packet_send(sock, pack) == 0); + l2tp_packet_free(pack); + + len = recv(sock, buf, sizeof(buf), 0); + CHECK(len > 0); + + /* Same bytes on the wire, every other secret at the receiving end */ + for (indx = 0; indx < 64; ++indx) { + char wrong[8]; + + snprintf(wrong, sizeof(wrong), "wrong%02zu", indx); + memcpy(pkt.data, buf, len); + pkt.len = len; + + if (sendto(sock, pkt.data, pkt.len, 0, + (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { + perror("sendto"); + exit(1); + } + pack = NULL; + ret = l2tp_recv(sock, &pack, NULL, wrong, strlen(wrong)); + CHECK(ret == 0); + if (pack) { + /* Accepting is fine (the random prefix may happen to be + plausible), reading out of the AVP is not */ + const struct l2tp_attr_t *attr = find_attr(pack, Host_Name); + + CHECK(!attr || attr->length <= (int)len); + l2tp_packet_free(pack); + } + } +} + +int main(void) +{ + loopback_socket(); + + test_hidden_avp_length_prefix(); + test_hidden_avp_prerequisites(); + test_roundtrip(0); + test_roundtrip(1); + test_wrong_secret(); + + close(sock); + + if (failures) { + fprintf(stderr, "%d failure(s)\n", failures); + return 1; + } + + printf("all tests passed\n"); + + return 0; +} diff --git a/accel-pppd/ctrl/pppoe/CMakeLists.txt b/accel-pppd/ctrl/pppoe/CMakeLists.txt index fd4f9a36..92733b74 100644 --- a/accel-pppd/ctrl/pppoe/CMakeLists.txt +++ b/accel-pppd/ctrl/pppoe/CMakeLists.txt @@ -13,7 +13,12 @@ SET(sources ${sources} tr101.c) ENDIF(RADIUS) ADD_LIBRARY(pppoe SHARED ${sources}) -TARGET_LINK_LIBRARIES(pppoe vlan-mon connlimit) +# if MUSL is set then we need to link with the connlimit library +IF (MUSL) + TARGET_LINK_LIBRARIES(pppoe vlan-mon connlimit ${crypto_lib}) +ELSE (MUSL) + TARGET_LINK_LIBRARIES(pppoe vlan-mon ${crypto_lib}) +ENDIF (MUSL) set_property(TARGET pppoe PROPERTY CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set_property(TARGET pppoe PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/accel-ppp) diff --git a/accel-pppd/ctrl/pppoe/cli.c b/accel-pppd/ctrl/pppoe/cli.c index d8399543..453c0cf3 100644 --- a/accel-pppd/ctrl/pppoe/cli.c +++ b/accel-pppd/ctrl/pppoe/cli.c @@ -88,16 +88,20 @@ help: static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt, void *client) { + struct pppoe_stat_t stat; + + pppoe_stat_get(&stat); + cli_send(client, "pppoe:\r\n"); - cli_sendv(client, " starting: %u\r\n", stat_starting); - cli_sendv(client, " active: %u\r\n", stat_active); - cli_sendv(client, " delayed PADO: %u\r\n", stat_delayed_pado); - cli_sendv(client, " recv PADI: %lu\r\n", stat_PADI_recv); - cli_sendv(client, " drop PADI: %lu\r\n", stat_PADI_drop); - cli_sendv(client, " sent PADO: %lu\r\n", stat_PADO_sent); - cli_sendv(client, " recv PADR(dup): %lu(%lu)\r\n", stat_PADR_recv, stat_PADR_dup_recv); - cli_sendv(client, " sent PADS: %lu\r\n", stat_PADS_sent); - cli_sendv(client, " filtered: %lu\r\n", stat_filtered); + cli_sendv(client, " starting: %u\r\n", stat.starting); + cli_sendv(client, " active: %u\r\n", stat.active); + cli_sendv(client, " delayed PADO: %u\r\n", stat.delayed_PADO); + cli_sendv(client, " recv PADI: %lu\r\n", stat.PADI_recv); + cli_sendv(client, " drop PADI: %lu\r\n", stat.PADI_drop); + cli_sendv(client, " sent PADO: %lu\r\n", stat.PADO_sent); + cli_sendv(client, " recv PADR(dup): %lu(%lu)\r\n", stat.PADR_recv, stat.PADR_dup_recv); + cli_sendv(client, " sent PADS: %lu\r\n", stat.PADS_sent); + cli_sendv(client, " filtered: %lu\r\n", stat.filtered); return CLI_CMD_OK; } diff --git a/accel-pppd/ctrl/pppoe/disc.c b/accel-pppd/ctrl/pppoe/disc.c index 8a82e1d2..f7f5c781 100644 --- a/accel-pppd/ctrl/pppoe/disc.c +++ b/accel-pppd/ctrl/pppoe/disc.c @@ -77,7 +77,7 @@ static struct disc_net *init_net(const struct ap_net *net) fcntl(sock, F_SETFD, FD_CLOEXEC); net->set_nonblocking(sock, 1); - n = _malloc(sizeof(*net) + (HASH_BITS + 1) * sizeof(struct tree)); + n = _malloc(sizeof(*n) + (HASH_BITS + 1) * sizeof(struct tree)); tree = n->tree; for (i = 0; i <= HASH_BITS; i++) { @@ -110,7 +110,7 @@ static void free_net(struct disc_net *net) pthread_mutex_lock(&nets_lock); for (i = 0; i < MAX_NET; i++) { if (nets[i] == net) { - memcpy(nets + i, nets + i + 1, net_cnt - i - 1); + memmove(nets + i, nets + i + 1, (net_cnt - i - 1) * sizeof(nets[0])); net_cnt--; break; } @@ -329,7 +329,7 @@ static int disc_read(struct triton_md_handler_t *h) } if (mac_filter_check(ethhdr->h_source)) { - __sync_add_and_fetch(&stat_filtered, 1); + pppoe_stat_add_filtered(); continue; } @@ -363,6 +363,7 @@ static int disc_read(struct triton_md_handler_t *h) if (hdr->type != 1) { if (conf_verbose) log_warn("pppoe: discarding packet (unsupported type %i)\n", hdr->type); + continue; } if (forward(net, src.sll_ifindex, pack, n)) diff --git a/accel-pppd/ctrl/pppoe/dpado.c b/accel-pppd/ctrl/pppoe/dpado.c index 71faa130..dc570dda 100644 --- a/accel-pppd/ctrl/pppoe/dpado.c +++ b/accel-pppd/ctrl/pppoe/dpado.c @@ -11,6 +11,7 @@ #include "triton.h" #include "log.h" #include "memdebug.h" +#include "utils.h" #include "pppoe.h" @@ -60,27 +61,15 @@ void dpado_check_prev(int conn_cnt) pthread_mutex_unlock(&dpado_range_lock); } -static void strip(char *str) -{ - char *ptr = str; - char *endptr = strchr(str, 0); - while (1) { - ptr = strchr(ptr, ' '); - if (ptr) - memmove(ptr, ptr + 1, endptr - ptr - 1); - else - break; - } -} - int dpado_parse(const char *str) { char *str1 = _strdup(str); char *ptr1, *ptr2, *ptr3, *endptr; + unsigned int active = pppoe_stat_active(); LIST_HEAD(range_list); struct dpado_range_t *r; - strip(str1); + u_strstrip(str1, ' '); ptr1 = str1; @@ -96,17 +85,23 @@ int dpado_parse(const char *str) memset(r, 0, sizeof(*r)); r->pado_delay = strtol(ptr1, &endptr, 10); - if (*endptr) + if (*endptr) { + _free(r); goto out_err; + } if (list_empty(&range_list)) r->conn_cnt = INT_MAX; else { - if (!ptr3) + if (!ptr3) { + _free(r); goto out_err; + } r->conn_cnt = strtol(ptr3 + 1, &endptr, 10); - if (*endptr) + if (*endptr) { + _free(r); goto out_err; + } } list_add_tail(&r->entry, &range_list); @@ -131,7 +126,7 @@ int dpado_parse(const char *str) dpado_range_prev = NULL; list_for_each_entry(r, &dpado_range_list, entry) { - if (!dpado_range_prev || stat_active >= r->conn_cnt) { + if (!dpado_range_prev || active >= r->conn_cnt) { dpado_range_prev = r; if (r->entry.next != &dpado_range_list) dpado_range_next = list_entry(r->entry.next, typeof(*r), entry); @@ -159,6 +154,11 @@ int dpado_parse(const char *str) return 0; out_err: + while (!list_empty(&range_list)) { + r = list_entry(range_list.next, typeof(*r), entry); + list_del(&r->entry); + _free(r); + } _free(str1); log_emerg("pppoe: pado_delay: invalid format\n"); return -1; diff --git a/accel-pppd/ctrl/pppoe/mac_filter.c b/accel-pppd/ctrl/pppoe/mac_filter.c index ba78df6b..3a6a00d6 100644 --- a/accel-pppd/ctrl/pppoe/mac_filter.c +++ b/accel-pppd/ctrl/pppoe/mac_filter.c @@ -92,15 +92,17 @@ static int mac_filter_load(const char *opt) log_warn("pppoe: mac-filter:%s:%i: address is invalid\n", name, line); continue; } - mac = _malloc(sizeof(*mac)); for (i = 0; i < ETH_ALEN; i++) { - if (n[i] > 255) { - log_warn("pppoe: mac-filter:%s:%i: address is invalid\n", name, line); - _free(mac); - continue; - } - mac->addr[i] = n[i]; + if (n[i] > 255) + break; } + if (i < ETH_ALEN) { + log_warn("pppoe: mac-filter:%s:%i: address is invalid\n", name, line); + continue; + } + mac = _malloc(sizeof(*mac)); + for (i = 0; i < ETH_ALEN; i++) + mac->addr[i] = n[i]; list_add_tail(&mac->entry, &mac_list); } pthread_rwlock_unlock(&lock); diff --git a/accel-pppd/ctrl/pppoe/pppoe.c b/accel-pppd/ctrl/pppoe/pppoe.c index dd623acc..bd92cbf8 100644 --- a/accel-pppd/ctrl/pppoe/pppoe.c +++ b/accel-pppd/ctrl/pppoe/pppoe.c @@ -11,11 +11,6 @@ #include <net/ethernet.h> #include <netpacket/packet.h> #include <arpa/inet.h> -#ifdef HAVE_PRINTF_H -#include <printf.h> -#endif - -#include "crypto.h" #include "events.h" #include "triton.h" @@ -29,6 +24,7 @@ #endif #include "iputils.h" +#include "utils.h" #include "connlimit.h" #include "vlan_mon.h" @@ -38,16 +34,13 @@ #define SID_MAX 65536 -#ifndef min -#define min(x,y) ((x)<(y)?(x):(y)) -#endif - struct pppoe_conn_t { struct list_head entry; struct triton_context_t ctx; struct pppoe_serv_t *serv; uint16_t sid; uint8_t addr[ETH_ALEN]; + unsigned int ppp_starting:1; unsigned int ppp_started:1; struct pppoe_tag *relay_sid; @@ -83,7 +76,7 @@ struct padi_t }; struct iplink_arg { - pcre *re; + pcre2_code *re; const char *opt; void *cli; long *arg1; @@ -114,17 +107,8 @@ static mempool_t conn_pool; static mempool_t pado_pool; static mempool_t padi_pool; -unsigned int stat_starting; -unsigned int stat_active; -unsigned int stat_delayed_pado; -unsigned long stat_PADI_recv; -unsigned long stat_PADI_drop; -unsigned long stat_PADO_sent; -unsigned long stat_PADR_recv; -unsigned long stat_PADR_dup_recv; -unsigned long stat_PADS_sent; +static struct pppoe_stat_t pppoe_stat; unsigned int total_padi_cnt; -unsigned long stat_filtered; pthread_rwlock_t serv_lock = PTHREAD_RWLOCK_INITIALIZER; LIST_HEAD(serv_list); @@ -136,6 +120,35 @@ static unsigned long *sid_map; static unsigned long *sid_ptr; static int sid_idx; +void __export pppoe_stat_get(struct pppoe_stat_t *stat) +{ + stat->starting = __atomic_load_n(&pppoe_stat.starting, __ATOMIC_RELAXED); + stat->active = __atomic_load_n(&pppoe_stat.active, __ATOMIC_RELAXED); + stat->delayed_PADO = __atomic_load_n(&pppoe_stat.delayed_PADO, __ATOMIC_RELAXED); + stat->PADI_recv = __atomic_load_n(&pppoe_stat.PADI_recv, __ATOMIC_RELAXED); + stat->PADI_drop = __atomic_load_n(&pppoe_stat.PADI_drop, __ATOMIC_RELAXED); + stat->PADO_sent = __atomic_load_n(&pppoe_stat.PADO_sent, __ATOMIC_RELAXED); + stat->PADR_recv = __atomic_load_n(&pppoe_stat.PADR_recv, __ATOMIC_RELAXED); + stat->PADR_dup_recv = __atomic_load_n(&pppoe_stat.PADR_dup_recv, __ATOMIC_RELAXED); + stat->PADS_sent = __atomic_load_n(&pppoe_stat.PADS_sent, __ATOMIC_RELAXED); + stat->filtered = __atomic_load_n(&pppoe_stat.filtered, __ATOMIC_RELAXED); +} + +unsigned int __export pppoe_stat_starting(void) +{ + return __atomic_load_n(&pppoe_stat.starting, __ATOMIC_RELAXED); +} + +unsigned int __export pppoe_stat_active(void) +{ + return __atomic_load_n(&pppoe_stat.active, __ATOMIC_RELAXED); +} + +void __export pppoe_stat_add_filtered(void) +{ + __atomic_add_fetch(&pppoe_stat.filtered, 1, __ATOMIC_RELAXED); +} + static uint8_t bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static void pppoe_send_PADT(struct pppoe_conn_t *conn); @@ -173,9 +186,12 @@ static void disconnect(struct pppoe_conn_t *conn) struct pppoe_serv_t *serv = conn->serv; if (conn->ppp_started) { - dpado_check_prev(__sync_fetch_and_sub(&stat_active, 1)); + dpado_check_prev(__atomic_fetch_sub(&pppoe_stat.active, 1, __ATOMIC_RELAXED)); conn->ppp_started = 0; ap_session_terminate(&conn->ppp.ses, TERM_USER_REQUEST, 1); + } else if (conn->ppp_starting) { + __atomic_sub_fetch(&pppoe_stat.starting, 1, __ATOMIC_RELAXED); + conn->ppp_starting = 0; } pppoe_send_PADT(conn); @@ -232,7 +248,7 @@ static void ppp_finished(struct ap_session *ses) log_ppp_debug("pppoe: ppp finished\n"); if (conn->ppp_started) { - dpado_check_prev(__sync_fetch_and_sub(&stat_active, 1)); + dpado_check_prev(__atomic_fetch_sub(&pppoe_stat.active, 1, __ATOMIC_RELAXED)); conn->ppp_started = 0; triton_context_call(&conn->ctx, (triton_event_func)disconnect, conn); } @@ -279,9 +295,15 @@ static void pppoe_conn_ctx_switch(struct triton_context_t *ctx, void *arg) static struct pppoe_conn_t *allocate_channel(struct pppoe_serv_t *serv, const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid, const struct pppoe_tag *service_name, const struct pppoe_tag *tr101, const uint8_t *cookie, uint16_t ppp_max_payload) { + struct pppoe_tag empty_service_name = { + .tag_type = htons(TAG_SERVICE_NAME), + }; struct pppoe_conn_t *conn; unsigned long *old_sid_ptr; + if (!service_name) + service_name = &empty_service_name; + conn = mempool_alloc(conn_pool); if (!conn) { log_error("pppoe: out of memory\n"); @@ -450,6 +472,9 @@ static void connect_channel(struct pppoe_conn_t *conn) struct sockaddr_pppox sp; triton_event_fire(EV_CTRL_STARTING, &conn->ppp.ses); + conn->ppp_starting = 1; + __atomic_add_fetch(&pppoe_stat.starting, 1, __ATOMIC_RELAXED); + triton_event_fire(EV_CTRL_STARTED, &conn->ppp.ses); sock = net->socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE); @@ -486,9 +511,11 @@ static void connect_channel(struct pppoe_conn_t *conn) } #endif + conn->ppp_starting = 0; conn->ppp_started = 1; - dpado_check_next(__sync_add_and_fetch(&stat_active, 1)); + __atomic_sub_fetch(&pppoe_stat.starting, 1, __ATOMIC_RELAXED); + dpado_check_next(__atomic_add_fetch(&pppoe_stat.active, 1, __ATOMIC_RELAXED)); return; @@ -529,7 +556,12 @@ static void print_tag_octets(struct pppoe_tag *tag) static void print_tag_u16(struct pppoe_tag *tag) { - log_info2("%i", (uint16_t)ntohs(*(uint16_t *)tag->tag_data)); + if (ntohs(tag->tag_len) != sizeof(uint16_t)) { + log_info2("invalid"); + return; + } + + log_info2("%i", u_read_be16(tag->tag_data)); } static void print_packet(const char *ifname, const char *op, uint8_t *pack) @@ -606,7 +638,7 @@ static void print_packet(const char *ifname, const char *op, uint8_t *pack) if (ntohs(tag->tag_len) < 4) log_info2(" <Vendor-Specific invalid>"); else - log_info2(" <Vendor-Specific %x>", ntohl(*(uint32_t *)tag->tag_data)); + log_info2(" <Vendor-Specific %x>", u_read_be32(tag->tag_data)); break; case TAG_RELAY_SESSION_ID: log_info2(" <Relay-Session-Id "); @@ -674,7 +706,10 @@ static void generate_cookie(struct pppoe_serv_t *serv, const uint8_t *src, uint8 } else memset(u1.raw + 16, 0, 4); - *(uint32_t *)(u1.raw + 20) = ts.tv_sec + conf_cookie_timeout; + { + uint32_t expires = ts.tv_sec + conf_cookie_timeout; + memcpy(u1.raw + 20, &expires, sizeof(expires)); + } for (i = 0; i < 3; i++) DES_ecb_encrypt(&u1.b[i], &u2.b[i], &ks, DES_ENCRYPT); @@ -712,8 +747,12 @@ static int check_cookie(struct pppoe_serv_t *serv, const uint8_t *src, const uin for (i = 0; i < 3; i++) DES_ecb_encrypt(&u2.b[i], &u1.b[i], &ks, DES_DECRYPT); - if (*(uint32_t *)(u1.raw + 20) < ts.tv_sec) - return 1; + { + uint32_t expires; + memcpy(&expires, u1.raw + 20, sizeof(expires)); + if (expires < ts.tv_sec) + return 1; + } MD5_Init(&ctx); MD5_Update(&ctx, serv->secret, SECRET_LENGTH); @@ -762,7 +801,7 @@ static int add_tag2(uint8_t *pack, size_t pack_size, const struct pppoe_tag *t) { struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); struct pppoe_tag *tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length)); - if (pack_size <= ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length) + ntohs(t->tag_len) || ntohs(t->tag_len) < 0) + if (pack_size <= ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length) + sizeof(*t) + ntohs(t->tag_len)) return -1; memcpy(tag, t, sizeof(*t) + ntohs(t->tag_len)); @@ -822,7 +861,7 @@ static void pppoe_send_PADO(struct pppoe_serv_t *serv, const uint8_t *addr, cons if (conf_verbose) print_packet(serv->ifname, "send", pack); - __sync_add_and_fetch(&stat_PADO_sent, 1); + __atomic_add_fetch(&pppoe_stat.PADO_sent, 1, __ATOMIC_RELAXED); pppoe_send(serv, pack); } @@ -871,7 +910,7 @@ static void pppoe_send_PADS(struct pppoe_conn_t *conn) if (conf_verbose) print_packet(conn->serv->ifname, "send", pack); - __sync_add_and_fetch(&stat_PADS_sent, 1); + __atomic_add_fetch(&pppoe_stat.PADS_sent, 1, __ATOMIC_RELAXED); pppoe_send(conn->serv, pack); } @@ -898,7 +937,7 @@ static void free_delayed_pado(struct delayed_pado_t *pado) { triton_timer_del(&pado->timer); - __sync_sub_and_fetch(&stat_delayed_pado, 1); + __atomic_sub_fetch(&pppoe_stat.delayed_PADO, 1, __ATOMIC_RELAXED); list_del(&pado->entry); if (pado->host_uniq) @@ -984,19 +1023,19 @@ static void pppoe_recv_PADI(struct pppoe_serv_t *serv, uint8_t *pack, int size) struct timespec ts; uint16_t ppp_max_payload = 0; - __sync_add_and_fetch(&stat_PADI_recv, 1); + __atomic_add_fetch(&pppoe_stat.PADI_recv, 1, __ATOMIC_RELAXED); if (ap_shutdown || pado_delay == -1) return; - if (conf_max_starting && ap_session_stat.starting >= conf_max_starting) + if (conf_max_starting && ap_session_stat_starting() >= conf_max_starting) return; - if (conf_max_sessions && ap_session_stat.active + ap_session_stat.starting >= conf_max_sessions) + if (conf_max_sessions && ap_session_stat_active() + ap_session_stat_starting() >= conf_max_sessions) return; if (check_padi_limit(serv, ethhdr->h_source)) { - __sync_add_and_fetch(&stat_PADI_drop, 1); + __atomic_add_fetch(&pppoe_stat.PADI_drop, 1, __ATOMIC_RELAXED); if (conf_verbose) { clock_gettime(CLOCK_MONOTONIC, &ts); if (ts.tv_sec - 60 >= serv->last_padi_limit_warn) { @@ -1013,11 +1052,13 @@ static void pppoe_recv_PADI(struct pppoe_serv_t *serv, uint8_t *pack, int size) len = ntohs(hdr->length); for (n = 0; n < len; n += sizeof(*tag) + ntohs(tag->tag_len)) { tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n); + if (n + sizeof(*tag) > len) + return; if (n + sizeof(*tag) + ntohs(tag->tag_len) > len) return; switch (ntohs(tag->tag_type)) { case TAG_END_OF_LIST: - break; + goto tags_done; case TAG_SERVICE_NAME: if (tag->tag_len == 0 && conf_accept_blank_service) { service_match = 1; @@ -1044,10 +1085,11 @@ static void pppoe_recv_PADI(struct pppoe_serv_t *serv, uint8_t *pack, int size) break; case TAG_PPP_MAX_PAYLOAD: if (ntohs(tag->tag_len) == 2) - ppp_max_payload = ntohs(*(uint16_t *)tag->tag_data); + ppp_max_payload = u_read_be16(tag->tag_data); break; } } +tags_done: if (conf_verbose) print_packet(serv->ifname, "recv", pack); @@ -1098,7 +1140,7 @@ static void pppoe_recv_PADI(struct pppoe_serv_t *serv, uint8_t *pack, int size) triton_timer_add(&serv->ctx, &pado->timer, 0); list_add_tail(&pado->entry, &serv->pado_list); - __sync_add_and_fetch(&stat_delayed_pado, 1); + __atomic_add_fetch(&pppoe_stat.delayed_PADO, 1, __ATOMIC_RELAXED); } else pppoe_send_PADO(serv, ethhdr->h_source, host_uniq_tag, relay_sid_tag, service_name_tag, ppp_max_payload); } @@ -1118,15 +1160,15 @@ static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size) int vendor_id; uint16_t ppp_max_payload = 0; - __sync_add_and_fetch(&stat_PADR_recv, 1); + __atomic_add_fetch(&pppoe_stat.PADR_recv, 1, __ATOMIC_RELAXED); if (ap_shutdown) return; - if (conf_max_starting && ap_session_stat.starting >= conf_max_starting) + if (conf_max_starting && ap_session_stat_starting() >= conf_max_starting) return; - if (conf_max_sessions && ap_session_stat.active + ap_session_stat.starting >= conf_max_sessions) + if (conf_max_sessions && ap_session_stat_active() + ap_session_stat_starting() >= conf_max_sessions) return; if (!memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN)) { @@ -1159,7 +1201,7 @@ static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size) } switch (ntohs(tag->tag_type)) { case TAG_END_OF_LIST: - break; + goto padr_tags_done; case TAG_SERVICE_NAME: service_name_tag = tag; if (tag->tag_len == 0) @@ -1190,16 +1232,18 @@ static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size) case TAG_VENDOR_SPECIFIC: if (ntohs(tag->tag_len) < 4) continue; - vendor_id = ntohl(*(uint32_t *)tag->tag_data); + vendor_id = u_read_be32(tag->tag_data); if (vendor_id == VENDOR_ADSL_FORUM) if (conf_tr101) tr101_tag = tag; + break; case TAG_PPP_MAX_PAYLOAD: if (ntohs(tag->tag_len) == 2) - ppp_max_payload = ntohs(*(uint16_t *)tag->tag_data); + ppp_max_payload = u_read_be16(tag->tag_data); break; } } +padr_tags_done: if (!ac_cookie_tag) { if (conf_verbose) @@ -1207,6 +1251,12 @@ static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size) return; } + if (!service_name_tag) { + if (conf_verbose) + log_warn("pppoe: discard PADR packet (no Service-Name tag present)\n"); + return; + } + if (ntohs(ac_cookie_tag->tag_len) != COOKIE_LENGTH) { if (conf_verbose) log_warn("pppoe: discard PADR packet (incorrect AC-Cookie tag length)\n"); @@ -1229,7 +1279,7 @@ static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size) pthread_mutex_lock(&serv->lock); conn = find_channel(serv, (uint8_t *)ac_cookie_tag->tag_data); if (conn && !conn->ppp.ses.username) { - __sync_add_and_fetch(&stat_PADR_dup_recv, 1); + __atomic_add_fetch(&pppoe_stat.PADR_dup_recv, 1, __ATOMIC_RELAXED); pppoe_send_PADS(conn); } pthread_mutex_unlock(&serv->lock); @@ -1368,8 +1418,12 @@ out_err: static int __pppoe_add_interface_re(int index, int flags, const char *name, int iflink, int vid, struct iplink_arg *arg) { - if (pcre_exec(arg->re, NULL, name, strlen(name), 0, 0, NULL, 0) < 0) + pcre2_match_data *match_data = pcre2_match_data_create(0, NULL); + if (pcre2_match(arg->re, (PCRE2_SPTR)name, strlen(name), 0, 0, match_data, NULL) < 0) { + pcre2_match_data_free(match_data); return 0; + } + pcre2_match_data_free(match_data); __pppoe_server_start(name, arg->opt, arg->cli, iflink, vid, 0); @@ -1378,11 +1432,11 @@ static int __pppoe_add_interface_re(int index, int flags, const char *name, int static void pppoe_add_interface_re(const char *opt, void *cli) { - pcre *re = NULL; - const char *pcre_err; + pcre2_code *re = NULL; + int pcre_err; char *pattern; const char *ptr; - int pcre_offset; + PCRE2_SIZE pcre_offset; struct iplink_arg arg; for (ptr = opt; *ptr && *ptr != ','; ptr++); @@ -1391,10 +1445,14 @@ static void pppoe_add_interface_re(const char *opt, void *cli) memcpy(pattern, opt + 3, ptr - (opt + 3)); pattern[ptr - (opt + 3)] = 0; - re = pcre_compile2(pattern, 0, NULL, &pcre_err, &pcre_offset, NULL); + re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, &pcre_err, &pcre_offset, NULL); if (!re) { - log_error("pppoe: %s at %i\r\n", pcre_err, pcre_offset); + PCRE2_UCHAR err_msg[64]; + pcre2_get_error_message(pcre_err, err_msg, sizeof(err_msg)); + if (cli) + cli_sendv(cli, "pppoe: %s at %i\r\n", err_msg, (int)pcre_offset); + log_error("pppoe: %s at %i\r\n", err_msg, (int)pcre_offset); return; } @@ -1404,7 +1462,7 @@ static void pppoe_add_interface_re(const char *opt, void *cli) iplink_list((iplink_list_func)__pppoe_add_interface_re, &arg); - pcre_free(re); + pcre2_code_free(re); _free(pattern); } @@ -1637,12 +1695,6 @@ void pppoe_server_stop(const char *ifname) pthread_rwlock_unlock(&serv_lock); } -void __export pppoe_get_stat(unsigned int **starting, unsigned int **active) -{ - *starting = &stat_starting; - *active = &stat_active; -} - static int init_secret(struct pppoe_serv_t *serv) { DES_cblock key; @@ -1676,10 +1728,10 @@ void pppoe_vlan_mon_notify(int ifindex, int vid, int vlan_ifindex) struct ifreq ifr; char *ptr; int len, r, svid; - pcre *re = NULL; - const char *pcre_err; + pcre2_code *re = NULL; + int pcre_err; char *pattern; - int pcre_offset; + PCRE2_SIZE pcre_offset; char ifname[IFNAMSIZ]; if (!sect) @@ -1777,15 +1829,17 @@ void pppoe_vlan_mon_notify(int ifindex, int vid, int vlan_ifindex) memcpy(pattern, opt->val + 3, ptr - (opt->val + 3)); pattern[ptr - (opt->val + 3)] = 0; - re = pcre_compile2(pattern, 0, NULL, &pcre_err, &pcre_offset, NULL); + re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, &pcre_err, &pcre_offset, NULL); _free(pattern); if (!re) continue; - r = pcre_exec(re, NULL, ifr.ifr_name, len, 0, 0, NULL, 0); - pcre_free(re); + pcre2_match_data *match_data = pcre2_match_data_create(0, NULL); + r = pcre2_match(re, (PCRE2_SPTR)ifr.ifr_name, len, 0, 0, match_data, NULL); + pcre2_match_data_free(match_data); + pcre2_code_free(re); if (r < 0) continue; @@ -1860,8 +1914,12 @@ static int __load_vlan_mon_re(int index, int flags, const char *name, int iflink long mask1[4096/8/sizeof(long)]; struct pppoe_serv_t *serv; - if (pcre_exec(arg->re, NULL, name, strlen(name), 0, 0, NULL, 0) < 0) + pcre2_match_data *match_data = pcre2_match_data_create(0, NULL); + if (pcre2_match(arg->re, (PCRE2_SPTR)name, strlen(name), 0, 0, match_data, NULL) < 0) { + pcre2_match_data_free(match_data); return 0; + } + pcre2_match_data_free(match_data); memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, name); @@ -1894,11 +1952,11 @@ static int __load_vlan_mon_re(int index, int flags, const char *name, int iflink static void load_vlan_mon_re(const char *opt, long *mask, int len) { - pcre *re = NULL; - const char *pcre_err; + pcre2_code *re = NULL; + int pcre_err; char *pattern; const char *ptr; - int pcre_offset; + PCRE2_SIZE pcre_offset; struct iplink_arg arg; for (ptr = opt; *ptr && *ptr != ','; ptr++); @@ -1907,10 +1965,12 @@ static void load_vlan_mon_re(const char *opt, long *mask, int len) memcpy(pattern, opt + 3, ptr - (opt + 3)); pattern[ptr - (opt + 3)] = 0; - re = pcre_compile2(pattern, 0, NULL, &pcre_err, &pcre_offset, NULL); + re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, &pcre_err, &pcre_offset, NULL); if (!re) { - log_error("pppoe: '%s': %s at %i\r\n", pattern, pcre_err, pcre_offset); + PCRE2_UCHAR err_msg[64]; + pcre2_get_error_message(pcre_err, err_msg, sizeof(err_msg)); + log_error("pppoe: '%s': %s at %i\r\n", pattern, err_msg, (int)pcre_offset); return; } @@ -1920,7 +1980,7 @@ static void load_vlan_mon_re(const char *opt, long *mask, int len) iplink_list((iplink_list_func)__load_vlan_mon_re, &arg); - pcre_free(re); + pcre2_code_free(re); _free(pattern); } diff --git a/accel-pppd/ctrl/pppoe/pppoe.h b/accel-pppd/ctrl/pppoe/pppoe.h index 7ba4ac63..42067590 100644 --- a/accel-pppd/ctrl/pppoe/pppoe.h +++ b/accel-pppd/ctrl/pppoe/pppoe.h @@ -6,8 +6,10 @@ #include <linux/if.h> #include <linux/if_pppox.h> +#include <openssl/md5.h> +#include <openssl/des.h> + #include "rbtree.h" -#include "crypto.h" /* PPPoE codes */ #define CODE_PADI 0x09 @@ -107,16 +109,24 @@ extern int conf_accept_any_service; extern char *conf_ac_name; extern char *conf_pado_delay; -extern unsigned int stat_starting; -extern unsigned int stat_active; -extern unsigned int stat_delayed_pado; -extern unsigned long stat_PADI_recv; -extern unsigned long stat_PADO_sent; -extern unsigned long stat_PADR_recv; -extern unsigned long stat_PADR_dup_recv; -extern unsigned long stat_PADS_sent; -extern unsigned long stat_PADI_drop; -extern unsigned long stat_filtered; +struct pppoe_stat_t +{ + unsigned int starting; + unsigned int active; + unsigned int delayed_PADO; + unsigned long PADI_recv; + unsigned long PADI_drop; + unsigned long PADO_sent; + unsigned long PADR_recv; + unsigned long PADR_dup_recv; + unsigned long PADS_sent; + unsigned long filtered; +}; + +void pppoe_stat_get(struct pppoe_stat_t *stat); +unsigned int pppoe_stat_starting(void); +unsigned int pppoe_stat_active(void); +void pppoe_stat_add_filtered(void); extern pthread_rwlock_t serv_lock; extern struct list_head serv_list; @@ -140,4 +150,3 @@ int tr101_send_access_request(struct pppoe_tag *tr101, struct rad_packet_t *pack int tr101_send_accounting_request(struct pppoe_tag *tr101, struct rad_packet_t *pack); #endif - diff --git a/accel-pppd/ctrl/pppoe/tr101.c b/accel-pppd/ctrl/pppoe/tr101.c index bb8b845a..06aeff86 100644 --- a/accel-pppd/ctrl/pppoe/tr101.c +++ b/accel-pppd/ctrl/pppoe/tr101.c @@ -8,6 +8,7 @@ #include "log.h" #include "radius.h" #include "memdebug.h" +#include "utils.h" #include "pppoe.h" @@ -75,85 +76,85 @@ static int tr101_send_request(struct pppoe_tag *tr101, struct rad_packet_t *pack case OPT_ACTUAL_DATA_RATE_UP: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Actual-Data-Rate-Upstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Actual-Data-Rate-Upstream", u_read_be32(ptr))) return -1; break; case OPT_ACTUAL_DATA_RATE_DOWN: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Actual-Data-Rate-Downstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Actual-Data-Rate-Downstream", u_read_be32(ptr))) return -1; break; case OPT_MIN_DATA_RATE_UP: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Minimum-Data-Rate-Upstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Minimum-Data-Rate-Upstream", u_read_be32(ptr))) return -1; break; case OPT_MIN_DATA_RATE_DOWN: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Minimum-Data-Rate-Downstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Minimum-Data-Rate-Downstream", u_read_be32(ptr))) return -1; break; case OPT_ATT_DATA_RATE_UP: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Attainable-Data-Rate-Upstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Attainable-Data-Rate-Upstream", u_read_be32(ptr))) return -1; break; case OPT_ATT_DATA_RATE_DOWN: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Attainable-Data-Rate-Downstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Attainable-Data-Rate-Downstream", u_read_be32(ptr))) return -1; break; case OPT_MAX_DATA_RATE_UP: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Maximum-Data-Rate-Upstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Maximum-Data-Rate-Upstream", u_read_be32(ptr))) return -1; break; case OPT_MAX_DATA_RATE_DOWN: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Maximum-Data-Rate-Downstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Maximum-Data-Rate-Downstream", u_read_be32(ptr))) return -1; break; case OPT_MIN_DATA_RATE_UP_LP: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Minimum-Data-Rate-Upstream-Low-Power", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Minimum-Data-Rate-Upstream-Low-Power", u_read_be32(ptr))) return -1; break; case OPT_MIN_DATA_RATE_DOWN_LP: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Minimum-Data-Rate-Downstream-Low-Power", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Minimum-Data-Rate-Downstream-Low-Power", u_read_be32(ptr))) return -1; break; case OPT_MAX_INTERL_DELAY_UP: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Maximum-Interleaving-Delay-Upstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Maximum-Interleaving-Delay-Upstream", u_read_be32(ptr))) return -1; break; case OPT_ACTUAL_INTERL_DELAY_UP: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Actual-Interleaving-Delay-Upstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Actual-Interleaving-Delay-Upstream", u_read_be32(ptr))) return -1; break; case OPT_MAX_INTER_DELAY_DOWN: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Maximum-Interleaving-Delay-Downstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Maximum-Interleaving-Delay-Downstream", u_read_be32(ptr))) return -1; break; case OPT_ACTUAL_INTER_DELAY_DOWN: if (len != 4) goto inval; - if (rad_packet_add_int(pack, "ADSL-Forum", "Actual-Interleaving-Delay-Downstream", ntohl(*(uint32_t *)ptr))) + if (rad_packet_add_int(pack, "ADSL-Forum", "Actual-Interleaving-Delay-Downstream", u_read_be32(ptr))) return -1; break; case ACCESS_LOOP_ENCAP: diff --git a/accel-pppd/ctrl/pptp/pptp.c b/accel-pppd/ctrl/pptp/pptp.c index a95fe8ae..dac7c041 100644 --- a/accel-pppd/ctrl/pptp/pptp.c +++ b/accel-pppd/ctrl/pptp/pptp.c @@ -11,7 +11,7 @@ #include <netinet/in.h> #include <sys/socket.h> -#include "if_pppox.h" +#include <linux/if_pppox.h> #include "events.h" #include "list.h" @@ -25,6 +25,7 @@ #include "cli.h" #include "connlimit.h" +#include "pptp.h" #include "memdebug.h" @@ -55,6 +56,19 @@ struct pptp_conn_t struct ppp_t ppp; }; +struct pptp_stat_t +{ + unsigned int starting; + unsigned int active; +}; + +struct pptp_serv_t +{ + struct triton_context_t ctx; + struct triton_md_handler_t hnd; + struct pptp_stat_t stat; +}; + static int conf_ppp_max_mtu = PPTP_MAX_MTU; static int conf_timeout = 5; static int conf_echo_interval = 0; @@ -69,14 +83,53 @@ static const char *conf_ifname; static mempool_t conn_pool; -static unsigned int stat_starting; -static unsigned int stat_active; - static int pptp_read(struct triton_md_handler_t *h); static int pptp_write(struct triton_md_handler_t *h); static void pptp_timeout(struct triton_timer_t *); static void ppp_started(struct ap_session *); static void ppp_finished(struct ap_session *); +static void pptp_ctx_switch(struct triton_context_t *ctx, void *arg); +static int pptp_connect(struct triton_md_handler_t *h); +static void pptp_serv_close(struct triton_context_t *ctx); + +static struct pptp_serv_t serv = +{ + .hnd.read = pptp_connect, + .ctx.close = pptp_serv_close, + .ctx.before_switch = pptp_ctx_switch, +}; + +static void pptp_stat_inc(unsigned int *stat) +{ + __atomic_add_fetch(stat, 1, __ATOMIC_RELAXED); +} + +static void pptp_stat_dec(unsigned int *stat) +{ + __atomic_sub_fetch(stat, 1, __ATOMIC_RELAXED); +} + +static void pptp_stat_move(unsigned int *from, unsigned int *to) +{ + pptp_stat_dec(from); + pptp_stat_inc(to); +} + +static void pptp_stat_get(struct pptp_stat_t *stat) +{ + stat->starting = __atomic_load_n(&serv.stat.starting, __ATOMIC_RELAXED); + stat->active = __atomic_load_n(&serv.stat.active, __ATOMIC_RELAXED); +} + +unsigned int __export pptp_stat_starting(void) +{ + return __atomic_load_n(&serv.stat.starting, __ATOMIC_RELAXED); +} + +unsigned int __export pptp_stat_active(void) +{ + return __atomic_load_n(&serv.stat.active, __ATOMIC_RELAXED); +} static void pptp_ctx_switch(struct triton_context_t *ctx, void *arg) { @@ -101,11 +154,11 @@ static void disconnect(struct pptp_conn_t *conn) triton_timer_del(&conn->echo_timer); if (conn->state == STATE_PPP) { - __sync_sub_and_fetch(&stat_active, 1); + pptp_stat_dec(&serv.stat.active); conn->state = STATE_CLOSE; ap_session_terminate(&conn->ppp.ses, TERM_LOST_CARRIER, 1); } else if (conn->state != STATE_CLOSE) - __sync_sub_and_fetch(&stat_starting, 1); + pptp_stat_dec(&serv.stat.starting); triton_event_fire(EV_CTRL_FINISHED, &conn->ppp.ses); @@ -142,13 +195,14 @@ again: if (errno != EPIPE) { if (conf_verbose) log_ppp_info2("pptp: write: %s\n", strerror(errno)); - return -1; } + return -1; } } if ( n<size ) { memcpy(conn->out_buf, (uint8_t *)buf + n, size - n); + conn->out_size = size - n; triton_md_enable_handler(&conn->hnd, MD_MODE_WRITE); } @@ -306,7 +360,10 @@ static int pptp_out_call_rqst(struct pptp_conn_t *conn) src_addr.sa_protocol = PX_PROTO_PPTP; src_addr.sa_addr.pptp.call_id = 0; addrlen = sizeof(addr); - getsockname(conn->hnd.fd, (struct sockaddr*)&addr, &addrlen); + if (getsockname(conn->hnd.fd, (struct sockaddr*)&addr, &addrlen)) { + log_ppp_error("pptp: getsockname: %s\n", strerror(errno)); + return -1; + } src_addr.sa_addr.pptp.sin_addr = addr.sin_addr; memset(&dst_addr, 0, sizeof(dst_addr)); @@ -314,7 +371,10 @@ static int pptp_out_call_rqst(struct pptp_conn_t *conn) dst_addr.sa_protocol = PX_PROTO_PPTP; dst_addr.sa_addr.pptp.call_id = htons(msg->call_id); addrlen = sizeof(addr); - getpeername(conn->hnd.fd, (struct sockaddr*)&addr, &addrlen); + if (getpeername(conn->hnd.fd, (struct sockaddr*)&addr, &addrlen)) { + log_ppp_error("pptp: getpeername: %s\n", strerror(errno)); + return -1; + } dst_addr.sa_addr.pptp.sin_addr = addr.sin_addr; pptp_sock = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_PPTP); @@ -331,7 +391,11 @@ static int pptp_out_call_rqst(struct pptp_conn_t *conn) return -1; } addrlen = sizeof(src_addr); - getsockname(pptp_sock, (struct sockaddr*)&src_addr, &addrlen); + if (getsockname(pptp_sock, (struct sockaddr*)&src_addr, &addrlen)) { + log_ppp_error("pptp: getsockname: %s\n", strerror(errno)); + close(pptp_sock); + return -1; + } if (connect(pptp_sock, (struct sockaddr*)&dst_addr, sizeof(dst_addr))) { log_ppp_error("failed to connect PPTP socket (%s)\n", strerror(errno)); @@ -339,11 +403,13 @@ static int pptp_out_call_rqst(struct pptp_conn_t *conn) return -1; } - if (send_pptp_out_call_rply(conn, msg, src_addr.sa_addr.pptp.call_id, PPTP_CALL_RES_OK, 0)) + if (send_pptp_out_call_rply(conn, msg, src_addr.sa_addr.pptp.call_id, PPTP_CALL_RES_OK, 0)) { + close(pptp_sock); return -1; + } conn->call_id = src_addr.sa_addr.pptp.call_id; - conn->peer_call_id = msg->call_id; + conn->peer_call_id = ntohs(msg->call_id); conn->ppp.fd = pptp_sock; conn->ppp.ses.chan_name = _strdup(inet_ntoa(dst_addr.sa_addr.pptp.sin_addr)); @@ -356,8 +422,7 @@ static int pptp_out_call_rqst(struct pptp_conn_t *conn) return -1; } conn->state = STATE_PPP; - __sync_sub_and_fetch(&stat_starting, 1); - __sync_add_and_fetch(&stat_active, 1); + pptp_stat_move(&serv.stat.starting, &serv.stat.active); if (conn->timeout_timer.tpd) triton_timer_del(&conn->timeout_timer); @@ -397,7 +462,7 @@ static int pptp_call_clear_rqst(struct pptp_conn_t *conn) triton_timer_del(&conn->echo_timer); if (conn->state == STATE_PPP) { - __sync_sub_and_fetch(&stat_active, 1); + pptp_stat_dec(&serv.stat.active); conn->state = STATE_CLOSE; ap_session_terminate(&conn->ppp.ses, TERM_USER_REQUEST, 1); } @@ -443,7 +508,7 @@ static void pptp_send_echo(struct triton_timer_t *t) .header = PPTP_HEADER_CTRL(PPTP_ECHO_RQST), }; - if (++conn->echo_sent == conf_echo_failure) { + if (conf_echo_failure && ++conn->echo_sent >= conf_echo_failure) { log_ppp_warn("pptp: no echo reply\n"); disconnect(conn); return; @@ -514,6 +579,10 @@ static int pptp_read(struct triton_md_handler_t *h) log_ppp_error("pptp: invalid magic\n"); goto drop; } + if (ntohs(hdr->length) < sizeof(*hdr)) { + log_ppp_error("pptp: message is too short\n"); + goto drop; + } if (ntohs(hdr->length) >= PPTP_CTRL_SIZE_MAX) { log_ppp_error("pptp: message is too long\n"); goto drop; @@ -578,7 +647,7 @@ static void pptp_close(struct triton_context_t *ctx) { struct pptp_conn_t *conn = container_of(ctx, typeof(*conn), ctx); if (conn->state == STATE_PPP) { - __sync_sub_and_fetch(&stat_active, 1); + pptp_stat_dec(&serv.stat.active); conn->state = STATE_CLOSE; ap_session_terminate(&conn->ppp.ses, TERM_ADMIN_RESET, 1); if (send_pptp_call_disconnect_notify(conn, 3)) { @@ -609,7 +678,7 @@ static void ppp_finished(struct ap_session *ses) if (conn->state != STATE_CLOSE) { log_ppp_debug("pptp: ppp finished\n"); conn->state = STATE_CLOSE; - __sync_sub_and_fetch(&stat_active, 1); + pptp_stat_dec(&serv.stat.active); if (send_pptp_call_disconnect_notify(conn, 3)) triton_context_call(&conn->ctx, (void (*)(void*))disconnect, conn); @@ -626,20 +695,15 @@ static void ppp_finished(struct ap_session *ses) //================================== -struct pptp_serv_t -{ - struct triton_context_t ctx; - struct triton_md_handler_t hnd; -}; - static int pptp_connect(struct triton_md_handler_t *h) { - struct sockaddr_in addr; - socklen_t size = sizeof(addr); + struct sockaddr_in addr, laddr; + socklen_t size; int sock; struct pptp_conn_t *conn; while(1) { + size = sizeof(addr); sock = accept(h->fd, (struct sockaddr *)&addr, &size); if (sock < 0) { if (errno == EAGAIN) @@ -653,12 +717,12 @@ static int pptp_connect(struct triton_md_handler_t *h) continue; } - if (conf_max_starting && ap_session_stat.starting >= conf_max_starting) { + if (conf_max_starting && ap_session_stat_starting() >= conf_max_starting) { close(sock); continue; } - if (conf_max_sessions && ap_session_stat.active + ap_session_stat.starting >= conf_max_sessions) { + if (conf_max_sessions && ap_session_stat_active() + ap_session_stat_starting() >= conf_max_sessions) { close(sock); continue; } @@ -676,6 +740,13 @@ static int pptp_connect(struct triton_md_handler_t *h) continue; } + size = sizeof(laddr); + if (getsockname(sock, (struct sockaddr *)&laddr, &size)) { + log_error("pptp: getsockname: %s, closing connection...\n", strerror(errno)); + close(sock); + continue; + } + if (fcntl(sock, F_SETFL, O_NONBLOCK)) { log_error("pptp: failed to set nonblocking mode: %s, closing connection...\n", strerror(errno)); close(sock); @@ -708,8 +779,7 @@ static int pptp_connect(struct triton_md_handler_t *h) conn->ctrl.calling_station_id = _malloc(17); conn->ctrl.called_station_id = _malloc(17); u_inet_ntoa(addr.sin_addr.s_addr, conn->ctrl.calling_station_id); - getsockname(sock, &addr, &size); - u_inet_ntoa(addr.sin_addr.s_addr, conn->ctrl.called_station_id); + u_inet_ntoa(laddr.sin_addr.s_addr, conn->ctrl.called_station_id); ppp_init(&conn->ppp); conn->ppp.ses.ctrl = &conn->ctrl; @@ -733,7 +803,7 @@ static int pptp_connect(struct triton_md_handler_t *h) triton_event_fire(EV_CTRL_STARTING, &conn->ppp.ses); - __sync_add_and_fetch(&stat_starting, 1); + pptp_stat_inc(&serv.stat.starting); } return 0; } @@ -744,28 +814,19 @@ static void pptp_serv_close(struct triton_context_t *ctx) triton_context_unregister(ctx); } -static struct pptp_serv_t serv= -{ - .hnd.read = pptp_connect, - .ctx.close = pptp_serv_close, - .ctx.before_switch = pptp_ctx_switch, -}; - static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt, void *client) { + struct pptp_stat_t stat; + + pptp_stat_get(&stat); + cli_send(client, "pptp:\r\n"); - cli_sendv(client," starting: %u\r\n", stat_starting); - cli_sendv(client," active: %u\r\n", stat_active); + cli_sendv(client," starting: %u\r\n", stat.starting); + cli_sendv(client," active: %u\r\n", stat.active); return CLI_CMD_OK; } -void __export pptp_get_stat(unsigned int **starting, unsigned int **active) -{ - *starting = &stat_starting; - *active = &stat_active; -} - static void load_config(void) { char *opt; @@ -834,7 +895,7 @@ static void pptp_init(void) { struct sockaddr_in addr; char *opt; - int fd; + int fd, f = 1; fd = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_PPTP); if (fd >= 0) @@ -850,12 +911,17 @@ static void pptp_init(void) fcntl(serv.hnd.fd, F_SETFD, fcntl(serv.hnd.fd, F_GETFD) | FD_CLOEXEC); + memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; opt = conf_get_opt("pptp", "bind"); - if (opt) - addr.sin_addr.s_addr = inet_addr(opt); - else + if (opt) { + if (!inet_aton(opt, &addr.sin_addr)) { + log_emerg("pptp: failed to parse bind address '%s'\n", opt); + close(serv.hnd.fd); + return; + } + } else addr.sin_addr.s_addr = htonl(INADDR_ANY); opt = conf_get_opt("pptp", "port"); @@ -864,7 +930,7 @@ static void pptp_init(void) else addr.sin_port = htons(PPTP_PORT); - setsockopt(serv.hnd.fd, SOL_SOCKET, SO_REUSEADDR, &serv.hnd.fd, 4); + setsockopt(serv.hnd.fd, SOL_SOCKET, SO_REUSEADDR, &f, sizeof(f)); if (bind (serv.hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) { log_emerg("pptp: failed to bind socket: %s\n", strerror(errno)); close(serv.hnd.fd); diff --git a/accel-pppd/ctrl/pptp/pptp.h b/accel-pppd/ctrl/pptp/pptp.h new file mode 100644 index 00000000..29f24ca2 --- /dev/null +++ b/accel-pppd/ctrl/pptp/pptp.h @@ -0,0 +1,7 @@ +#ifndef __PPTP_H +#define __PPTP_H + +unsigned int pptp_stat_starting(void); +unsigned int pptp_stat_active(void); + +#endif diff --git a/accel-pppd/ctrl/pptp/pptp_prot.h b/accel-pppd/ctrl/pptp/pptp_prot.h index ee8cb341..3726334a 100644 --- a/accel-pppd/ctrl/pptp/pptp_prot.h +++ b/accel-pppd/ctrl/pptp/pptp_prot.h @@ -281,7 +281,6 @@ struct pptp_set_link_info /* for control message type 15 */ (type==PPTP_WAN_ERR_NTFY )?sizeof(struct pptp_wan_err_ntfy): \ (type==PPTP_SET_LINK_INFO )?sizeof(struct pptp_set_link_info): \ 0) -#define max(a,b) (((a)>(b))?(a):(b)) #define PPTP_CTRL_SIZE_MAX ( \ max(sizeof(struct pptp_start_ctrl_conn), \ max(sizeof(struct pptp_echo_rqst), \ diff --git a/accel-pppd/ctrl/sstp/CMakeLists.txt b/accel-pppd/ctrl/sstp/CMakeLists.txt index 3cb1799c..3a79526f 100644 --- a/accel-pppd/ctrl/sstp/CMakeLists.txt +++ b/accel-pppd/ctrl/sstp/CMakeLists.txt @@ -2,6 +2,6 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) ADD_LIBRARY(sstp SHARED sstp.c) -TARGET_LINK_LIBRARIES(sstp util) +TARGET_LINK_LIBRARIES(sstp util ${crypto_lib}) INSTALL(TARGETS sstp LIBRARY DESTINATION lib${LIB_SUFFIX}/accel-ppp) diff --git a/accel-pppd/ctrl/sstp/if_ppposeq.h b/accel-pppd/ctrl/sstp/if_ppposeq.h new file mode 120000 index 00000000..f525ccdb --- /dev/null +++ b/accel-pppd/ctrl/sstp/if_ppposeq.h @@ -0,0 +1 @@ +../../../drivers/ppposeq/ppposeq.h
\ No newline at end of file diff --git a/accel-pppd/ctrl/sstp/sstp.c b/accel-pppd/ctrl/sstp/sstp.c index 2e2c4d3b..0920f5af 100644 --- a/accel-pppd/ctrl/sstp/sstp.c +++ b/accel-pppd/ctrl/sstp/sstp.c @@ -4,6 +4,7 @@ #include <stdarg.h> #include <errno.h> #include <string.h> +#include <inttypes.h> #include <fcntl.h> #include <time.h> #include <termios.h> @@ -19,10 +20,17 @@ #include <sys/stat.h> #include "linux_ppp.h" -#ifdef CRYPTO_OPENSSL +/* + * Suppress OpenSSL 3.0 deprecation warnings for the DH API: it is deprecated + * but still functional, and still required for protocol compatibility. The + * project sets this for every target (see the top level CMakeLists.txt); it + * is repeated here because it only takes effect if it is defined before the + * first OpenSSL header is pulled in. + */ +#define OPENSSL_API_COMPAT 0x10100000L #include <openssl/ssl.h> -#include <openssl/err.h> -#endif +#include <openssl/dh.h> +#include <openssl/err.h> #include "triton.h" #include "events.h" @@ -38,16 +46,10 @@ #include "memdebug.h" #include "proxy_prot.h" +#include "sstp.h" #include "sstp_prot.h" +#include "if_ppposeq.h" -#ifndef min -#define min(x,y) ((x) < (y) ? (x) : (y)) -#endif -#ifndef max -#define max(x,y) ((x) > (y) ? (x) : (y)) -#endif - -#define PPP_SYNC 0 /* buggy yet */ #define PPP_BUF_SIZE 8192 #define PPP_BUF_IOVEC 256 #define PPP_F_ESCAPE 1 @@ -71,6 +73,20 @@ enum { STATE_FINISHED, }; +enum { + HTTP_ERR_ALLOW = -1, + HTTP_ERR_DENY = 0, + HTTP_ERR_REDIRECT = 1, + HTTP_ERR_REDIRECT_APPEND = 2, +}; + +/* seqpacket needs the ppposeq module, async is the legacy pty path */ +enum { + PPP_MODE_AUTO = -1, + PPP_MODE_ASYNC = 0, + PPP_MODE_SEQPACKET = 1, +}; + struct sockaddr_t { socklen_t len; union { @@ -102,9 +118,7 @@ struct buffer_t { struct sstp_stream_t { union { int fd; -#ifdef CRYPTO_OPENSSL SSL *ssl; -#endif }; ssize_t (*read)(struct sstp_stream_t *stream, void *buf, size_t count); ssize_t (*recv)(struct sstp_stream_t *stream, void *buf, size_t count, int flags); @@ -139,6 +153,7 @@ struct sstp_conn_t { int ppp_state; int ppp_flags; + int ppp_mode; struct buffer_t *ppp_in; struct list_head ppp_queue; @@ -147,21 +162,23 @@ struct sstp_conn_t { struct ap_ctrl ctrl; }; -static struct sstp_serv_t { +struct sstp_serv_t { struct triton_context_t ctx; struct triton_md_handler_t hnd; struct sockaddr_t addr; -#ifdef CRYPTO_OPENSSL SSL_CTX *ssl_ctx; -#endif -} serv; + struct sstp_stat_t stat; +}; + +static struct sstp_serv_t serv; static int conf_timeout = SSTP_NEGOTIOATION_TIMEOUT; static int conf_hello_interval = SSTP_HELLO_TIMEOUT; static int conf_verbose = 0; static int conf_ppp_max_mtu = 1452; +static int conf_ppp_mode = PPP_MODE_AUTO; static const char *conf_ip_pool; static const char *conf_ipv6_pool; static const char *conf_dpv6_pool; @@ -176,14 +193,11 @@ static struct hash_t conf_hash_sha1 = { .len = 0 }; static struct hash_t conf_hash_sha256 = { .len = 0 }; //static int conf_bypass_auth = 0; static const char *conf_hostname = NULL; -static int conf_http_mode = -1; +static int conf_http_mode = HTTP_ERR_ALLOW; static const char *conf_http_url = NULL; static mempool_t conn_pool; -static unsigned int stat_starting; -static unsigned int stat_active; - static inline void sstp_queue(struct sstp_conn_t *conn, struct buffer_t *buf); static int sstp_send(struct sstp_conn_t *conn, struct buffer_t *buf); static inline void sstp_queue_deferred(struct sstp_conn_t *conn, struct buffer_t *buf); @@ -193,6 +207,38 @@ static void sstp_disconnect(struct sstp_conn_t *conn); static int sstp_handler(struct sstp_conn_t *conn, struct buffer_t *buf); static int http_handler(struct sstp_conn_t *conn, struct buffer_t *buf); +void __export sstp_stat_get(struct sstp_stat_t *stat) +{ + stat->starting = __atomic_load_n(&serv.stat.starting, __ATOMIC_RELAXED); + stat->active = __atomic_load_n(&serv.stat.active, __ATOMIC_RELAXED); +} + +unsigned int __export sstp_stat_starting(void) +{ + return __atomic_load_n(&serv.stat.starting, __ATOMIC_RELAXED); +} + +unsigned int __export sstp_stat_active(void) +{ + return __atomic_load_n(&serv.stat.active, __ATOMIC_RELAXED); +} + +static void sstp_stat_inc(unsigned int *stat) +{ + __atomic_add_fetch(stat, 1, __ATOMIC_RELAXED); +} + +static void sstp_stat_dec(unsigned int *stat) +{ + __atomic_sub_fetch(stat, 1, __ATOMIC_RELAXED); +} + +static void sstp_stat_move(unsigned int *from, unsigned int *to) +{ + sstp_stat_dec(from); + sstp_stat_inc(to); +} + /* * FCS lookup table as calculated by genfcstab. */ @@ -489,7 +535,6 @@ static struct sstp_stream_t *stream_init(int fd) /* ssl stream */ -#ifdef CRYPTO_OPENSSL static ssize_t ssl_stream_read(struct sstp_stream_t *stream, void *buf, size_t count) { int ret, err; @@ -585,7 +630,6 @@ error: ssl_stream_free(stream); return NULL; } -#endif /* proxy */ @@ -830,7 +874,7 @@ static char *http_getvalue(char *line, const char *name, int len) return sep ? line : NULL; } -static int http_send_response(struct sstp_conn_t *conn, char *proto, char *status, char *headers) +static int http_send_response(struct sstp_conn_t *conn, char *proto, char *status, char *headers, u_int64_t length) { char datetime[sizeof("aaa, dd bbb yyyy HH:MM:SS GMT")]; char linebuf[1024], *line; @@ -843,7 +887,12 @@ static int http_send_response(struct sstp_conn_t *conn, char *proto, char *statu /* "Server: %s\r\n" */ "Date: %s\r\n" "%s" - "\r\n", proto, status, /* "accel-ppp",*/ datetime, headers ? : ""); + "Content-Length: %" PRIu64 "\r\n" + "Connection: %s\r\n" + "\r\n", + proto, status, /* "accel-ppp",*/ datetime, + headers ? : "", + length, length ? "keep-alive" : "close"); if (!buf) { log_error("sstp: no memory\n"); return -1; @@ -879,18 +928,18 @@ static int http_recv_request(struct sstp_conn_t *conn, uint8_t *data, int len) log_ppp_info2("recv [HTTP <%s>]\n", line); if (vstrsep(line, " ", &method, &request, &proto) < 3) { - if (conf_http_mode) - http_send_response(conn, "HTTP/1.1", "400 Bad Request", NULL); + if (conf_http_mode != HTTP_ERR_DENY) + http_send_response(conn, "HTTP/1.1", "400 Bad Request", NULL, 0); return -1; } if (strncasecmp(proto, "HTTP/1", sizeof("HTTP/1") - 1) != 0) { - if (conf_http_mode) - http_send_response(conn, "HTTP/1.1", "400 Bad Request", NULL); + if (conf_http_mode != HTTP_ERR_DENY) + http_send_response(conn, "HTTP/1.1", "400 Bad Request", NULL, 0); return -1; } if (strcasecmp(method, SSTP_HTTP_METHOD) != 0 && strcasecmp(method, "GET") != 0) { - if (conf_http_mode) - http_send_response(conn, proto, "501 Not Implemented", NULL); + if (conf_http_mode != HTTP_ERR_DENY) + http_send_response(conn, proto, "501 Not Implemented", NULL, 0); return -1; } @@ -911,25 +960,24 @@ static int http_recv_request(struct sstp_conn_t *conn, uint8_t *data, int len) } if (host_error) { - if (conf_http_mode) - http_send_response(conn, proto, "404 Not Found", NULL); + if (conf_http_mode != HTTP_ERR_DENY) + http_send_response(conn, proto, "404 Not Found", NULL, 0); return -1; } if (strcasecmp(method, SSTP_HTTP_METHOD) != 0 || strcasecmp(request, SSTP_HTTP_URI) != 0) { if (conf_http_mode > 0) { if (_asprintf(&line, "Location: %s%s\r\n", - conf_http_url, (conf_http_mode == 2) ? request : "") < 0) + conf_http_url, (conf_http_mode == HTTP_ERR_REDIRECT_APPEND) ? request : "") < 0) return -1; - http_send_response(conn, proto, "301 Moved Permanently", line); + http_send_response(conn, proto, "301 Moved Permanently", line, 0); _free(line); - } else if (conf_http_mode < 0) - http_send_response(conn, proto, "404 Not Found", NULL); + } else if (conf_http_mode == HTTP_ERR_ALLOW) + http_send_response(conn, proto, "404 Not Found", NULL, 0); return -1; } - return http_send_response(conn, proto, "200 OK", - "Content-Length: 18446744073709551615\r\n"); + return http_send_response(conn, proto, "200 OK", NULL, -1); } static int http_handler(struct sstp_conn_t *conn, struct buffer_t *buf) @@ -1006,24 +1054,12 @@ static int ppp_allocate_pty(int *master, int *slave, int flags) goto error; } -#if PPP_SYNC - value = N_SYNC_PPP; -#else value = N_PPP; -#endif if (ioctl(sfd, TIOCSETD, &value) < 0) { log_ppp_error("sstp: ppp: set pty line discipline: %s\n", strerror(errno)); goto error; } -#if PPP_SYNC - value = N_HDLC; - if (ioctl(mfd, TIOCSETD, &value) < 0) { - log_ppp_error("sstp: ppp: set pty line discipline: %s\n", strerror(errno)); - goto error; - } -#endif - if ((value = fcntl(mfd, F_GETFL)) < 0 || fcntl(mfd, F_SETFL, value | flags) < 0 || (value = fcntl(sfd, F_GETFL)) < 0 || fcntl(sfd, F_SETFL, value | flags) < 0) { log_ppp_error("sstp: ppp: set pty status flags: %s\n", strerror(errno)); @@ -1040,6 +1076,59 @@ error: return -1; } +/* + * ppposeq channel: the socket is both the ppp endpoint we exchange frames + * on and the fd establish_ppp() gets the channel from, as pppox_ioctl + * answers PPPIOCGCHAN on it. One datagram is one frame, so no framing. + */ +static int ppp_allocate_seq(int *master, int *slave, int flags) +{ + struct sockaddr_ppposeq sa = { + .sa_family = AF_PPPOX, + .sa_protocol = PX_PROTO_OSEQ, + }; + int value, mfd, sfd; + + mfd = socket(AF_PPPOX, SOCK_SEQPACKET, PX_PROTO_OSEQ); + if (mfd < 0) { + log_ppp_error("sstp: ppp: create socket: %s\n", strerror(errno)); + return -1; + } + + if (connect(mfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + log_ppp_error("sstp: ppp: connect socket: %s\n", strerror(errno)); + goto error_mfd; + } + + sfd = dup(mfd); + if (sfd < 0) { + log_ppp_error("sstp: ppp: dup socket: %s\n", strerror(errno)); + goto error_mfd; + } + + if (flags & O_CLOEXEC) { + fcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC); + fcntl(sfd, F_SETFD, fcntl(sfd, F_GETFD) | FD_CLOEXEC); + flags &= ~O_CLOEXEC; + } + + /* status flags are inherited by shared file descriptors */ + if ((value = fcntl(mfd, F_GETFL)) < 0 || fcntl(mfd, F_SETFL, value | flags) < 0) { + log_ppp_error("sstp: ppp: set socket status flags: %s\n", strerror(errno)); + goto error; + } + + *master = mfd; + *slave = sfd; + return 0; + +error: + close(sfd); +error_mfd: + close(mfd); + return -1; +} + static void ppp_started(struct ap_session *ses) { struct ppp_t *ppp = container_of(ses, typeof(*ppp), ses); @@ -1073,18 +1162,16 @@ static void ppp_finished(struct ap_session *ses) } } -static int ppp_read(struct triton_md_handler_t *h) +static int ppp_read_pty(struct triton_md_handler_t *h) { struct sstp_conn_t *conn = container_of(h, typeof(*conn), ppp_hnd); struct buffer_t *buf; struct sstp_hdr *hdr; uint8_t pppbuf[PPP_BUF_SIZE], *src; int i, n; -#if !PPP_SYNC uint8_t byte; buf = conn->ppp_in; -#endif while (1) { n = read(h->fd, pppbuf, sizeof(pppbuf)); if (n < 0) { @@ -1109,29 +1196,7 @@ static int ppp_read(struct triton_md_handler_t *h) } src = pppbuf; -#if PPP_SYNC - while (n > 0) { - if (src[0] == PPP_ALLSTATIONS) - i = conn->ppp.mtu + 4 - (src[2] & 1); - else - i = conn->ppp.mtu + 2 - (src[0] & 1); - if (i > n) - i = n; - buf = alloc_buf(i + sizeof(*hdr)); - if (!buf) { - log_ppp_error("sstp: ppp: no memory\n"); - goto drop; - } - hdr = buf_put(buf, sizeof(*hdr)); - buf_put_data(buf, src, i); - INIT_SSTP_DATA_HDR(hdr, buf->len); - sstp_queue(conn, buf); - - n -= i; - src += i; - } -#else if (!buf) { alloc: conn->ppp_in = buf = alloc_buf(SSTP_MAX_PACKET_SIZE + PPP_FCSLEN); @@ -1167,6 +1232,11 @@ static int ppp_read(struct triton_md_handler_t *h) switch (byte) { case PPP_FLAG: if (buf->len <= PPP_FCSLEN || conn->ppp_flags) { + /* skip idle flag */ + if (buf->len == 0 && conn->ppp_flags == 0) + break; + if (conf_verbose) + log_ppp_info2("sstp: ppp: read: malformed packet\n"); buf_set_length(buf, 0); conn->ppp_flags = 0; break; @@ -1181,7 +1251,6 @@ static int ppp_read(struct triton_md_handler_t *h) break; } } -#endif } if (!list_empty(&conn->out_queue)) triton_md_enable_handler(&conn->hnd, MD_MODE_WRITE); @@ -1192,7 +1261,65 @@ drop: return 1; } -static int ppp_write(struct triton_md_handler_t *h) +static int ppp_read_seq(struct triton_md_handler_t *h) +{ + struct sstp_conn_t *conn = container_of(h, typeof(*conn), ppp_hnd); + struct buffer_t *buf; + struct sstp_hdr *hdr; + int n; + + buf = conn->ppp_in; + while (1) { + if (!buf) { + alloc: + conn->ppp_in = buf = alloc_buf(conn->ppp.mtu ? + conn->ppp.mtu + PPP_HDRLEN + sizeof(*hdr) : + SSTP_MAX_PACKET_SIZE); + if (!buf) { + log_ppp_error("sstp: ppp: no memory\n"); + goto drop; + } + buf_reserve(buf, sizeof(*hdr)); + } + + n = recv(h->fd, buf->tail, buf_tailroom(buf), MSG_TRUNC); + if (n < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) + break; + log_ppp_error("sstp: ppp: recv: %s\n", strerror(errno)); + goto drop; + } else if (n > buf_tailroom(buf)) { + if (conf_verbose) + log_ppp_info2("sstp: ppp: recv: too long packet\n"); + continue; + } + + switch (conn->sstp_state) { + case STATE_SERVER_CALL_CONNECTED_PENDING: + case STATE_SERVER_CALL_CONNECTED: + break; + default: + continue; + } + + buf_put(buf, n); + hdr = buf_push(buf, sizeof(*hdr)); + INIT_SSTP_DATA_HDR(hdr, buf->len); + sstp_queue(conn, buf); + goto alloc; + } + if (!list_empty(&conn->out_queue)) + triton_md_enable_handler(&conn->hnd, MD_MODE_WRITE); + return 0; + +drop: + sstp_disconnect(conn); + return 1; +} + +static int ppp_write_pty(struct triton_md_handler_t *h) { struct sstp_conn_t *conn = container_of(h, typeof(*conn), ppp_hnd); struct iovec iov[PPP_BUF_IOVEC]; @@ -1200,7 +1327,7 @@ static int ppp_write(struct triton_md_handler_t *h) ssize_t n; int i; - if (!list_empty(&conn->ppp_queue)) { + while (!list_empty(&conn->ppp_queue)) { i = n = 0; list_for_each_entry(buf, &conn->ppp_queue, entry) { if (i < PPP_BUF_IOVEC && n < PPP_BUF_SIZE) { @@ -1217,8 +1344,9 @@ static int ppp_write(struct triton_md_handler_t *h) goto again; if (errno == EAGAIN) goto defer; - if (conf_verbose && errno != EPIPE) - log_ppp_info2("sstp: ppp: write: %s\n", strerror(errno)); + if (errno == EPIPE) + goto drop; + log_ppp_error("sstp: ppp: write: %s\n", strerror(errno)); goto drop; } else if (n == 0) goto defer; @@ -1232,9 +1360,43 @@ static int ppp_write(struct triton_md_handler_t *h) list_del(&buf->entry); free_buf(buf); } while (n > 0); + } + triton_md_disable_handler(h, MD_MODE_WRITE); + return 0; - if (!list_empty(&conn->ppp_queue)) - goto defer; +defer: + triton_md_enable_handler(h, MD_MODE_WRITE); + return 0; + +drop: + triton_context_call(&conn->ctx, (triton_event_func)sstp_disconnect, conn); + return 1; +} + +static int ppp_write_seq(struct triton_md_handler_t *h) +{ + struct sstp_conn_t *conn = container_of(h, typeof(*conn), ppp_hnd); + struct buffer_t *buf; + ssize_t n; + + while (!list_empty(&conn->ppp_queue)) { + buf = list_first_entry(&conn->ppp_queue, typeof(*buf), entry); + again: + n = send(conn->ppp_hnd.fd, buf->head, buf->len, 0); + if (n < 0) { + if (errno == EINTR) + goto again; + if (errno == EAGAIN) + goto defer; + log_ppp_error("sstp: ppp: send: %s\n", strerror(errno)); + goto drop; + } else if (n < buf->len) { + log_ppp_error("sstp: ppp: send: too short packet\n"); + goto drop; + } + + list_del(&buf->entry); + free_buf(buf); } triton_md_disable_handler(h, MD_MODE_WRITE); return 0; @@ -1482,13 +1644,25 @@ static int sstp_recv_msg_call_connect_request(struct sstp_conn_t *conn, struct s return sstp_send_msg_call_connect_nak(conn); } - if (ppp_allocate_pty(&master, &slave, O_CLOEXEC | O_NONBLOCK) < 0) + switch (conn->ppp_mode) { + case PPP_MODE_ASYNC: + if (ppp_allocate_pty(&master, &slave, O_CLOEXEC | O_NONBLOCK) < 0) + return -1; + conn->ppp_hnd.read = ppp_read_pty; + conn->ppp_hnd.write = ppp_write_pty; + break; + case PPP_MODE_SEQPACKET: + if (ppp_allocate_seq(&master, &slave, O_CLOEXEC | O_NONBLOCK) < 0) + return -1; + conn->ppp_hnd.read = ppp_read_seq; + conn->ppp_hnd.write = ppp_write_seq; + break; + default: + log_ppp_error("sstp: invalid ppp-mode\n"); return -1; + } conn->ppp_hnd.fd = master; - conn->ppp_hnd.read = ppp_read; - conn->ppp_hnd.write = ppp_write; - triton_md_register_handler(&conn->ctx, &conn->ppp_hnd); triton_md_enable_handler(&conn->ppp_hnd, MD_MODE_READ); @@ -1500,8 +1674,7 @@ static int sstp_recv_msg_call_connect_request(struct sstp_conn_t *conn, struct s goto error; conn->sstp_state = STATE_SERVER_CALL_CONNECTED_PENDING; - __sync_sub_and_fetch(&stat_starting, 1); - __sync_add_and_fetch(&stat_active, 1); + sstp_stat_move(&serv.stat.starting, &serv.stat.active); triton_event_fire(EV_CTRL_STARTED, &conn->ppp.ses); conn->ppp_state = STATE_STARTING; @@ -1528,12 +1701,10 @@ static int sstp_recv_msg_call_connected(struct sstp_conn_t *conn, struct sstp_ct uint8_t hash; unsigned int len; struct npioctl np; -#ifdef CRYPTO_OPENSSL typeof(*msg) buf; uint8_t md[EVP_MAX_MD_SIZE], *ptr; const EVP_MD *evp; unsigned int mdlen; -#endif if (conf_verbose) log_ppp_info2("recv [SSTP SSTP_MSG_CALL_CONNECTED]\n"); @@ -1571,9 +1742,7 @@ static int sstp_recv_msg_call_connected(struct sstp_conn_t *conn, struct sstp_ct log_ppp_error("sstp: invalid SHA256 Cert Hash\n"); return sstp_abort(conn, 0); } -#ifdef CRYPTO_OPENSSL evp = EVP_sha256(); -#endif } else if (hash & CERT_HASH_PROTOCOL_SHA1) { len = SHA_DIGEST_LENGTH; if (conf_hash_sha1.len == len && @@ -1581,9 +1750,7 @@ static int sstp_recv_msg_call_connected(struct sstp_conn_t *conn, struct sstp_ct log_ppp_error("sstp: invalid SHA1 Cert Hash\n"); return sstp_abort(conn, 0); } -#ifdef CRYPTO_OPENSSL evp = EVP_sha1(); -#endif } else { log_ppp_error("sstp: invalid Hash Protocol 0x%02x\n", msg->attr.hash_protocol_bitmask); @@ -1608,7 +1775,6 @@ static int sstp_recv_msg_call_connected(struct sstp_conn_t *conn, struct sstp_ct return 0; } -#ifdef CRYPTO_OPENSSL ptr = mempcpy(md, SSTP_CMK_SEED, SSTP_CMK_SEED_SIZE); *ptr++ = len; *ptr++ = 0; @@ -1624,7 +1790,6 @@ static int sstp_recv_msg_call_connected(struct sstp_conn_t *conn, struct sstp_ct log_ppp_error("sstp: invalid Compound MAC\n"); return sstp_abort(conn, 0); } -#endif } if (conn->timeout_timer.tpd) @@ -1791,11 +1956,9 @@ static int sstp_recv_data_packet(struct sstp_conn_t *conn, struct sstp_hdr *hdr) { struct buffer_t *buf; int size; -#if !PPP_SYNC uint8_t *src, *dst, byte; uint16_t fcs; int n; -#endif switch (conn->sstp_state) { case STATE_SERVER_CALL_CONNECTED_PENDING: @@ -1809,16 +1972,21 @@ static int sstp_recv_data_packet(struct sstp_conn_t *conn, struct sstp_hdr *hdr) if (size == 0) return 0; -#if PPP_SYNC - buf = alloc_buf(size); - if (!buf) { - log_error("sstp: no memory\n"); - return -1; + if (conn->ppp_mode == PPP_MODE_SEQPACKET) { + /* one datagram is one frame, no framing needed */ + buf = alloc_buf(size); + if (!buf) { + log_error("sstp: no memory\n"); + return -1; + } + + buf_put_data(buf, hdr->data, size); + + return ppp_send(conn, buf); } - buf_put_data(buf, hdr->data, size); -#else - buf = alloc_buf(size*2 + 2 + PPP_FCSLEN); + /* payload and FCS octets may both double when escaped, plus 2 flags */ + buf = alloc_buf((size + PPP_FCSLEN) * 2 + 2); if (!buf) { log_error("sstp: no memory\n"); return -1; @@ -1847,7 +2015,6 @@ static int sstp_recv_data_packet(struct sstp_conn_t *conn, struct sstp_hdr *hdr) *dst++ = PPP_FLAG; buf_put(buf, dst - buf->tail); -#endif return ppp_send(conn, buf); } @@ -1911,8 +2078,8 @@ static int sstp_handler(struct sstp_conn_t *conn, struct buffer_t *buf) } n = ntohs(hdr->length); - if (n > SSTP_MAX_PACKET_SIZE) { - log_ppp_error("recv [SSTP too long packet]\n"); + if (n < sizeof(*hdr) || n > SSTP_MAX_PACKET_SIZE) { + log_ppp_error("recv [SSTP invalid packet length %d]\n", n); return -1; } else if (n > buf->len) break; @@ -2064,8 +2231,9 @@ static int sstp_write(struct triton_md_handler_t *h) continue; if (errno == EAGAIN) goto defer; - if (conf_verbose && errno != EPIPE) - log_ppp_info2("sstp: write: %s\n", strerror(errno)); + if (errno == EPIPE) + goto drop; + log_ppp_error("sstp: write: %s\n", strerror(errno)); goto drop; } else if (n == 0) goto defer; @@ -2099,6 +2267,31 @@ static int sstp_send(struct sstp_conn_t *conn, struct buffer_t *buf) return 0; } +static void sstp_flush(struct sstp_conn_t *conn) +{ + struct buffer_t *buf; + int n; + + while (!list_empty(&conn->out_queue)) { + buf = list_first_entry(&conn->out_queue, typeof(*buf), entry); + while (buf->len) { + n = conn->stream->write(conn->stream, buf->head, buf->len); + if (n < 0) { + if (errno == EINTR) + continue; + if (errno == EPIPE) + break; + log_ppp_error("sstp: write: %s\n", strerror(errno)); + break; + } else if (n == 0) + break; + buf_pull(buf, n); + } + list_del(&buf->entry); + free_buf(buf); + } +} + static void sstp_msg_echo(struct triton_timer_t *t) { struct sstp_conn_t *conn = container_of(t, typeof(*conn), hello_timer); @@ -2200,6 +2393,7 @@ static void sstp_disconnect(struct sstp_conn_t *conn) triton_timer_del(&conn->hello_timer); if (conn->hnd.tpd) { + sstp_flush(conn); triton_md_unregister_handler(&conn->hnd, 0); conn->stream->close(conn->stream); } @@ -2208,17 +2402,17 @@ static void sstp_disconnect(struct sstp_conn_t *conn) switch (conn->ppp_state) { case STATE_INIT: - __sync_sub_and_fetch(&stat_starting, 1); + sstp_stat_dec(&serv.stat.starting); break; case STATE_STARTING: case STATE_AUTHORIZED: case STATE_STARTED: conn->ppp_state = STATE_FINISHED; - __sync_sub_and_fetch(&stat_active, 1); + sstp_stat_dec(&serv.stat.active); ap_session_terminate(&conn->ppp.ses, TERM_LOST_CARRIER, 1); break; case STATE_FINISHED: - __sync_sub_and_fetch(&stat_active, 1); + sstp_stat_dec(&serv.stat.active); break; } triton_event_fire(EV_CTRL_FINISHED, &conn->ppp.ses); @@ -2254,11 +2448,9 @@ static void sstp_start(struct sstp_conn_t *conn) { log_debug("sstp: starting\n"); -#ifdef CRYPTO_OPENSSL if (serv.ssl_ctx) conn->stream = ssl_stream_init(conn->hnd.fd, serv.ssl_ctx); else -#endif conn->stream = stream_init(conn->hnd.fd); if (!conn->stream) { log_error("sstp: stream open error: %s\n", strerror(errno)); @@ -2299,12 +2491,12 @@ static int sstp_connect(struct triton_md_handler_t *h) continue; } - if (conf_max_starting && ap_session_stat.starting >= conf_max_starting) { + if (conf_max_starting && ap_session_stat_starting() >= conf_max_starting) { close(sock); continue; } - if (conf_max_sessions && ap_session_stat.active + ap_session_stat.starting >= conf_max_sessions) { + if (conf_max_sessions && ap_session_stat_active() + ap_session_stat_starting() >= conf_max_sessions) { close(sock); continue; } @@ -2371,6 +2563,7 @@ static int sstp_connect(struct triton_md_handler_t *h) conn->sstp_state = STATE_SERVER_CALL_DISCONNECTED; conn->ppp_state = STATE_INIT; + conn->ppp_mode = conf_ppp_mode; conn->handler = conf_proxyproto ? proxy_handler : http_handler; //conn->bypass_auth = conf_bypass_auth; @@ -2425,7 +2618,7 @@ static int sstp_connect(struct triton_md_handler_t *h) triton_event_fire(EV_CTRL_STARTING, &conn->ppp.ses); - __sync_add_and_fetch(&stat_starting, 1); + sstp_stat_inc(&serv.stat.starting); } return 0; @@ -2438,17 +2631,14 @@ static void sstp_serv_close(struct triton_context_t *ctx) triton_md_unregister_handler(&serv->hnd, 1); triton_context_unregister(ctx); -#ifdef CRYPTO_OPENSSL if (serv->ssl_ctx) SSL_CTX_free(serv->ssl_ctx); serv->ssl_ctx = NULL; -#endif if (serv->addr.u.sa.sa_family == AF_UNIX && serv->addr.u.sun.sun_path[0]) unlink(serv->addr.u.sun.sun_path); } -#ifdef CRYPTO_OPENSSL #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME static int ssl_servername(SSL *ssl, int *al, void *arg) { @@ -2482,6 +2672,13 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) #endif #endif +static void ssl_set_cert_hashes(const X509 *cert) { + if (conf_hash_protocol & CERT_HASH_PROTOCOL_SHA1) + X509_digest(cert, EVP_sha1(), conf_hash_sha1.hash, &conf_hash_sha1.len); + if (conf_hash_protocol & CERT_HASH_PROTOCOL_SHA256) + X509_digest(cert, EVP_sha256(), conf_hash_sha256.hash, &conf_hash_sha256.len); +} + static void ssl_load_config(struct sstp_serv_t *serv, const char *servername) { SSL_CTX *old_ctx, *ssl_ctx = NULL; @@ -2489,26 +2686,6 @@ static void ssl_load_config(struct sstp_serv_t *serv, const char *servername) BIO *in = NULL; char *opt; - opt = conf_get_opt("sstp", "ssl-pemfile"); - if (opt) { - in = BIO_new(BIO_s_file()); - if (!in) { - log_error("sstp: %s error: %s\n", "ssl-pemfile", ERR_error_string(ERR_get_error(), NULL)); - goto error; - } - - if (BIO_read_filename(in, opt) <= 0) { - log_error("sstp: %s error: %s\n", "ssl-pemfile", ERR_error_string(ERR_get_error(), NULL)); - goto error; - } - - cert = PEM_read_bio_X509(in, NULL, NULL, NULL); - if (!cert) { - log_error("sstp: %s error: %s\n", "ssl-pemfile", ERR_error_string(ERR_get_error(), NULL)); - goto error; - } - } - opt = conf_get_opt("sstp", "accept"); if (opt && strhas(opt, "ssl", ',')) { legacy_ssl: @@ -2601,6 +2778,8 @@ static void ssl_load_config(struct sstp_serv_t *serv, const char *servername) #else DH *dh; + in = BIO_new(BIO_s_file()); + if (BIO_read_filename(in, opt) <= 0) { log_error("sstp: %s error: %s\n", "ssl-dhparam", ERR_error_string(ERR_get_error(), NULL)); goto error; @@ -2612,6 +2791,10 @@ static void ssl_load_config(struct sstp_serv_t *serv, const char *servername) goto error; } + if (!BIO_free(in)) + abort(); + in = NULL; + SSL_CTX_set_tmp_dh(ssl_ctx, dh); DH_free(dh); #endif @@ -2664,12 +2847,21 @@ static void ssl_load_config(struct sstp_serv_t *serv, const char *servername) if (opt && atoi(opt)) SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - if (cert && SSL_CTX_use_certificate(ssl_ctx, cert) != 1) { - log_error("sstp: %s error: %s\n", "ssl-pemfile", ERR_error_string(ERR_get_error(), NULL)); - goto error; + opt = conf_get_opt("sstp", "ssl-pemfile"); + if (opt) { + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, opt) != 1) { + log_error("sstp: %s error: %s\n", "ssl-pemfile", ERR_error_string(ERR_get_error(), NULL)); + goto error; + } + // cert is a reference. Do not free it. + X509 *cert_ref = SSL_CTX_get0_certificate(ssl_ctx); + if (!cert_ref) { + log_error("sstp: %s error: %s\n", "ssl-pemfile", ERR_error_string(ERR_get_error(), NULL)); + goto error; + } + ssl_set_cert_hashes(cert_ref); } - - opt = conf_get_opt("sstp", "ssl-keyfile") ? : conf_get_opt("sstp", "ssl-pemfile"); + opt = conf_get_opt("sstp", "ssl-keyfile") ? : opt; if ((opt && SSL_CTX_use_PrivateKey_file(ssl_ctx, opt, SSL_FILETYPE_PEM) != 1) || SSL_CTX_check_private_key(ssl_ctx) != 1) { log_error("sstp: %s error: %s\n", "ssl-keyfile", ERR_error_string(ERR_get_error(), NULL)); @@ -2697,13 +2889,30 @@ static void ssl_load_config(struct sstp_serv_t *serv, const char *servername) opt = conf_get_opt("sstp", "ssl"); if (opt && atoi(opt) > 0) goto legacy_ssl; - } - if (cert) { - if (conf_hash_protocol & CERT_HASH_PROTOCOL_SHA1) - X509_digest(cert, EVP_sha1(), conf_hash_sha1.hash, &conf_hash_sha1.len); - if (conf_hash_protocol & CERT_HASH_PROTOCOL_SHA256) - X509_digest(cert, EVP_sha256(), conf_hash_sha256.hash, &conf_hash_sha256.len); + opt = conf_get_opt("sstp", "ssl-pemfile"); + if (opt) { + in = BIO_new(BIO_s_file()); + if (!in) { + log_error("sstp: %s error: %s\n", "ssl-pemfile", ERR_error_string(ERR_get_error(), NULL)); + goto error; + } + + if (BIO_read_filename(in, opt) <= 0) { + log_error("sstp: %s error: %s\n", "ssl-pemfile", ERR_error_string(ERR_get_error(), NULL)); + goto error; + } + + cert = PEM_read_bio_X509(in, NULL, NULL, NULL); + if (!cert) { + log_error("sstp: %s error: %s\n", "ssl-pemfile", ERR_error_string(ERR_get_error(), NULL)); + goto error; + } + if (!BIO_free(in)) + abort(); + in = NULL; + ssl_set_cert_hashes(cert); + } } old_ctx = serv->ssl_ctx; @@ -2715,10 +2924,9 @@ error: SSL_CTX_free(ssl_ctx); if (cert) X509_free(cert); - if (in) - BIO_free(in); + if (in && !BIO_free(in)) + abort(); } -#endif static void ev_mppe_keys(struct ev_mppe_keys_t *ev) { @@ -2752,19 +2960,17 @@ static void ev_ses_authorized(struct ap_session *ses) static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt, void *client) { + struct sstp_stat_t stat; + + sstp_stat_get(&stat); + cli_send(client, "sstp:\r\n"); - cli_sendv(client," starting: %u\r\n", stat_starting); - cli_sendv(client," active: %u\r\n", stat_active); + cli_sendv(client," starting: %u\r\n", stat.starting); + cli_sendv(client," active: %u\r\n", stat.active); return CLI_CMD_OK; } -void __export sstp_get_stat(unsigned int **starting, unsigned int **active) -{ - *starting = &stat_starting; - *active = &stat_active; -} - static void load_config(void) { int ipmode; @@ -2779,15 +2985,15 @@ static void load_config(void) opt = conf_get_opt("sstp", "http-error"); if (opt) { if (strcmp(opt, "deny") == 0) - conf_http_mode = 0; + conf_http_mode = HTTP_ERR_DENY; else if (strcmp(opt, "allow") == 0) - conf_http_mode = -1; + conf_http_mode = HTTP_ERR_ALLOW; else if (strstr(opt, "://") != NULL) { conf_http_url = opt; opt = strstr(opt, "://") + 3; while (*opt == '/') opt++; - conf_http_mode = strchr(opt, '/') ? 1 : 2; + conf_http_mode = strchr(opt, '/') ? HTTP_ERR_REDIRECT : HTTP_ERR_REDIRECT_APPEND; } } @@ -2803,15 +3009,15 @@ static void load_config(void) opt = conf_get_opt("sstp", "accept"); conf_proxyproto = opt && strhas(opt, "proxy", ','); -#ifdef CRYPTO_OPENSSL ssl_load_config(&serv, conf_hostname); - opt = serv.ssl_ctx ? "enabled" : "disabled"; -#else - opt = "not available"; -#endif + if (conf_verbose) { - log_info2("sstp: SSL/TLS support %s, PROXY support %s\n", - opt, conf_proxyproto ? "enabled" : "disabled"); + log_info2("sstp: SSL/TLS %s, PROXY %s, PPP mode %s\n", + serv.ssl_ctx ? "enabled" : "disabled", + conf_proxyproto ? "enabled" : "disabled", + conf_ppp_mode == PPP_MODE_AUTO ? "AUTO" : + conf_ppp_mode == PPP_MODE_ASYNC ? "ASYNC" : + conf_ppp_mode == PPP_MODE_SEQPACKET ? "SEQPACKET" : "unknown"); } opt = conf_get_opt("sstp", "cert-hash-sha1"); @@ -2885,7 +3091,7 @@ static void sstp_init(void) struct sockaddr_t *addr = &serv.addr; struct linger linger; struct stat st; - int port, value; + int port, value, fd; char *opt; opt = conf_get_opt("sstp", "port"); @@ -2963,6 +3169,32 @@ static void sstp_init(void) goto error_unlink; } + opt = conf_get_opt("sstp", "ppp-mode"); + if (opt) { + if (!strcmp(opt, "auto")) + conf_ppp_mode = PPP_MODE_AUTO; + else if (!strcmp(opt, "seqpacket")) + conf_ppp_mode = PPP_MODE_SEQPACKET; + else if (!strcmp(opt, "async")) + conf_ppp_mode = PPP_MODE_ASYNC; + } + if (conf_ppp_mode != PPP_MODE_ASYNC) { + fd = socket(AF_PPPOX, SOCK_SEQPACKET, PX_PROTO_OSEQ); + if (fd >= 0) + close(fd); + else if (access("/sys/module/ppposeq", F_OK) && system("modprobe -q ppposeq")) + log_warn("failed to load ppposeq kernel module\n"); + } + if (conf_ppp_mode == PPP_MODE_AUTO) { + fd = socket(AF_PPPOX, SOCK_SEQPACKET, PX_PROTO_OSEQ); + if (fd >= 0) { + conf_ppp_mode = PPP_MODE_SEQPACKET; + close(fd); + } else { + conf_ppp_mode = PPP_MODE_ASYNC; + } + } + conn_pool = mempool_create(sizeof(struct sstp_conn_t)); load_config(); diff --git a/accel-pppd/ctrl/sstp/sstp.h b/accel-pppd/ctrl/sstp/sstp.h new file mode 100644 index 00000000..eff8e053 --- /dev/null +++ b/accel-pppd/ctrl/sstp/sstp.h @@ -0,0 +1,14 @@ +#ifndef __SSTP_H +#define __SSTP_H + +struct sstp_stat_t +{ + unsigned int starting; + unsigned int active; +}; + +void sstp_stat_get(struct sstp_stat_t *stat); +unsigned int sstp_stat_starting(void); +unsigned int sstp_stat_active(void); + +#endif |
