summaryrefslogtreecommitdiff
path: root/accel-pppd/ctrl
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd/ctrl')
-rw-r--r--accel-pppd/ctrl/ipoe/arp.c6
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4.c37
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4_options.c84
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.c284
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.h16
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe_netlink.c126
-rw-r--r--accel-pppd/ctrl/l2tp/CMakeLists.txt2
-rw-r--r--accel-pppd/ctrl/l2tp/l2tp.c291
-rw-r--r--accel-pppd/ctrl/l2tp/l2tp.h3
-rw-r--r--accel-pppd/ctrl/l2tp/packet.c96
-rw-r--r--accel-pppd/ctrl/l2tp/packet_test.c491
-rw-r--r--accel-pppd/ctrl/pppoe/CMakeLists.txt7
-rw-r--r--accel-pppd/ctrl/pppoe/cli.c22
-rw-r--r--accel-pppd/ctrl/pppoe/disc.c7
-rw-r--r--accel-pppd/ctrl/pppoe/dpado.c36
-rw-r--r--accel-pppd/ctrl/pppoe/mac_filter.c16
-rw-r--r--accel-pppd/ctrl/pppoe/pppoe.c204
-rw-r--r--accel-pppd/ctrl/pppoe/pppoe.h33
-rw-r--r--accel-pppd/ctrl/pppoe/tr101.c29
-rw-r--r--accel-pppd/ctrl/pptp/pptp.c168
-rw-r--r--accel-pppd/ctrl/pptp/pptp.h7
-rw-r--r--accel-pppd/ctrl/pptp/pptp_prot.h1
-rw-r--r--accel-pppd/ctrl/sstp/CMakeLists.txt2
l---------accel-pppd/ctrl/sstp/if_ppposeq.h1
-rw-r--r--accel-pppd/ctrl/sstp/sstp.c584
-rw-r--r--accel-pppd/ctrl/sstp/sstp.h14
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