summaryrefslogtreecommitdiff
path: root/accel-pppd
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd')
-rw-r--r--accel-pppd/CMakeLists.txt36
-rw-r--r--accel-pppd/accel-ppp.conf119
-rw-r--r--accel-pppd/accel-ppp.conf.5529
-rw-r--r--accel-pppd/auth/CMakeLists.txt5
-rw-r--r--accel-pppd/auth/auth_chap_md5.c10
-rw-r--r--accel-pppd/auth/auth_mschap_v1.c4
-rw-r--r--accel-pppd/auth/auth_mschap_v2.c4
-rw-r--r--accel-pppd/backup/backup_file.c11
-rw-r--r--accel-pppd/cli/cli.c64
-rw-r--r--accel-pppd/cli/cli.h8
-rw-r--r--accel-pppd/cli/cli_p.h84
-rw-r--r--accel-pppd/cli/show_sessions.c20
-rw-r--r--accel-pppd/cli/std_cmd.c105
-rw-r--r--accel-pppd/cli/tcp.c52
-rw-r--r--accel-pppd/cli/telnet.c52
-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
-rw-r--r--accel-pppd/extra/CMakeLists.txt4
-rw-r--r--accel-pppd/extra/bitpool.h88
-rw-r--r--accel-pppd/extra/bitpool_test.c256
-rw-r--r--accel-pppd/extra/chap-secrets.c29
-rw-r--r--accel-pppd/extra/ippool.c1115
-rw-r--r--accel-pppd/extra/ipv6pool.c1108
-rw-r--r--accel-pppd/extra/logwtmp.c7
-rw-r--r--accel-pppd/extra/metrics.c1356
-rw-r--r--accel-pppd/extra/net-snmp/statCore.c9
-rw-r--r--accel-pppd/extra/net-snmp/statIPOE.c110
-rw-r--r--accel-pppd/extra/net-snmp/statL2TP.c110
-rw-r--r--accel-pppd/extra/net-snmp/statPPP.c145
-rw-r--r--accel-pppd/extra/net-snmp/statPPPOE.c106
-rw-r--r--accel-pppd/extra/net-snmp/statPPTP.c111
-rw-r--r--accel-pppd/extra/net-snmp/statSSTP.c110
-rw-r--r--accel-pppd/extra/pppd_compat.c8
-rw-r--r--accel-pppd/ifcfg.c6
-rw-r--r--accel-pppd/include/ap_session.h12
l---------accel-pppd/include/crypto.h1
-rw-r--r--accel-pppd/include/if_pppox.h234
-rw-r--r--accel-pppd/include/ipv6_dns.h48
-rw-r--r--accel-pppd/ipdb.c14
-rw-r--r--accel-pppd/ipv6/dhcpv6.c77
-rw-r--r--accel-pppd/ipv6/dhcpv6_packet.c161
-rw-r--r--accel-pppd/ipv6/ipv6_dns_test.c157
-rw-r--r--accel-pppd/ipv6/nd.c20
-rw-r--r--accel-pppd/libnetlink/genl.c8
-rw-r--r--accel-pppd/libnetlink/iputils.c150
-rw-r--r--accel-pppd/libnetlink/iputils.h9
-rw-r--r--accel-pppd/log.c5
-rw-r--r--accel-pppd/logs/CMakeLists.txt10
-rw-r--r--accel-pppd/logs/log_file.c12
-rw-r--r--accel-pppd/logs/log_pgsql.c38
-rw-r--r--accel-pppd/logs/log_tcp.c4
-rw-r--r--accel-pppd/main.c29
-rw-r--r--accel-pppd/memdebug.c23
-rw-r--r--accel-pppd/ppp/ccp_mppe.c3
-rw-r--r--accel-pppd/ppp/ipv6cp_opt_intfid.c26
-rw-r--r--accel-pppd/ppp/ppp.c43
-rw-r--r--accel-pppd/ppp/ppp_ccp.c59
-rw-r--r--accel-pppd/ppp/ppp_ipcp.c90
-rw-r--r--accel-pppd/ppp/ppp_ipcp.h3
-rw-r--r--accel-pppd/ppp/ppp_ipv6cp.c105
-rw-r--r--accel-pppd/ppp/ppp_ipv6cp.h3
-rw-r--r--accel-pppd/ppp/ppp_lcp.c100
-rw-r--r--accel-pppd/radius/CMakeLists.txt1
-rw-r--r--accel-pppd/radius/acct.c105
-rw-r--r--accel-pppd/radius/auth.c47
-rw-r--r--accel-pppd/radius/backup.c21
-rw-r--r--accel-pppd/radius/dict.c2
-rw-r--r--accel-pppd/radius/dm_coa.c12
-rw-r--r--accel-pppd/radius/packet.c116
-rw-r--r--accel-pppd/radius/radius.c458
-rw-r--r--accel-pppd/radius/radius.h2
-rw-r--r--accel-pppd/radius/radius_p.h67
-rw-r--r--accel-pppd/radius/req.c23
-rw-r--r--accel-pppd/radius/serv.c280
-rw-r--r--accel-pppd/session.c78
-rw-r--r--accel-pppd/session_backup.c11
-rw-r--r--accel-pppd/triton/list.h4
-rw-r--r--accel-pppd/triton/md.c8
-rw-r--r--accel-pppd/triton/mempool.c27
-rw-r--r--accel-pppd/triton/mempool.h6
-rw-r--r--accel-pppd/triton/timer.c8
-rw-r--r--accel-pppd/triton/triton.c213
-rw-r--r--accel-pppd/triton/triton.h10
-rw-r--r--accel-pppd/triton/triton_p.h24
-rw-r--r--accel-pppd/utils.c11
-rw-r--r--accel-pppd/utils.h82
110 files changed, 8749 insertions, 2659 deletions
diff --git a/accel-pppd/CMakeLists.txt b/accel-pppd/CMakeLists.txt
index cd7186ba..49c2ff61 100644
--- a/accel-pppd/CMakeLists.txt
+++ b/accel-pppd/CMakeLists.txt
@@ -44,32 +44,18 @@ IF (RADIUS)
ADD_SUBDIRECTORY(radius)
ENDIF (RADIUS)
-# define __free_fn_t in musl
INCLUDE (CheckCSourceCompiles)
CHECK_C_SOURCE_COMPILES("
-#include <stdlib.h>
-#include <search.h>
+#include <utmp.h>
int main(void)
{
- __free_fn_t *f;
+ logwtmp(\"\", \"\", \"\");
return 0;
-}" HAVE_FREE_FN_T)
-IF (HAVE_FREE_FN_T)
- ADD_DEFINITIONS(-DHAVE_FREE_FN_T)
-ENDIF (HAVE_FREE_FN_T)
+}" HAVE_LOGWTMP)
-INCLUDE (CheckCSourceCompiles)
-CHECK_C_SOURCE_COMPILES("
-#include <linux/if_arp.h>
-#include <net/ethernet.h>
-int main(void)
-{
- return 0;
-}" HAVE_GOOD_IFARP)
-
-IF (HAVE_GOOD_IFARP)
- ADD_DEFINITIONS(-DHAVE_GOOD_IFARP)
-ENDIF (HAVE_GOOD_IFARP)
+IF (HAVE_LOGWTMP)
+ ADD_DEFINITIONS(-DHAVE_LOGWTMP)
+ENDIF (HAVE_LOGWTMP)
ADD_SUBDIRECTORY(triton)
@@ -90,12 +76,6 @@ ENDIF (SHAPER)
INCLUDE(CheckIncludeFile)
CHECK_INCLUDE_FILE("linux/netfilter/ipset/ip_set.h" HAVE_IPSET)
-# MUSL does not have printf.h
-CHECK_INCLUDE_FILE("printf.h" HAVE_PRINTF_H)
-IF (HAVE_PRINTF_H)
- ADD_DEFINITIONS(-DHAVE_PRINTF_H)
-ENDIF (HAVE_PRINTF_H)
-
INCLUDE(CheckFunctionExists)
CHECK_FUNCTION_EXISTS(setns HAVE_SETNS)
@@ -163,9 +143,9 @@ CHECK_FUNCTION_EXISTS(getcontext HAVE_GETCONTEXT)
CHECK_FUNCTION_EXISTS(setcontext HAVE_SETCONTEXT)
IF (HAVE_GETCONTEXT AND HAVE_SETCONTEXT)
- TARGET_LINK_LIBRARIES(accel-pppd triton rt pthread ${crypto_lib} pcre)
+ TARGET_LINK_LIBRARIES(accel-pppd triton rt pthread ${crypto_lib} pcre2-8)
ELSE (HAVE_GETCONTEXT AND HAVE_SETCONTEXT)
- TARGET_LINK_LIBRARIES(accel-pppd triton rt pthread ${crypto_lib} pcre ucontext)
+ TARGET_LINK_LIBRARIES(accel-pppd triton rt pthread ${crypto_lib} pcre2-8 ucontext)
ENDIF (HAVE_GETCONTEXT AND HAVE_SETCONTEXT)
set_property(TARGET accel-pppd PROPERTY CMAKE_SKIP_BUILD_RPATH FALSE)
diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf
index abfd1ac9..fe124392 100644
--- a/accel-pppd/accel-ppp.conf
+++ b/accel-pppd/accel-ppp.conf
@@ -2,10 +2,13 @@
log_file
#log_syslog
#log_tcp
-#log_pgsql
+#log_pgsql (deprecated, scheduled for removal)
connlimit
+radius
+#chap-secrets
+
pptp
l2tp
#sstp
@@ -17,9 +20,6 @@ auth_mschap_v1
auth_chap_md5
auth_pap
-radius
-#chap-secrets
-
ippool
pppd_compat
@@ -27,6 +27,7 @@ pppd_compat
#shaper
#net-snmp
#logwtmp
+#metrics
#ipv6_nd
@@ -35,6 +36,7 @@ pppd_compat
[core]
log-error=/var/log/accel-ppp/core.log
+#log-debug=/var/log/accel-ppp/debug.log
thread-count=4
[common]
@@ -42,11 +44,14 @@ thread-count=4
#single-session-ignore-case=0
#sid-case=upper
#sid-source=seq
+#seq-file=/var/lib/accel-ppp/seq
#max-sessions=1000
#max-starting=0
+#session-timeout=0
#check-ip=0
#nl-snd-buffer=32768
#nl-rcv-buffer=1048576
+#netns-run-dir=/var/run/netns
[ppp]
verbose=1
@@ -56,7 +61,14 @@ mru=1400
#accomp=deny
#pcomp=deny
#ccp=0
+#ccp-max-configure=3
#mppe=require
+#timeout=3
+#max-configure=10
+#max-failure=10
+#max-terminate=2
+#max-mtu=1500
+#check-ip=0
ipv4=require
ipv6=deny
ipv6-intf-id=0:0:0:1
@@ -78,7 +90,14 @@ unit-cache=1
[pptp]
verbose=1
+#bind=0.0.0.0
+#port=1723
#echo-interval=30
+#echo-failure=3
+#timeout=5
+#mppe=allow
+#ppp-max-mtu=1436
+#session-timeout=0
#ip-pool=pptp
#ipv6-pool=pptp
#ipv6-pool-delegate=pptp
@@ -88,16 +107,23 @@ verbose=1
verbose=1
#ac-name=xxx
#service-name=yyy
+#accept-any-service=0
+#accept-blank-service=0
#pado-delay=0
#pado-delay=0,100:100,200:200,-1:500
called-sid=mac
#tr101=1
#padi-limit=0
+#mac-filter=/path/to/mac-filter,allow
+#mppe=allow
+#session-timeout=0
#ip-pool=pppoe
#ipv6-pool=pppoe
#ipv6-pool-delegate=pppoe
#ifname=pppoe%d
+#ifname-in-sid=0
#sid-uppercase=0
+#cookie-timeout=5
#vlan-mon=eth0,10-200
#vlan-timeout=60
#vlan-name=%I.%N
@@ -106,6 +132,8 @@ interface=eth0
[l2tp]
verbose=1
+#bind=0.0.0.0
+#port=1701
#dictionary=/usr/local/share/accel-ppp/l2tp/dictionary
#hello-interval=60
#timeout=60
@@ -116,8 +144,14 @@ verbose=1
#host-name=accel-ppp
#dir300_quirk=0
#secret=
+#hide-avps=0
#dataseq=allow
#reorder-timeout=0
+#use-ephemeral-ports=0
+#ppp-max-mtu=1420
+#mppe=allow
+#session-timeout=0
+#avp_permissive=0
#ip-pool=l2tp
#ipv6-pool=l2tp
#ipv6-pool-delegate=l2tp
@@ -125,6 +159,8 @@ verbose=1
[sstp]
verbose=1
+#bind=0.0.0.0
+#port=443
#cert-hash-proto=sha1,sha256
#cert-hash-sha1=
#cert-hash-sha256=
@@ -141,6 +177,11 @@ verbose=1
#http-error=allow
#timeout=60
#hello-interval=60
+#ppp-mode=auto
+#ppp-max-mtu=1452
+#sndbuf=0
+#rcvbuf=0
+#session-timeout=0
#ip-pool=sstp
#ipv6-pool=sstp
#ipv6-pool-delegate=sstp
@@ -150,6 +191,7 @@ verbose=1
verbose=1
username=ifname
#password=username
+#netmask=24
lease-time=600
#renew-time=300
#rebind-time=525
@@ -159,6 +201,7 @@ max-lease-time=3600
#l4-redirect-ipset=l4
#l4-redirect-on-reject=300
#l4-redirect-ip-pool=pool1
+#noauth=0
shared=0
ifcfg=1
mode=L2
@@ -168,7 +211,12 @@ start=dhcpv4
#proxy-arp=0
#nat=0
#proto=100
+#src=192.0.2.1
#relay=10.10.10.10
+#relay-timeout=3
+#relay-retransmit=3
+#agent-remote-id=accel-ppp
+#link-selection=0.0.0.0
#vendor=Custom
#weight=0
#attr-dhcp-client-ip=DHCP-Client-IP-Address
@@ -185,6 +233,7 @@ start=dhcpv4
#attr-l4-redirect-ipset=l4-redirect
#lua-file=/etc/accel-ppp.lua
#offer-delay=0,100:100,200:200,-1:1000
+#offer-timeout=10
#vlan-mon=eth0,10-200
#vlan-timeout=60
#vlan-name=%I.%N
@@ -196,6 +245,8 @@ start=dhcpv4
#soft-terminate=0
#check-mac-change=1
#calling-sid=mac
+#ipv6=0
+#check-ip=0
#local-net=192.168.0.0/16
interface=eth0
@@ -209,14 +260,21 @@ interface=eth0
[radius]
#dictionary=/usr/local/share/accel-ppp/radius/dictionary
+#framed-route-strict=0
nas-identifier=accel-ppp
nas-ip-address=127.0.0.1
gw-ip-address=192.168.100.1
server=127.0.0.1,testing123,auth-port=1812,acct-port=1813,req-limit=50,fail-timeout=0,max-fail=10,weight=1
dae-server=127.0.0.1:3799,testing123
+#dae-allowed=127.0.0.1,192.0.2.0/24
+#dae-allowed is generally recommended to restrict DM/CoA sources, but not required.
verbose=1
#timeout=3
#max-try=3
+#req-limit=0
+#fail-timeout=0
+#max-fail=10
+#interim-verbose=0
#acct-timeout=120
#acct-delay-time=0
#acct-delay-start=0
@@ -226,7 +284,11 @@ verbose=1
#default-realm=
#strip-realm=0
#attr-tunnel-type=My-Tunnel-Type
+#sid-in-auth=0
#nas-port-id-in-req=1
+#bind=0.0.0.0
+#require-nas-identification=0
+blast-protection=1
[client-ip-range]
10.0.0.0/8
@@ -236,6 +298,9 @@ gw-ip-address=192.168.0.1
#vendor=Cisco
#attr=Cisco-AVPair
attr=Framed-Pool
+#shuffle=0
+# on reload, sessions whose address left the pools: keep (default) or disconnect
+#reload-orphan=keep
192.168.0.2-255
192.168.1.1-255,name=pool1
192.168.2.1-255,name=pool2
@@ -259,6 +324,8 @@ level=3
[log-pgsql]
conninfo=user=log
+#connect-inteval=5
+#log-query=insert into log (timestamp, username, sessionid, msg) values ($1, $2, $3, $4)
log-table=log
[pppd-compat]
@@ -278,6 +345,10 @@ gw-ip-address=192.168.100.1
[shaper]
#attr=Filter-Id
+#vendor=Cisco
+#attr-down=PPPD-Downstream-Speed
+#attr-up=PPPD-Upstream-Speed
+#burst-factor=0.1
#down-burst-factor=0.1
#up-burst-factor=1.0
#latency=50
@@ -302,6 +373,8 @@ verbose=1
telnet=127.0.0.1:2000
tcp=127.0.0.1:2001
#password=123
+#prompt=accel-ppp
+#history-file=/var/lib/accel-ppp/cli-history
#sessions-columns=ifname,username,ip,ip6,ip6-dp,type,state,uptime,uptime-raw,calling-sid,called-sid,sid,comp,inbound-if,service-name,rx-bytes,tx-bytes,rx-bytes-raw,tx-bytes-raw,rx-pkts,tx-pkts,netns,vrf
[snmp]
@@ -313,11 +386,27 @@ limit=10/min
burst=3
timeout=60
+#[metrics]
+# Exposes daemon statistics over HTTP. Same numbers as "accel-cmd show stat".
+# Enable by adding "metrics" to [modules] above.
+#format=prometheus
+#address=127.0.0.1:8080
+#allowed_ips=["127.0.0.1/32"]
+#read_timeout=5
+#max_clients=64
+# Include per-session identity and traffic data in JSON output. This has no
+# effect with format=prometheus. Disabled by default because usernames and
+# station IDs may be sensitive: restrict allowed_ips before enabling it.
+# Traffic counters are the ones last sampled by accounting, so their freshness
+# follows radius interim-interval / idle-timeout.
+#sessions=0
+
[ipv6-pool]
#gw-ip6-address=fc00:0:1::1
#vendor=
#attr-prefix=Delegated-IPv6-Prefix-Pool
#attr-address=Stateful-IPv6-Address-Pool
+#reload-orphan=keep
fc00:0:1::/48,64
fc00:0:2::/48,64,name=pool1
fc00:0:3::/48,64,name=pool2,next=pool1
@@ -326,14 +415,36 @@ delegate=fc00:2::/36,48,name=pool3
delegate=fc00:3::/36,48,name=pool4,next=pool3
[ipv6-dns]
+# Used for sessions which were not assigned their own DNS servers, e.g. by the
+# RADIUS DNS-Server-IPv6-Address attribute (RFC 6911)
#fc00:1::1
#fc00:1::2
#fc00:1::3
#dnssl=suffix1.local.net
#dnssl=suffix2.local.net.
+#lifetime=600
[ipv6-dhcp]
verbose=1
pref-lifetime=604800
valid-lifetime=2592000
route-via-gw=1
+#aftr-gw=aftr.example.net
+#server-id=0:0:0:1
+
+[ipv6-nd]
+#MaxRtrAdvInterval=600
+#MinRtrAdvInterval=198
+#MaxInitialRtrAdvCount=5
+#MaxInitialRtrAdvInterval=3
+#AdvManagedFlag=0
+#AdvOtherConfigFlag=0
+#AdvLinkMTU=0
+#AdvReachableTime=0
+#AdvRetransTimer=0
+#AdvCurHopLimit=64
+#AdvDefaultLifetime=1800
+#AdvValidLifetime=2592000
+#AdvPreferredLifetime=604800
+#AdvOnLinkFlag=1
+#AdvAutonomousFlag=1
diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5
index 72376c35..5ee7fd15 100644
--- a/accel-pppd/accel-ppp.conf.5
+++ b/accel-pppd/accel-ppp.conf.5
@@ -36,6 +36,7 @@ This is logging target which logs messages over TCP/IP.
.TP
.BI log_pgsql
This is logging target which logs messages to PostgreSQL.
+Deprecated and scheduled for removal, see the [log-pgsql] section.
.TP
.BI pptp
.br
@@ -49,6 +50,14 @@ PPPoE discovery stage handling module.
.br
SSTP controlling connection handling module.
.TP
+.BI l2tp
+.br
+L2TPv2 connection handling module.
+.TP
+.BI ipoe
+.br
+IPoE connection handling module.
+.TP
.BI auth_pap
PAP authentication module.
.TP
@@ -65,6 +74,10 @@ Microsoft CHAP (version 2) authentication module.
.br
RADIUS interaction module.
.TP
+.BI chap-secrets
+.br
+Authentication and address assignment from a pppd-compatible chap-secrets file.
+.TP
.BI ippool
.br
IPv4 address assigning module.
@@ -77,6 +90,26 @@ IPv6 address assigning module.
.br
IPv6 DHCP module.
.TP
+.BI ipv6_nd
+.br
+IPv6 Neighbor Discovery and Router Advertisement module.
+.TP
+.BI shaper
+.br
+Traffic shaping and policing module.
+.TP
+.BI net-snmp
+.br
+SNMP agent module. It must be built with NETSNMP enabled.
+.TP
+.BI logwtmp
+.br
+Module that writes session records to wtmp.
+.TP
+.BI metrics
+.br
+HTTP statistics endpoint module.
+.TP
.BI sigchld
Helper module to manage child processes, required by pppd_compat
.TP
@@ -91,6 +124,9 @@ Configuration of core module
.BI "log-error=" path
Path to file for core module error logging.
.TP
+.BI "log-debug=" path
+Path to file for core module debug logging.
+.TP
.BI "thread-count=" n
number of working threads, optimal - number of processors/cores
.SH [common]
@@ -116,7 +152,7 @@ Specifies whether accel-ppp should ignore the case when comparing username withi
Specifies in which case generate session identifier (default lower).
.TP
.BI "sid-source=" urandom|seq
-Specifies method assign session id (default urandom).
+Specifies method used to assign session IDs (default seq).
.br
.B urandom
Assign session id by random method
@@ -135,6 +171,7 @@ Specifies maximum concurrent session attempts which server may processed (defaul
.TP
.BI "check-ip=" 0|1
Specifies whether accel-ppp should check if IP already assigned to other client interface (default 0).
+Check is global (not VRF-aware); with check-ip=1 the same IPv4/IPv6 address will be rejected even if sessions are in different VRFs/netns. With check-ip=0 overlapping addresses can be assigned (useful for isolated VRFs, but take care with overlapping pools). This setting can be overridden by module-specific 'check-ip' options (e.g., in the [ppp] section).
.TP
.BI "nl-snd-buffer=" n
Specifies netlink maximum send buffer size (SO_SNDBUF option) (default 32768).
@@ -142,6 +179,9 @@ Specifies netlink maximum send buffer size (SO_SNDBUF option) (default 32768).
.BI "nl-rcv-buffer=" n
Specifies netlink maximum receive buffer size (SO_RCVBUF option) (default 1048576).
.TP
+.BI "netns-run-dir=" path
+Directory containing named network namespace handles (default /var/run/netns).
+.TP
.BI "session-timeout=" n
Specifies max sessions time in seconds, after this time session will be terminated.
.br
@@ -183,6 +223,12 @@ Protocol field compression negotiation.
.BI "ccp=" n
Disable CCP negotiation if this parameter is zero.
.TP
+.BI "ccp-max-configure=" n
+Specifies the maximum number of CCP Configure-Request packets sent without receiving a valid Configure-Ack, Configure-Nak or Configure-Reject before assuming the peer is unable to respond (default 3).
+.TP
+.BI "timeout=" n
+Specifies the timeout, in seconds, used by PPP finite-state machines (default 3).
+.TP
.BI "mppe=" require|prefer|deny
Specifies mppe negotiation preference.
.br
@@ -198,6 +244,21 @@ Specifies mppe negotiation preference.
Default behavior - don't ask client for mppe, but allow it if client wants.
Please note that RADIUS may override this option by MS-MPPE-Encryption-Policy attribute.
.TP
+.BI "max-configure=" n
+Specifies the maximum number of LCP/IPCP Configure-Request packets sent without receiving a valid Configure-Ack, Configure-Nak or Configure-Reject before assuming the peer is unable to respond (default 10).
+.TP
+.BI "max-failure=" n
+Specifies the maximum number of LCP/IPCP Configure-Nak returned before starting to send Configure-Reject (default 10).
+.TP
+.BI "max-terminate=" n
+Specifies the maximum number of Terminate-Request packets sent without receiving a Terminate-Ack before assuming the peer has disconnected (default 2).
+.TP
+.BI "max-mtu=" n
+Specifies the absolute maximum MTU value that can be negotiated.
+.TP
+.BI "check-ip=" 0|1
+Specifies whether to check if the assigned IP address is already in use by another session (overrides [common] check-ip for PPP sessions).
+.TP
.BI "ipv4=" deny|allow|prefer|require
Specify IPv4 (IPCP) negotiation algorithm:
.br
@@ -521,6 +582,81 @@ If enabled accel-ppp will terminate session when detects change of mac address o
.TP
.BI "calling-sid=" mac|ip
Specifies value of Calling-Station-Id attribute.
+.TP
+.BI "netmask=" n
+Specifies the IPv4 prefix length assigned to IPoE sessions (1 through 32).
+.TP
+.BI "nat=" 0|1
+Marks IPoE sessions as requiring NAT.
+.TP
+.BI "noauth=" 0|1
+Authorizes IPoE sessions without consulting a password database. The default is 0.
+.TP
+.BI "check-ip=" 0|1
+Overrides the common duplicate-address check for IPoE sessions.
+.TP
+.BI "ipv6=" 0|1
+Enables IPv6 on IPoE interfaces by default.
+.TP
+.BI "src=" x.x.x.x
+Specifies the source address used by the IPoE service.
+.TP
+.BI "relay-timeout=" n
+Specifies the DHCP relay reply timeout in seconds (default 3).
+.TP
+.BI "relay-retransmit=" n
+Specifies the maximum number of DHCP relay retransmissions (default 3).
+.TP
+.BI "ip-pool=" pool_name
+Specifies the default IPv4 address pool.
+.TP
+.BI "ipv6-pool=" pool_name
+Specifies the default IPv6 address pool.
+.TP
+.BI "ipv6-pool-delegate=" pool_name
+Specifies the default delegated IPv6 prefix pool.
+.TP
+.BI "lua-file=" path
+Loads the Lua file that supplies IPoE Lua hooks. Lua support must be built in.
+.TP
+.BI "vendor=" vendor
+Specifies the RADIUS vendor used when resolving the IPoE attribute options below.
+.TP
+.BI "attr-dhcp-client-ip=" attribute
+Specifies the RADIUS attribute containing the DHCP client address.
+.TP
+.BI "attr-dhcp-router-ip=" attribute
+Specifies the RADIUS attribute containing the DHCP router address.
+.TP
+.BI "attr-dhcp-mask=" attribute
+Specifies the RADIUS attribute containing the DHCP subnet mask.
+.TP
+.BI "attr-dhcp-lease-time=" attribute
+Specifies the RADIUS attribute containing the DHCP lease time.
+.TP
+.BI "attr-dhcp-renew-time=" attribute
+Specifies the RADIUS attribute containing the DHCP renewal time.
+.TP
+.BI "attr-dhcp-rebind-time=" attribute
+Specifies the RADIUS attribute containing the DHCP rebinding time.
+.TP
+.BI "attr-dhcp-opt82=" attribute
+Specifies the RADIUS attribute used for the complete DHCP Option 82 value.
+.TP
+.BI "attr-dhcp-opt82-remote-id=" attribute
+Specifies the RADIUS attribute used for the Option 82 remote ID.
+.TP
+.BI "attr-dhcp-opt82-circuit-id=" attribute
+Specifies the RADIUS attribute used for the Option 82 circuit ID.
+.TP
+.BI "attr-l4-redirect=" attribute
+Specifies the RADIUS attribute that enables Layer-4 redirection.
+.TP
+.BI "attr-l4-redirect-table=" attribute
+Specifies the RADIUS attribute containing the redirection routing-table ID.
+.TP
+.BI "attr-l4-redirect-ipset=" attribute
+Specifies the RADIUS attribute containing the redirection ipset name.
.SH [dns]
.TP
.BI "dns1=" x.x.x.x
@@ -536,12 +672,21 @@ Specifies primary NBNS to be sent to peer.
.BI "wins2=" x.x.x.x
Specifies secondary NBNS to be sent to peer.
.SH [ipv6-dns]
+These options apply to sessions which were not assigned DNS servers of their
+own. A RADIUS server may assign per session ones by returning up to 3
+DNS-Server-IPv6-Address attributes (RFC 6911) in the Access-Accept; those
+replace, rather than extend, the servers configured here, for that session
+only. Both the ipv6_nd module (RDNSS option of the router advertisements) and
+the ipv6_dhcp module (DNS_SERVERS option) honour them.
.TP
.BI "dns=" IPv6_address
Specifies IPv6 DNS to be sent to peer. You may specify up to 3 dns options.
.TP
.BI "dnssl=" name
Specify DNS Search List. You may specify multiple dns and dnssl options.
+.TP
+.BI "lifetime=" n
+Specifies the RDNSS and DNSSL lifetime advertised by the ipv6_nd module.
.SH [client-ip-range]
You have to explicitly specify range of ip address from which clients can connect to server in form:
.br
@@ -571,7 +716,7 @@ If this option is given and greater than zero then pptp module will send echo-re
seconds.
.TP
.BI "echo-failure=" n
-Specifies maximum number of echo-requests may be sent without valid echo-reply, if exceeds connection will be terminated.
+Specifies maximum number of echo-requests may be sent without valid echo-reply, if exceeds connection will be terminated. Zero disables the check: echo-requests are still sent, but missing replies never terminate the connection (default 3).
.TP
.BI "timeout=" n
Timeout waiting reply from client in seconds (default 5).
@@ -591,6 +736,15 @@ sessions. Default value is 1436.
Specifies max sessions time in seconds, after this time session will be terminated.
.br
This timeout can be redefined with radius attribute Session-Timeout. 0 value means infinite timeout.
+.TP
+.BI "ip-pool=" pool_name
+Specifies the name of the IPv4 pool used by PPTP sessions.
+.TP
+.BI "ipv6-pool=" pool_name
+Specifies the name of the IPv6 pool used by PPTP sessions.
+.TP
+.BI "ipv6-pool-delegate=" pool_name
+Specifies the name of the delegated IPv6 prefix pool used by PPTP sessions.
.SH [pppoe]
.br
Configuration of PPPoE module.
@@ -634,9 +788,11 @@ By default vlan-name=%I.%N.
.TP
.BI "ac-name=" ac-name
Specifies AC-Name tag value. If absent tag will not be sent.
+The compatibility alias AC-Name=ac-name is also accepted.
.TP
.BI "service-name=" service-name
Specifies Service-Name to respond. If absent any Service-Name is acceptable and client's Service-Name will be sent back.
+The compatibility alias Service-Name=service-name is also accepted.
.TP
.BI "accept-any-service=" n
If service-name specified still will answer with service names, but accepts any service name in PADR request. Useful
@@ -648,6 +804,7 @@ Useful when needs to allow only Service-Names described in the config and also b
.TP
.BI "pado-delay=" delay[,delay1:count1[,delay2:count2[,...]]]
Specifies delays (also in condition of connection count) to send PADO (ms).
+The compatibility alias PADO-Delay=delay is also accepted.
Last delay in list may be -1 which means don't accept new connections.
List have to be sorted by count key.
.TP
@@ -691,10 +848,31 @@ as a template, i.e pppoe%d => pppoe0.
Specifies max sessions time in seconds, after this time session will be terminated.
.br
This timeout can be redefined with radius attribute Session-Timeout. 0 value means infinite timeout.
+.TP
+.BI "ip-pool=" pool_name
+Specifies the name of the IPv4 pool to use for allocating client addresses.
+.TP
+.BI "ipv6-pool=" pool_name
+Specifies the name of the IPv6 pool to use for allocating client addresses.
+.TP
+.BI "ipv6-pool-delegate=" pool_name
+Specifies the name of the IPv6 prefix delegation pool to use.
+.TP
+.BI "ifname-in-sid=" 0|1
+If set to 1, the interface name is included in the Session ID.
+.TP
+.BI "sid-uppercase=" 0|1
+If set to 1, the Session ID is generated in uppercase.
+.TP
+.BI "cookie-timeout=" n
+Specifies the timeout in seconds for the PPPoE cookie (PADO/PADR stage).
.SH [l2tp]
.br
Configuration of L2TP module.
.TP
+.BI "dictionary=" path
+Specifies an alternate L2TP attribute dictionary.
+.TP
.BI "bind=" x.x.x.x
Specifies IP address to bind.
.TP
@@ -787,6 +965,21 @@ as a template, i.e l2tp%d => l2tp0.
Specifies max sessions time in seconds, after this time session will be terminated.
.br
This timeout can be redefined with radius attribute Session-Timeout. 0 value means infinite timeout.
+.TP
+.BI "avp_permissive=" 0|1
+If set to 1, ignore the Mandatory (M) bit in unknown Attribute-Value Pairs (AVPs) instead of terminating the connection.
+.TP
+.BI "dir300_quirk=" 0|1
+If set to 1, enables a workaround for D-Link DIR-300 routers that send packets with Tunnel ID 0.
+.TP
+.BI "ip-pool=" pool_name
+Specifies the name of the IPv4 pool to use for allocating client addresses.
+.TP
+.BI "ipv6-pool=" pool_name
+Specifies the name of the IPv6 pool to use for allocating client addresses.
+.TP
+.BI "ipv6-pool-delegate=" pool_name
+Specifies the name of the IPv6 prefix delegation pool to use.
.SH [sstp]
.br
Configuration of SSTP module.
@@ -809,11 +1002,26 @@ Timeout waiting reply from client in seconds.
Default is 60.
.TP
.BI "hello-interval=" n
-If this option is given and greater than zero then sstp will send echo-request every
+If this option is given and greater than zero then sstp will send echo-request every
.B n
seconds and drop connection without a reply.
Default is 60.
.TP
+.BI "ppp-mode=" auto|seqpacket|async
+Specifies transport between sstp and the kernel ppp layer.
+.br
+.B seqpacket
+- use the ppposeq module. One datagram carries one ppp frame, so no
+HDLC framing is done in userspace.
+.br
+.B async
+- use a pty with ppp_async and frame in userspace. Works on any kernel.
+.br
+.B auto
+- use seqpacket if the module is available, otherwise async.
+.br
+Default is auto.
+.TP
.BI "accept=" ssl,proxy
Specifies incoming connection acceptance mode.
.br
@@ -822,6 +1030,16 @@ Specifies incoming connection acceptance mode.
.br
.B proxy
- enable PROXY protocol 1 & 2 support.
+.br
+This is useful when accel-ppp is running behind a load balancer (like HAProxy or Nginx) or a reverse proxy. It allows accel-ppp to receive the original client IP address and port instead of the proxy's IP. The extracted real client IP is then used for:
+.br
+- \fBconnlimit\fR module checks (limiting connections per source IP).
+- \fBclient-ip-range\fR verification.
+- \fBCalling-Station-Id\fR attribute in RADIUS and logs.
+.br
+.TP
+.BI "ssl=" 0|1
+Deprecated compatibility option that enables legacy SSL mode. Use accept=ssl instead.
.TP
.BI "ssl-protocol=" ssl2|ssl3|tls1|tls1.1|tls1.2|tls1.3
Specifies the enabled SSL/TLS protocols supported by OpenSSL library.
@@ -842,14 +1060,22 @@ is greater of zero then server ciphers should be preferred over client ciphers.
Default is 0.
.TP
.BI "ssl-pemfile=" pemfile
-Specifies a file with the certificate in the PEM format for sstp server.
-Certificate is also used to compute initial SHA1 and SHA256 certificate hash.
+Specifies a PEM file with the server certificate. Files that contain only the
+leaf certificate are still supported for backward compatibility. When the file
+includes the full chain, certificates must be sorted starting with the subject's
+certificate (actual server certificate), followed by intermediate CA
+certificates if applicable, and ending at the highest level (root) CA. The leaf
+certificate (first in the file) is used to compute the initial SHA1 and SHA256
+certificate hash.
.TP
.BI "ssl-keyfile=" keyfile
Specifies a file with the secret key in the PEM format for sstp server.
If not set, secret key will be loaded from the
.BI pemfile
-certificate.
+parameter.
+.TP
+.BI "ssl-ca-file=" file
+Specifies a CA bundle loaded into the SSTP TLS context.
.TP
.BI "cert-hash-proto=" sha1,sha256
Specifies hashing methods that can be used to compute the Compound MAC in the Crypto Binding attribute.
@@ -894,6 +1120,21 @@ as a template, i.e sstp%d => sstp0.
Set the maximum MTU value that can be negotiated for PPP over SSTP sessions.
Default value is 1452, maximum is 4087.
.TP
+.BI "ip-pool=" pool_name
+Specifies the name of the IPv4 pool to use for allocating client addresses.
+.TP
+.BI "ipv6-pool=" pool_name
+Specifies the name of the IPv6 pool to use for allocating client addresses.
+.TP
+.BI "ipv6-pool-delegate=" pool_name
+Specifies the name of the IPv6 prefix delegation pool to use.
+.TP
+.BI "sndbuf=" n
+Specifies the TCP send buffer size (SO_SNDBUF) for the SSTP socket.
+.TP
+.BI "rcvbuf=" n
+Specifies the TCP receive buffer size (SO_RCVBUF) for the SSTP socket.
+.TP
.BI "session-timeout=" n
Specifies max sessions time in seconds, after this time session will be terminated.
.br
@@ -902,6 +1143,9 @@ This timeout can be redefined with radius attribute Session-Timeout. 0 value mea
.br
Configuration of RADIUS module.
.TP
+.BI "dictionary=" path
+Specifies an alternate RADIUS dictionary.
+.TP
.BI "nas-identifier=" identifier
Specifies value to send to RADIUS server in NAS-Identifier attribute and to be matched in DM/CoA requests.
.TP
@@ -950,6 +1194,11 @@ Specifies IP address, port to bind and secret for Dynamic Authorization Extensio
- device name or VRF to bind the connection. By default, the device name is undefined.
.br
.TP
+.BI "dae-allowed=" ip[,ip/cidr][,ip[/cidr]...]
+Specifies allowed source IPv4 addresses or CIDR ranges for DM/CoA requests.
+This setting is generally recommended to restrict DM/CoA sources, but it is not required.
+.br
+.TP
.BI "dm_coa_secret=" secret (deprecated, use dae-server instead)
Specifies secret to use in DM/CoA communication.
.TP
@@ -975,6 +1224,18 @@ Timeout to wait response from server (sec)
.BI "max-try=" n
Specifies number of tries to send Access-Request/Accounting-Request queries.
.TP
+.BI "req-limit=" n
+Specifies the global limit of simultaneous requests per RADIUS server. Zero means unlimited.
+.TP
+.BI "fail-timeout=" n
+Specifies how long, in seconds, an unresponsive RADIUS server remains unavailable.
+.TP
+.BI "max-fail=" n
+Specifies the number of consecutive unanswered requests before a RADIUS server is marked unavailable.
+.TP
+.BI "fail-time=" n
+Deprecated alias for fail-timeout.
+.TP
.BI "acct-timeout=" n
Specifies timeout of accounting interim update.
.TP
@@ -1004,6 +1265,22 @@ Specifies should accel-ppp generate and send Acct-Session-Id on Access-Request p
.TP
.BI "nas-port-id-in-req=" 0|1
Specifies should accel-ppp send NAS-Port-Id on Access-Request and Accounting-Request packets (default 1).
+.TP
+.BI "bind=" x.x.x.x
+Specifies the source IP address for outgoing RADIUS packets.
+.TP
+.BI "require-nas-identification=" 0|1
+If enabled, checks that the NAS-Identifier or NAS-IP-Address in DM/CoA requests matches the server's configuration.
+.TP
+.BI "blast-protection=" 0|1
+If this option is given and
+.B 1
+is specified then radius module will include Message-Authenticator attribute in Access-Request packets.
+.TP
+.BI "framed-route-strict=" 0|1
+If enabled, only accepts Framed-Route destinations that are valid IPv4 network prefixes
+for the specified mask (host bits must be zero). This enforces RFC-style network prefixes.
+Default is 0 (disabled).
.SH [log]
.br
Configuration of log and log_file modules.
@@ -1017,6 +1294,12 @@ Path to file to write emergency messages.
.BI "log-fail-file=" file
Path to file to write authentication failed session log.
.TP
+.BI "log-debug=" file
+Path to file to write debug messages.
+.TP
+.BI "color=" 0|1
+Enables ANSI color sequences in file log output.
+.TP
.BI "log-tcp=" x.x.x.x:port
Send logs to specified host.
.TP
@@ -1061,10 +1344,22 @@ log all messages including debug messages
.SH [log-pgsql]
.br
Configuration of log_pgsql module.
+.br
+This module is deprecated and scheduled for removal. It is built only when
+accel-ppp is configured with LOG_PGSQL_DEPRECATED=TRUE. If you depend on it,
+please object at https://github.com/accel-ppp/accel-ppp/issues.
.TP
.BI "conninfo=" conninfo
Conninfo to connect to PostgreSQL server.
.TP
+.BI "connect-inteval=" n
+Specifies the PostgreSQL reconnect interval in seconds (default 5).
+The option name intentionally preserves its historical spelling.
+.TP
+.BI "log-query=" query
+Specifies a custom parameterized INSERT query. Parameters $1 through $4 are
+timestamp, username, session ID, and message respectively.
+.TP
.BI "log-table=" table
Table to send log messages. Table must contain following field:
.br
@@ -1079,7 +1374,7 @@ text
.br
.B msg
text
-.SH [pppd_compat]
+.SH [pppd-compat]
.br
Configuration of pppd_compat module.
.TP
@@ -1100,6 +1395,10 @@ Prefix of radattr files (for example /var/run/radattr, resulting files will be /
.TP
.BI "verbose=" n
If specified and greater than zero pppd_module will produce verbose logging.
+.TP
+.BI "fork-limit=" n
+Limits the number of concurrently running compatibility scripts. The default is
+twice the number of online processors.
.SH [chap-secrets]
.br
Configuration of chap-secrets module.
@@ -1111,7 +1410,7 @@ Specifies address to use as local address of ppp interfaces if chap-secrets is u
Specifies alternate chap-secrets file location (default is /etc/ppp/chap-secrets).
.TP
.BI "encrypted=" 0|1
-Specifies either chap-secrets is encrypted (read README).
+Specifies whether chap-secrets is encrypted (see README.md).
.TP
.BI "username-hash=" hash1[,hash2]
Specifies hash chain to calculate username hash.
@@ -1126,16 +1425,24 @@ Configuration of ippool module.
Specifies single IP address to be used as local address of ppp interfaces.
.TP
.BI "shuffle=" 1|0
-Specifies whether to shuffle initial address list.
+Specifies whether to assign addresses in randomized order instead of sequentially.
+.TP
+.BI "reload-orphan=" keep|disconnect
+Controls what happens on configuration reload to sessions whose assigned address
+no longer belongs to any configured pool (a pool was shrunk or removed). With
+.B keep
+(default) such sessions keep running and their address is simply no longer tracked;
+with
+.B disconnect
+those sessions are terminated. Reload reconciles all live sessions against the new
+pools, so growing or renumbering pools no longer requires a restart.
.TP
.BI "gw=" range
-Specifies range of local address of ppp interfaces if form:
-.br
-.B x.x.x.x/mask[,name=pool_name][,next=next_pool_name]
-(for example 10.0.0.0/8)
-.br
-.B x.x.x.x-y[,name=pool_name][,next=next_pool_name]
-(for example 10.0.0.1-254)
+Deprecated and ignored. The per-address local gateway has long been overridden at
+allocation time by
+.B gw-ip-address
+(or 0 for IPoE), so this option no longer has any effect. It is still accepted to
+avoid breaking existing configuration files.
.TP
.BI "tunnel=" range
Specifies range of remote address of ppp interfaces if form:
@@ -1146,16 +1453,85 @@ Specifies range of remote address of ppp interfaces if form:
.TP
.BI "x.x.x.x/mask[,name=pool_name][,next=next_pool_name] or x.x.x.x-y[,name=pool_name][,next=next_pool_name]"
Also specifies range of remote address of ppp interfaces.
+.br
+You may also omit the "name=" key and use a shorthand pool name after the comma, e.g.
+.br
+.B 10.0.0.0/24,pool1
.TP
.BI "attr=" attribute
Specifies which Radius attribute contains pool name.
.TP
.BI "vendor=" vendor
If attribute is vendor-specific then specify vendor name in this option.
+.PP
+Note: overlapping ranges in different pools are not de-duplicated. If two pools cover the same address, that IP can be allocated from each pool independently. Use non-overlapping ranges (or a single pool with next= chaining) to avoid duplicate assignments.
.SH [ipv6-dhcp]
.TP
+.BI "verbose=" 0|1
+Enables verbose DHCPv6 logging.
+.TP
+.BI "pref-lifetime=" n
+Specifies the preferred address and prefix lifetime in seconds (default 604800).
+.TP
+.BI "valid-lifetime=" n
+Specifies the valid address and prefix lifetime in seconds (default 2592000).
+.TP
+.BI "route-via-gw=" 0|1
+Controls whether delegated IPv6 routes are installed through the session gateway (default 1).
+.TP
+.BI "server-id=" x:x:x:x
+Specifies the four 16-bit hexadecimal components used as the DHCPv6 server identifier.
+.TP
.BI "aftr-gw=" name
Specifies the AFTR-Name option value to be returned on DHCPv6 replies upon request from the client.
+.SH [ipv6-nd]
+.br
+Configuration of IPv6 Neighbor Discovery and Router Advertisements.
+.TP
+.BI "MaxRtrAdvInterval=" n
+Maximum unsolicited Router Advertisement interval in seconds (default 600).
+.TP
+.BI "MinRtrAdvInterval=" n
+Minimum unsolicited Router Advertisement interval in seconds (default one third of MaxRtrAdvInterval).
+.TP
+.BI "MaxInitialRtrAdvCount=" n
+Maximum number of initial Router Advertisements (default 5).
+.TP
+.BI "MaxInitialRtrAdvInterval=" n
+Maximum interval between initial Router Advertisements in seconds (default 3).
+.TP
+.BI "AdvManagedFlag=" 0|1
+Sets the Managed Address Configuration flag. By default it follows whether ipv6_dhcp is loaded.
+.TP
+.BI "AdvOtherConfigFlag=" 0|1
+Sets the Other Configuration flag. By default it follows whether ipv6_dhcp is loaded.
+.TP
+.BI "AdvLinkMTU=" n
+Specifies the MTU advertised in Router Advertisements. Zero omits the MTU option.
+.TP
+.BI "AdvReachableTime=" n
+Specifies the advertised reachable time in milliseconds.
+.TP
+.BI "AdvRetransTimer=" n
+Specifies the advertised retransmission timer in milliseconds.
+.TP
+.BI "AdvCurHopLimit=" n
+Specifies the advertised current hop limit (default 64).
+.TP
+.BI "AdvDefaultLifetime=" n
+Specifies the router lifetime in seconds (default three times MaxRtrAdvInterval).
+.TP
+.BI "AdvValidLifetime=" n
+Specifies the advertised prefix valid lifetime in seconds (default 2592000).
+.TP
+.BI "AdvPreferredLifetime=" n
+Specifies the advertised prefix preferred lifetime in seconds (default 604800).
+.TP
+.BI "AdvOnLinkFlag=" 0|1
+Sets the advertised prefix on-link flag (default 1).
+.TP
+.BI "AdvAutonomousFlag=" 0|1
+Sets the autonomous address-configuration flag. By default it is disabled when ipv6_dhcp is loaded and enabled otherwise.
.SH [ipv6-pool]
.br
Configuration of ipv6pool module.
@@ -1187,6 +1563,89 @@ Specifies which Radius attribute contains stateful address pool name.
.TP
.BI "vendor=" vendor
If attribute is vendor-specific then specify vendor name in this option.
+.TP
+.BI "reload-orphan=" keep|disconnect
+Same meaning as in the
+.B [ip-pool]
+section, applied to IPv6 address and delegated-prefix leases on configuration reload.
+.PP
+Note: each pool range is materialized as a bitmap with one bit per allocatable
+prefix, so the number of prefixes in a range (2^(prefix_len-mask)) is capped. Ranges
+that would exceed roughly 16.7 million prefixes (prefix_len-mask > 24) are rejected at
+parse time with an error rather than allocated.
+.SH [metrics]
+.br
+Exposes daemon statistics over HTTP. Requires the
+.B metrics
+module to be listed in the
+.B [modules]
+section. The same numbers shown by
+.B "accel-cmd show stat"
+are returned for every GET on
+.B /metrics
+\&, optionally followed by per session details in the JSON format, see
+.BR sessions .
+Any other path responds with 404, and any method other than GET
+responds with 405.
+.TP
+.BI "format=" prometheus|json
+Selects the body format. Defaults to
+.BR prometheus .
+.TP
+.BI "address=" host:port
+IPv4 listen address. Required. Use
+.BR 0.0.0.0:8080
+to listen on all interfaces.
+.TP
+.BI "allowed_ips=" cidr_list
+Optional comma-separated list of IPv4 CIDR entries permitted to fetch
+metrics. The bracketed form
+.RB "\fBallowed_ips = [\(dq" "1.2.3.4/32" "\(dq, \(dq" "5.6.7.0/24" "\(dq]\fR"
+and the bare form
+.B "1.2.3.4/32,5.6.7.0/24"
+are both accepted. A bare address without a prefix length is treated
+as
+.BR /32 .
+When the option is missing or empty, all peers are allowed. Peers
+that do not match a CIDR have their connection closed immediately
+after
+.BR accept (2).
+.TP
+.BI "read_timeout=" seconds
+Maximum time a client may take to deliver a complete HTTP request.
+Connections that have not produced a terminating
+.B \(dq\er\en\er\en\(dq
+within this deadline are closed. Defaults to 5 seconds. Set to
+.B 0
+to disable the deadline.
+.TP
+.BI "max_clients=" n
+Maximum number of in-flight HTTP clients. Excess connections are
+accepted then immediately closed. Defaults to 64. Set to
+.B 0
+to disable the cap.
+.TP
+.BI "sessions=" 0|1
+Adds a
+.B session_details
+array to the JSON body holding per session identity, addressing,
+protocol state, interface context, uptime and traffic counters. Has no
+effect with
+.BR format = prometheus ,
+which stays aggregate only. Defaults to
+.BR 0 .
+.br
+Two things to weigh before enabling it. Usernames, calling and called
+station identifiers become readable by every peer
+.B allowed_ips
+permits, which is every peer when that option is unset. And the traffic
+counters are the ones last sampled by accounting rather than a fresh
+read of the interface, so they are only as current as the
+.B radius
+.B interim-interval
+or the session
+.B idle-timeout
+makes them.
.SH [connlimit]
.br
This module limits connection rate from single source.
@@ -1205,6 +1664,9 @@ This module controls shaper.
.BI "attr=" name
Specifies which radius attribute contains rate information. Default - Filter-ID.
.TP
+.BI "vendor=" vendor
+Specifies the RADIUS vendor for vendor-specific rate attributes.
+.TP
.BI "attr-up=" name
.TP
.BI "attr-down=" name
@@ -1224,6 +1686,9 @@ Specifies latency (in milliseconds) parameter of tbf qdisc.
.BI "mpu=" n
Specifies mpu parameter of tbf qdisc and policer.
.TP
+.BI "mtu=" n
+Specifies the MTU used in shaper calculations.
+.TP
.BI "r2q=" n
Specifies r2q parameter of root htb qdisc.
.TP
@@ -1233,6 +1698,12 @@ Specifies quantum parameter of htb classes.
.BI "moderate-quantum=" 1|0
If fixed quantum is not specified and this option is specified then shaper module will check for quantum value is valid (in range 1000-200000).
.TP
+.BI "cburst=" n
+Specifies the HTB class ceil burst in bytes (default 1534).
+.TP
+.BI "ifb=" ifname
+Specifies the IFB interface used for upstream HTB shaping. Without it, upstream HTB falls back to policing.
+.TP
.BI "fwmark=" n
Specifies the fwmark for traffic that won't be passed through shaper.
.TP
@@ -1255,6 +1726,9 @@ Due to accel-ppp operates with rates in kilobit basis if you send rates in diffe
.TP
.BI "rate-limit=" download_speed/upload_speed
Specifies, should accel-ppp set default rate-limit for clients. Clients rate-limit will be overwritten by RADIUS filter attributes or chap-secrets rate-limit params.
+.TP
+.BI "verbose=" 0|1
+Enables verbose shaper logging.
.SH [cli]
.br
Configuration of the command line interface.
@@ -1266,13 +1740,19 @@ If \fIn\fR = 2 then cli module will also log passed commands.
.TP
.BI "tcp=" host:port
Defines on which IP address and port the TCP module will listen for incoming
-connections. When \fIhost\fR is empty, the TCP module listens on all local
-interfaces. It isn't loaded if this option isn't defined.
+connections. \fIhost\fR may be an IPv4 or an IPv6 address; IPv6 addresses may
+be enclosed in square brackets (e.g. \fI[::1]:2001\fR). When \fIhost\fR is
+empty, the TCP module listens on all local interfaces (use \fI[::]:port\fR to
+listen on all interfaces over IPv6). It isn't loaded if this option isn't
+defined.
.TP
.BI "telnet=" host:port
Defines on which IP address and port the Telnet module will listen for incoming
-connections. When \fIhost\fR is empty, the Telnet module listens on all local
-interfaces. It isn't loaded if this option isn't defined.
+connections. \fIhost\fR may be an IPv4 or an IPv6 address; IPv6 addresses may
+be enclosed in square brackets (e.g. \fI[::1]:2000\fR). When \fIhost\fR is
+empty, the Telnet module listens on all local interfaces (use \fI[::]:port\fR
+to listen on all interfaces over IPv6). It isn't loaded if this option isn't
+defined.
.TP
.BI "password=" passwd
Defines the password to be used by the TCP and Telnet modules for
@@ -1364,3 +1844,12 @@ command (defaults to
- virtual routing and forwarding
.br
Invalid column names are silently discarded.
+.SH [snmp]
+.br
+Configuration of the optional net-snmp module.
+.TP
+.BI "master=" 0|1
+Runs as an AgentX subagent when set to 0 (the default), or as the SNMP master agent when set to 1.
+.TP
+.BI "agent-name=" name
+Specifies the AgentX subagent name (default accel-ppp).
diff --git a/accel-pppd/auth/CMakeLists.txt b/accel-pppd/auth/CMakeLists.txt
index 7a1eb0b0..0270301a 100644
--- a/accel-pppd/auth/CMakeLists.txt
+++ b/accel-pppd/auth/CMakeLists.txt
@@ -3,7 +3,10 @@ ADD_LIBRARY(auth_chap_md5 SHARED auth_chap_md5.c)
ADD_LIBRARY(auth_mschap_v1 SHARED auth_mschap_v1.c)
ADD_LIBRARY(auth_mschap_v2 SHARED auth_mschap_v2.c)
+TARGET_LINK_LIBRARIES(auth_chap_md5 ${crypto_lib})
+TARGET_LINK_LIBRARIES(auth_mschap_v1 ${crypto_lib})
+TARGET_LINK_LIBRARIES(auth_mschap_v2 ${crypto_lib})
+
INSTALL(TARGETS auth_pap auth_chap_md5 auth_mschap_v1 auth_mschap_v2
LIBRARY DESTINATION lib${LIB_SUFFIX}/accel-ppp
)
-
diff --git a/accel-pppd/auth/auth_chap_md5.c b/accel-pppd/auth/auth_chap_md5.c
index 3ff685e6..a5890523 100644
--- a/accel-pppd/auth/auth_chap_md5.c
+++ b/accel-pppd/auth/auth_chap_md5.c
@@ -7,7 +7,7 @@
#include <stdio.h>
#include <arpa/inet.h>
-#include "crypto.h"
+#include <openssl/md5.h>
#include "log.h"
#include "events.h"
@@ -36,7 +36,7 @@ static int conf_timeout = 5;
static int conf_interval = 0;
static int conf_max_failure = 3;
static int conf_any_login = 0;
-static const char *conf_challenge_name = "accel-ppp";
+static char *conf_challenge_name;
struct chap_hdr {
uint16_t proto;
@@ -73,7 +73,6 @@ struct chap_auth_data {
struct triton_timer_t interval;
int failure;
char *name;
- char *mschap_error;
unsigned int started:1;
};
@@ -489,8 +488,9 @@ static void load_config(void)
conf_any_login = atoi(opt);
opt = conf_get_opt("auth", "challenge-name");
- if (opt)
- conf_challenge_name = opt;
+ if (conf_challenge_name)
+ _free(conf_challenge_name);
+ conf_challenge_name = _strdup(opt ? opt : "accel-ppp");
}
static void auth_chap_md5_init()
diff --git a/accel-pppd/auth/auth_mschap_v1.c b/accel-pppd/auth/auth_mschap_v1.c
index bddaabf5..909bcc5c 100644
--- a/accel-pppd/auth/auth_mschap_v1.c
+++ b/accel-pppd/auth/auth_mschap_v1.c
@@ -8,7 +8,9 @@
#include <byteswap.h>
#include <arpa/inet.h>
-#include "crypto.h"
+#include <openssl/md4.h>
+#include <openssl/sha.h>
+#include <openssl/des.h>
#include "log.h"
#include "ppp.h"
diff --git a/accel-pppd/auth/auth_mschap_v2.c b/accel-pppd/auth/auth_mschap_v2.c
index 3e944500..440a0995 100644
--- a/accel-pppd/auth/auth_mschap_v2.c
+++ b/accel-pppd/auth/auth_mschap_v2.c
@@ -8,7 +8,9 @@
#include <byteswap.h>
#include <arpa/inet.h>
-#include "crypto.h"
+#include <openssl/md4.h>
+#include <openssl/sha.h>
+#include <openssl/des.h>
#include "log.h"
#include "ppp.h"
diff --git a/accel-pppd/backup/backup_file.c b/accel-pppd/backup/backup_file.c
index 06dc02d4..eaaf87d2 100644
--- a/accel-pppd/backup/backup_file.c
+++ b/accel-pppd/backup/backup_file.c
@@ -10,11 +10,12 @@
#include <sys/uio.h>
#include <sys/mman.h>
+#include <openssl/md5.h>
+
#include "triton.h"
#include "log.h"
#include "ap_session.h"
#include "backup.h"
-#include "crypto.h"
#include "memdebug.h"
#define VERSION 1
@@ -95,7 +96,7 @@ static int fs_commit(struct backup_data *d)
ptr = (uint8_t *)(tag + 1);
*ptr = tag->id; ptr++;
*ptr = tag->internal ? 1 : 0; ptr++;
- *(uint16_t *)ptr = tag->size;
+ memcpy(ptr, &tag->size, sizeof(tag->size));
MD5_Update(&md5, tag + 1, 4 + tag->size);
iov[i].iov_base = tag + 1;
iov[i].iov_len = 4 + tag->size;
@@ -277,14 +278,16 @@ static void restore_session(const char *fn, int internal)
}
if (!internal && ptr[1]) {
- ptr += 4 + *(uint16_t *)(ptr + 2);
+ uint16_t tag_size;
+ memcpy(&tag_size, ptr + 2, sizeof(tag_size));
+ ptr += 4 + tag_size;
continue;
}
tag = fs_alloc_tag(d, 0);
tag->id = *ptr; ptr++;
tag->internal = (*ptr & 0x01) ? 1 : 0; ptr ++;
- tag->size = *(uint16_t *)ptr; ptr += 2;
+ memcpy(&tag->size, ptr, sizeof(tag->size)); ptr += 2;
tag->data = ptr; ptr += tag->size;
list_add_tail(&tag->entry, &mod->tag_list);
diff --git a/accel-pppd/cli/cli.c b/accel-pppd/cli/cli.c
index 7d440727..6b71cb04 100644
--- a/accel-pppd/cli/cli.c
+++ b/accel-pppd/cli/cli.c
@@ -63,51 +63,6 @@ void __export cli_register_simple_cmd2(
va_end(ap);
}
-void __export cli_register_regexp_cmd(struct cli_regexp_cmd_t *cmd)
-{
- int err;
- int erroffset;
- const char *errptr;
-
- if (cmd->exec == NULL) {
- log_emerg("cli: impossible to register regexp command"
- " without an execution callback function\n");
- _exit(EXIT_FAILURE);
- }
- if (cmd->pattern == NULL) {
- log_emerg("cli: impossible to register regexp command"
- " without pattern\n");
- _exit(EXIT_FAILURE);
- }
- cmd->re = pcre_compile2(cmd->pattern, cmd->options, &err,
- &errptr, &erroffset, NULL);
- if (!cmd->re) {
- log_emerg("cli: failed to compile regexp \"%s\": %s (error %i)"
- " at positon %i (unprocessed characters: \"%s\")\n",
- cmd->pattern, errptr, err, erroffset,
- cmd->pattern + erroffset);
- _exit(EXIT_FAILURE);
- }
-
- if (cmd->h_pattern) {
- cmd->h_re = pcre_compile2(cmd->h_pattern, cmd->h_options, &err,
- &errptr, &erroffset, NULL);
- if (!cmd->h_re) {
- log_emerg("cli: failed to compile help regexp \"%s\":"
- " %s (error %i) at position %i (unprocessed"
- " characters: \"%s\")\n",
- cmd->h_pattern, errptr, err, erroffset,
- cmd->h_pattern + erroffset);
- _exit(EXIT_FAILURE);
- }
- } else {
- cmd->h_re = NULL;
- cmd->h_pattern = NULL;
- }
-
- list_add_tail(&cmd->entry, &regexp_cmd_list);
-}
-
int __export cli_send(void *client, const char *data)
{
struct cli_client_t *cln = (struct cli_client_t *)client;
@@ -189,13 +144,15 @@ static int cli_process_help_cmd(struct cli_client_t *cln)
cmd_found = 1;
list_for_each_entry(recmd, &regexp_cmd_list, entry) {
+ pcre2_match_data *match_data = pcre2_match_data_create(0, NULL);
if (cmd[0] == '\0'
- || pcre_exec(recmd->h_re, NULL, cmd, strlen(cmd),
- 0, 0, NULL, 0) >= 0) {
+ || pcre2_match(recmd->h_re, (PCRE2_SPTR)cmd, strlen(cmd),
+ 0, 0, match_data, NULL) >= 0) {
cmd_found = 1;
if (recmd->help)
recmd->help(cmd, cln);
}
+ pcre2_match_data_free(match_data);
}
nb_items = split(cmd, items);
@@ -230,14 +187,19 @@ static int cli_process_regexp_cmd(struct cli_client_t *cln, int *err)
int res;
cmd = skip_space(cmd);
- list_for_each_entry(recmd, &regexp_cmd_list, entry)
- if (pcre_exec(recmd->re, NULL, cmd, strlen(cmd),
- 0, 0, NULL, 0) >= 0) {
+ list_for_each_entry(recmd, &regexp_cmd_list, entry) {
+ pcre2_match_data *match_data = pcre2_match_data_create(0, NULL);
+ if (pcre2_match(recmd->re, (PCRE2_SPTR)cmd, strlen(cmd),
+ 0, 0, match_data, NULL) >= 0) {
found = 1;
res = recmd->exec(cmd, cln);
- if (res != CLI_CMD_OK)
+ if (res != CLI_CMD_OK) {
+ pcre2_match_data_free(match_data);
break;
+ }
}
+ pcre2_match_data_free(match_data);
+ }
if (found)
*err = res;
diff --git a/accel-pppd/cli/cli.h b/accel-pppd/cli/cli.h
index 6eda5d3f..3d8069b3 100644
--- a/accel-pppd/cli/cli.h
+++ b/accel-pppd/cli/cli.h
@@ -1,7 +1,8 @@
#ifndef __CLI_H
#define __CLI_H
-#include <pcre.h>
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
#include "list.h"
@@ -23,11 +24,11 @@ struct cli_simple_cmd_t
struct cli_regexp_cmd_t
{
struct list_head entry;
- pcre *re;
+ pcre2_code *re;
const char *pattern;
int options;
int (*exec)(const char *cmd, void *client);
- pcre *h_re;
+ pcre2_code *h_re;
const char *h_pattern;
int h_options;
int (*help)(const char *cmd, void *client);
@@ -42,7 +43,6 @@ void cli_register_simple_cmd2(
int hdr_len,
...
);
-void cli_register_regexp_cmd(struct cli_regexp_cmd_t *cmd);
void cli_show_ses_register(const char *name, const char *desc, void (*print)(struct ap_session *ses, char *buf));
int cli_send(void *client, const char *data);
diff --git a/accel-pppd/cli/cli_p.h b/accel-pppd/cli/cli_p.h
index 0fcba309..79208e13 100644
--- a/accel-pppd/cli/cli_p.h
+++ b/accel-pppd/cli/cli_p.h
@@ -2,6 +2,11 @@
#define __CLI_P_H
#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
#include "triton.h"
@@ -15,6 +20,85 @@ struct cli_client_t
int cli_process_cmd(struct cli_client_t *cln);
+/* Format peer address (IPv4, IPv6 or IPv4-mapped IPv6) for logging.
+ * buf must be at least INET6_ADDRSTRLEN bytes long. */
+static inline const char *cli_addr_str(const struct sockaddr_storage *addr,
+ char *buf, size_t size)
+{
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)addr;
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)addr;
+
+ buf[0] = '\0';
+ if (addr->ss_family == AF_INET6) {
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+ inet_ntop(AF_INET, &sin6->sin6_addr.s6_addr32[3], buf, size);
+ else
+ inet_ntop(AF_INET6, &sin6->sin6_addr, buf, size);
+ } else
+ inet_ntop(AF_INET, &sin->sin_addr, buf, size);
+
+ return buf;
+}
+
+/* Parse "host:port", "[host]:port" or ":port" listener specification.
+ * str is modified in place, *host points into str afterwards (NULL for
+ * empty host). For unbracketed hosts the last ':' separates the port,
+ * so bare IPv6 addresses like "::1:2001" are accepted too.
+ * Returns 0 on success, -1 on invalid format. */
+static inline int cli_parse_hostport(char *str, const char **host, int *port)
+{
+ char *d;
+
+ if (*str == '[') {
+ ++str;
+ d = strchr(str, ']');
+ if (!d || d[1] != ':')
+ return -1;
+ *d++ = '\0';
+ } else {
+ d = strrchr(str, ':');
+ if (!d)
+ return -1;
+ }
+
+ *d = '\0';
+ *port = atoi(d + 1);
+ if (*port <= 0)
+ return -1;
+
+ *host = *str ? str : NULL;
+
+ return 0;
+}
+
+/* Fill sockaddr for binding a CLI listener. host may be an IPv4 or IPv6
+ * address; NULL host means any IPv4 address (use "::" for IPv6 wildcard).
+ * Returns 0 on success, -1 if host is not a valid address. */
+static inline int cli_bind_addr(const char *host, int port,
+ struct sockaddr_storage *addr, socklen_t *len)
+{
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
+ struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+
+ memset(addr, 0, sizeof(*addr));
+
+ if (host && inet_pton(AF_INET6, host, &sin6->sin6_addr) > 0) {
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(port);
+ *len = sizeof(*sin6);
+ } else {
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(port);
+ if (!host)
+ sin->sin_addr.s_addr = htonl(INADDR_ANY);
+ else if (inet_pton(AF_INET, host, &sin->sin_addr) <= 0)
+ return -1;
+ *len = sizeof(*sin);
+ }
+
+ return 0;
+}
+
extern char *conf_cli_passwd;
extern char *conf_cli_prompt;
diff --git a/accel-pppd/cli/show_sessions.c b/accel-pppd/cli/show_sessions.c
index 22f5318a..75649c4a 100644
--- a/accel-pppd/cli/show_sessions.c
+++ b/accel-pppd/cli/show_sessions.c
@@ -128,9 +128,9 @@ static int show_ses_exec(const char *cmd, char * const *f, int f_cnt, void *cli)
struct column_t *match_key = NULL;
char *match_pattern = NULL;
struct column_t *order_key = NULL;
- pcre *re = NULL;
- const char *pcre_err;
- int pcre_offset;
+ pcre2_code *re = NULL;
+ int pcre_err;
+ PCRE2_SIZE pcre_offset;
struct column_t *column;
struct col_t *col;
struct row_t *row;
@@ -169,9 +169,11 @@ static int show_ses_exec(const char *cmd, char * const *f, int f_cnt, void *cli)
}
if (match_key) {
- re = pcre_compile2(match_pattern, 0, NULL, &pcre_err, &pcre_offset, NULL);
+ re = pcre2_compile((PCRE2_SPTR)match_pattern, PCRE2_ZERO_TERMINATED, 0, &pcre_err, &pcre_offset, NULL);
if (!re) {
- cli_sendv(cli, "match: %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));
+ cli_sendv(cli, "match: %s at %i\r\n", err_msg, (int)pcre_offset);
return CLI_CMD_OK;
}
}
@@ -245,6 +247,7 @@ static int show_ses_exec(const char *cmd, char * const *f, int f_cnt, void *cli)
goto oom;
cell->col = col;
list_add_tail(&cell->entry, &row->cell_list);
+ cell->buf[0] = 0;
col->column->print(ses, cell->buf);
n = strlen(cell->buf);
if (n > col->width)
@@ -262,10 +265,13 @@ static int show_ses_exec(const char *cmd, char * const *f, int f_cnt, void *cli)
row = list_entry(t_list.next, typeof(*row), entry);
list_del(&row->entry);
if (match_key) {
- if (pcre_exec(re, NULL, row->match_key, strlen(row->match_key), 0, 0, NULL, 0) < 0) {
+ pcre2_match_data *match_data = pcre2_match_data_create(0, NULL);
+ if (pcre2_match(re, (PCRE2_SPTR)row->match_key, strlen(row->match_key), 0, 0, match_data, NULL) < 0) {
free_row(row);
+ pcre2_match_data_free(match_data);
continue;
}
+ pcre2_match_data_free(match_data);
}
if (order_key)
insert_row(&r_list, row);
@@ -362,7 +368,7 @@ out:
}
if (re)
- pcre_free(re);
+ pcre2_code_free(re);
return CLI_CMD_OK;
diff --git a/accel-pppd/cli/std_cmd.c b/accel-pppd/cli/std_cmd.c
index fc073526..1d15a28c 100644
--- a/accel-pppd/cli/std_cmd.c
+++ b/accel-pppd/cli/std_cmd.c
@@ -5,6 +5,7 @@
#include <signal.h>
#include <malloc.h>
#include <arpa/inet.h>
+#include <inttypes.h>
#include "triton.h"
#include "events.h"
@@ -29,6 +30,9 @@ static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt,
#ifdef MEMDEBUG
struct mallinfo mi = mallinfo();
#endif
+ struct triton_stat_t stat;
+
+ triton_stat_get(&stat);
sprintf(statm_fname, "/proc/%i/statm", getpid());
f = fopen(statm_fname, "r");
@@ -38,14 +42,14 @@ static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt,
}
clock_gettime(CLOCK_MONOTONIC, &ts);
- dt = ts.tv_sec - triton_stat.start_time;
+ dt = ts.tv_sec - stat.start_time;
day = dt / (60 * 60 * 24);
dt %= 60 * 60 * 24;
hour = dt / (60 * 60);
dt %= 60 * 60;
cli_sendv(client, "uptime: %i.%02i:%02lu:%02lu\r\n", day, hour, dt / 60, dt % 60);
- cli_sendv(client, "cpu: %i%%\r\n", triton_stat.cpu);
+ cli_sendv(client, "cpu: %i%%\r\n", stat.cpu);
#ifdef MEMDEBUG
cli_send(client, "memory:\r\n");
cli_sendv(client, " rss/virt: %lu/%lu kB\r\n", vmrss * page_size_kb, vmsize * page_size_kb);
@@ -57,23 +61,29 @@ static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt,
cli_sendv(client, "mem(rss/virt): %lu/%lu kB\r\n", vmrss * page_size_kb, vmsize * page_size_kb);
#endif
cli_send(client, "core:\r\n");
- cli_sendv(client, " mempool_allocated: %u\r\n", triton_stat.mempool_allocated);
- cli_sendv(client, " mempool_available: %u\r\n", triton_stat.mempool_available);
- cli_sendv(client, " thread_count: %u\r\n", triton_stat.thread_count);
- cli_sendv(client, " thread_active: %u\r\n", triton_stat.thread_active);
- cli_sendv(client, " context_count: %u\r\n", triton_stat.context_count);
- cli_sendv(client, " context_sleeping: %u\r\n", triton_stat.context_sleeping);
- cli_sendv(client, " context_pending: %u\r\n", triton_stat.context_pending);
- cli_sendv(client, " md_handler_count: %u\r\n", triton_stat.md_handler_count);
- cli_sendv(client, " md_handler_pending: %u\r\n", triton_stat.md_handler_pending);
- cli_sendv(client, " timer_count: %u\r\n", triton_stat.timer_count);
- cli_sendv(client, " timer_pending: %u\r\n", triton_stat.timer_pending);
+ cli_sendv(client, " mempool_allocated: %" PRIu64 "\r\n", stat.mempool_allocated);
+ cli_sendv(client, " mempool_available: %" PRIu64 "\r\n", stat.mempool_available);
+ cli_sendv(client, " thread_count: %u\r\n", stat.thread_count);
+ cli_sendv(client, " thread_active: %u\r\n", stat.thread_active);
+ cli_sendv(client, " context_count: %u\r\n", stat.context_count);
+ cli_sendv(client, " context_sleeping: %u\r\n", stat.context_sleeping);
+ cli_sendv(client, " context_pending: %u\r\n", stat.context_pending);
+ cli_sendv(client, " md_handler_count: %u\r\n", stat.md_handler_count);
+ cli_sendv(client, " md_handler_pending: %u\r\n", stat.md_handler_pending);
+ cli_sendv(client, " timer_count: %u\r\n", stat.timer_count);
+ cli_sendv(client, " timer_pending: %u\r\n", stat.timer_pending);
//===========
- cli_send(client, "sessions:\r\n");
- cli_sendv(client, " starting: %u\r\n", ap_session_stat.starting);
- cli_sendv(client, " active: %u\r\n", ap_session_stat.active);
- cli_sendv(client, " finishing: %u\r\n", ap_session_stat.finishing);
+ {
+ struct ap_session_stat ses_stat;
+
+ ap_session_stat_get(&ses_stat);
+
+ cli_send(client, "sessions:\r\n");
+ cli_sendv(client, " starting: %u\r\n", ses_stat.starting);
+ cli_sendv(client, " active: %u\r\n", ses_stat.active);
+ cli_sendv(client, " finishing: %u\r\n", ses_stat.finishing);
+ }
return CLI_CMD_OK;
}
@@ -123,9 +133,9 @@ static int terminate_exec1(char * const *f, int f_cnt, void *cli)
{
struct ap_session *ses;
int hard = 0;
- pcre *re;
- const char *pcre_err;
- int pcre_offset;
+ pcre2_code *re;
+ int pcre_err;
+ PCRE2_SIZE pcre_offset;
if (f_cnt == 5) {
if (!strcmp(f[4], "hard"))
@@ -135,9 +145,11 @@ static int terminate_exec1(char * const *f, int f_cnt, void *cli)
} else if (f_cnt != 4)
return CLI_CMD_SYNTAX;
- re = pcre_compile2(f[3], 0, NULL, &pcre_err, &pcre_offset, NULL);
+ re = pcre2_compile((PCRE2_SPTR)f[3], PCRE2_ZERO_TERMINATED, 0, &pcre_err, &pcre_offset, NULL);
if (!re) {
- cli_sendv(cli, "match: %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));
+ cli_sendv(cli, "match: %s at %i\r\n", err_msg, (int)pcre_offset);
return CLI_CMD_OK;
}
@@ -145,8 +157,12 @@ static int terminate_exec1(char * const *f, int f_cnt, void *cli)
list_for_each_entry(ses, &ses_list, entry) {
if (!ses->username)
continue;
- if (pcre_exec(re, NULL, ses->username, strlen(ses->username), 0, 0, NULL, 0) < 0)
+ pcre2_match_data *match_data = pcre2_match_data_create(0, NULL);
+ if (pcre2_match(re, (PCRE2_SPTR)ses->username, strlen(ses->username), 0, 0, match_data, NULL) < 0) {
+ pcre2_match_data_free(match_data);
continue;
+ }
+ pcre2_match_data_free(match_data);
if (hard)
triton_context_call(ses->ctrl->ctx, (triton_event_func)__terminate_hard, ses);
else
@@ -154,7 +170,7 @@ static int terminate_exec1(char * const *f, int f_cnt, void *cli)
}
pthread_rwlock_unlock(&ses_lock);
- pcre_free(re);
+ pcre2_code_free(re);
return CLI_CMD_OK;
}
@@ -324,26 +340,41 @@ static int shutdown_exec(const char *cmd, char * const *f, int f_cnt, void *cli)
}
//==========================
-static int conf_reload_res;
-static struct triton_context_t *conf_reload_ctx;
-static void conf_reload_notify(int r)
+struct conf_reload_req {
+ struct triton_context_t *ctx;
+ int res;
+};
+static void conf_reload_notify(int r, void *arg)
{
+ struct conf_reload_req *req = arg;
+
if (!r)
triton_event_fire(EV_CONFIG_RELOAD, NULL);
- conf_reload_res = r;
- triton_context_wakeup(conf_reload_ctx);
+ req->res = r;
+ triton_context_wakeup(req->ctx);
}
static int reload_exec(const char *cmd, char * const *f, int f_cnt, void *cli)
{
- if (f_cnt == 1) {
- conf_reload_ctx = triton_context_self();
- triton_conf_reload(conf_reload_notify);
- triton_context_schedule();
- if (conf_reload_res)
- cli_send(cli, "failed\r\n");
- return CLI_CMD_OK;
- } else
+ struct conf_reload_req *req;
+
+ if (f_cnt != 1)
return CLI_CMD_SYNTAX;
+
+ /* heap-allocated: triton_context_schedule() can migrate this
+ * context to another worker thread's stack before notify runs */
+ req = _malloc(sizeof(*req));
+ req->ctx = triton_context_self();
+
+ if (triton_conf_reload(conf_reload_notify, req)) {
+ _free(req);
+ cli_send(cli, "reload is already in progress\r\n");
+ return CLI_CMD_OK;
+ }
+ triton_context_schedule();
+ if (req->res)
+ cli_send(cli, "failed\r\n");
+ _free(req);
+ return CLI_CMD_OK;
}
static void reload_help(char * const *fields, int fields_cnt, void *client)
diff --git a/accel-pppd/cli/tcp.c b/accel-pppd/cli/tcp.c
index 8952661a..beba16ca 100644
--- a/accel-pppd/cli/tcp.c
+++ b/accel-pppd/cli/tcp.c
@@ -24,7 +24,7 @@ struct tcp_client_t {
struct cli_client_t cli_client;
struct list_head entry;
struct triton_md_handler_t hnd;
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
struct list_head xmit_queue;
struct buffer_t *xmit_buf;
uint8_t *cmdline;
@@ -174,8 +174,10 @@ static int cln_read(struct triton_md_handler_t *h)
goto disconn_hard;
cln->auth = 1;
} else {
- if (conf_verbose == 2)
- log_info2("cli: %s: %s\n", inet_ntoa(cln->addr.sin_addr), cln->cmdline);
+ if (conf_verbose == 2) {
+ char buf[INET6_ADDRSTRLEN];
+ log_info2("cli: %s: %s\n", cli_addr_str(&cln->addr, buf, sizeof(buf)), cln->cmdline);
+ }
cli_process_cmd(&cln->cli_client);
}
@@ -248,12 +250,14 @@ disconn:
static int serv_read(struct triton_md_handler_t *h)
{
- struct sockaddr_in addr;
- socklen_t size = sizeof(addr);
+ struct sockaddr_storage addr;
+ socklen_t size;
int sock;
struct tcp_client_t *conn;
+ char buf[INET6_ADDRSTRLEN];
while(1) {
+ size = sizeof(addr);
sock = accept(h->fd, (struct sockaddr *)&addr, &size);
if (sock < 0) {
if (errno == EAGAIN)
@@ -263,7 +267,7 @@ static int serv_read(struct triton_md_handler_t *h)
}
if (conf_verbose)
- log_info2("cli: tcp: new connection from %s\n", inet_ntoa(addr.sin_addr));
+ log_info2("cli: tcp: new connection from %s\n", cli_addr_str(&addr, buf, sizeof(buf)));
if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
log_error("cli: tcp: failed to set nonblocking mode: %s, closing connection...\n", strerror(errno));
@@ -320,9 +324,16 @@ static struct triton_md_handler_t serv_hnd = {
static void start_server(const char *host, int port)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int f = 1;
+
+ if (cli_bind_addr(host, port, &addr, &addrlen) < 0) {
+ log_emerg("cli: tcp: invalid address '%s'\n", host);
+ return;
+ }
- serv_hnd.fd = socket(PF_INET, SOCK_STREAM, 0);
+ serv_hnd.fd = socket(addr.ss_family, SOCK_STREAM, 0);
if (serv_hnd.fd < 0) {
log_emerg("cli: tcp: failed to create server socket: %s\n", strerror(errno));
return;
@@ -330,16 +341,8 @@ static void start_server(const char *host, int port)
fcntl(serv_hnd.fd, F_SETFD, fcntl(serv_hnd.fd, F_GETFD) | FD_CLOEXEC);
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- if (host)
- addr.sin_addr.s_addr = inet_addr(host);
- else
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &serv_hnd.fd, 4);
- if (bind (serv_hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
+ setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &f, sizeof(f));
+ if (bind (serv_hnd.fd, (struct sockaddr *) &addr, addrlen) < 0) {
log_emerg("cli: tcp: failed to bind socket: %s\n", strerror(errno));
close(serv_hnd.fd);
return;
@@ -378,7 +381,8 @@ static void load_config(void)
static void init(void)
{
const char *opt;
- char *host, *d;
+ const char *addr;
+ char *host;
int port;
opt = conf_get_opt("cli", "tcp");
@@ -386,20 +390,14 @@ static void init(void)
return;
host = strdup(opt);
- d = strstr(host, ":");
- if (!d)
- goto err_fmt;
-
- *d = 0;
- port = atoi(d + 1);
- if (port <= 0)
+ if (cli_parse_hostport(host, &addr, &port) < 0)
goto err_fmt;
load_config();
temp_buf = malloc(RECV_BUF_SIZE);
- start_server(host, port);
+ start_server(addr, port);
triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
diff --git a/accel-pppd/cli/telnet.c b/accel-pppd/cli/telnet.c
index 7d5009c2..88a9d8b0 100644
--- a/accel-pppd/cli/telnet.c
+++ b/accel-pppd/cli/telnet.c
@@ -37,7 +37,7 @@ struct telnet_client_t {
struct cli_client_t cli_client;
struct list_head entry;
struct triton_md_handler_t hnd;
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
struct list_head xmit_queue;
struct buffer_t *xmit_buf;
int xmit_pos;
@@ -305,8 +305,10 @@ static int telnet_input_char(struct telnet_client_t *cln, uint8_t c)
list_add(&b->entry, cln->history.next);
cln->history_pos = cln->history.next;
- if (conf_verbose == 2)
- log_info2("cli: %s: %s\n", inet_ntoa(cln->addr.sin_addr), cln->cmdline);
+ if (conf_verbose == 2) {
+ char abuf[INET6_ADDRSTRLEN];
+ log_info2("cli: %s: %s\n", cli_addr_str(&cln->addr, abuf, sizeof(abuf)), cln->cmdline);
+ }
if (cli_process_cmd(&cln->cli_client))
return -1;
@@ -556,13 +558,15 @@ disconn:
static int serv_read(struct triton_md_handler_t *h)
{
- struct sockaddr_in addr;
- socklen_t size = sizeof(addr);
+ struct sockaddr_storage addr;
+ socklen_t size;
int sock;
struct telnet_client_t *conn;
struct buffer_t *b, *b2;
+ char abuf[INET6_ADDRSTRLEN];
while(1) {
+ size = sizeof(addr);
sock = accept(h->fd, (struct sockaddr *)&addr, &size);
if (sock < 0) {
if (errno == EAGAIN)
@@ -572,7 +576,7 @@ static int serv_read(struct triton_md_handler_t *h)
}
if (conf_verbose)
- log_info2("cli: telnet: new connection from %s\n", inet_ntoa(addr.sin_addr));
+ log_info2("cli: telnet: new connection from %s\n", cli_addr_str(&addr, abuf, sizeof(abuf)));
fcntl(sock, F_SETFL, O_NONBLOCK);
fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC);
@@ -657,9 +661,16 @@ static struct triton_md_handler_t serv_hnd = {
static void start_server(const char *host, int port)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int f = 1;
+
+ if (cli_bind_addr(host, port, &addr, &addrlen) < 0) {
+ log_emerg("cli: telnet: invalid address '%s'\n", host);
+ return;
+ }
- serv_hnd.fd = socket(PF_INET, SOCK_STREAM, 0);
+ serv_hnd.fd = socket(addr.ss_family, SOCK_STREAM, 0);
if (serv_hnd.fd < 0) {
log_emerg("cli: telnet: failed to create server socket: %s\n", strerror(errno));
return;
@@ -667,16 +678,8 @@ static void start_server(const char *host, int port)
fcntl(serv_hnd.fd, F_SETFD, fcntl(serv_hnd.fd, F_GETFD) | FD_CLOEXEC);
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- if (host)
- addr.sin_addr.s_addr = inet_addr(host);
- else
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &serv_hnd.fd, 4);
- if (bind (serv_hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
+ setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &f, sizeof(f));
+ if (bind (serv_hnd.fd, (struct sockaddr *) &addr, addrlen) < 0) {
log_emerg("cli: telnet: failed to bind socket: %s\n", strerror(errno));
close(serv_hnd.fd);
return;
@@ -755,7 +758,8 @@ static void load_config(void)
static void init(void)
{
const char *opt;
- char *host, *d;
+ const char *addr;
+ char *host;
int port;
opt = conf_get_opt("cli", "telnet");
@@ -763,13 +767,7 @@ static void init(void)
return;
host = strdup(opt);
- d = strstr(host, ":");
- if (!d)
- goto err_fmt;
-
- *d = 0;
- port = atoi(d + 1);
- if (port <= 0)
+ if (cli_parse_hostport(host, &addr, &port) < 0)
goto err_fmt;
opt = conf_get_opt("cli", "history-file");
@@ -783,7 +781,7 @@ static void init(void)
load_history_file();
- start_server(host, port);
+ start_server(addr, port);
atexit(save_history_file);
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
diff --git a/accel-pppd/extra/CMakeLists.txt b/accel-pppd/extra/CMakeLists.txt
index 6badcd5e..08466f24 100644
--- a/accel-pppd/extra/CMakeLists.txt
+++ b/accel-pppd/extra/CMakeLists.txt
@@ -5,11 +5,13 @@ set_property(TARGET pppd_compat PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/l
ADD_LIBRARY(ippool SHARED ippool.c)
ADD_LIBRARY(ipv6pool SHARED ipv6pool.c)
ADD_LIBRARY(chap-secrets SHARED chap-secrets.c)
+TARGET_LINK_LIBRARIES(chap-secrets ${crypto_lib})
ADD_LIBRARY(logwtmp SHARED logwtmp.c)
TARGET_LINK_LIBRARIES(logwtmp util)
ADD_LIBRARY(connlimit SHARED connlimit.c)
+ADD_LIBRARY(metrics SHARED metrics.c)
-INSTALL(TARGETS pppd_compat ippool ipv6pool sigchld chap-secrets logwtmp connlimit
+INSTALL(TARGETS pppd_compat ippool ipv6pool sigchld chap-secrets logwtmp connlimit metrics
LIBRARY DESTINATION lib${LIB_SUFFIX}/accel-ppp
)
diff --git a/accel-pppd/extra/bitpool.h b/accel-pppd/extra/bitpool.h
new file mode 100644
index 00000000..fa739a28
--- /dev/null
+++ b/accel-pppd/extra/bitpool.h
@@ -0,0 +1,88 @@
+#ifndef __BITPOOL_H
+#define __BITPOOL_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * Fixed-size bit array shared by the IPv4/IPv6 address pool allocators.
+ *
+ * Semantics: bit == 1 => allocated/unavailable, bit == 0 => free. A freshly
+ * calloc'd map is therefore all-free. The caller is responsible for pre-setting
+ * to 1 any bit it never wants handed out:
+ * - "remainder" bits in the final word (indices >= count), and
+ * - reserved in-range bits (e.g. the p2p gateway collision),
+ * so the word-at-a-time scan can never return a non-existent unit.
+ */
+
+typedef unsigned long bm_word_t;
+#define BM_WORD_BITS (8 * sizeof(bm_word_t))
+#define BM_NWORDS(n) (((n) + BM_WORD_BITS - 1) / BM_WORD_BITS)
+#define BM_INVALID ((uint64_t)-1)
+
+static inline void bm_set(bm_word_t *bm, uint64_t i)
+{
+ bm[i / BM_WORD_BITS] |= (bm_word_t)1 << (i % BM_WORD_BITS);
+}
+
+static inline void bm_clear(bm_word_t *bm, uint64_t i)
+{
+ bm[i / BM_WORD_BITS] &= ~((bm_word_t)1 << (i % BM_WORD_BITS));
+}
+
+static inline int bm_test(const bm_word_t *bm, uint64_t i)
+{
+ return (bm[i / BM_WORD_BITS] >> (i % BM_WORD_BITS)) & 1;
+}
+
+/*
+ * Find the first free (0) bit at or after `from`, wrapping once back to 0.
+ * `count` is the number of valid bits; remainder bits in the final word must
+ * already be set to 1 by the caller so they are never returned. Returns the
+ * bit index in [0,count), or BM_INVALID if every bit is used.
+ *
+ * Word-at-a-time: a fully-used word (~w == 0) is skipped in one branch, so the
+ * scan is O(1) amortized for a sparse pool and O(count/word) worst case.
+ */
+static inline uint64_t bm_find_free(const bm_word_t *bm, uint64_t count, uint64_t from)
+{
+ uint64_t nwords, fw, i;
+ unsigned fb;
+
+ if (!count)
+ return BM_INVALID;
+ if (from >= count)
+ from = 0;
+
+ nwords = BM_NWORDS(count);
+ fw = from / BM_WORD_BITS;
+ fb = from % BM_WORD_BITS;
+
+ /* Probe nwords+1 times: the start word is examined first with its low bits
+ * (< from) masked off, and once more at the end with only those low bits,
+ * so every bit is considered exactly once across the wrap. */
+ for (i = 0; i <= nwords; i++) {
+ uint64_t word = fw + i;
+ bm_word_t inv;
+
+ if (word >= nwords)
+ word -= nwords;
+
+ inv = ~bm[word];
+
+ if (i == 0)
+ inv &= ~(((bm_word_t)1 << fb) - 1); /* skip bits below `from` */
+ else if (i == nwords)
+ inv &= ((bm_word_t)1 << fb) - 1; /* wrapped: only those skipped bits */
+
+ if (inv) {
+ uint64_t bit = word * BM_WORD_BITS + __builtin_ctzl(inv);
+ if (bit < count)
+ return bit;
+ }
+ }
+
+ return BM_INVALID;
+}
+
+#endif
diff --git a/accel-pppd/extra/bitpool_test.c b/accel-pppd/extra/bitpool_test.c
new file mode 100644
index 00000000..347f1770
--- /dev/null
+++ b/accel-pppd/extra/bitpool_test.c
@@ -0,0 +1,256 @@
+/*
+ * Standalone sanity test for the bitmap IP-pool allocator core.
+ *
+ * Not part of the cmake build. Compile and run with:
+ * gcc -O2 -Wall -o /tmp/bitpool_test accel-pppd/extra/bitpool_test.c && /tmp/bitpool_test
+ *
+ * It validates:
+ * - bitpool.h: bm_set/clear/test, bm_find_free wrap + remainder bits +
+ * exhaustion, and that every freed bit is re-handed exactly once.
+ * - IPv4 addr<->bit round-trip for p2p and net30 geometry, incl. alignment
+ * and out-of-range rejection.
+ * - IPv6 prefix<->bit round-trip (the exact helpers used by ipv6pool.c),
+ * cross-checked against __int128 reference math, incl. prefix_len==128,
+ * mask<=64 / mask>64, and an unaligned base.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "bitpool.h"
+
+static int failures;
+#define CHECK(cond) do { if (!(cond)) { \
+ fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); failures++; } } while (0)
+
+/* ---- mimic a finalized range bitmap: count bits free, remainder bits set ---- */
+static bm_word_t *make_map(uint64_t count)
+{
+ uint64_t nw = BM_NWORDS(count), b;
+ bm_word_t *bm = calloc(nw, sizeof(bm_word_t));
+ for (b = count; b < nw * BM_WORD_BITS; b++)
+ bm_set(bm, b);
+ return bm;
+}
+
+static void test_bitmap_basic(void)
+{
+ uint64_t count = 200; /* not a multiple of 64 -> exercises remainder */
+ bm_word_t *bm = make_map(count);
+ uint64_t i, bit, n = 0;
+ char *seen = calloc(count, 1);
+
+ /* allocate the whole pool via rolling cursor; each bit unique and < count */
+ uint64_t cursor = 0;
+ while ((bit = bm_find_free(bm, count, cursor)) != BM_INVALID) {
+ CHECK(bit < count);
+ CHECK(!seen[bit]);
+ seen[bit] = 1;
+ bm_set(bm, bit);
+ cursor = bit + 1;
+ n++;
+ CHECK(n <= count);
+ }
+ CHECK(n == count); /* exactly count handed out */
+ for (i = 0; i < count; i++)
+ CHECK(seen[i]); /* every unit handed out once */
+ CHECK(bm_find_free(bm, count, 0) == BM_INVALID); /* exhausted */
+
+ /* free three, confirm exactly those three come back */
+ bm_clear(bm, 5); bm_clear(bm, 130); bm_clear(bm, 199);
+ memset(seen, 0, count);
+ for (n = 0; (bit = bm_find_free(bm, count, 0)) != BM_INVALID; n++) {
+ CHECK(bit == 5 || bit == 130 || bit == 199);
+ seen[bit] = 1; bm_set(bm, bit);
+ }
+ CHECK(n == 3 && seen[5] && seen[130] && seen[199]);
+
+ /* wrap-around: only bit 1 free, search starting past it must still find it */
+ memset(bm, 0xff, BM_NWORDS(count) * sizeof(bm_word_t));
+ bm_clear(bm, 1);
+ CHECK(bm_find_free(bm, count, 50) == 1);
+ CHECK(bm_find_free(bm, count, 0) == 1);
+
+ /* zero-size pool */
+ CHECK(bm_find_free(bm, 0, 0) == BM_INVALID);
+
+ free(seen);
+ free(bm);
+ printf("ok bitmap basic/wrap/remainder/exhaustion\n");
+}
+
+/* ---- IPv4 geometry (mirrors ippool.c range_addr_to_bit / bit->addr) ---- */
+static uint64_t v4_addr_to_bit(uint32_t start, uint32_t step, int peer_off,
+ uint64_t count, uint32_t peer)
+{
+ uint32_t base, delta;
+ uint64_t bit;
+ if (peer < (uint32_t)peer_off) return BM_INVALID;
+ base = peer - peer_off;
+ if (base < start) return BM_INVALID;
+ delta = base - start;
+ if (delta % step) return BM_INVALID;
+ bit = delta / step;
+ if (bit >= count) return BM_INVALID;
+ return bit;
+}
+
+static void test_v4(void)
+{
+ /* p2p: 10.0.0.0 - 10.0.1.255 (512 addrs), step 1, peer_off 0 */
+ uint32_t start = (10u << 24);
+ uint32_t end = (10u << 24) | 0x1ff;
+ uint64_t count = (uint64_t)(end - start) + 1;
+ uint64_t i;
+ CHECK(count == 512);
+ for (i = 0; i < count; i++) {
+ uint32_t peer = start + (uint32_t)i * 1 + 0;
+ CHECK(v4_addr_to_bit(start, 1, 0, count, peer) == i);
+ }
+ CHECK(v4_addr_to_bit(start, 1, 0, count, start - 1) == BM_INVALID);
+ CHECK(v4_addr_to_bit(start, 1, 0, count, end + 1) == BM_INVALID);
+ printf("ok v4 p2p round-trip (512 addrs)\n");
+
+ /* net30: same range, step 4, gw_off 1, peer_off 2 -> 128 blocks */
+ count = ((uint64_t)(end - start) + 1) / 4;
+ CHECK(count == 128);
+ for (i = 0; i < count; i++) {
+ uint32_t base = start + (uint32_t)i * 4;
+ uint32_t peer = base + 2; /* .2 client */
+ CHECK(v4_addr_to_bit(start, 4, 2, count, peer) == i);
+ /* .1 router and .0/.3 are NOT valid leases */
+ CHECK(v4_addr_to_bit(start, 4, 2, count, base + 1) == BM_INVALID);
+ CHECK(v4_addr_to_bit(start, 4, 2, count, base + 0) == BM_INVALID);
+ CHECK(v4_addr_to_bit(start, 4, 2, count, base + 3) == BM_INVALID);
+ }
+ printf("ok v4 net30 round-trip + alignment (128 blocks)\n");
+}
+
+/* ---- IPv6 helpers: EXACT copies of the ones in ipv6pool.c ---- */
+static void in6_sub(uint8_t *o, const uint8_t *a, const uint8_t *b)
+{
+ int i, borrow = 0;
+ for (i = 15; i >= 0; i--) {
+ int v = (int)a[i] - b[i] - borrow;
+ if (v < 0) { v += 256; borrow = 1; } else borrow = 0;
+ o[i] = v;
+ }
+}
+static int in6_shr_u64(const uint8_t *d, int shift, uint64_t *out)
+{
+ int p; uint64_t v = 0;
+ for (p = 0; p < 64; p++) {
+ int sp = shift + p;
+ if (sp < 128 && (d[15 - (sp >> 3)] & (1 << (sp & 7)))) v |= (uint64_t)1 << p;
+ }
+ for (p = shift + 64; p < 128; p++)
+ if (d[15 - (p >> 3)] & (1 << (p & 7))) return -1;
+ *out = v; return 0;
+}
+static void in6_index_to_addr(uint8_t *out, const uint8_t *start, uint64_t i, int shift)
+{
+ uint8_t add[16] = { 0 }; int p, b, carry = 0;
+ for (p = 0; p < 64; p++)
+ if (i & ((uint64_t)1 << p)) { int sp = shift + p; if (sp < 128) add[15 - (sp >> 3)] |= (1 << (sp & 7)); }
+ memcpy(out, start, 16);
+ for (b = 15; b >= 0; b--) { int s = out[b] + add[b] + carry; out[b] = s & 0xff; carry = s >> 8; }
+}
+static int in6_cmp(const uint8_t *a, const uint8_t *b)
+{
+ int i; for (i = 0; i < 16; i++) { if (a[i] < b[i]) return -1; if (a[i] > b[i]) return 1; } return 0;
+}
+static uint64_t v6_addr_to_bit(const uint8_t *start, int shift, uint64_t count, const uint8_t *addr)
+{
+ uint8_t delta[16]; uint64_t i; int p;
+ if (in6_cmp(addr, start) < 0) return BM_INVALID;
+ in6_sub(delta, addr, start);
+ for (p = 0; p < shift; p++)
+ if (delta[15 - (p >> 3)] & (1 << (p & 7))) return BM_INVALID;
+ if (in6_shr_u64(delta, shift, &i)) return BM_INVALID;
+ if (i >= count) return BM_INVALID;
+ return i;
+}
+
+/* ---- __int128 reference ---- */
+static __uint128_t to_u128(const uint8_t a[16]) { __uint128_t v = 0; int i; for (i = 0; i < 16; i++) v = (v << 8) | a[i]; return v; }
+static void from_u128(uint8_t a[16], __uint128_t v) { int i; for (i = 15; i >= 0; i--) { a[i] = v & 0xff; v >>= 8; } }
+
+static void test_v6_one(const char *label, const uint8_t start[16], int mask, int prefix_len)
+{
+ int shift = 128 - prefix_len;
+ __uint128_t s = to_u128(start);
+ __uint128_t hostmask = (mask == 0) ? ~(__uint128_t)0 : (((__uint128_t)1 << (128 - mask)) - 1);
+ __uint128_t end = s | hostmask;
+ uint8_t endb[16]; uint8_t delta[16]; uint64_t span, count, i, step_lo;
+ __uint128_t step = (__uint128_t)1 << shift;
+
+ from_u128(endb, end);
+ in6_sub(delta, endb, start);
+ CHECK(in6_shr_u64(delta, shift, &span) == 0);
+ count = span + 1;
+ CHECK(count == (uint64_t)(((end - s) >> shift) + 1)); /* matches reference */
+
+ /* round-trip boundary + sampled indices */
+ uint64_t samples[] = { 0, 1, count / 2, count - 1 };
+ for (size_t k = 0; k < sizeof(samples) / sizeof(samples[0]); k++) {
+ i = samples[k];
+ if (i >= count) continue;
+ uint8_t addr[16]; in6_index_to_addr(addr, start, i, shift);
+ __uint128_t ref = s + (__uint128_t)i * step;
+ uint8_t refb[16]; from_u128(refb, ref);
+ CHECK(memcmp(addr, refb, 16) == 0); /* bit->addr matches ref */
+ CHECK(v6_addr_to_bit(start, shift, count, addr) == i); /* addr->bit round-trip */
+ /* misaligned address (only when there is room below a prefix) */
+ if (shift > 0) {
+ uint8_t bad[16]; memcpy(bad, addr, 16); bad[15] |= 1;
+ CHECK(v6_addr_to_bit(start, shift, count, bad) == BM_INVALID);
+ }
+ }
+ /* below start and past end reject */
+ if (s > 0) { uint8_t below[16]; from_u128(below, s - 1); CHECK(v6_addr_to_bit(start, shift, count, below) == BM_INVALID); }
+ { uint8_t past[16]; from_u128(past, s + (__uint128_t)count * step); CHECK(v6_addr_to_bit(start, shift, count, past) == BM_INVALID); }
+
+ (void)step_lo;
+ printf("ok v6 %s (mask=%d plen=%d shift=%d count=%llu)\n",
+ label, mask, prefix_len, shift, (unsigned long long)count);
+}
+
+static void test_v6(void)
+{
+ uint8_t a[16];
+
+ /* 2001:db8::/48 carved into /64s -> 65536 prefixes (mask<=64) */
+ memset(a, 0, 16); a[0] = 0x20; a[1] = 0x01; a[2] = 0x0d; a[3] = 0xb8;
+ test_v6_one("/48->/64", a, 48, 64);
+
+ /* mask>64: 2001:db8:0:0:8000::/72 -> /80 (mask>64 path) */
+ memset(a, 0, 16); a[0] = 0x20; a[1] = 0x01; a[2] = 0x0d; a[3] = 0xb8; a[8] = 0x80;
+ test_v6_one("/72->/80", a, 72, 80);
+
+ /* prefix_len == 128: single addresses, step 1 */
+ memset(a, 0, 16); a[0] = 0xfc; a[15] = 0x00;
+ test_v6_one("/120->/128", a, 120, 128);
+
+ /* unaligned base within the mask host bits (start not on a /64 boundary) */
+ memset(a, 0, 16); a[0] = 0x20; a[1] = 0x01; a[2] = 0x0d; a[3] = 0xb8; a[7] = 0x05;
+ test_v6_one("/48->/64 unaligned base", a, 48, 64);
+
+ /* big delegation: /32 -> /56 = 2^24 prefixes (the cap boundary) */
+ memset(a, 0, 16); a[0] = 0x2a; a[1] = 0x00;
+ test_v6_one("/32->/56 (cap)", a, 32, 56);
+}
+
+int main(void)
+{
+ test_bitmap_basic();
+ test_v4();
+ test_v6();
+ if (failures) {
+ printf("\n%d CHECK(s) FAILED\n", failures);
+ return 1;
+ }
+ printf("\nALL TESTS PASSED\n");
+ return 0;
+}
diff --git a/accel-pppd/extra/chap-secrets.c b/accel-pppd/extra/chap-secrets.c
index b486bb11..5fd0102c 100644
--- a/accel-pppd/extra/chap-secrets.c
+++ b/accel-pppd/extra/chap-secrets.c
@@ -6,9 +6,10 @@
#include <netinet/in.h>
#include <arpa/inet.h>
-#ifdef CRYPTO_OPENSSL
-#include "crypto.h"
-#endif
+#include <openssl/md4.h>
+#include <openssl/sha.h>
+#include <openssl/des.h>
+#include <openssl/evp.h>
#include "pwdb.h"
#include "ipdb.h"
@@ -29,13 +30,11 @@ static int conf_netmask = 0;
static void *pd_key;
static struct ipdb_t ipdb;
-#ifdef CRYPTO_OPENSSL
struct hash_chain
{
struct list_head entry;
const EVP_MD *md;
};
-#endif
struct cs_pd_t
{
@@ -46,9 +45,7 @@ struct cs_pd_t
char *pool;
};
-#ifdef CRYPTO_OPENSSL
static LIST_HEAD(hash_chain);
-#endif
static char *skip_word(char *ptr)
{
@@ -126,19 +123,16 @@ static struct cs_pd_t *create_pd(struct ap_session *ses, const char *username)
int n;
struct cs_pd_t *pd;
struct in_addr in;
-#ifdef CRYPTO_OPENSSL
char username_hash[EVP_MAX_MD_SIZE * 2 + 1];
uint8_t hash[EVP_MAX_MD_SIZE];
struct hash_chain *hc;
EVP_MD_CTX *md_ctx = NULL;
char c;
int i;
-#endif
if (!conf_chap_secrets)
return NULL;
-#ifdef CRYPTO_OPENSSL
if (conf_encrypted && !list_empty(&hash_chain)) {
unsigned int size = 0;
list_for_each_entry(hc, &hash_chain, entry) {
@@ -156,7 +150,6 @@ static struct cs_pd_t *create_pd(struct ap_session *ses, const char *username)
username = username_hash;
}
-#endif
f = fopen(conf_chap_secrets, "r");
if (!f) {
@@ -192,10 +185,8 @@ out:
return NULL;
found:
-#ifdef CRYPTO_OPENSSL
if (conf_encrypted && strlen(ptr[1]) != 32)
goto out;
-#endif
pd = _malloc(sizeof(*pd));
if (!pd) {
@@ -205,7 +196,6 @@ found:
memset(pd, 0, sizeof(*pd));
pd->pd.key = &pd_key;
-#ifdef CRYPTO_OPENSSL
if (conf_encrypted) {
pd->passwd = _malloc(16);
if (!pd->passwd) {
@@ -221,7 +211,6 @@ found:
ptr[1][i*2 + 2] = c;
}
} else
-#endif
{
pd->passwd = _strdup(ptr[1]);
if (!pd->passwd) {
@@ -335,10 +324,8 @@ static char* get_passwd(struct pwdb_t *pwdb, struct ap_session *ses, const char
{
struct cs_pd_t *pd = find_pd(ses);
-#ifdef CRYPTO_OPENSSL
if (conf_encrypted)
return NULL;
-#endif
if (!pd)
pd = create_pd(ses, username);
@@ -349,7 +336,6 @@ static char* get_passwd(struct pwdb_t *pwdb, struct ap_session *ses, const char
return _strdup(pd->passwd);
}
-#ifdef CRYPTO_OPENSSL
static void des_encrypt(const uint8_t *input, const uint8_t *key, uint8_t *output)
{
int i, j, parity;
@@ -684,7 +670,6 @@ static int check_passwd(struct pwdb_t *pwdb, struct ap_session *ses, pwdb_callba
return r;
}
-#endif
static struct ipdb_t ipdb = {
.get_ipv4 = get_ip,
@@ -692,12 +677,9 @@ static struct ipdb_t ipdb = {
static struct pwdb_t pwdb = {
.get_passwd = get_passwd,
-#ifdef CRYPTO_OPENSSL
.check = check_passwd,
-#endif
};
-#ifdef CRYPTO_OPENSSL
static void clear_hash_chain(void)
{
struct hash_chain *hc;
@@ -731,7 +713,6 @@ static void parse_hash_chain(const char *opt)
ptr1 = ptr2 + 1;
}
}
-#endif
static void parse_gw_ip_address(const char *opt)
{
@@ -792,12 +773,10 @@ static void load_config(void)
else
conf_encrypted = 0;
-#ifdef CRYPTO_OPENSSL
clear_hash_chain();
opt = conf_get_opt("chap-secrets", "username-hash");
if (opt)
parse_hash_chain(opt);
-#endif
}
static void init(void)
diff --git a/accel-pppd/extra/ippool.c b/accel-pppd/extra/ippool.c
index 3ae48e95..28aecd95 100644
--- a/accel-pppd/extra/ippool.c
+++ b/accel-pppd/extra/ippool.c
@@ -3,138 +3,161 @@
#include <stdint.h>
#include <unistd.h>
#include <string.h>
+#include <pthread.h>
#include <arpa/inet.h>
+#include "triton.h"
#include "events.h"
#include "log.h"
#include "list.h"
#include "spinlock.h"
#include "backup.h"
+#include "ap_session.h"
#include "ap_session_backup.h"
#include "ipdb.h"
+#include "cli.h"
#ifdef RADIUS
#include "radius.h"
#endif
+#include "bitpool.h"
#include "memdebug.h"
-struct ippool_t;
-
-typedef void (*generate_func)(struct ippool_t *);
+/*
+ * Bitmap IPv4 address pool.
+ *
+ * Each pool holds a list of contiguous ranges; each range owns one bitmap
+ * (1 bit per allocatable block). A lease is a small per-session malloc wrapper
+ * around the ipv4db_item_t returned to the session, so pool memory is never
+ * shared/mutated by sessions. The whole pool set is rebuilt and swapped on
+ * EV_CONFIG_RELOAD, reconciling live sessions against the new ranges.
+ *
+ * TODO: a sparse/hierarchical allocator would lift IPPOOL_MAX_BITS; the dense
+ * bitmap is fine for realistic IPv4 ranges (a /8 sits right at the cap).
+ */
+
+#define IPPOOL_MAX_BITS (1u << 24) /* ~16.7M units, ~2MB bitmap */
+
+enum {
+ ORPHAN_KEEP = 0, /* keep the session, no-op its later put */
+ ORPHAN_DISCONNECT, /* terminate sessions whose address left the pools */
+};
-struct ippool_t
-{
+/* one contiguous range = one bitmap. step/offsets encode the allocator:
+ * p2p: step=1, gw_offset=-1 (local addr from gw-ip-address/0), peer_offset=0
+ * net30: step=4, gw_offset=1 (.1 router), peer_offset=2 (.2 client) */
+struct ip_range {
struct list_head entry;
- char *name;
- struct list_head gw_list;
- struct list_head tunnel_list;
- struct list_head items;
- uint32_t startip;
- uint32_t endip;
- struct ippool_t *next;
- generate_func generate;
- spinlock_t lock;
+ uint32_t start; /* host order, first block base */
+ uint32_t end; /* host order, last address in range */
+ uint32_t step;
+ int gw_offset;
+ int peer_offset;
+ uint64_t count; /* number of blocks = bits in bitmap */
+ uint64_t cursor; /* round-robin search hint */
+ uint64_t used;
+ bm_word_t *bitmap;
};
-struct ippool_item_t
-{
- struct list_head entry;
- struct ippool_t *pool;
- struct ipv4db_item_t it;
+struct ip_pool {
+ struct list_head entry; /* in set->pools; named pools only */
+ char *name; /* NULL for the default pool */
+ struct ip_pool *next; /* overflow chain */
+ struct list_head ranges;
+ spinlock_t lock; /* guards every range's bitmap/cursor/used */
};
-struct ipaddr_t
-{
+struct pool_set {
+ struct list_head pools; /* named pools */
+ struct ip_pool *def_pool; /* unnamed default (not on `pools`) */
+ in_addr_t gw_ip_address; /* prevents the configured gateway address from being handed out as a peer address */
+ int shuffle;
+ int orphan_policy;
+};
+
+struct ip_lease {
+ struct ip_pool *pool; /* NULL once orphaned by a reload */
+ struct ip_range *range; /* Range that supplied this lease's peer address; NULL if orphaned */
+ struct ipv4db_item_t it; /* ses->ipv4 = &it */
+};
+
+/* collected during reconcile, acted on after locks are dropped */
+struct disc_node {
struct list_head entry;
- in_addr_t addr;
+ struct ap_session *ses;
};
static struct ipdb_t ipdb;
-static in_addr_t conf_gw_ip_address;
-static int conf_shuffle;
+static pthread_rwlock_t pool_set_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+static struct pool_set *cur_set;
#ifdef RADIUS
static int conf_vendor = 0;
static int conf_attr = 88; // Framed-Pool
-#endif
-
-static int cnt;
-static LIST_HEAD(pool_list);
-static struct ippool_t *def_pool;
-struct ippool_t *create_pool(char *name)
+static int parse_attr_opt(const char *opt)
{
- struct ippool_t *p = malloc(sizeof(*p));
+ struct rad_dict_attr_t *attr;
+ struct rad_dict_vendor_t *vendor;
- memset(p, 0, sizeof(*p));
- p->name = name;
+ if (conf_vendor)
+ vendor = rad_dict_find_vendor_id(conf_vendor);
+ else
+ vendor = NULL;
- INIT_LIST_HEAD(&p->gw_list);
- INIT_LIST_HEAD(&p->tunnel_list);
- INIT_LIST_HEAD(&p->items);
- spinlock_init(&p->lock);
+ if (conf_vendor) {
+ if (vendor)
+ attr = rad_dict_find_vendor_attr(vendor, opt);
+ else
+ attr = NULL;
+ } else
+ attr = rad_dict_find_attr(opt);
- if (name)
- list_add_tail(&p->entry, &pool_list);
+ if (attr)
+ return attr->id;
- return p;
+ return atoi(opt);
}
-struct ippool_t *find_pool(char *name, int create)
+static int parse_vendor_opt(const char *opt)
{
- struct ippool_t *p;
-
- list_for_each_entry(p, &pool_list, entry) {
- if (!strcmp(p->name, name))
- return p;
- }
+ struct rad_dict_vendor_t *vendor;
- if (create)
- return create_pool(name);
+ vendor = rad_dict_find_vendor_name(opt);
+ if (vendor)
+ return vendor->id;
- return NULL;
+ return atoi(opt);
}
+#endif
-static void parse_gw_ip_address(const char *val)
+/* ===== randomness for shuffle ===== */
+
+static uint64_t rand_u64(void)
{
- char addr[17];
- char *ptr;
+ uint64_t r = 0;
- if (!val)
- return;
+ if (read(urandom_fd, &r, sizeof(r)) != sizeof(r))
+ r = 0;
- ptr = strchr(val, '/');
- if (ptr) {
- // safeguard, don't crash on oversized or undersized strings
- if (ptr - val > 15 || ptr - val < 7)
- return;
- memcpy(addr, val, ptr - val);
- addr[ptr - val] = 0;
- conf_gw_ip_address = inet_addr(addr);
- }
- else
- conf_gw_ip_address = inet_addr(val);
+ return r;
}
+/* ===== range arithmetic ===== */
+
//parses ranges like x.x.x.x/mask
static int parse1(const char *str, uint32_t *begin, uint32_t *end)
{
int n;
unsigned int f1, f2, f3, f4, m;
- n = sscanf(str, "%u.%u.%u.%u/%u",&f1, &f2, &f3, &f4, &m);
+ n = sscanf(str, "%u.%u.%u.%u/%u", &f1, &f2, &f3, &f4, &m);
if (n != 5)
return -1;
- if (f1 > 255)
- return -1;
- if (f2 > 255)
- return -1;
- if (f3 > 255)
- return -1;
- if (f4 > 255)
+ if (f1 > 255 || f2 > 255 || f3 > 255 || f4 > 255)
return -1;
if (m == 0 || m > 32)
return -1;
@@ -153,16 +176,10 @@ static int parse2(const char *str, uint32_t *begin, uint32_t *end)
int n;
unsigned int f1, f2, f3, f4, f5;
- n = sscanf(str, "%u.%u.%u.%u-%u",&f1, &f2, &f3, &f4, &f5);
+ n = sscanf(str, "%u.%u.%u.%u-%u", &f1, &f2, &f3, &f4, &f5);
if (n != 5)
return -1;
- if (f1 > 255)
- return -1;
- if (f2 > 255)
- return -1;
- if (f3 > 255)
- return -1;
- if (f4 > 255)
+ if (f1 > 255 || f2 > 255 || f3 > 255 || f4 > 255)
return -1;
if (f5 < f4 || f5 > 255)
return -1;
@@ -173,222 +190,514 @@ static int parse2(const char *str, uint32_t *begin, uint32_t *end)
return 0;
}
-static void add_range(struct ippool_t *p, struct list_head *list, const char *name, void (*generate)(struct ippool_t *))
+/* bit index of a leased peer address within a range, or BM_INVALID */
+static uint64_t range_addr_to_bit(const struct ip_range *r, uint32_t peer_host)
{
- uint32_t i,startip, endip;
- struct ipaddr_t *ip;
+ uint32_t base, delta;
+ uint64_t bit;
+
+ if (peer_host < (uint32_t)r->peer_offset)
+ return BM_INVALID;
+ base = peer_host - r->peer_offset;
+ if (base < r->start)
+ return BM_INVALID;
+ delta = base - r->start;
+ if (delta % r->step)
+ return BM_INVALID;
+ bit = delta / r->step;
+ if (bit >= r->count)
+ return BM_INVALID;
+
+ return bit;
+}
- if (parse1(name, &startip, &endip)) {
- if (parse2(name, &startip, &endip)) {
- fprintf(stderr, "ippool: cann't parse '%s'\n", name);
- _exit(EXIT_FAILURE);
- }
+/* ===== pool set construction ===== */
+
+static struct ip_pool *create_pool(struct pool_set *set, char *name)
+{
+ struct ip_pool *p = _malloc(sizeof(*p));
+
+ if (!p)
+ return NULL;
+
+ memset(p, 0, sizeof(*p));
+ p->name = name;
+ INIT_LIST_HEAD(&p->ranges);
+ spinlock_init(&p->lock);
+
+ if (name)
+ list_add_tail(&p->entry, &set->pools);
+
+ return p;
+}
+
+static struct ip_pool *find_pool(struct pool_set *set, const char *name, int create)
+{
+ struct ip_pool *p;
+
+ list_for_each_entry(p, &set->pools, entry) {
+ if (p->name && !strcmp(p->name, name))
+ return p;
}
- for (i = startip; i <= endip; i++) {
- ip = malloc(sizeof(*ip));
- ip->addr = htonl(i);
- list_add_tail(&ip->entry, list);
- cnt++;
+ if (create) {
+ char *dup = _strdup(name);
+ if (!dup)
+ return NULL;
+ return create_pool(set, dup);
}
- p->startip = startip;
- p->endip = endip;
- p->generate = generate;
+ return NULL;
}
-static uint8_t get_random()
+static int add_range_to_pool(struct ip_pool *p, const char *str,
+ uint32_t step, int gw_offset, int peer_offset)
{
- static uint8_t buf[128];
- static int pos = 0;
- int r;
+ uint32_t start, end;
+ uint64_t count;
+ struct ip_range *r;
- if (pos == 0)
- read(urandom_fd, buf, 128);
+ if (parse1(str, &start, &end)) {
+ if (parse2(str, &start, &end)) {
+ log_error("ippool: can't parse range '%s'\n", str);
+ return -1;
+ }
+ }
- r = buf[pos++];
+ if (end < start) {
+ log_error("ippool: range '%s' ends before it starts\n", str);
+ return -1;
+ }
- if (pos == 128)
- pos = 0;
+ count = ((uint64_t)(end - start) + 1) / step; /* floor: net30 tail dropped */
+ if (count == 0) {
+ log_warn("ippool: range '%s' is empty for this allocator\n", str);
+ return 0;
+ }
+ if (count > IPPOOL_MAX_BITS) {
+ log_error("ippool: range '%s' has %llu units, exceeds cap %u; skipping\n",
+ str, (unsigned long long)count, IPPOOL_MAX_BITS);
+ return -1;
+ }
- return r;
+ r = _malloc(sizeof(*r));
+ if (!r)
+ return -1;
+ memset(r, 0, sizeof(*r));
+ r->start = start;
+ r->end = end;
+ r->step = step;
+ r->gw_offset = gw_offset;
+ r->peer_offset = peer_offset;
+ r->count = count;
+ list_add_tail(&r->entry, &p->ranges);
+
+ return 0;
}
-static void generate_pool_p2p(struct ippool_t *p)
+/* allocate + finalize bitmaps once the whole section (incl. gw-ip-address) is known */
+static int finalize_ranges(struct pool_set *set, struct ip_pool *p)
{
- struct ippool_item_t *it;
- struct ipaddr_t *addr = NULL;
- struct ipaddr_t *peer_addr;
- struct list_head *pos = NULL, *pos1 = p->tunnel_list.next, *pos2 = p->tunnel_list.prev;
- uint8_t r, t = 0;
-
- while (1) {
- if (list_empty(&p->tunnel_list))
- break;
- else {
- if (conf_shuffle) {
- if (pos1 == &p->tunnel_list)
- pos1 = pos1->next;
-
- if (pos2 == &p->tunnel_list)
- pos2 = pos2->prev;
-
- if (t++ < 10)
- r = get_random();
- else
- r = get_random()%64;
-
- if (r < 32)
- pos = pos1;
- else if (r < 64)
- pos = pos2;
-
- pos1 = pos1->next;
- pos2 = pos2->prev;
-
- if (r >= 64)
- continue;
-
- peer_addr = list_entry(pos, typeof(*peer_addr), entry);
- if (pos == pos1)
- pos1 = pos1->next;
-
- if (pos == pos2)
- pos2 = pos2->prev;
-
- list_del(&peer_addr->entry);
- t = 0;
- } else {
- peer_addr = list_entry(p->tunnel_list.next, typeof(*peer_addr), entry);
- list_del(&peer_addr->entry);
+ struct ip_range *r;
+
+ list_for_each_entry(r, &p->ranges, entry) {
+ uint64_t nw = BM_NWORDS(r->count);
+ uint64_t b;
+
+ r->bitmap = _malloc(nw * sizeof(bm_word_t));
+ if (!r->bitmap)
+ return -1;
+ memset(r->bitmap, 0, nw * sizeof(bm_word_t));
+
+ /* remainder bits past count must never be handed out */
+ for (b = r->count; b < nw * BM_WORD_BITS; b++)
+ bm_set(r->bitmap, b);
+
+ /* p2p: reserve the bit colliding with the configured gateway,
+ * reproducing the old generate_pool_p2p skip */
+ if (r->step == 1 && r->peer_offset == 0 && set->gw_ip_address) {
+ uint32_t gw = ntohl(set->gw_ip_address);
+ if (gw >= r->start && gw <= r->end) {
+ uint64_t bit = gw - r->start;
+ if (bit < r->count && !bm_test(r->bitmap, bit)) {
+ bm_set(r->bitmap, bit);
+ r->used++;
+ }
}
}
+ }
- if (!conf_gw_ip_address) {
- if (list_empty(&p->gw_list))
- break;
- else {
- addr = list_entry(p->gw_list.next, typeof(*addr), entry);
- list_del(&addr->entry);
- }
- } else if (conf_gw_ip_address == peer_addr->addr)
- continue;
+ return 0;
+}
- it = malloc(sizeof(*it));
- if (!it) {
- fprintf(stderr, "ippool: out of memory\n");
- break;
+static void parse_gw_ip_address(const char *val, in_addr_t *out)
+{
+ char addr[17];
+ char *ptr;
+
+ if (!val)
+ return;
+
+ ptr = strchr(val, '/');
+ if (ptr) {
+ if (ptr - val > 15 || ptr - val < 7)
+ return;
+ memcpy(addr, val, ptr - val);
+ addr[ptr - val] = 0;
+ *out = inet_addr(addr);
+ } else
+ *out = inet_addr(val);
+}
+
+/* parse ,name= / ,allocator= / ,next= from a raw option line */
+static int parse_line_opts(struct pool_set *set, const char *opt, struct ip_pool **pool,
+ uint32_t *step, int *gw_offset, int *peer_offset)
+{
+ char *name, *ptr;
+
+ name = strstr(opt, ",name=");
+ if (name) {
+ name += sizeof(",name=") - 1;
+ ptr = strchrnul(name, ',');
+ name = _strndup(name, ptr - name);
+ if (!name)
+ return -1;
+ *pool = find_pool(set, name, 1);
+ _free(name);
+ } else if ((name = strchr(opt, ',')) && !strchr(name + 1, '=')) {
+ name = _strndup(name + 1, strchrnul(name + 1, ',') - (name + 1));
+ if (!name)
+ return -1;
+ *pool = find_pool(set, name, 1);
+ _free(name);
+ } else
+ *pool = set->def_pool;
+
+ if (!*pool)
+ return -1;
+
+ /* defaults: p2p */
+ *step = 1;
+ *gw_offset = -1;
+ *peer_offset = 0;
+
+ name = strstr(opt, ",allocator=");
+ if (name) {
+ name += sizeof(",allocator=") - 1;
+ ptr = strchrnul(name, ',');
+ if (!strncmp(name, "p2p", ptr - name) && (size_t)(ptr - name) == 3) {
+ *step = 1; *gw_offset = -1; *peer_offset = 0;
+ } else if (!strncmp(name, "net30", ptr - name) && (size_t)(ptr - name) == 5) {
+ *step = 4; *gw_offset = 1; *peer_offset = 2;
+ } else {
+ log_error("ippool: '%s': unknown allocator\n", opt);
+ return -1;
}
+ }
- memset(it, 0, sizeof(*it));
- it->pool = p;
- it->it.owner = &ipdb;
- if (conf_gw_ip_address)
- it->it.addr = conf_gw_ip_address;
- else
- it->it.addr = addr->addr;
+ name = strstr(opt, ",next=");
+ if (name) {
+ struct ip_pool *next;
+ name += sizeof(",next=") - 1;
+ ptr = strchrnul(name, ',');
+ name = _strndup(name, ptr - name);
+ if (!name)
+ return -1;
+ next = find_pool(set, name, 1);
+ _free(name);
+ if (next)
+ (*pool)->next = next;
+ }
- it->it.peer_addr = peer_addr->addr;
+ return 0;
+}
- list_add_tail(&it->entry, &p->items);
+static void free_pool(struct ip_pool *p)
+{
+ struct ip_range *r;
+
+ while (!list_empty(&p->ranges)) {
+ r = list_first_entry(&p->ranges, typeof(*r), entry);
+ list_del(&r->entry);
+ if (r->bitmap)
+ _free(r->bitmap);
+ _free(r);
}
+ if (p->name)
+ _free(p->name);
+ _free(p);
}
-static void generate_pool_net30(struct ippool_t *p)
+static void free_pool_set(struct pool_set *set)
{
- struct ippool_item_t *it;
- struct ipaddr_t *addr[4];
- int i;
+ struct ip_pool *p;
- while (1) {
- memset(addr, 0, sizeof(addr));
+ if (!set)
+ return;
- for (i = 0; i < 4; i++) {
- if (list_empty(&p->tunnel_list))
- break;
+ while (!list_empty(&set->pools)) {
+ p = list_first_entry(&set->pools, typeof(*p), entry);
+ list_del(&p->entry);
+ free_pool(p);
+ }
+ if (set->def_pool)
+ free_pool(set->def_pool);
+ _free(set);
+}
- addr[i] = list_entry(p->tunnel_list.next, typeof(*addr[i]), entry);
- list_del(&addr[i]->entry);
+static struct pool_set *build_pool_set(void)
+{
+ struct conf_sect_t *s = conf_get_section("ip-pool");
+ struct conf_option_t *opt;
+ struct pool_set *set;
+ struct ip_pool *p;
+
+ set = _malloc(sizeof(*set));
+ if (!set)
+ return NULL;
+ memset(set, 0, sizeof(*set));
+ INIT_LIST_HEAD(&set->pools);
+ set->orphan_policy = ORPHAN_KEEP;
+
+#ifdef RADIUS
+ /* statics persist across reloads; reset to defaults so a removed
+ * vendor/attr line doesn't leave stale values behind */
+ conf_vendor = 0;
+ conf_attr = 88; // Framed-Pool
+#endif
+
+ if (!s)
+ return set; /* no section: an empty (inert) set */
+
+ set->def_pool = create_pool(set, NULL);
+ if (!set->def_pool)
+ goto err;
+
+ list_for_each_entry(opt, &s->items, entry) {
+ const char *range_str;
+ struct ip_pool *pool;
+ uint32_t step;
+ int gw_offset, peer_offset;
+
+#ifdef RADIUS
+ if (triton_module_loaded("radius")) {
+ if (!strcmp(opt->name, "vendor")) {
+ if (opt->val)
+ conf_vendor = parse_vendor_opt(opt->val);
+ continue;
+ } else if (!strcmp(opt->name, "attr")) {
+ if (opt->val)
+ conf_attr = parse_attr_opt(opt->val);
+ continue;
+ }
+ }
+#endif
+ if (!strcmp(opt->name, "gw-ip-address")) {
+ parse_gw_ip_address(opt->val, &set->gw_ip_address);
+ continue;
+ }
+ if (!strcmp(opt->name, "shuffle")) {
+ set->shuffle = opt->val ? atoi(opt->val) : 0;
+ continue;
+ }
+ if (!strcmp(opt->name, "reload-orphan")) {
+ if (opt->val && !strcmp(opt->val, "disconnect"))
+ set->orphan_policy = ORPHAN_DISCONNECT;
+ else
+ set->orphan_policy = ORPHAN_KEEP;
+ continue;
+ }
+ if (!strcmp(opt->name, "gw")) {
+ /* deprecated/no-op: the per-block local address has long been
+ * overwritten at get time by gw-ip-address/0. Accept and ignore. */
+ log_warn("ippool: 'gw=' is deprecated and ignored\n");
+ continue;
}
- if (!addr[2])
- break;
+ if (!strcmp(opt->name, "tunnel"))
+ range_str = opt->val;
+ else if (!opt->val || strchr(opt->name, ','))
+ range_str = opt->name;
+ else
+ continue; /* unrecognized option */
+ if (!range_str)
+ continue;
- it = malloc(sizeof(*it));
- if (!it) {
- log_emerg("ippool: out of memory\n");
- break;
+ if (parse_line_opts(set, opt->raw, &pool, &step, &gw_offset, &peer_offset)) {
+ log_error("ippool: failed to parse '%s'\n", opt->raw);
+ continue;
}
- memset(it, 0, sizeof(*it));
- it->pool = p;
- it->it.owner = &ipdb;
- it->it.addr = addr[1]->addr;
- it->it.peer_addr = addr[2]->addr;
+ add_range_to_pool(pool, range_str, step, gw_offset, peer_offset);
+ }
+
+ if (finalize_ranges(set, set->def_pool))
+ goto err;
+ list_for_each_entry(p, &set->pools, entry) {
+ if (finalize_ranges(set, p))
+ goto err;
+ if (list_empty(&p->ranges))
+ log_warn("ippool: pool '%s' is empty or not defined\n", p->name);
+ }
+
+ return set;
+
+err:
+ free_pool_set(set);
+ return NULL;
+}
- list_add_tail(&it->entry, &p->items);
+/* ===== address lookup across a set ===== */
- for (i = 0; i < 4; i++) {
- if (addr[i])
- free(addr[i]);
+static int pool_contains(struct ip_pool *p, uint32_t peer_host,
+ struct ip_range **out_r, uint64_t *out_bit)
+{
+ struct ip_range *r;
+
+ list_for_each_entry(r, &p->ranges, entry) {
+ uint64_t bit = range_addr_to_bit(r, peer_host);
+ if (bit != BM_INVALID) {
+ *out_r = r;
+ *out_bit = bit;
+ return 1;
}
}
+ return 0;
+}
- for (i = 0; i < 4; i++) {
- if (addr[i])
- free(addr[i]);
+/* find the pool+range+bit owning `peer_host`, preferring `pref_name` on overlap */
+static int find_target(struct pool_set *set, uint32_t peer_host, const char *pref_name,
+ struct ip_pool **op, struct ip_range **orr, uint64_t *obit)
+{
+ struct ip_pool *p;
+
+ if (!set)
+ return 0;
+
+ if (pref_name) {
+ p = find_pool(set, pref_name, 0);
+ if (p && pool_contains(p, peer_host, orr, obit)) {
+ *op = p;
+ return 1;
+ }
+ }
+
+ if (set->def_pool && pool_contains(set->def_pool, peer_host, orr, obit)) {
+ *op = set->def_pool;
+ return 1;
+ }
+ list_for_each_entry(p, &set->pools, entry) {
+ if (pool_contains(p, peer_host, orr, obit)) {
+ *op = p;
+ return 1;
+ }
}
+
+ return 0;
}
+/* ===== ipdb get/put ===== */
static struct ipv4db_item_t *get_ip(struct ap_session *ses)
{
- struct ippool_item_t *it;
- struct ippool_t *pool, *start;
+ struct pool_set *set;
+ struct ip_pool *pool, *start, *found_p = NULL;
+ struct ip_range *r, *found_r = NULL;
+ struct ip_lease *lease;
+ uint64_t bit = BM_INVALID;
+ uint64_t rnd;
+ uint32_t base;
+
+ pthread_rwlock_rdlock(&pool_set_rwlock);
+ set = cur_set;
+ if (!set) {
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
if (ses->ipv4_pool_name)
- pool = find_pool(ses->ipv4_pool_name, 0);
+ pool = find_pool(set, ses->ipv4_pool_name, 0);
else
- pool = def_pool;
+ pool = set->def_pool;
- if (!pool)
+ if (!pool) {
+ pthread_rwlock_unlock(&pool_set_rwlock);
return NULL;
+ }
+
+ /* draw randomness once, outside the lock: read(urandom_fd) is a
+ * blocking syscall and must not run while holding pool->lock */
+ rnd = set->shuffle ? rand_u64() : 0;
start = pool;
do {
spin_lock(&pool->lock);
- if (!list_empty(&pool->items)) {
- it = list_entry(pool->items.next, typeof(*it), entry);
- list_del(&it->entry);
- } else
- it = NULL;
+ list_for_each_entry(r, &pool->ranges, entry) {
+ uint64_t from = set->shuffle ? (r->count ? rnd % r->count : 0) : r->cursor;
+ bit = bm_find_free(r->bitmap, r->count, from);
+ if (bit != BM_INVALID) {
+ bm_set(r->bitmap, bit);
+ r->used++;
+ r->cursor = bit + 1;
+ found_r = r;
+ found_p = pool;
+ break;
+ }
+ }
spin_unlock(&pool->lock);
+ if (found_r)
+ break;
+ pool = pool->next;
+ } while (pool && pool != start);
- if (it) {
- if (ses->ctrl->ppp)
- it->it.addr = conf_gw_ip_address;
- else
- it->it.addr = 0;
+ if (!found_r) {
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
- it->it.mask = 0;
+ lease = _malloc(sizeof(*lease));
+ if (!lease) {
+ spin_lock(&found_p->lock);
+ bm_clear(found_r->bitmap, bit);
+ found_r->used--;
+ spin_unlock(&found_p->lock);
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
- return &it->it;
- }
+ memset(lease, 0, sizeof(*lease));
+ lease->pool = found_p;
+ lease->range = found_r;
+ lease->it.owner = &ipdb;
+ base = found_r->start + (uint32_t)bit * found_r->step;
+ lease->it.peer_addr = htonl(base + found_r->peer_offset);
+ lease->it.addr = ses->ctrl->ppp ? set->gw_ip_address : 0;
+ lease->it.mask = 0;
- pool = pool->next;
- } while (pool && pool != start);
+ pthread_rwlock_unlock(&pool_set_rwlock);
- return NULL;
+ return &lease->it;
}
static void put_ip(struct ap_session *ses, struct ipv4db_item_t *it)
{
- struct ippool_item_t *pit = container_of(it, typeof(*pit), it);
+ struct ip_lease *lease = container_of(it, typeof(*lease), it);
+
+ pthread_rwlock_rdlock(&pool_set_rwlock);
+ if (lease->pool && lease->range) {
+ uint64_t bit = range_addr_to_bit(lease->range, ntohl(it->peer_addr));
+ spin_lock(&lease->pool->lock);
+ if (bit != BM_INVALID && bm_test(lease->range->bitmap, bit)) {
+ bm_clear(lease->range->bitmap, bit);
+ lease->range->used--;
+ }
+ spin_unlock(&lease->pool->lock);
+ }
+ /* else: orphaned by a reload - nothing to release, just free the wrapper */
+ pthread_rwlock_unlock(&pool_set_rwlock);
- spin_lock(&pit->pool->lock);
- list_add_tail(&pit->entry, &pit->pool->items);
- spin_unlock(&pit->pool->lock);
+ _free(lease);
}
static struct ipdb_t ipdb = {
@@ -417,57 +726,53 @@ static int session_save(struct ap_session *ses, struct backup_mod *m)
static int session_restore(struct ap_session *ses, struct backup_mod *m)
{
struct backup_tag *tag;
- in_addr_t addr = 0, peer_addr;
- struct ippool_t *p;
- struct ippool_item_t *it, *it0 = NULL;
+ in_addr_t addr = 0, peer_addr = 0;
+ struct ip_pool *np;
+ struct ip_range *nr;
+ uint64_t bit;
m = backup_find_mod(m->data, MODID_COMMON);
list_for_each_entry(tag, &m->tag_list, entry) {
switch (tag->id) {
case SES_TAG_IPV4_ADDR:
- addr = *(in_addr_t *)tag->data;
+ memcpy(&addr, tag->data, sizeof(addr));
break;
case SES_TAG_IPV4_PEER_ADDR:
- peer_addr = *(in_addr_t *)tag->data;
+ memcpy(&peer_addr, tag->data, sizeof(peer_addr));
break;
}
}
- spin_lock(&def_pool->lock);
- list_for_each_entry(it, &def_pool->items, entry) {
- if (peer_addr == it->it.peer_addr && addr == it->it.addr) {
- list_del(&it->entry);
- it0 = it;
- break;
- }
- }
- spin_unlock(&def_pool->lock);
-
- if (!it0) {
- list_for_each_entry(p, &pool_list, entry) {
- spin_lock(&p->lock);
- list_for_each_entry(it, &p->items, entry) {
- if (peer_addr == it->it.peer_addr && addr == it->it.addr) {
- list_del(&it->entry);
- it0 = it;
- break;
- }
+ pthread_rwlock_rdlock(&pool_set_rwlock);
+ if (find_target(cur_set, ntohl(peer_addr), NULL, &np, &nr, &bit)) {
+ struct ip_lease *lease = _malloc(sizeof(*lease));
+ if (lease) {
+ memset(lease, 0, sizeof(*lease));
+ lease->pool = np;
+ lease->range = nr;
+ lease->it.owner = &ipdb;
+ lease->it.addr = addr;
+ lease->it.peer_addr = peer_addr;
+ spin_lock(&np->lock);
+ if (!bm_test(nr->bitmap, bit)) {
+ bm_set(nr->bitmap, bit);
+ nr->used++;
}
- spin_unlock(&p->lock);
- if (it0)
- break;
+ spin_unlock(&np->lock);
+ ses->ipv4 = &lease->it;
}
}
+ pthread_rwlock_unlock(&pool_set_rwlock);
- if (it0)
- ses->ipv4 = &it0->it;
- else {
+ if (!ses->ipv4) {
ses->ipv4 = _malloc(sizeof(*ses->ipv4));
- memset(ses->ipv4, 0, sizeof(*ses->ipv4));
- ses->ipv4->addr = addr;
- ses->ipv4->peer_addr = peer_addr;
- ses->ipv4->owner = &ipdb_b;
+ if (ses->ipv4) {
+ memset(ses->ipv4, 0, sizeof(*ses->ipv4));
+ ses->ipv4->addr = addr;
+ ses->ipv4->peer_addr = peer_addr;
+ ses->ipv4->owner = &ipdb_b;
+ }
}
return 0;
@@ -480,6 +785,101 @@ static struct backup_module backup_mod = {
};
#endif
+/* ===== reconcile on reload ===== */
+
+static void reconcile_v4(struct pool_set *new_set, struct ap_session *ses,
+ int policy, struct list_head *disc)
+{
+ struct ipv4db_item_t *it = ses->ipv4;
+ uint32_t peer_host;
+ struct ip_pool *np;
+ struct ip_range *nr;
+ uint64_t bit;
+
+ if (!it || !it->owner)
+ return;
+
+ peer_host = ntohl(it->peer_addr);
+
+ if (it->owner == &ipdb) {
+ struct ip_lease *lease = container_of(it, typeof(*lease), it);
+ if (find_target(new_set, peer_host, ses->ipv4_pool_name, &np, &nr, &bit)) {
+ spin_lock(&np->lock);
+ if (!bm_test(nr->bitmap, bit)) {
+ bm_set(nr->bitmap, bit);
+ nr->used++;
+ }
+ spin_unlock(&np->lock);
+ lease->pool = np;
+ lease->range = nr;
+ } else {
+ lease->pool = NULL;
+ lease->range = NULL;
+ if (policy == ORPHAN_DISCONNECT) {
+ struct disc_node *d = _malloc(sizeof(*d));
+ if (d) {
+ d->ses = ses;
+ list_add_tail(&d->entry, disc);
+ }
+ }
+ }
+ } else {
+ /* foreign owner (radius/chap-secrets/static): reserve the bit so we
+ * never hand out a live address. We do not own or free it. */
+ if (find_target(new_set, peer_host, NULL, &np, &nr, &bit)) {
+ spin_lock(&np->lock);
+ if (!bm_test(nr->bitmap, bit)) {
+ bm_set(nr->bitmap, bit);
+ nr->used++;
+ }
+ spin_unlock(&np->lock);
+ }
+ }
+}
+
+static void terminate_orphan(void *arg)
+{
+ struct ap_session *ses = arg;
+ ap_session_terminate(ses, TERM_NAS_REBOOT, 0);
+}
+
+static void load_config(void *data)
+{
+ struct pool_set *new_set, *old_set;
+ struct ap_session *ses;
+ struct disc_node *d;
+ LIST_HEAD(disc_list);
+ int policy;
+
+ new_set = build_pool_set();
+ if (!new_set) {
+ log_error("ippool: reload failed, keeping current pools\n");
+ return;
+ }
+ policy = new_set->orphan_policy;
+
+ pthread_rwlock_wrlock(&pool_set_rwlock);
+ pthread_rwlock_rdlock(&ses_lock);
+
+ list_for_each_entry(ses, &ses_list, entry)
+ reconcile_v4(new_set, ses, policy, &disc_list);
+
+ old_set = cur_set;
+ cur_set = new_set;
+
+ pthread_rwlock_unlock(&ses_lock);
+ pthread_rwlock_unlock(&pool_set_rwlock);
+
+ free_pool_set(old_set);
+
+ while (!list_empty(&disc_list)) {
+ d = list_first_entry(&disc_list, typeof(*d), entry);
+ list_del(&d->entry);
+ triton_context_call(d->ses->ctrl->ctx, terminate_orphan, d->ses);
+ _free(d);
+ }
+}
+
#ifdef RADIUS
static int parse_attr(struct ap_session *ses, struct rad_attr_t *attr)
{
@@ -516,97 +916,70 @@ static void ev_radius_access_accept(struct ev_radius_t *ev)
parse_attr(ev->ses, attr);
}
}
+#endif
-static int parse_attr_opt(const char *opt)
-{
- struct rad_dict_attr_t *attr;
- struct rad_dict_vendor_t *vendor;
+/* ===== cli ===== */
- if (conf_vendor)
- vendor = rad_dict_find_vendor_id(conf_vendor);
- else
- vendor = NULL;
+static int show_ippool_exec(const char *cmd, char * const *fields, int fields_cnt, void *client)
+{
+ struct ip_pool *pool;
+ struct ip_range *r;
+ uint64_t total, used;
- if (conf_vendor) {
- if (vendor)
- attr = rad_dict_find_vendor_attr(vendor, opt);
- else
- attr = NULL;
- }else
- attr = rad_dict_find_attr(opt);
+ cli_send(client, "IP Pool Usage Report\r\n");
+ cli_send(client, "====================\r\n");
- if (attr)
- return attr->id;
+ pthread_rwlock_rdlock(&pool_set_rwlock);
+ if (!cur_set) {
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return CLI_CMD_OK;
+ }
- return atoi(opt);
-}
+ if (cur_set->def_pool) {
+ pool = cur_set->def_pool;
+ total = used = 0;
+ spin_lock(&pool->lock);
+ list_for_each_entry(r, &pool->ranges, entry) {
+ total += r->count;
+ used += r->used;
+ }
+ spin_unlock(&pool->lock);
+ if (total > 0)
+ cli_sendv(client, "<default>\r\n total: %llu\r\n used: %llu\r\n available: %llu\r\n usage: %llu%%\r\n",
+ (unsigned long long)total, (unsigned long long)used,
+ (unsigned long long)(total - used),
+ (unsigned long long)(used * 100 / total));
+ }
-static int parse_vendor_opt(const char *opt)
-{
- struct rad_dict_vendor_t *vendor;
+ list_for_each_entry(pool, &cur_set->pools, entry) {
+ if (!pool->name)
+ continue;
+ total = used = 0;
+ spin_lock(&pool->lock);
+ list_for_each_entry(r, &pool->ranges, entry) {
+ total += r->count;
+ used += r->used;
+ }
+ spin_unlock(&pool->lock);
+ if (total > 0)
+ cli_sendv(client, "%s\r\n total: %llu\r\n used: %llu\r\n available: %llu\r\n usage: %llu%%\r\n",
+ pool->name, (unsigned long long)total, (unsigned long long)used,
+ (unsigned long long)(total - used),
+ (unsigned long long)(used * 100 / total));
+ }
- vendor = rad_dict_find_vendor_name(opt);
- if (vendor)
- return vendor->id;
+ pthread_rwlock_unlock(&pool_set_rwlock);
- return atoi(opt);
+ return CLI_CMD_OK;
}
-#endif
-static int parse_options(const char *opt, struct ippool_t **pool, generate_func *generate, struct ippool_t **next)
+static void show_ippool_help(char * const *fields, int fields_cnt, void *client)
{
- char *name, *ptr;
-
- name = strstr(opt, ",name=");
- if (name) {
- name += sizeof(",name=") - 1;
- ptr = strchrnul(name, ',');
- name = _strndup(name, ptr - name);
- if (!name)
- return -1;
- *pool = find_pool(name, 1);
- } else if ((name = strchr(opt, ',')) && !strchr(name + 1, '=')) {
- name = _strdup(name + 1);
- if (!name)
- return -1;
- *pool = find_pool(name, 1);
- } else
- *pool = def_pool;
-
- name = strstr(opt, ",allocator=");
- if (name) {
- name += sizeof(",allocator=") - 1;
- ptr = strchrnul(name, ',');
- name = strncpy(alloca(ptr - name + 1), name, ptr - name + 1);
- if (strcmp(name, "p2p") == 0)
- *generate = generate_pool_p2p;
- else if (strcmp(name, "net30") == 0)
- *generate = generate_pool_net30;
- else {
- log_error("ipool: '%s': unknown allocator\n", opt);
- return -1;
- }
- } else
- *generate = generate_pool_p2p;
-
- name = strstr(opt, ",next=");
- if (name) {
- name += sizeof(",next=") - 1;
- ptr = strchrnul(name, ',');
- name = strncpy(alloca(ptr - name + 1), name, ptr - name + 1);
- *next = find_pool(name, 0);
- if (!*next) {
- name = _strdup(name);
- if (!name)
- return -1;
- *next = find_pool(name, 1);
- }
- } else
- *next = NULL;
-
- return 0;
+ cli_send(client, "show ippool - shows IP pool statistics\r\n");
}
+/* ===== init ===== */
+
static void ippool_init1(void)
{
ipdb_register(&ipdb);
@@ -614,59 +987,11 @@ static void ippool_init1(void)
static void ippool_init2(void)
{
- struct conf_sect_t *s = conf_get_section("ip-pool");
- struct conf_option_t *opt;
- struct ippool_t *pool, *next;
- generate_func generate;
+ load_config(NULL);
- if (!s)
- return;
-
- def_pool = create_pool(NULL);
-
- list_for_each_entry(opt, &s->items, entry) {
-#ifdef RADIUS
- if (triton_module_loaded("radius")) {
- if (!strcmp(opt->name, "vendor")) {
- conf_vendor = parse_vendor_opt(opt->val);
- continue;
- } else if (!strcmp(opt->name, "attr")) {
- conf_attr = parse_attr_opt(opt->val);
- continue;
- }
- }
-#endif
- if (!strcmp(opt->name, "gw-ip-address"))
- parse_gw_ip_address(opt->val);
- else if (!strcmp(opt->name, "shuffle"))
- conf_shuffle = atoi(opt->val);
- else {
- if (parse_options(opt->raw, &pool, &generate, &next)) {
- log_error("ippool: failed to parse '%s'\n", opt->raw);
- continue;
- }
-
- if (!strcmp(opt->name, "gw"))
- add_range(pool, &pool->gw_list, opt->val, generate);
- else if (!strcmp(opt->name, "tunnel"))
- add_range(pool, &pool->tunnel_list, opt->val, generate);
- else if (!opt->val || strchr(opt->name, ','))
- add_range(pool, &pool->tunnel_list, opt->name, generate);
-
- if (next)
- pool->next = next;
- }
- }
-
- if (def_pool->generate)
- def_pool->generate(def_pool);
-
- list_for_each_entry(pool, &pool_list, entry) {
- if (pool->generate)
- pool->generate(pool);
- else
- log_warn("ippool: pool '%s' is empty or not defined\n", pool->name);
- }
+ if (triton_event_register_handler(EV_CONFIG_RELOAD, load_config) < 0)
+ log_error("ippool: registration of CONFIG_RELOAD event failed,"
+ " pools will not reload\n");
#ifdef USE_BACKUP
backup_register_module(&backup_mod);
@@ -676,6 +1001,8 @@ static void ippool_init2(void)
if (triton_module_loaded("radius"))
triton_event_register_handler(EV_RADIUS_ACCESS_ACCEPT, (triton_event_func)ev_radius_access_accept);
#endif
+
+ cli_register_simple_cmd2(show_ippool_exec, show_ippool_help, 2, "show", "ippool");
}
DEFINE_INIT(51, ippool_init1);
diff --git a/accel-pppd/extra/ipv6pool.c b/accel-pppd/extra/ipv6pool.c
index c5cabef5..05b84fe8 100644
--- a/accel-pppd/extra/ipv6pool.c
+++ b/accel-pppd/extra/ipv6pool.c
@@ -3,76 +3,112 @@
#include <stdint.h>
#include <unistd.h>
#include <string.h>
-#include <unistd.h>
#include <fcntl.h>
+#include <pthread.h>
#include <arpa/inet.h>
+#include <netinet/in.h>
#include <endian.h>
+#include "triton.h"
#include "events.h"
#include "ipdb.h"
#include "list.h"
#include "log.h"
#include "spinlock.h"
+#include "ap_session.h"
#ifdef RADIUS
#include "radius.h"
#endif
+#include "bitpool.h"
#include "memdebug.h"
-enum ippool_type
-{
- IPPOOL_ADDRESS,
- IPPOOL_PREFIX
+/*
+ * Bitmap IPv6 address (IA_NA) and delegated-prefix (IA_PD) pools.
+ *
+ * Same model as the IPv4 pool: each pool holds a list of contiguous ranges,
+ * each range owns one bitmap indexing per-lease prefixes (bit i -> start +
+ * i*2^(128-prefix_len)). A lease is a per-session malloc wrapper that embeds
+ * the single ipv6db_addr_t node linked into the item's addr/prefix list. The
+ * whole set is rebuilt and swapped on EV_CONFIG_RELOAD with a reconcile pass.
+ *
+ * TODO: a sparse/hierarchical allocator would lift IPPOOL_MAX_BITS; the dense
+ * bitmap requires (prefix_len - mask) <= 24, i.e. <= ~16.7M prefixes per range.
+ */
+
+#define IPPOOL_MAX_BITS (1u << 24)
+#define IPPOOL_MAX_SHIFT 24 /* prefix_len - mask cap (2^24 entries) */
+
+enum ippool_type {
+ IPPOOL_ADDRESS,
+ IPPOOL_PREFIX,
};
-struct ippool_t
-{
+enum {
+ ORPHAN_KEEP = 0,
+ ORPHAN_DISCONNECT,
+};
+
+struct ip6_range {
struct list_head entry;
- char *name;
- struct list_head gw_list;
- struct list_head items;
- struct ippool_t *next;
- spinlock_t lock;
+ struct in6_addr start;
+ int prefix_len;
+ int shift; /* 128 - prefix_len */
+ uint64_t count; /* number of prefixes = bits */
+ uint64_t cursor;
+ uint64_t used;
+ bm_word_t *bitmap;
};
-struct ippool_item_t
-{
+struct ip6_pool {
struct list_head entry;
- struct ippool_t *pool;
- struct ipv6db_item_t it;
+ char *name; /* NULL for default pool */
+ struct ip6_pool *next;
+ struct list_head ranges;
+ spinlock_t lock;
};
-struct dppool_item_t
-{
+struct pool_set {
+ struct list_head ippools; /* named NA pools */
+ struct list_head dppools; /* named PD pools */
+ struct ip6_pool *def_ippool;
+ struct ip6_pool *def_dppool;
+ struct in6_addr gw_addr;
+ int orphan_policy;
+};
+
+struct ip6_lease {
+ struct ip6_pool *pool;
+ struct ip6_range *range;
+ struct ipv6db_item_t it; /* ses->ipv6 = &it */
+ struct ipv6db_addr_t node; /* linked into it.addr_list */
+};
+
+struct dp_lease {
+ struct ip6_pool *pool;
+ struct ip6_range *range;
+ struct ipv6db_prefix_t it; /* ses->ipv6_dp = &it */
+ struct ipv6db_addr_t node; /* linked into it.prefix_list */
+};
+
+struct disc_node {
struct list_head entry;
- struct ippool_t *pool;
- struct ipv6db_prefix_t it;
+ struct ap_session *ses;
};
+static struct ipdb_t ipdb;
+
+static pthread_rwlock_t pool_set_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+static struct pool_set *cur_set;
+
#ifdef RADIUS
static int conf_vendor = 0;
static int conf_dppool_attr = 171; // Delegated-IPv6-Prefix-Pool
static int conf_ippool_attr = 172; // Stateful-IPv6-Address-Pool
#endif
-static LIST_HEAD(ippool_list);
-static LIST_HEAD(dppool_list);
-static struct ipdb_t ipdb;
-static struct in6_addr conf_gw_addr;
-static struct ippool_t *def_ippool;
-static struct ippool_t *def_dppool;
-
-static void in6_addr_add(struct in6_addr *res, const struct in6_addr *arg)
-{
- uint16_t n = 0;
- int i;
-
- for (i = 15; i >= 0; i--) {
- n = (uint16_t)res->s6_addr[i] + arg->s6_addr[i] + (n >> 8);
- res->s6_addr[i] = n & 0xff;
- }
-}
+/* ===== 128-bit helpers (big-endian s6_addr[16]) ===== */
static int in6_addr_cmp(const struct in6_addr *n1, const struct in6_addr *n2)
{
@@ -88,154 +124,212 @@ static int in6_addr_cmp(const struct in6_addr *n1, const struct in6_addr *n2)
return 0;
}
-static struct ippool_t *create_pool(enum ippool_type type, char *name)
+/* o = a - b (assumes a >= b) */
+static void in6_sub(uint8_t *o, const uint8_t *a, const uint8_t *b)
{
- struct ippool_t *pool = malloc(sizeof(*pool));
- struct list_head *pool_list = (type == IPPOOL_PREFIX) ? &dppool_list : &ippool_list;
+ int i, borrow = 0;
- memset(pool, 0, sizeof(*pool));
- pool->name = name;
+ for (i = 15; i >= 0; i--) {
+ int v = (int)a[i] - b[i] - borrow;
+ if (v < 0) {
+ v += 256;
+ borrow = 1;
+ } else
+ borrow = 0;
+ o[i] = v;
+ }
+}
- INIT_LIST_HEAD(&pool->items);
- spinlock_init(&pool->lock);
+/* floor(delta >> shift) into *out; returns -1 if it doesn't fit in uint64 */
+static int in6_shr_u64(const uint8_t *d, int shift, uint64_t *out)
+{
+ int p;
+ uint64_t v = 0;
- if (name)
- list_add_tail(&pool->entry, pool_list);
+ for (p = 0; p < 64; p++) {
+ int sp = shift + p;
+ if (sp < 128 && (d[15 - (sp >> 3)] & (1 << (sp & 7))))
+ v |= (uint64_t)1 << p;
+ }
+ for (p = shift + 64; p < 128; p++) {
+ if (d[15 - (p >> 3)] & (1 << (p & 7)))
+ return -1;
+ }
- return pool;
+ *out = v;
+ return 0;
}
-static struct ippool_t *find_pool(enum ippool_type type, char *name, int create)
+/* out = start + (i << shift) */
+static void in6_index_to_addr(struct in6_addr *out, const struct in6_addr *start,
+ uint64_t i, int shift)
{
- struct ippool_t *pool;
- struct list_head *pool_list = (type == IPPOOL_PREFIX) ? &dppool_list : &ippool_list;
-
- list_for_each_entry(pool, pool_list, entry) {
- if (!strcmp(pool->name, name))
- return pool;
+ uint8_t add[16] = { 0 };
+ int p, b, carry = 0;
+
+ for (p = 0; p < 64; p++) {
+ if (i & ((uint64_t)1 << p)) {
+ int sp = shift + p;
+ if (sp < 128)
+ add[15 - (sp >> 3)] |= (1 << (sp & 7));
+ }
}
- if (create)
- return create_pool(type, name);
-
- return NULL;
+ memcpy(out, start, 16);
+ for (b = 15; b >= 0; b--) {
+ int s = out->s6_addr[b] + add[b] + carry;
+ out->s6_addr[b] = s & 0xff;
+ carry = s >> 8;
+ }
}
-static void generate_ippool(struct ippool_t *pool, struct in6_addr *addr, int mask, int prefix_len)
+/* bit index of a leased prefix within a range, or BM_INVALID */
+static uint64_t range_addr_to_bit(const struct ip6_range *r, const struct in6_addr *addr)
{
- struct ippool_item_t *it;
- struct ipv6db_addr_t *a;
- struct in6_addr ip, end, step;
+ uint8_t delta[16];
+ uint64_t i;
+ int p;
- memcpy(&ip, addr, sizeof(ip));
+ if (in6_addr_cmp(addr, &r->start) < 0)
+ return BM_INVALID;
- memcpy(&end, addr, sizeof(end));
- if (mask > 64)
- *(uint64_t *)(end.s6_addr + 8) = htobe64(be64toh(*(uint64_t *)(end.s6_addr + 8)) | ((1llu << (128 - mask)) - 1));
- else {
- memset(end.s6_addr + 8, 0xff, 8);
- *(uint64_t *)end.s6_addr = htobe64(be64toh(*(uint64_t *)end.s6_addr) | ((1llu << (64 - mask)) - 1));
+ in6_sub(delta, addr->s6_addr, r->start.s6_addr);
+
+ /* must sit on a prefix boundary: low `shift` bits zero */
+ for (p = 0; p < r->shift; p++) {
+ if (delta[15 - (p >> 3)] & (1 << (p & 7)))
+ return BM_INVALID;
}
- memset(&step, 0, sizeof(step));
- if (prefix_len > 64)
- *(uint64_t *)(step.s6_addr + 8) = htobe64(1llu << (128 - prefix_len));
- else
- *(uint64_t *)step.s6_addr = htobe64(1llu << (64 - prefix_len));
+ if (in6_shr_u64(delta, r->shift, &i))
+ return BM_INVALID;
+ if (i >= r->count)
+ return BM_INVALID;
- while (in6_addr_cmp(&ip, &end) <= 0) {
- it = malloc(sizeof(*it));
- memset(it, 0, sizeof(*it));
- it->pool = pool;
- it->it.owner = &ipdb;
- INIT_LIST_HEAD(&it->it.addr_list);
- a = malloc(sizeof(*a));
- memset(a, 0, sizeof(*a));
- memcpy(&a->addr, &ip, sizeof(ip));
- a->prefix_len = prefix_len;
- list_add_tail(&a->entry, &it->it.addr_list);
- list_add_tail(&it->entry, &pool->items);
- in6_addr_add(&ip, &step);
- }
+ return i;
}
-static void generate_dppool(struct ippool_t *pool, struct in6_addr *addr, int mask, int prefix_len)
+/* ===== pool set construction ===== */
+
+static struct ip6_pool *create_pool(struct list_head *pool_list, char *name)
{
- struct dppool_item_t *it;
- struct in6_addr ip, end, step;
- struct ipv6db_addr_t *a;
+ struct ip6_pool *p = _malloc(sizeof(*p));
- memcpy(&ip, addr, sizeof(ip));
+ if (!p)
+ return NULL;
- memcpy(&end, addr, sizeof(end));
- if (mask > 64)
- *(uint64_t *)(end.s6_addr + 8) = htobe64(be64toh(*(uint64_t *)(end.s6_addr + 8)) | ((1llu << (128 - mask)) - 1));
- else {
- memset(end.s6_addr + 8, 0xff, 8);
- *(uint64_t *)end.s6_addr = htobe64(be64toh(*(uint64_t *)end.s6_addr) | ((1llu << (64 - mask)) - 1));
- }
+ memset(p, 0, sizeof(*p));
+ p->name = name;
+ INIT_LIST_HEAD(&p->ranges);
+ spinlock_init(&p->lock);
- memset(&step, 0, sizeof(step));
- if (prefix_len > 64)
- *(uint64_t *)(step.s6_addr + 8) = htobe64(1llu << (128 - prefix_len));
- else
- *(uint64_t *)step.s6_addr = htobe64(1llu << (64 - prefix_len));
+ if (name)
+ list_add_tail(&p->entry, pool_list);
+
+ return p;
+}
- while (in6_addr_cmp(&ip, &end) <= 0) {
- it = malloc(sizeof(*it));
- memset(it, 0, sizeof(*it));
- it->pool = pool;
- it->it.owner = &ipdb;
- INIT_LIST_HEAD(&it->it.prefix_list);
- a = malloc(sizeof(*a));
- memset(a, 0, sizeof(*a));
- memcpy(&a->addr, &ip, sizeof(ip));
- a->prefix_len = prefix_len;
- list_add_tail(&a->entry, &it->it.prefix_list);
- list_add_tail(&it->entry, &pool->items);
- in6_addr_add(&ip, &step);
+static struct ip6_pool *find_pool(struct list_head *pool_list, const char *name, int create)
+{
+ struct ip6_pool *p;
+
+ list_for_each_entry(p, pool_list, entry) {
+ if (p->name && !strcmp(p->name, name))
+ return p;
}
+
+ if (create) {
+ char *dup = _strdup(name);
+ if (!dup)
+ return NULL;
+ return create_pool(pool_list, dup);
+ }
+
+ return NULL;
}
-static void add_prefix(enum ippool_type type, struct ippool_t *pool, const char *_val)
+/* parse "<addr>/<mask>,<prefix_len>" and append a range to `pool` */
+static void add_prefix(struct ip6_pool *pool, const char *_val)
{
char *val = _strdup(_val);
char *ptr1, *ptr2;
- struct in6_addr addr;
- int prefix_len;
- int mask;
+ struct in6_addr start, end;
+ int prefix_len, mask, shift;
+ uint64_t count;
+ struct ip6_range *r;
+
+ if (!val)
+ return;
ptr1 = strchr(val, '/');
if (!ptr1)
goto err;
-
*ptr1 = 0;
ptr2 = strchr(ptr1 + 1, ',');
if (!ptr2)
goto err;
-
*ptr2 = 0;
- if (inet_pton(AF_INET6, val, &addr) == 0)
+ if (inet_pton(AF_INET6, val, &start) == 0)
goto err;
-
if (sscanf(ptr1 + 1, "%i", &mask) != 1)
goto err;
-
if (mask < 7 || mask > 127)
goto err;
-
if (sscanf(ptr2 + 1, "%i", &prefix_len) != 1)
goto err;
-
- if (prefix_len > 128 || prefix_len < mask)
+ if (prefix_len > 128 || prefix_len < mask)
goto err;
- if (type == IPPOOL_PREFIX)
- generate_dppool(pool, &addr, mask, prefix_len);
- else
- generate_ippool(pool, &addr, mask, prefix_len);
+ if (prefix_len - mask > IPPOOL_MAX_SHIFT) {
+ log_error("ipv6_pool: range '%s' has 2^%d prefixes, exceeds cap 2^%d; skipping\n",
+ _val, prefix_len - mask, IPPOOL_MAX_SHIFT);
+ _free(val);
+ return;
+ }
+
+ shift = 128 - prefix_len;
+
+ /* end = start | hostmask(mask) (matches the original generator) */
+ memcpy(&end, &start, sizeof(end));
+ if (mask > 64) {
+ uint64_t value;
+ memcpy(&value, end.s6_addr + 8, sizeof(value));
+ value = htobe64(be64toh(value) | ((1llu << (128 - mask)) - 1));
+ memcpy(end.s6_addr + 8, &value, sizeof(value));
+ }
+ else {
+ uint64_t value;
+ memset(end.s6_addr + 8, 0xff, 8);
+ memcpy(&value, end.s6_addr, sizeof(value));
+ value = htobe64(be64toh(value) | ((1llu << (64 - mask)) - 1));
+ memcpy(end.s6_addr, &value, sizeof(value));
+ }
+
+ {
+ uint8_t delta[16];
+ uint64_t span;
+ in6_sub(delta, end.s6_addr, start.s6_addr);
+ if (in6_shr_u64(delta, shift, &span) || span >= IPPOOL_MAX_BITS) {
+ log_error("ipv6_pool: range '%s' exceeds cap; skipping\n", _val);
+ _free(val);
+ return;
+ }
+ count = span + 1;
+ }
+
+ r = _malloc(sizeof(*r));
+ if (!r) {
+ _free(val);
+ return;
+ }
+ memset(r, 0, sizeof(*r));
+ memcpy(&r->start, &start, sizeof(start));
+ r->prefix_len = prefix_len;
+ r->shift = shift;
+ r->count = count;
+ list_add_tail(&r->entry, &pool->ranges);
_free(val);
return;
@@ -245,133 +339,130 @@ err:
_free(val);
}
-static struct ipv6db_item_t *get_ip(struct ap_session *ses)
+static int finalize_ranges(struct ip6_pool *p)
{
- struct ippool_item_t *it;
- struct ipv6db_addr_t *a;
- struct ippool_t *pool, *start;
-
- if (ses->ipv6_pool_name)
- pool = find_pool(IPPOOL_ADDRESS, ses->ipv6_pool_name, 0);
- else
- pool = def_ippool;
-
- if (!pool)
- return NULL;
+ struct ip6_range *r;
- start = pool;
- do {
- spin_lock(&pool->lock);
- if (!list_empty(&pool->items)) {
- it = list_entry(pool->items.next, typeof(*it), entry);
- list_del(&it->entry);
- } else
- it = NULL;
- spin_unlock(&pool->lock);
+ list_for_each_entry(r, &p->ranges, entry) {
+ uint64_t nw = BM_NWORDS(r->count);
+ uint64_t b;
- if (it) {
- a = list_entry(it->it.addr_list.next, typeof(*a), entry);
- if (a->prefix_len == 128) {
- memcpy(&it->it.intf_id, conf_gw_addr.s6_addr + 8, 8);
- memcpy(&it->it.peer_intf_id, a->addr.s6_addr + 8, 8);
- } else {
- it->it.intf_id = 0;
- it->it.peer_intf_id = 0;
- }
-
- return &it->it;
- }
-
- pool = pool->next;
- } while (pool && pool != start);
-
- return NULL;
-}
+ r->bitmap = _malloc(nw * sizeof(bm_word_t));
+ if (!r->bitmap)
+ return -1;
+ memset(r->bitmap, 0, nw * sizeof(bm_word_t));
-static void put_ip(struct ap_session *ses, struct ipv6db_item_t *it)
-{
- struct ippool_item_t *pit = container_of(it, typeof(*pit), it);
+ for (b = r->count; b < nw * BM_WORD_BITS; b++)
+ bm_set(r->bitmap, b);
+ }
- spin_lock(&pit->pool->lock);
- list_add_tail(&pit->entry, &pit->pool->items);
- spin_unlock(&pit->pool->lock);
+ return 0;
}
-static struct ipv6db_prefix_t *get_dp(struct ap_session *ses)
+static int parse_line_opts(struct pool_set *set, enum ippool_type type, const char *opt,
+ struct ip6_pool **pool)
{
- struct dppool_item_t *it;
- struct ippool_t *pool, *start;
-
- if (ses->dpv6_pool_name)
- pool = find_pool(IPPOOL_PREFIX, ses->dpv6_pool_name, 0);
- else
- pool = def_dppool;
+ struct list_head *pool_list = (type == IPPOOL_PREFIX) ? &set->dppools : &set->ippools;
+ char *name, *ptr;
- if (!pool)
- return NULL;
+ name = strstr(opt, ",name=");
+ if (name) {
+ name += sizeof(",name=") - 1;
+ ptr = strchrnul(name, ',');
+ name = _strndup(name, ptr - name);
+ if (!name)
+ return -1;
+ *pool = find_pool(pool_list, name, 1);
+ _free(name);
+ } else
+ *pool = (type == IPPOOL_PREFIX) ? set->def_dppool : set->def_ippool;
- start = pool;
- do {
- spin_lock(&pool->lock);
- if (!list_empty(&pool->items)) {
- it = list_entry(pool->items.next, typeof(*it), entry);
- list_del(&it->entry);
- } else
- it = NULL;
- spin_unlock(&pool->lock);
+ if (!*pool)
+ return -1;
- if (it)
- return &it->it;
+ name = strstr(opt, ",next=");
+ if (name) {
+ struct ip6_pool *next;
+ name += sizeof(",next=") - 1;
+ ptr = strchrnul(name, ',');
+ name = _strndup(name, ptr - name);
+ if (!name)
+ return -1;
+ next = find_pool(pool_list, name, 1);
+ _free(name);
+ if (next)
+ (*pool)->next = next;
+ }
- pool = pool->next;
- } while (pool && pool != start);
+ return 0;
+}
- return NULL;
+static void free_pool(struct ip6_pool *p)
+{
+ struct ip6_range *r;
+
+ while (!list_empty(&p->ranges)) {
+ r = list_first_entry(&p->ranges, typeof(*r), entry);
+ list_del(&r->entry);
+ if (r->bitmap)
+ _free(r->bitmap);
+ _free(r);
+ }
+ if (p->name)
+ _free(p->name);
+ _free(p);
}
-static void put_dp(struct ap_session *ses, struct ipv6db_prefix_t *it)
+static void free_pool_list(struct list_head *l)
{
- struct dppool_item_t *pit = container_of(it, typeof(*pit), it);
+ struct ip6_pool *p;
- spin_lock(&pit->pool->lock);
- list_add_tail(&pit->entry, &pit->pool->items);
- spin_unlock(&pit->pool->lock);
+ while (!list_empty(l)) {
+ p = list_first_entry(l, typeof(*p), entry);
+ list_del(&p->entry);
+ free_pool(p);
+ }
}
-static struct ipdb_t ipdb = {
- .get_ipv6 = get_ip,
- .put_ipv6 = put_ip,
- .get_ipv6_prefix = get_dp,
- .put_ipv6_prefix = put_dp,
-};
-
-#ifdef RADIUS
-static void ev_radius_access_accept(struct ev_radius_t *ev)
+static void free_pool_set(struct pool_set *set)
{
- struct rad_attr_t *attr;
- struct ap_session *ses = ev->ses;
+ if (!set)
+ return;
- list_for_each_entry(attr, &ev->reply->attrs, entry) {
- if (attr->attr->type != ATTR_TYPE_STRING)
- continue;
- if (attr->vendor && attr->vendor->id != conf_vendor)
- continue;
- if (!attr->vendor && conf_vendor)
- continue;
+ free_pool_list(&set->ippools);
+ free_pool_list(&set->dppools);
+ if (set->def_ippool)
+ free_pool(set->def_ippool);
+ if (set->def_dppool)
+ free_pool(set->def_dppool);
+ _free(set);
+}
- if (conf_dppool_attr && conf_dppool_attr == attr->attr->id) {
- if (ses->dpv6_pool_name)
- _free(ses->dpv6_pool_name);
- ses->dpv6_pool_name = _strdup(attr->val.string);
- } else
- if (conf_ippool_attr && conf_ippool_attr == attr->attr->id) {
- if (ses->ipv6_pool_name)
- _free(ses->ipv6_pool_name);
- ses->ipv6_pool_name = _strdup(attr->val.string);
- }
+static int finalize_set(struct pool_set *set)
+{
+ struct ip6_pool *p;
+
+ if (set->def_ippool && finalize_ranges(set->def_ippool))
+ return -1;
+ if (set->def_dppool && finalize_ranges(set->def_dppool))
+ return -1;
+ list_for_each_entry(p, &set->ippools, entry) {
+ if (finalize_ranges(p))
+ return -1;
+ if (list_empty(&p->ranges))
+ log_warn("ipv6_pool: pool '%s' is empty or not defined\n", p->name);
}
+ list_for_each_entry(p, &set->dppools, entry) {
+ if (finalize_ranges(p))
+ return -1;
+ if (list_empty(&p->ranges))
+ log_warn("ipv6_pool: delegate pool '%s' is empty or not defined\n", p->name);
+ }
+
+ return 0;
}
+#ifdef RADIUS
static int parse_attr_opt(const char *opt)
{
struct rad_dict_attr_t *attr;
@@ -408,83 +499,77 @@ static int parse_vendor_opt(const char *opt)
}
#endif
-static int parse_options(enum ippool_type type, const char *opt, struct ippool_t **pool, struct ippool_t **next)
-{
- char *name, *ptr;
-
- name = strstr(opt, ",name=");
- if (name) {
- name += sizeof(",name=") - 1;
- ptr = strchrnul(name, ',');
- name = _strndup(name, ptr - name);
- if (!name)
- return -1;
- *pool = find_pool(type, name, 1);
- } else if (type == IPPOOL_PREFIX)
- *pool = def_dppool;
- else
- *pool = def_ippool;
-
- name = strstr(opt, ",next=");
- if (name) {
- name += sizeof(",next=") - 1;
- ptr = strchrnul(name, ',');
- name = strncpy(alloca(ptr - name + 1), name, ptr - name + 1);
- *next = find_pool(type, name, 0);
- if (!*next) {
- name = _strdup(name);
- if (!name)
- return -1;
- *next = find_pool(type, name, 1);
- }
- } else
- *next = NULL;
-
- return 0;
-}
-
-static void ippool_init1(void)
-{
- ipdb_register(&ipdb);
-}
-
-static void ippool_init2(void)
+static struct pool_set *build_pool_set(void)
{
struct conf_sect_t *s = conf_get_section("ipv6-pool");
struct conf_option_t *opt;
- struct ippool_t *pool, *next;
- char *val;
- enum ippool_type type;
+ struct pool_set *set;
#ifdef RADIUS
int dppool_attr = 0, ippool_attr = 0;
#endif
+ set = _malloc(sizeof(*set));
+ if (!set)
+ return NULL;
+ memset(set, 0, sizeof(*set));
+ INIT_LIST_HEAD(&set->ippools);
+ INIT_LIST_HEAD(&set->dppools);
+ set->orphan_policy = ORPHAN_KEEP;
+
+#ifdef RADIUS
+ /* statics persist across reloads; reset to defaults so a removed
+ * vendor/attr line doesn't leave stale values behind. A stale
+ * conf_vendor would otherwise force conf_dppool_attr/conf_ippool_attr
+ * to 0 below, silently disabling pool-name matching. */
+ conf_vendor = 0;
+ conf_dppool_attr = 171; // Delegated-IPv6-Prefix-Pool
+ conf_ippool_attr = 172; // Stateful-IPv6-Address-Pool
+#endif
+
if (!s)
- return;
+ return set;
- def_ippool = create_pool(IPPOOL_ADDRESS, NULL);
- def_dppool = create_pool(IPPOOL_PREFIX, NULL);
+ set->def_ippool = create_pool(&set->ippools, NULL);
+ set->def_dppool = create_pool(&set->dppools, NULL);
+ if (!set->def_ippool || !set->def_dppool)
+ goto err;
list_for_each_entry(opt, &s->items, entry) {
+ enum ippool_type type;
+ const char *val;
+ struct ip6_pool *pool;
+
#ifdef RADIUS
if (triton_module_loaded("radius")) {
if (!strcmp(opt->name, "vendor")) {
- conf_vendor = parse_vendor_opt(opt->val);
+ if (opt->val)
+ conf_vendor = parse_vendor_opt(opt->val);
continue;
} else if (!strcmp(opt->name, "attr-prefix")) {
- dppool_attr = parse_attr_opt(opt->val);
+ if (opt->val)
+ dppool_attr = parse_attr_opt(opt->val);
continue;
} else if (!strcmp(opt->name, "attr-address")) {
- ippool_attr = parse_attr_opt(opt->val);
+ if (opt->val)
+ ippool_attr = parse_attr_opt(opt->val);
continue;
}
}
#endif
if (!strcmp(opt->name, "gw-ip6-address")) {
- if (inet_pton(AF_INET6, opt->val, &conf_gw_addr) == 0)
+ if (opt->val && inet_pton(AF_INET6, opt->val, &set->gw_addr) == 0)
log_error("ipv6_pool: failed to parse '%s'\n", opt->raw);
continue;
- } else if (!strcmp(opt->name, "delegate")) {
+ }
+ if (!strcmp(opt->name, "reload-orphan")) {
+ if (opt->val && !strcmp(opt->val, "disconnect"))
+ set->orphan_policy = ORPHAN_DISCONNECT;
+ else
+ set->orphan_policy = ORPHAN_KEEP;
+ continue;
+ }
+
+ if (!strcmp(opt->name, "delegate")) {
type = IPPOOL_PREFIX;
val = opt->val;
} else {
@@ -492,24 +577,15 @@ static void ippool_init2(void)
val = opt->name;
}
- if (parse_options(type, opt->raw, &pool, &next)) {
+ if (!val)
+ continue;
+
+ if (parse_line_opts(set, type, opt->raw, &pool)) {
log_error("ipv6_pool: failed to parse '%s'\n", opt->raw);
continue;
}
- add_prefix(type, pool, val);
-
- if (next)
- pool->next = next;
- }
-
- list_for_each_entry(pool, &ippool_list, entry) {
- if (list_empty(&pool->items))
- log_warn("ipv6_pool: pool '%s' is empty or not defined\n", pool->name);
- }
- list_for_each_entry(pool, &dppool_list, entry) {
- if (list_empty(&pool->items))
- log_warn("ipv6_pool: delegate pool '%s' is empty or not defined\n", pool->name);
+ add_prefix(pool, val);
}
#ifdef RADIUS
@@ -518,9 +594,435 @@ static void ippool_init2(void)
conf_dppool_attr = dppool_attr;
if (conf_vendor || ippool_attr)
conf_ippool_attr = ippool_attr;
- triton_event_register_handler(EV_RADIUS_ACCESS_ACCEPT, (triton_event_func)ev_radius_access_accept);
}
#endif
+
+ if (finalize_set(set))
+ goto err;
+
+ return set;
+
+err:
+ free_pool_set(set);
+ return NULL;
+}
+
+/* ===== address lookup ===== */
+
+static int pool_contains(struct ip6_pool *p, const struct in6_addr *addr,
+ struct ip6_range **out_r, uint64_t *out_bit)
+{
+ struct ip6_range *r;
+
+ list_for_each_entry(r, &p->ranges, entry) {
+ uint64_t bit = range_addr_to_bit(r, addr);
+ if (bit != BM_INVALID) {
+ *out_r = r;
+ *out_bit = bit;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int find_target(struct list_head *pool_list, struct ip6_pool *def_pool,
+ const struct in6_addr *addr, const char *pref_name,
+ struct ip6_pool **op, struct ip6_range **orr, uint64_t *obit)
+{
+ struct ip6_pool *p;
+
+ if (pref_name) {
+ p = find_pool(pool_list, pref_name, 0);
+ if (p && pool_contains(p, addr, orr, obit)) {
+ *op = p;
+ return 1;
+ }
+ }
+
+ if (def_pool && pool_contains(def_pool, addr, orr, obit)) {
+ *op = def_pool;
+ return 1;
+ }
+ list_for_each_entry(p, pool_list, entry) {
+ if (pool_contains(p, addr, orr, obit)) {
+ *op = p;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* alloc a free bit from a pool's next-chain; returns range+bit+pool or NULL */
+static struct ip6_range *alloc_from(struct ip6_pool *pool, struct ip6_pool **found_p, uint64_t *found_bit)
+{
+ struct ip6_pool *start = pool;
+ struct ip6_range *r, *found_r = NULL;
+ uint64_t bit = BM_INVALID;
+
+ do {
+ spin_lock(&pool->lock);
+ list_for_each_entry(r, &pool->ranges, entry) {
+ bit = bm_find_free(r->bitmap, r->count, r->cursor);
+ if (bit != BM_INVALID) {
+ bm_set(r->bitmap, bit);
+ r->used++;
+ r->cursor = bit + 1;
+ found_r = r;
+ *found_p = pool;
+ *found_bit = bit;
+ break;
+ }
+ }
+ spin_unlock(&pool->lock);
+ if (found_r)
+ return found_r;
+ pool = pool->next;
+ } while (pool && pool != start);
+
+ return NULL;
+}
+
+/* ===== ipdb get/put: NA ===== */
+
+static struct ipv6db_item_t *get_ip(struct ap_session *ses)
+{
+ struct pool_set *set;
+ struct ip6_pool *pool, *found_p = NULL;
+ struct ip6_range *r;
+ struct ip6_lease *lease;
+ uint64_t bit;
+
+ pthread_rwlock_rdlock(&pool_set_rwlock);
+ set = cur_set;
+ if (!set) {
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
+
+ pool = ses->ipv6_pool_name ? find_pool(&set->ippools, ses->ipv6_pool_name, 0) : set->def_ippool;
+ if (!pool) {
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
+
+ r = alloc_from(pool, &found_p, &bit);
+ if (!r) {
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
+
+ lease = _malloc(sizeof(*lease));
+ if (!lease) {
+ spin_lock(&found_p->lock);
+ bm_clear(r->bitmap, bit);
+ r->used--;
+ spin_unlock(&found_p->lock);
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
+
+ memset(lease, 0, sizeof(*lease));
+ lease->pool = found_p;
+ lease->range = r;
+ lease->it.owner = &ipdb;
+ INIT_LIST_HEAD(&lease->it.addr_list);
+ in6_index_to_addr(&lease->node.addr, &r->start, bit, r->shift);
+ lease->node.prefix_len = r->prefix_len;
+ list_add_tail(&lease->node.entry, &lease->it.addr_list);
+
+ if (r->prefix_len == 128) {
+ memcpy(&lease->it.intf_id, set->gw_addr.s6_addr + 8, 8);
+ memcpy(&lease->it.peer_intf_id, lease->node.addr.s6_addr + 8, 8);
+ } else {
+ lease->it.intf_id = 0;
+ lease->it.peer_intf_id = 0;
+ }
+
+ pthread_rwlock_unlock(&pool_set_rwlock);
+
+ return &lease->it;
+}
+
+static void put_ip(struct ap_session *ses, struct ipv6db_item_t *it)
+{
+ struct ip6_lease *lease = container_of(it, typeof(*lease), it);
+
+ pthread_rwlock_rdlock(&pool_set_rwlock);
+ if (lease->pool && lease->range) {
+ uint64_t bit = range_addr_to_bit(lease->range, &lease->node.addr);
+ spin_lock(&lease->pool->lock);
+ if (bit != BM_INVALID && bm_test(lease->range->bitmap, bit)) {
+ bm_clear(lease->range->bitmap, bit);
+ lease->range->used--;
+ }
+ spin_unlock(&lease->pool->lock);
+ }
+ pthread_rwlock_unlock(&pool_set_rwlock);
+
+ _free(lease);
+}
+
+/* ===== ipdb get/put: PD ===== */
+
+static struct ipv6db_prefix_t *get_dp(struct ap_session *ses)
+{
+ struct pool_set *set;
+ struct ip6_pool *pool, *found_p = NULL;
+ struct ip6_range *r;
+ struct dp_lease *lease;
+ uint64_t bit;
+
+ pthread_rwlock_rdlock(&pool_set_rwlock);
+ set = cur_set;
+ if (!set) {
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
+
+ pool = ses->dpv6_pool_name ? find_pool(&set->dppools, ses->dpv6_pool_name, 0) : set->def_dppool;
+ if (!pool) {
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
+
+ r = alloc_from(pool, &found_p, &bit);
+ if (!r) {
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
+
+ lease = _malloc(sizeof(*lease));
+ if (!lease) {
+ spin_lock(&found_p->lock);
+ bm_clear(r->bitmap, bit);
+ r->used--;
+ spin_unlock(&found_p->lock);
+ pthread_rwlock_unlock(&pool_set_rwlock);
+ return NULL;
+ }
+
+ memset(lease, 0, sizeof(*lease));
+ lease->pool = found_p;
+ lease->range = r;
+ lease->it.owner = &ipdb;
+ INIT_LIST_HEAD(&lease->it.prefix_list);
+ in6_index_to_addr(&lease->node.addr, &r->start, bit, r->shift);
+ lease->node.prefix_len = r->prefix_len;
+ list_add_tail(&lease->node.entry, &lease->it.prefix_list);
+
+ pthread_rwlock_unlock(&pool_set_rwlock);
+
+ return &lease->it;
+}
+
+static void put_dp(struct ap_session *ses, struct ipv6db_prefix_t *it)
+{
+ struct dp_lease *lease = container_of(it, typeof(*lease), it);
+
+ pthread_rwlock_rdlock(&pool_set_rwlock);
+ if (lease->pool && lease->range) {
+ uint64_t bit = range_addr_to_bit(lease->range, &lease->node.addr);
+ spin_lock(&lease->pool->lock);
+ if (bit != BM_INVALID && bm_test(lease->range->bitmap, bit)) {
+ bm_clear(lease->range->bitmap, bit);
+ lease->range->used--;
+ }
+ spin_unlock(&lease->pool->lock);
+ }
+ pthread_rwlock_unlock(&pool_set_rwlock);
+
+ _free(lease);
+}
+
+static struct ipdb_t ipdb = {
+ .get_ipv6 = get_ip,
+ .put_ipv6 = put_ip,
+ .get_ipv6_prefix = get_dp,
+ .put_ipv6_prefix = put_dp,
+};
+
+/* ===== reconcile on reload ===== */
+
+static void reserve_bit(struct ip6_pool *np, struct ip6_range *nr, uint64_t bit)
+{
+ spin_lock(&np->lock);
+ if (!bm_test(nr->bitmap, bit)) {
+ bm_set(nr->bitmap, bit);
+ nr->used++;
+ }
+ spin_unlock(&np->lock);
+}
+
+static void reconcile_na(struct pool_set *new_set, struct ap_session *ses,
+ int policy, struct list_head *disc)
+{
+ struct ipv6db_item_t *it = ses->ipv6;
+ struct ip6_pool *np;
+ struct ip6_range *nr;
+ uint64_t bit;
+
+ if (!it || !it->owner)
+ return;
+
+ if (it->owner == &ipdb) {
+ struct ip6_lease *lease = container_of(it, typeof(*lease), it);
+ if (find_target(&new_set->ippools, new_set->def_ippool, &lease->node.addr,
+ ses->ipv6_pool_name, &np, &nr, &bit)) {
+ reserve_bit(np, nr, bit);
+ lease->pool = np;
+ lease->range = nr;
+ } else {
+ lease->pool = NULL;
+ lease->range = NULL;
+ if (policy == ORPHAN_DISCONNECT) {
+ struct disc_node *d = _malloc(sizeof(*d));
+ if (d) {
+ d->ses = ses;
+ list_add_tail(&d->entry, disc);
+ }
+ }
+ }
+ } else {
+ struct ipv6db_addr_t *a;
+ list_for_each_entry(a, &it->addr_list, entry) {
+ if (find_target(&new_set->ippools, new_set->def_ippool, &a->addr,
+ NULL, &np, &nr, &bit))
+ reserve_bit(np, nr, bit);
+ }
+ }
+}
+
+static void reconcile_pd(struct pool_set *new_set, struct ap_session *ses,
+ int policy, struct list_head *disc)
+{
+ struct ipv6db_prefix_t *it = ses->ipv6_dp;
+ struct ip6_pool *np;
+ struct ip6_range *nr;
+ uint64_t bit;
+
+ if (!it || !it->owner)
+ return;
+
+ if (it->owner == &ipdb) {
+ struct dp_lease *lease = container_of(it, typeof(*lease), it);
+ if (find_target(&new_set->dppools, new_set->def_dppool, &lease->node.addr,
+ ses->dpv6_pool_name, &np, &nr, &bit)) {
+ reserve_bit(np, nr, bit);
+ lease->pool = np;
+ lease->range = nr;
+ } else {
+ lease->pool = NULL;
+ lease->range = NULL;
+ if (policy == ORPHAN_DISCONNECT) {
+ struct disc_node *d = _malloc(sizeof(*d));
+ if (d) {
+ d->ses = ses;
+ list_add_tail(&d->entry, disc);
+ }
+ }
+ }
+ } else {
+ struct ipv6db_addr_t *a;
+ list_for_each_entry(a, &it->prefix_list, entry) {
+ if (find_target(&new_set->dppools, new_set->def_dppool, &a->addr,
+ NULL, &np, &nr, &bit))
+ reserve_bit(np, nr, bit);
+ }
+ }
+}
+
+static void terminate_orphan(void *arg)
+{
+ struct ap_session *ses = arg;
+ ap_session_terminate(ses, TERM_NAS_REBOOT, 0);
+}
+
+static void load_config(void *data)
+{
+ struct pool_set *new_set, *old_set;
+ struct ap_session *ses;
+ struct disc_node *d;
+ LIST_HEAD(disc_list);
+ int policy;
+
+ new_set = build_pool_set();
+ if (!new_set) {
+ log_error("ipv6_pool: reload failed, keeping current pools\n");
+ return;
+ }
+ policy = new_set->orphan_policy;
+
+ pthread_rwlock_wrlock(&pool_set_rwlock);
+ pthread_rwlock_rdlock(&ses_lock);
+
+ list_for_each_entry(ses, &ses_list, entry) {
+ reconcile_na(new_set, ses, policy, &disc_list);
+ reconcile_pd(new_set, ses, policy, &disc_list);
+ }
+
+ old_set = cur_set;
+ cur_set = new_set;
+
+ pthread_rwlock_unlock(&ses_lock);
+ pthread_rwlock_unlock(&pool_set_rwlock);
+
+ free_pool_set(old_set);
+
+ while (!list_empty(&disc_list)) {
+ d = list_first_entry(&disc_list, typeof(*d), entry);
+ list_del(&d->entry);
+ triton_context_call(d->ses->ctrl->ctx, terminate_orphan, d->ses);
+ _free(d);
+ }
+}
+
+#ifdef RADIUS
+static void ev_radius_access_accept(struct ev_radius_t *ev)
+{
+ struct rad_attr_t *attr;
+ struct ap_session *ses = ev->ses;
+
+ list_for_each_entry(attr, &ev->reply->attrs, entry) {
+ if (attr->attr->type != ATTR_TYPE_STRING)
+ continue;
+ if (attr->vendor && attr->vendor->id != conf_vendor)
+ continue;
+ if (!attr->vendor && conf_vendor)
+ continue;
+
+ if (conf_dppool_attr && conf_dppool_attr == attr->attr->id) {
+ if (ses->dpv6_pool_name)
+ _free(ses->dpv6_pool_name);
+ ses->dpv6_pool_name = _strdup(attr->val.string);
+ } else if (conf_ippool_attr && conf_ippool_attr == attr->attr->id) {
+ if (ses->ipv6_pool_name)
+ _free(ses->ipv6_pool_name);
+ ses->ipv6_pool_name = _strdup(attr->val.string);
+ }
+ }
+}
+#endif
+
+/* ===== init ===== */
+
+static void ippool_init1(void)
+{
+ ipdb_register(&ipdb);
+}
+
+static void ippool_init2(void)
+{
+ load_config(NULL);
+
+ if (triton_event_register_handler(EV_CONFIG_RELOAD, load_config) < 0)
+ log_error("ipv6_pool: registration of CONFIG_RELOAD event failed,"
+ " pools will not reload\n");
+
+#ifdef RADIUS
+ if (triton_module_loaded("radius"))
+ triton_event_register_handler(EV_RADIUS_ACCESS_ACCEPT, (triton_event_func)ev_radius_access_accept);
+#endif
}
DEFINE_INIT(51, ippool_init1);
diff --git a/accel-pppd/extra/logwtmp.c b/accel-pppd/extra/logwtmp.c
index f95b62c4..f66e6403 100644
--- a/accel-pppd/extra/logwtmp.c
+++ b/accel-pppd/extra/logwtmp.c
@@ -14,6 +14,7 @@
#include "memdebug.h"
+#ifdef HAVE_LOGWTMP
static void ev_ses_started(struct ap_session *ses)
{
logwtmp(ses->ifname, ses->username ?: "", ses->ctrl->calling_station_id);
@@ -29,5 +30,11 @@ static void init(void)
triton_event_register_handler(EV_SES_STARTED, (triton_event_func)ev_ses_started);
triton_event_register_handler(EV_SES_FINISHED, (triton_event_func)ev_ses_finished);
}
+#else
+static void init(void)
+{
+ log_warn("logwtmp is not supported on your platfrom, check libc doc\n");
+}
+#endif
DEFINE_INIT(200, init);
diff --git a/accel-pppd/extra/metrics.c b/accel-pppd/extra/metrics.c
new file mode 100644
index 00000000..3d422289
--- /dev/null
+++ b/accel-pppd/extra/metrics.c
@@ -0,0 +1,1356 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <time.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <dlfcn.h>
+
+#include "triton.h"
+#include "events.h"
+#include "log.h"
+#include "list.h"
+#include "utils.h"
+#include "ap_session.h"
+#include "ipdb.h"
+#include "version.h"
+
+#include "memdebug.h"
+
+/* Per-protocol session counters live in their respective shared modules.
+ * Resolve them with dlsym(RTLD_DEFAULT, ...) lazily rather than via direct
+ * (weak) references: when metrics is dlopen()ed before the protocol module
+ * the loader binds undefined refs to NULL and never updates them when a
+ * later RTLD_GLOBAL dlopen brings the symbols in. dlsym walks the live
+ * global scope at call time, so it picks them up regardless of order.
+ */
+typedef unsigned int (*proto_stat_fn)(void);
+
+struct proto_stat {
+ const char *module;
+ const char *starting_sym;
+ const char *active_sym;
+ proto_stat_fn starting;
+ proto_stat_fn active;
+};
+
+static struct proto_stat proto_stats[] = {
+ { "pppoe", "pppoe_stat_starting", "pppoe_stat_active" },
+ { "l2tp", "l2tp_stat_starting", "l2tp_stat_active" },
+ { "pptp", "pptp_stat_starting", "pptp_stat_active" },
+ { "sstp", "sstp_stat_starting", "sstp_stat_active" },
+ { "ipoe", "ipoe_stat_starting", "ipoe_stat_active" },
+};
+
+static int proto_resolve(struct proto_stat *p)
+{
+ if (!triton_module_loaded(p->module))
+ return 0;
+ if (!p->starting)
+ p->starting = (proto_stat_fn)(uintptr_t)dlsym(RTLD_DEFAULT, p->starting_sym);
+ if (!p->active)
+ p->active = (proto_stat_fn)(uintptr_t)dlsym(RTLD_DEFAULT, p->active_sym);
+ return p->starting && p->active;
+}
+
+enum metrics_format {
+ METRICS_FORMAT_PROMETHEUS,
+ METRICS_FORMAT_JSON,
+};
+
+#define METRICS_RECV_BUF_SIZE 2048
+#define METRICS_HDR_RESERVE 256 /* room reserved for the response header */
+#define METRICS_DEFAULT_READ_TIMEOUT 5 /* seconds */
+#define METRICS_DEFAULT_MAX_CLIENTS 64
+
+struct metrics_client_t {
+ struct list_head entry;
+ struct triton_md_handler_t hnd;
+ struct triton_timer_t timer;
+ struct sockaddr_in addr;
+ char *recv_buf;
+ int recv_pos;
+ char *xmit_buf;
+ int xmit_pos;
+ int xmit_len;
+ unsigned int disconnect:1;
+};
+
+struct metrics_acl_t {
+ struct list_head entry;
+ uint32_t net; /* host byte order */
+ uint32_t mask; /* host byte order */
+};
+
+static enum metrics_format conf_format = METRICS_FORMAT_PROMETHEUS;
+/* TODO: Support simultaneous Prometheus and JSON output, selected by endpoint
+ * (for example, /metrics and /metrics.json) instead of a process-wide format.
+ */
+static char *conf_address;
+static LIST_HEAD(conf_allowed);
+static int conf_read_timeout = METRICS_DEFAULT_READ_TIMEOUT;
+static int conf_max_clients = METRICS_DEFAULT_MAX_CLIENTS;
+static int conf_sessions;
+
+#define METRICS_ACCEPT_BACKOFF 1 /* seconds */
+#define METRICS_ACCEPT_BATCH 16 /* max accept()s per serv_read tick */
+
+static struct triton_context_t serv_ctx;
+static struct triton_md_handler_t serv_hnd;
+static struct triton_timer_t accept_resume_timer;
+static LIST_HEAD(clients);
+static unsigned int client_count;
+static int serv_running;
+static int accept_paused;
+
+static int parse_format(const char *opt, enum metrics_format *out)
+{
+ if (!strcasecmp(opt, "prometheus")) {
+ *out = METRICS_FORMAT_PROMETHEUS;
+ return 0;
+ }
+ if (!strcasecmp(opt, "json")) {
+ *out = METRICS_FORMAT_JSON;
+ return 0;
+ }
+ return -1;
+}
+
+/* TODO: IPv6 support. The listener, the ACL, and every sockaddr below are
+ * IPv4-only for now; bracketed-host syntax ("[::1]:8080"), AF_INET6 sockets,
+ * and IPv6 CIDRs in allowed_ips are intentionally left for a follow-up.
+ */
+static int parse_listen_address(const char *str, struct sockaddr_in *addr)
+{
+ char *buf, *colon;
+ int port;
+ int ret = -1;
+
+ buf = strdup(str);
+ if (!buf)
+ return -1;
+
+ colon = strrchr(buf, ':');
+ if (!colon)
+ goto out;
+
+ *colon = 0;
+ port = atoi(colon + 1);
+ if (port <= 0 || port > 65535)
+ goto out;
+
+ memset(addr, 0, sizeof(*addr));
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(port);
+ if (buf[0] == 0 || !strcmp(buf, "*") || !strcmp(buf, "0.0.0.0"))
+ addr->sin_addr.s_addr = htonl(INADDR_ANY);
+ else if (inet_pton(AF_INET, buf, &addr->sin_addr) != 1)
+ goto out;
+
+ ret = 0;
+out:
+ free(buf);
+ return ret;
+}
+
+static void free_acl(struct list_head *head)
+{
+ struct metrics_acl_t *acl;
+
+ while (!list_empty(head)) {
+ acl = list_first_entry(head, typeof(*acl), entry);
+ list_del(&acl->entry);
+ _free(acl);
+ }
+}
+
+/* Strip surrounding whitespace, optional matching single/double quotes,
+ * and any trailing comma. Returns NULL if the token becomes empty.
+ */
+static char *clean_token(char *s)
+{
+ char *end;
+ size_t len;
+
+ while (*s == ' ' || *s == '\t')
+ s++;
+
+ len = strlen(s);
+ while (len && (s[len - 1] == ' ' || s[len - 1] == '\t' ||
+ s[len - 1] == ',' || s[len - 1] == '\r' ||
+ s[len - 1] == '\n'))
+ s[--len] = 0;
+
+ if (len >= 2 && ((s[0] == '"' && s[len - 1] == '"') ||
+ (s[0] == '\'' && s[len - 1] == '\''))) {
+ s[len - 1] = 0;
+ s++;
+ len -= 2;
+ }
+
+ while (*s == ' ' || *s == '\t')
+ s++;
+ end = s + strlen(s);
+ while (end > s && (end[-1] == ' ' || end[-1] == '\t'))
+ *--end = 0;
+
+ return *s ? s : NULL;
+}
+
+/* TODO: IPv6 support. Only IPv4 CIDR or a bare IPv4 address are accepted;
+ * IPv6 entries in allowed_ips are rejected at parse time. ip_allowed() and
+ * struct metrics_acl_t store the network in a 32-bit host-order word, so
+ * adding IPv6 here will also need the matching widening downstream.
+ */
+static int parse_acl_entry(const char *str, struct metrics_acl_t **out)
+{
+ struct metrics_acl_t *acl;
+ struct in_addr addr;
+ uint8_t prefix;
+ uint32_t mask;
+
+ if (!u_parse_ip4cidr(str, &addr, &prefix)) {
+ /* Accept a bare IP as /32 */
+ if (inet_pton(AF_INET, str, &addr) != 1)
+ return -1;
+ prefix = 32;
+ }
+
+ acl = _malloc(sizeof(*acl));
+ if (!acl)
+ return -1;
+
+ mask = prefix ? (uint32_t)0xffffffffu << (32 - prefix) : 0;
+ acl->net = ntohl(addr.s_addr) & mask;
+ acl->mask = mask;
+ *out = acl;
+ return 0;
+}
+
+/* Parse `allowed_ips` value. Accepts:
+ * "1.2.3.4/32, 5.6.7.0/24"
+ * ["1.2.3.4/32", "5.6.7.0/24"]
+ * On success the supplied list is populated and 0 is returned. Returns -1 on
+ * any parse error; in that case the partial list is freed.
+ */
+static int parse_allowed_ips(const char *value, struct list_head *list)
+{
+ char *buf, *p, *tok;
+ int ret = -1;
+
+ if (!value || !*value)
+ return 0;
+
+ buf = strdup(value);
+ if (!buf)
+ return -1;
+
+ p = buf;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '[')
+ p++;
+ {
+ size_t len = strlen(p);
+ while (len && (p[len - 1] == ' ' || p[len - 1] == '\t' ||
+ p[len - 1] == ']' || p[len - 1] == '\r' ||
+ p[len - 1] == '\n'))
+ p[--len] = 0;
+ }
+
+ while ((tok = strsep(&p, ",")) != NULL) {
+ struct metrics_acl_t *acl;
+ char *clean = clean_token(tok);
+
+ if (!clean)
+ continue;
+ if (parse_acl_entry(clean, &acl) < 0) {
+ log_error("metrics: invalid entry in allowed_ips: '%s'\n", clean);
+ free_acl(list);
+ goto out;
+ }
+ list_add_tail(&acl->entry, list);
+ }
+
+ ret = 0;
+out:
+ free(buf);
+ return ret;
+}
+
+static int ip_allowed(uint32_t addr_nbo)
+{
+ struct metrics_acl_t *acl;
+ uint32_t addr;
+
+ if (list_empty(&conf_allowed))
+ return 1;
+
+ addr = ntohl(addr_nbo);
+ list_for_each_entry(acl, &conf_allowed, entry) {
+ if ((addr & acl->mask) == acl->net)
+ return 1;
+ }
+ return 0;
+}
+
+static int load_config(void)
+{
+ const char *opt;
+ enum metrics_format fmt = METRICS_FORMAT_PROMETHEUS;
+ char *address = NULL;
+ struct sockaddr_in dummy;
+ LIST_HEAD(new_allowed);
+
+ opt = conf_get_opt("metrics", "format");
+ if (opt && parse_format(opt, &fmt) < 0) {
+ log_error("metrics: unknown format '%s', expected 'prometheus' or 'json'\n", opt);
+ return -1;
+ }
+
+ opt = conf_get_opt("metrics", "address");
+ if (!opt) {
+ log_emerg("metrics: 'address' option is required (host:port)\n");
+ return -1;
+ }
+ if (parse_listen_address(opt, &dummy) < 0) {
+ log_emerg("metrics: invalid address '%s', expected host:port\n", opt);
+ return -1;
+ }
+ address = _strdup(opt);
+ if (!address) {
+ log_emerg("metrics: out of memory while loading config\n");
+ return -1;
+ }
+
+ opt = conf_get_opt("metrics", "allowed_ips");
+ if (opt && parse_allowed_ips(opt, &new_allowed) < 0) {
+ _free(address);
+ return -1;
+ }
+
+ conf_format = fmt;
+ if (conf_address)
+ _free(conf_address);
+ conf_address = address;
+
+ free_acl(&conf_allowed);
+ list_replace_init(&new_allowed, &conf_allowed);
+
+ opt = conf_get_opt("metrics", "read_timeout");
+ if (opt) {
+ int n = atoi(opt);
+ conf_read_timeout = n > 0 ? n : 0;
+ }
+
+ opt = conf_get_opt("metrics", "max_clients");
+ if (opt) {
+ int n = atoi(opt);
+ conf_max_clients = n > 0 ? n : 0;
+ }
+
+ opt = conf_get_opt("metrics", "sessions");
+ conf_sessions = opt ? atoi(opt) != 0 : 0;
+
+ return 0;
+}
+
+static const char *content_type(void)
+{
+ switch (conf_format) {
+ case METRICS_FORMAT_JSON:
+ return "application/json";
+ case METRICS_FORMAT_PROMETHEUS:
+ default:
+ return "text/plain; version=0.0.4; charset=utf-8";
+ }
+}
+
+struct strbuf {
+ char *data;
+ size_t len;
+ size_t cap;
+ int oom;
+};
+
+static int strbuf_reserve(struct strbuf *sb, size_t want)
+{
+ size_t need = sb->len + want + 1;
+ size_t ncap;
+ char *p;
+
+ if (sb->oom)
+ return -1;
+ if (need <= sb->cap)
+ return 0;
+
+ ncap = sb->cap ? sb->cap : 1024;
+ while (ncap < need)
+ ncap *= 2;
+
+ p = _realloc(sb->data, ncap);
+ if (!p) {
+ sb->oom = 1;
+ return -1;
+ }
+ sb->data = p;
+ sb->cap = ncap;
+ return 0;
+}
+
+static void strbuf_appendf(struct strbuf *sb, const char *fmt, ...)
+ __attribute__((format(gnu_printf, 2, 3)));
+
+static void strbuf_appendf(struct strbuf *sb, const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+ char *dst;
+ size_t avail;
+
+ if (sb->oom)
+ return;
+
+ for (;;) {
+ /* On the very first append sb->data is still NULL and
+ * sb->cap is zero. Computing sb->data + sb->len in that
+ * state would be NULL pointer arithmetic (UB per the C
+ * standard); pass NULL directly to vsnprintf instead,
+ * which is well-defined when the size is zero.
+ */
+ avail = sb->cap - sb->len;
+ dst = sb->data ? sb->data + sb->len : NULL;
+
+ va_start(ap, fmt);
+ n = vsnprintf(dst, avail, fmt, ap);
+ va_end(ap);
+
+ if (n < 0) {
+ sb->oom = 1;
+ return;
+ }
+ if ((size_t)n < avail) {
+ sb->len += n;
+ return;
+ }
+ if (strbuf_reserve(sb, n + 1) < 0)
+ return;
+ }
+}
+
+static void strbuf_free(struct strbuf *sb)
+{
+ if (sb->data)
+ _free(sb->data);
+ sb->data = NULL;
+ sb->len = sb->cap = 0;
+}
+
+struct accel_stats {
+ time_t uptime;
+ unsigned int cpu;
+ unsigned long rss_bytes;
+ unsigned long virt_bytes;
+ struct triton_stat_t core;
+ struct ap_session_stat sessions;
+};
+
+static void read_proc_mem(unsigned long *rss, unsigned long *virt)
+{
+ char path[64];
+ unsigned long vmsize = 0, vmrss = 0;
+ long page_size = sysconf(_SC_PAGESIZE);
+ FILE *f;
+
+ snprintf(path, sizeof(path), "/proc/%i/statm", getpid());
+ f = fopen(path, "r");
+ if (f) {
+ if (fscanf(f, "%lu %lu", &vmsize, &vmrss) != 2) {
+ vmsize = 0;
+ vmrss = 0;
+ }
+ fclose(f);
+ }
+
+ *rss = (unsigned long)vmrss * (page_size > 0 ? page_size : 4096);
+ *virt = (unsigned long)vmsize * (page_size > 0 ? page_size : 4096);
+}
+
+static void gather_stats(struct accel_stats *s)
+{
+ struct timespec ts;
+
+ memset(s, 0, sizeof(*s));
+ triton_stat_get(&s->core);
+ ap_session_stat_get(&s->sessions);
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ s->uptime = ts.tv_sec - s->core.start_time;
+ s->cpu = s->core.cpu;
+
+ read_proc_mem(&s->rss_bytes, &s->virt_bytes);
+}
+
+static void emit_prom_gauge(struct strbuf *sb, const char *name,
+ const char *help, unsigned long long value)
+{
+ strbuf_appendf(sb, "# HELP %s %s\n", name, help);
+ strbuf_appendf(sb, "# TYPE %s gauge\n", name);
+ strbuf_appendf(sb, "%s %llu\n", name, value);
+}
+
+static const char *session_state_name(int state)
+{
+ switch (state) {
+ case AP_STATE_STARTING:
+ return "starting";
+ case AP_STATE_ACTIVE:
+ return "active";
+ case AP_STATE_FINISHING:
+ return "finishing";
+ case AP_STATE_RESTORE:
+ return "restore";
+ default:
+ return "unknown";
+ }
+}
+
+static void append_prefix_len(char *buf, size_t len, int prefix_len)
+{
+ size_t pos = strlen(buf);
+
+ if (pos < len)
+ snprintf(buf + pos, len - pos, "/%i", prefix_len);
+}
+
+static void session_ipv6(struct ap_session *ses, char *buf, size_t len, int with_plen)
+{
+ struct ipv6db_addr_t *a;
+ struct in6_addr addr;
+
+ buf[0] = 0;
+ if (!ses->ipv6 || list_empty(&ses->ipv6->addr_list))
+ return;
+
+ a = list_first_entry(&ses->ipv6->addr_list, typeof(*a), entry);
+ if (!a->prefix_len)
+ return;
+ build_ip6_addr(a, ses->ipv6->peer_intf_id, &addr);
+ if (!inet_ntop(AF_INET6, &addr, buf, len))
+ return;
+ if (with_plen)
+ append_prefix_len(buf, len, a->prefix_len);
+}
+
+static void session_ipv6_dp(struct ap_session *ses, char *buf, size_t len)
+{
+ struct ipv6db_addr_t *a;
+
+ buf[0] = 0;
+ if (!ses->ipv6_dp || list_empty(&ses->ipv6_dp->prefix_list))
+ return;
+
+ a = list_first_entry(&ses->ipv6_dp->prefix_list, typeof(*a), entry);
+ if (!inet_ntop(AF_INET6, &a->addr, buf, len))
+ return;
+ append_prefix_len(buf, len, a->prefix_len);
+}
+
+/* Bare peer address, no prefix length: this is the address the peer is
+ * reachable at, mirroring the "ip" column of "accel-cmd show sessions". */
+static void session_ip(struct ap_session *ses, char *buf, size_t len)
+{
+ if (ses->ipv4) {
+ inet_ntop(AF_INET, &ses->ipv4->peer_addr, buf, len);
+ return;
+ }
+ session_ipv6(ses, buf, len, 0);
+}
+
+static unsigned long long session_uptime(struct ap_session *ses, time_t now)
+{
+ time_t end = ses->stop_time ? ses->stop_time : now;
+
+ return end > ses->start_time ? (unsigned long long)(end - ses->start_time) : 0;
+}
+
+static void render_prometheus(struct strbuf *sb)
+{
+ struct accel_stats s;
+
+ gather_stats(&s);
+
+ strbuf_appendf(sb, "# HELP accel_ppp_build_info accel-ppp build information\n");
+ strbuf_appendf(sb, "# TYPE accel_ppp_build_info gauge\n");
+ strbuf_appendf(sb, "accel_ppp_build_info{version=\"%s\"} 1\n", ACCEL_PPP_VERSION);
+
+ emit_prom_gauge(sb, "accel_ppp_uptime_seconds",
+ "Daemon uptime in seconds",
+ (unsigned long long)s.uptime);
+ emit_prom_gauge(sb, "accel_ppp_cpu_percent",
+ "Daemon CPU usage in percent",
+ (unsigned long long)s.cpu);
+ emit_prom_gauge(sb, "accel_ppp_memory_rss_bytes",
+ "Resident set size of the daemon in bytes",
+ (unsigned long long)s.rss_bytes);
+ emit_prom_gauge(sb, "accel_ppp_memory_virt_bytes",
+ "Virtual memory size of the daemon in bytes",
+ (unsigned long long)s.virt_bytes);
+
+ emit_prom_gauge(sb, "accel_ppp_core_mempool_allocated_bytes",
+ "Bytes currently allocated from triton mempools",
+ (unsigned long long)s.core.mempool_allocated);
+ emit_prom_gauge(sb, "accel_ppp_core_mempool_available_bytes",
+ "Bytes currently free in triton mempools",
+ (unsigned long long)s.core.mempool_available);
+ emit_prom_gauge(sb, "accel_ppp_core_threads",
+ "Total number of triton worker threads",
+ s.core.thread_count);
+ emit_prom_gauge(sb, "accel_ppp_core_threads_active",
+ "Number of triton worker threads currently active",
+ s.core.thread_active);
+ emit_prom_gauge(sb, "accel_ppp_core_contexts",
+ "Total number of triton contexts",
+ s.core.context_count);
+ emit_prom_gauge(sb, "accel_ppp_core_contexts_sleeping",
+ "Number of triton contexts currently sleeping",
+ s.core.context_sleeping);
+ emit_prom_gauge(sb, "accel_ppp_core_contexts_pending",
+ "Number of triton contexts waiting to run",
+ s.core.context_pending);
+ emit_prom_gauge(sb, "accel_ppp_core_md_handlers",
+ "Total number of triton md handlers",
+ s.core.md_handler_count);
+ emit_prom_gauge(sb, "accel_ppp_core_md_handlers_pending",
+ "Number of triton md handlers with pending events",
+ s.core.md_handler_pending);
+ emit_prom_gauge(sb, "accel_ppp_core_timers",
+ "Total number of triton timers",
+ s.core.timer_count);
+ emit_prom_gauge(sb, "accel_ppp_core_timers_pending",
+ "Number of triton timers pending fire",
+ s.core.timer_pending);
+
+ strbuf_appendf(sb, "# HELP accel_ppp_sessions Number of sessions in each state\n");
+ strbuf_appendf(sb, "# TYPE accel_ppp_sessions gauge\n");
+ strbuf_appendf(sb, "accel_ppp_sessions{state=\"starting\"} %u\n", s.sessions.starting);
+ strbuf_appendf(sb, "accel_ppp_sessions{state=\"active\"} %u\n", s.sessions.active);
+ strbuf_appendf(sb, "accel_ppp_sessions{state=\"finishing\"} %u\n", s.sessions.finishing);
+
+ strbuf_appendf(sb, "# HELP accel_ppp_protocol_sessions Sessions per protocol and state\n");
+ strbuf_appendf(sb, "# TYPE accel_ppp_protocol_sessions gauge\n");
+ for (size_t i = 0; i < sizeof(proto_stats) / sizeof(proto_stats[0]); i++) {
+ struct proto_stat *p = &proto_stats[i];
+
+ if (!proto_resolve(p))
+ continue;
+ strbuf_appendf(sb, "accel_ppp_protocol_sessions{protocol=\"%s\",state=\"starting\"} %u\n",
+ p->module, p->starting());
+ strbuf_appendf(sb, "accel_ppp_protocol_sessions{protocol=\"%s\",state=\"active\"} %u\n",
+ p->module, p->active());
+ }
+}
+
+/* Length of the well formed UTF-8 sequence starting at s, 0 if the bytes
+ * there are not one. Overlong forms, surrogates and out of range code
+ * points are rejected. */
+static int utf8_seq_len(const unsigned char *s)
+{
+ unsigned int cp, min;
+ int n, i;
+
+ if (s[0] < 0x80)
+ return 1;
+
+ if ((s[0] & 0xe0) == 0xc0) {
+ n = 2;
+ min = 0x80;
+ cp = s[0] & 0x1f;
+ } else if ((s[0] & 0xf0) == 0xe0) {
+ n = 3;
+ min = 0x800;
+ cp = s[0] & 0x0f;
+ } else if ((s[0] & 0xf8) == 0xf0) {
+ n = 4;
+ min = 0x10000;
+ cp = s[0] & 0x07;
+ } else
+ return 0;
+
+ for (i = 1; i < n; i++) {
+ if ((s[i] & 0xc0) != 0x80)
+ return 0;
+ cp = (cp << 6) | (s[i] & 0x3f);
+ }
+
+ if (cp < min || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff))
+ return 0;
+
+ return n;
+}
+
+static void append_json_string(struct strbuf *sb, const char *str)
+{
+ const unsigned char *s = (const unsigned char *)str;
+ const char *esc;
+ int n;
+
+ strbuf_appendf(sb, "\"");
+ while (*s) {
+ esc = NULL;
+ switch (*s) {
+ case '"':
+ esc = "\\\"";
+ break;
+ case '\\':
+ esc = "\\\\";
+ break;
+ case '\b':
+ esc = "\\b";
+ break;
+ case '\f':
+ esc = "\\f";
+ break;
+ case '\n':
+ esc = "\\n";
+ break;
+ case '\r':
+ esc = "\\r";
+ break;
+ case '\t':
+ esc = "\\t";
+ break;
+ }
+
+ if (esc) {
+ strbuf_appendf(sb, "%s", esc);
+ s++;
+ continue;
+ }
+
+ if (*s < 0x20) {
+ strbuf_appendf(sb, "\\u%04x", *s);
+ s++;
+ continue;
+ }
+
+ /* Usernames and station ids come from the peer and are not
+ * validated anywhere, so a single malformed sequence would
+ * otherwise make the whole document undecodable. */
+ n = utf8_seq_len(s);
+ if (!n) {
+ strbuf_appendf(sb, "\\ufffd");
+ s++;
+ continue;
+ }
+
+ strbuf_appendf(sb, "%.*s", n, (const char *)s);
+ s += n;
+ }
+ strbuf_appendf(sb, "\"");
+}
+
+static void emit_json_proto(struct strbuf *sb, const char *name, int *first,
+ unsigned int starting, unsigned int active)
+{
+ if (!*first)
+ strbuf_appendf(sb, ",");
+ *first = 0;
+ strbuf_appendf(sb, "\"%s\":{\"starting\":%u,\"active\":%u}",
+ name, starting, active);
+}
+
+static void append_json_field(struct strbuf *sb, int *first, const char *name,
+ const char *value)
+{
+ if (!*first)
+ strbuf_appendf(sb, ",");
+ *first = 0;
+ append_json_string(sb, name);
+ strbuf_appendf(sb, ":");
+ append_json_string(sb, value ? value : "");
+}
+
+/* TODO: Copy a bounded snapshot of the required fields under ses_lock, then
+ * serialize it after unlocking. This would shorten lock hold time and make it
+ * practical to add a response-size limit or pagination for large deployments.
+ */
+/* The whole list is walked with ses_lock held, so nothing in here may
+ * block or touch the session. In particular ap_session_read_stats() is
+ * not used: it issues a synchronous netlink round trip per session,
+ * which would stall every session setup and teardown for the duration
+ * of a scrape, it writes back into the session while only the read lock
+ * is held, and it needs the thread local "net" of the session's
+ * namespace, which this context does not have. The accounting counters
+ * last sampled by the session itself are reported instead. */
+static void render_json_sessions(struct strbuf *sb)
+{
+ struct ap_session *ses;
+ time_t now = _time();
+ char ip[INET6_ADDRSTRLEN];
+ char ipv6[INET6_ADDRSTRLEN + 5];
+ char ipv6_dp[INET6_ADDRSTRLEN + 5];
+ int first = 1;
+ int f;
+
+ strbuf_appendf(sb, ",\"session_details\":[");
+ pthread_rwlock_rdlock(&ses_lock);
+ list_for_each_entry(ses, &ses_list, entry) {
+ session_ip(ses, ip, sizeof(ip));
+ session_ipv6(ses, ipv6, sizeof(ipv6), 1);
+ session_ipv6_dp(ses, ipv6_dp, sizeof(ipv6_dp));
+
+ strbuf_appendf(sb, "%s", first ? "{" : ",{");
+ first = 0;
+ f = 1;
+ append_json_field(sb, &f, "session_id", ses->sessionid);
+ append_json_field(sb, &f, "ifname", ses->ifname);
+ append_json_field(sb, &f, "username", ses->username);
+ append_json_field(sb, &f, "ip", ip);
+ append_json_field(sb, &f, "ipv6", ipv6);
+ append_json_field(sb, &f, "delegated_ipv6_prefix", ipv6_dp);
+ append_json_field(sb, &f, "protocol", ses->ctrl ? ses->ctrl->name : NULL);
+ append_json_field(sb, &f, "state", session_state_name(ses->state));
+ append_json_field(sb, &f, "calling_station_id", ses->ctrl ? ses->ctrl->calling_station_id : NULL);
+ append_json_field(sb, &f, "called_station_id", ses->ctrl ? ses->ctrl->called_station_id : NULL);
+ append_json_field(sb, &f, "service_name", ses->ctrl ? ses->ctrl->service_name : NULL);
+ append_json_field(sb, &f, "inbound_if", ses->ctrl ? ses->ctrl->ifname : NULL);
+ append_json_field(sb, &f, "compression", ses->comp);
+ append_json_field(sb, &f, "vrf", ses->vrf_name);
+ append_json_field(sb, &f, "netns", ses->net ? ses->net->name : NULL);
+ strbuf_appendf(sb,
+ ",\"uptime_seconds\":%llu,\"rx_bytes\":%" PRIu64
+ ",\"tx_bytes\":%" PRIu64 ",\"rx_packets\":%" PRIu64
+ ",\"tx_packets\":%" PRIu64 "}",
+ session_uptime(ses, now), ses->acct_rx_bytes,
+ ses->acct_tx_bytes, ses->acct_rx_packets,
+ ses->acct_tx_packets);
+ }
+ pthread_rwlock_unlock(&ses_lock);
+ strbuf_appendf(sb, "]");
+}
+
+static void render_json(struct strbuf *sb)
+{
+ struct accel_stats s;
+ int first = 1;
+
+ gather_stats(&s);
+
+ strbuf_appendf(sb, "{");
+ strbuf_appendf(sb, "\"build\":{\"version\":");
+ append_json_string(sb, ACCEL_PPP_VERSION);
+ strbuf_appendf(sb, "},");
+
+ strbuf_appendf(sb, "\"uptime_seconds\":%llu,", (unsigned long long)s.uptime);
+ strbuf_appendf(sb, "\"cpu_percent\":%u,", s.cpu);
+ strbuf_appendf(sb, "\"memory\":{\"rss_bytes\":%lu,\"virt_bytes\":%lu},",
+ s.rss_bytes, s.virt_bytes);
+
+ strbuf_appendf(sb, "\"core\":{");
+ strbuf_appendf(sb, "\"mempool_allocated_bytes\":%" PRIu64 ",", s.core.mempool_allocated);
+ strbuf_appendf(sb, "\"mempool_available_bytes\":%" PRIu64 ",", s.core.mempool_available);
+ strbuf_appendf(sb, "\"threads\":%u,", s.core.thread_count);
+ strbuf_appendf(sb, "\"threads_active\":%u,", s.core.thread_active);
+ strbuf_appendf(sb, "\"contexts\":%u,", s.core.context_count);
+ strbuf_appendf(sb, "\"contexts_sleeping\":%u,", s.core.context_sleeping);
+ strbuf_appendf(sb, "\"contexts_pending\":%u,", s.core.context_pending);
+ strbuf_appendf(sb, "\"md_handlers\":%u,", s.core.md_handler_count);
+ strbuf_appendf(sb, "\"md_handlers_pending\":%u,", s.core.md_handler_pending);
+ strbuf_appendf(sb, "\"timers\":%u,", s.core.timer_count);
+ strbuf_appendf(sb, "\"timers_pending\":%u", s.core.timer_pending);
+ strbuf_appendf(sb, "},");
+
+ strbuf_appendf(sb,
+ "\"sessions\":{\"starting\":%u,\"active\":%u,\"finishing\":%u},",
+ s.sessions.starting, s.sessions.active, s.sessions.finishing);
+
+ strbuf_appendf(sb, "\"protocols\":{");
+ for (size_t i = 0; i < sizeof(proto_stats) / sizeof(proto_stats[0]); i++) {
+ struct proto_stat *p = &proto_stats[i];
+
+ if (!proto_resolve(p))
+ continue;
+ emit_json_proto(sb, p->module, &first, p->starting(), p->active());
+ }
+ strbuf_appendf(sb, "}");
+ if (conf_sessions)
+ render_json_sessions(sb);
+
+ strbuf_appendf(sb, "}\n");
+}
+
+/* Try to drain cln->xmit_buf to the socket. Returns 0 if the entire
+ * response was flushed, 1 if a partial write occurred and MD_MODE_WRITE
+ * was enabled to finish later, -1 if the connection is broken (caller
+ * should disconnect).
+ */
+static int xmit_flush(struct metrics_client_t *cln)
+{
+ int n;
+
+ while (cln->xmit_pos < cln->xmit_len) {
+ n = write(cln->hnd.fd,
+ cln->xmit_buf + cln->xmit_pos,
+ cln->xmit_len - cln->xmit_pos);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ triton_md_enable_handler(&cln->hnd, MD_MODE_WRITE);
+ return 1;
+ }
+ return -1;
+ }
+ cln->xmit_pos += n;
+ }
+ return 0;
+}
+
+static int format_header(char *buf, size_t size, int status, const char *reason,
+ const char *ctype, size_t body_len)
+{
+ int hlen;
+
+ hlen = snprintf(buf, size,
+ "HTTP/1.1 %d %s\r\n"
+ "Server: accel-ppp\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: %zu\r\n"
+ "Connection: close\r\n"
+ "\r\n",
+ status, reason, ctype, body_len);
+ if (hlen <= 0 || hlen >= (int)size)
+ return -1;
+
+ return hlen;
+}
+
+static void start_xmit(struct metrics_client_t *cln)
+{
+ int rc = xmit_flush(cln);
+
+ if (rc < 0)
+ cln->disconnect = 1;
+ else if (rc == 0)
+ cln->disconnect = 1; /* fully flushed, ready to close */
+}
+
+static void send_response(struct metrics_client_t *cln, int status, const char *reason,
+ const char *ctype, const char *body, int body_len)
+{
+ char header[METRICS_HDR_RESERVE];
+ int hlen, total;
+
+ if (cln->xmit_buf)
+ return; /* response already in flight */
+
+ hlen = format_header(header, sizeof(header), status, reason, ctype,
+ body_len > 0 ? (size_t)body_len : 0);
+ if (hlen < 0) {
+ cln->disconnect = 1;
+ return;
+ }
+
+ total = hlen + (body_len > 0 ? body_len : 0);
+ cln->xmit_buf = _malloc(total);
+ if (!cln->xmit_buf) {
+ cln->disconnect = 1;
+ return;
+ }
+ memcpy(cln->xmit_buf, header, hlen);
+ if (body_len > 0)
+ memcpy(cln->xmit_buf + hlen, body, body_len);
+ cln->xmit_pos = 0;
+ cln->xmit_len = total;
+
+ start_xmit(cln);
+}
+
+static void send_simple(struct metrics_client_t *cln, int status, const char *reason)
+{
+ char body[128];
+ int len;
+
+ len = snprintf(body, sizeof(body), "%d %s\n", status, reason);
+ send_response(cln, status, reason, "text/plain; charset=utf-8", body, len);
+}
+
+/* Sends a body rendered into sb and takes ownership of its buffer. The
+ * first METRICS_HDR_RESERVE bytes of sb are unused padding the header is
+ * written into, so a body that can be several megabytes with sessions=1
+ * is not copied a second time. */
+static void send_rendered(struct metrics_client_t *cln, const char *ctype,
+ struct strbuf *sb)
+{
+ char header[METRICS_HDR_RESERVE];
+ size_t body_len = sb->len - METRICS_HDR_RESERVE;
+ int hlen;
+
+ if (cln->xmit_buf)
+ return; /* response already in flight */
+
+ hlen = format_header(header, sizeof(header), 200, "OK", ctype, body_len);
+ if (hlen < 0 || sb->len > INT_MAX) {
+ send_simple(cln, 500, "Internal Server Error");
+ return;
+ }
+
+ memcpy(sb->data + METRICS_HDR_RESERVE - hlen, header, hlen);
+ cln->xmit_buf = sb->data;
+ cln->xmit_pos = METRICS_HDR_RESERVE - hlen;
+ cln->xmit_len = (int)sb->len;
+ sb->data = NULL;
+ sb->len = sb->cap = 0;
+
+ start_xmit(cln);
+}
+
+static void serve_metrics(struct metrics_client_t *cln)
+{
+ struct strbuf sb = {0};
+
+ /* Reserve room for the response header in front of the body so it
+ * can be handed to the client without another copy. */
+ if (strbuf_reserve(&sb, METRICS_HDR_RESERVE))
+ goto err;
+ sb.len = METRICS_HDR_RESERVE;
+
+ switch (conf_format) {
+ case METRICS_FORMAT_PROMETHEUS:
+ render_prometheus(&sb);
+ break;
+ case METRICS_FORMAT_JSON:
+ render_json(&sb);
+ break;
+ }
+
+ if (sb.oom || !sb.data)
+ goto err;
+
+ send_rendered(cln, content_type(), &sb);
+ strbuf_free(&sb);
+ return;
+
+err:
+ send_simple(cln, 500, "Internal Server Error");
+ strbuf_free(&sb);
+}
+
+static void client_timeout(struct triton_timer_t *t);
+
+static void disconnect_client(struct metrics_client_t *cln)
+{
+ if (cln->timer.tpd)
+ triton_timer_del(&cln->timer);
+ list_del(&cln->entry);
+ client_count--;
+ triton_md_unregister_handler(&cln->hnd, 1);
+ if (cln->recv_buf)
+ _free(cln->recv_buf);
+ if (cln->xmit_buf)
+ _free(cln->xmit_buf);
+ _free(cln);
+}
+
+static void client_timeout(struct triton_timer_t *t)
+{
+ struct metrics_client_t *cln = container_of(t, typeof(*cln), timer);
+
+ disconnect_client(cln);
+}
+
+static void handle_request(struct metrics_client_t *cln)
+{
+ char *line_end, *space1, *space2;
+ char *method, *path;
+
+ line_end = strstr(cln->recv_buf, "\r\n");
+ if (!line_end)
+ return;
+ *line_end = 0;
+
+ method = cln->recv_buf;
+ space1 = strchr(method, ' ');
+ if (!space1) {
+ send_simple(cln, 400, "Bad Request");
+ return;
+ }
+ *space1 = 0;
+ path = space1 + 1;
+ space2 = strchr(path, ' ');
+ if (space2)
+ *space2 = 0;
+
+ if (strcmp(method, "GET")) {
+ send_simple(cln, 405, "Method Not Allowed");
+ return;
+ }
+
+ if (!strcmp(path, "/metrics"))
+ serve_metrics(cln);
+ else
+ send_simple(cln, 404, "Not Found");
+}
+
+static int cln_read(struct triton_md_handler_t *h)
+{
+ struct metrics_client_t *cln = container_of(h, typeof(*cln), hnd);
+ int n;
+
+ while (1) {
+ if (cln->recv_pos >= METRICS_RECV_BUF_SIZE - 1) {
+ send_simple(cln, 413, "Request Entity Too Large");
+ break;
+ }
+
+ n = read(h->fd, cln->recv_buf + cln->recv_pos,
+ METRICS_RECV_BUF_SIZE - 1 - cln->recv_pos);
+ if (n == 0) {
+ cln->disconnect = 1;
+ break;
+ }
+ if (n < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ cln->disconnect = 1;
+ break;
+ }
+
+ cln->recv_pos += n;
+ cln->recv_buf[cln->recv_pos] = 0;
+
+ if (strstr(cln->recv_buf, "\r\n\r\n")) {
+ handle_request(cln);
+ break;
+ }
+ }
+
+ if (cln->disconnect) {
+ disconnect_client(cln);
+ return -1;
+ }
+ if (cln->xmit_buf)
+ triton_md_disable_handler(&cln->hnd, MD_MODE_READ);
+ return 0;
+}
+
+static int cln_write(struct triton_md_handler_t *h)
+{
+ struct metrics_client_t *cln = container_of(h, typeof(*cln), hnd);
+ int rc;
+
+ rc = xmit_flush(cln);
+ if (rc == 1)
+ return 0; /* still partial — keep MD_MODE_WRITE enabled */
+ if (rc < 0) {
+ disconnect_client(cln);
+ return -1;
+ }
+ triton_md_disable_handler(&cln->hnd, MD_MODE_WRITE);
+ disconnect_client(cln);
+ return -1;
+}
+
+static void accept_resume(struct triton_timer_t *t)
+{
+ triton_timer_del(t);
+ accept_paused = 0;
+ triton_md_enable_handler(&serv_hnd, MD_MODE_READ);
+}
+
+static void accept_pause(void)
+{
+ if (accept_paused)
+ return;
+ accept_paused = 1;
+ triton_md_disable_handler(&serv_hnd, MD_MODE_READ);
+ accept_resume_timer.expire = accept_resume;
+ accept_resume_timer.expire_tv.tv_sec = METRICS_ACCEPT_BACKOFF;
+ triton_timer_add(&serv_ctx, &accept_resume_timer, 0);
+}
+
+static int serv_read(struct triton_md_handler_t *h)
+{
+ struct sockaddr_in addr;
+ socklen_t size = sizeof(addr);
+ int sock;
+ struct metrics_client_t *cln;
+ int batch;
+
+ /* Cap the number of accepts handled per dispatch. Without this, an
+ * overflow burst (peers exceeding max_clients or denied by
+ * allowed_ips) keeps us in this loop accept()ing and immediately
+ * closing sockets, never yielding back to the triton dispatcher.
+ * Since the per-client read-timeout timers share serv_ctx, that
+ * would delay client_timeout() and let stalled clients live past
+ * read_timeout. Return after METRICS_ACCEPT_BATCH iterations; if
+ * the listening fd is still readable triton will dispatch us again
+ * on the next loop after timers have had a chance to fire.
+ */
+ for (batch = 0; batch < METRICS_ACCEPT_BATCH; batch++) {
+ sock = accept(h->fd, (struct sockaddr *)&addr, &size);
+ if (sock < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return 0;
+ if (errno == EINTR || errno == ECONNABORTED)
+ continue;
+ /* For persistent resource-exhaustion errors
+ * (EMFILE, ENFILE, ENOBUFS, ENOMEM) the kernel will
+ * keep the listening fd readable, so a bare
+ * `continue` spins the worker. Disable the listener
+ * briefly and retry via a one-shot timer.
+ */
+ log_error("metrics: accept failed: %s; backing off %ds\n",
+ strerror(errno), METRICS_ACCEPT_BACKOFF);
+ accept_pause();
+ return 0;
+ }
+
+ if (!ip_allowed(addr.sin_addr.s_addr)) {
+ close(sock);
+ continue;
+ }
+
+ if (conf_max_clients && client_count >= (unsigned int)conf_max_clients) {
+ close(sock);
+ continue;
+ }
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
+ log_error("metrics: failed to set nonblocking mode: %s\n", strerror(errno));
+ close(sock);
+ continue;
+ }
+
+ cln = _malloc(sizeof(*cln));
+ if (!cln) {
+ close(sock);
+ continue;
+ }
+ memset(cln, 0, sizeof(*cln));
+ cln->addr = addr;
+ cln->hnd.fd = sock;
+ cln->hnd.read = cln_read;
+ cln->hnd.write = cln_write;
+ cln->recv_buf = _malloc(METRICS_RECV_BUF_SIZE);
+ if (!cln->recv_buf) {
+ close(sock);
+ _free(cln);
+ continue;
+ }
+
+ list_add_tail(&cln->entry, &clients);
+ client_count++;
+ triton_md_register_handler(&serv_ctx, &cln->hnd);
+ triton_md_enable_handler(&cln->hnd, MD_MODE_READ);
+
+ if (conf_read_timeout > 0) {
+ cln->timer.expire = client_timeout;
+ cln->timer.expire_tv.tv_sec = conf_read_timeout;
+ triton_timer_add(&serv_ctx, &cln->timer, 0);
+ }
+ }
+
+ return 0;
+}
+
+static void serv_close(struct triton_context_t *ctx)
+{
+ struct metrics_client_t *cln;
+
+ if (accept_resume_timer.tpd)
+ triton_timer_del(&accept_resume_timer);
+
+ while (!list_empty(&clients)) {
+ cln = list_entry(clients.next, typeof(*cln), entry);
+ disconnect_client(cln);
+ }
+
+ triton_md_unregister_handler(&serv_hnd, 1);
+ triton_context_unregister(ctx);
+}
+
+static struct triton_context_t serv_ctx = {
+ .close = serv_close,
+};
+
+static struct triton_md_handler_t serv_hnd = {
+ .read = serv_read,
+};
+
+static int start_server(void)
+{
+ struct sockaddr_in addr;
+ int reuse = 1;
+
+ if (parse_listen_address(conf_address, &addr) < 0) {
+ log_emerg("metrics: invalid listen address '%s'\n", conf_address);
+ return -1;
+ }
+
+ serv_hnd.fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (serv_hnd.fd < 0) {
+ log_emerg("metrics: failed to create socket: %s\n", strerror(errno));
+ return -1;
+ }
+
+ fcntl(serv_hnd.fd, F_SETFD, fcntl(serv_hnd.fd, F_GETFD) | FD_CLOEXEC);
+ setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
+
+ if (bind(serv_hnd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ log_emerg("metrics: failed to bind %s: %s\n", conf_address, strerror(errno));
+ goto err;
+ }
+
+ if (listen(serv_hnd.fd, 16) < 0) {
+ log_emerg("metrics: failed to listen on %s: %s\n", conf_address, strerror(errno));
+ goto err;
+ }
+
+ if (fcntl(serv_hnd.fd, F_SETFL, O_NONBLOCK)) {
+ log_emerg("metrics: failed to set nonblocking mode: %s\n", strerror(errno));
+ goto err;
+ }
+
+ triton_context_register(&serv_ctx, NULL);
+ triton_context_set_priority(&serv_ctx, 0);
+ triton_md_register_handler(&serv_ctx, &serv_hnd);
+ triton_md_enable_handler(&serv_hnd, MD_MODE_READ);
+ triton_context_wakeup(&serv_ctx);
+
+ serv_running = 1;
+ return 0;
+
+err:
+ close(serv_hnd.fd);
+ serv_hnd.fd = -1;
+ return -1;
+}
+
+static void init(void)
+{
+ /* TODO: Add optional HTTP authentication before exposing session_details;
+ * allowed_ips limits network reachability but does not identify callers.
+ */
+ if (load_config() < 0)
+ return;
+
+ if (start_server() < 0)
+ return;
+
+ log_info2("metrics: listening on %s, format %s\n",
+ conf_address,
+ conf_format == METRICS_FORMAT_PROMETHEUS ? "prometheus" : "json");
+}
+
+DEFINE_INIT(100, init);
diff --git a/accel-pppd/extra/net-snmp/statCore.c b/accel-pppd/extra/net-snmp/statCore.c
index c2e64689..7e9047da 100644
--- a/accel-pppd/extra/net-snmp/statCore.c
+++ b/accel-pppd/extra/net-snmp/statCore.c
@@ -49,7 +49,7 @@ handle_statCoreUpTime(netsnmp_mib_handler *handler,
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
- ts.tv_sec -= triton_stat.start_time;
+ ts.tv_sec -= triton_stat_start_time();
/* We are never called for a GETNEXT if it's registered as a
"instance", as it's "magically" handled for us. */
@@ -80,6 +80,8 @@ handle_statCoreCPU(netsnmp_mib_handler *handler,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
+ long cpu;
+
/* We are never called for a GETNEXT if it's registered as a
"instance", as it's "magically" handled for us. */
@@ -89,9 +91,10 @@ handle_statCoreCPU(netsnmp_mib_handler *handler,
switch(reqinfo->mode) {
case MODE_GET:
+ cpu = triton_stat_cpu();
snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
- (u_char *)&triton_stat.cpu /* XXX: a pointer to the scalar's data */,
- sizeof(triton_stat.cpu)/* XXX: the length of the data in bytes */);
+ (u_char *)&cpu /* XXX: a pointer to the scalar's data */,
+ sizeof(cpu)/* XXX: the length of the data in bytes */);
break;
diff --git a/accel-pppd/extra/net-snmp/statIPOE.c b/accel-pppd/extra/net-snmp/statIPOE.c
index 24f154fd..33430fe6 100644
--- a/accel-pppd/extra/net-snmp/statIPOE.c
+++ b/accel-pppd/extra/net-snmp/statIPOE.c
@@ -8,18 +8,17 @@
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "triton.h"
+#include "accel-pppd/ctrl/ipoe/ipoe.h"
#include "statIPOE.h"
-/*
- * The variables we want to tie the relevant OIDs to.
- * The agent will handle all GET and (if applicable) SET requests
- * to these variables automatically, changing the values as needed.
- */
-
-void ipoe_get_stat(unsigned int **, unsigned int **);
-
-static unsigned int *stat_starting;
-static unsigned int *stat_active;
+static int handle_statIPOEStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
+static int handle_statIPOEActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
/*
* Our initialization routine, called automatically by the agent
@@ -28,9 +27,6 @@ static unsigned int *stat_active;
void
init_statIPOE(void)
{
- netsnmp_handler_registration *reg;
- netsnmp_watcher_info *winfo;
-
static oid statIPOEStarting_oid[] = { 1,3,6,1,4,1,8072,100,1,6,1 };
static oid statIPOEActive_oid[] = { 1,3,6,1,4,1,8072,100,1,6,2 };
@@ -43,51 +39,75 @@ init_statIPOE(void)
if (!triton_module_loaded("ipoe"))
return;
- ipoe_get_stat(&stat_starting, &stat_active);
-
- /*
- * Register scalar watchers for each of the MIB objects.
- * The ASN type and RO/RW status are taken from the MIB definition,
- * but can be adjusted if needed.
- *
- * In most circumstances, the scalar watcher will handle all
- * of the necessary processing. But the NULL parameter in the
- * netsnmp_create_handler_registration() call can be used to
- * supply a user-provided handler if necessary.
- *
- * This approach can also be used to handle Counter64, string-
- * and OID-based watched scalars (although variable-sized writeable
- * objects will need some more specialised initialisation).
- */
DEBUGMSGTL(("statIPOE",
"Initializing statIPOEStarting scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statIPOEStarting", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statIPOEStarting", handle_statIPOEStarting,
statIPOEStarting_oid, OID_LENGTH(statIPOEStarting_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- stat_starting, sizeof(*stat_starting),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statIPOEStarting" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statIPOEStarting" );
}
DEBUGMSGTL(("statIPOE",
"Initializing statIPOEActive scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statIPOEActive", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statIPOEActive", handle_statIPOEActive,
statIPOEActive_oid, OID_LENGTH(statIPOEActive_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- stat_active, sizeof(*stat_active),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statIPOEActive" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statIPOEActive" );
}
DEBUGMSGTL(("statIPOE",
"Done initalizing statIPOE module\n"));
}
+
+static int handle_statIPOEStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = ipoe_stat_starting();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statIPOEStarting\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static int handle_statIPOEActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = ipoe_stat_active();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statIPOEActive\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
diff --git a/accel-pppd/extra/net-snmp/statL2TP.c b/accel-pppd/extra/net-snmp/statL2TP.c
index 59998386..36e6f90b 100644
--- a/accel-pppd/extra/net-snmp/statL2TP.c
+++ b/accel-pppd/extra/net-snmp/statL2TP.c
@@ -8,19 +8,18 @@
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "triton.h"
+#include "accel-pppd/ctrl/l2tp/l2tp.h"
#include "statL2TP.h"
-/*
- * The variables we want to tie the relevant OIDs to.
- * The agent will handle all GET and (if applicable) SET requests
- * to these variables automatically, changing the values as needed.
- */
-
-void l2tp_get_stat(unsigned int **, unsigned int **);
-
-static unsigned int *stat_starting;
-static unsigned int *stat_active;
+static int handle_statL2TPStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
+static int handle_statL2TPActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
/*
* Our initialization routine, called automatically by the agent
@@ -29,9 +28,6 @@ static unsigned int *stat_active;
void
init_statL2TP(void)
{
- netsnmp_handler_registration *reg;
- netsnmp_watcher_info *winfo;
-
static oid statL2TPStarting_oid[] = { 1,3,6,1,4,1,8072,100,1,4,1 };
static oid statL2TPActive_oid[] = { 1,3,6,1,4,1,8072,100,1,4,2 };
@@ -44,51 +40,75 @@ init_statL2TP(void)
if (!triton_module_loaded("l2tp"))
return;
- l2tp_get_stat(&stat_starting, &stat_active);
-
- /*
- * Register scalar watchers for each of the MIB objects.
- * The ASN type and RO/RW status are taken from the MIB definition,
- * but can be adjusted if needed.
- *
- * In most circumstances, the scalar watcher will handle all
- * of the necessary processing. But the NULL parameter in the
- * netsnmp_create_handler_registration() call can be used to
- * supply a user-provided handler if necessary.
- *
- * This approach can also be used to handle Counter64, string-
- * and OID-based watched scalars (although variable-sized writeable
- * objects will need some more specialised initialisation).
- */
DEBUGMSGTL(("statL2TP",
"Initializing statL2TPStarting scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statL2TPStarting", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statL2TPStarting", handle_statL2TPStarting,
statL2TPStarting_oid, OID_LENGTH(statL2TPStarting_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- stat_starting, sizeof(*stat_starting),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statL2TPStarting" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statL2TPStarting" );
}
DEBUGMSGTL(("statL2TP",
"Initializing statL2TPActive scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statL2TPActive", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statL2TPActive", handle_statL2TPActive,
statL2TPActive_oid, OID_LENGTH(statL2TPActive_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- stat_active, sizeof(*stat_active),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statL2TPActive" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statL2TPActive" );
}
DEBUGMSGTL(("statL2TP",
"Done initalizing statL2TP module\n"));
}
+
+static int handle_statL2TPStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = l2tp_stat_starting();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statL2TPStarting\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static int handle_statL2TPActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = l2tp_stat_active();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statL2TPActive\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
diff --git a/accel-pppd/extra/net-snmp/statPPP.c b/accel-pppd/extra/net-snmp/statPPP.c
index 37e4630c..7dc1f659 100644
--- a/accel-pppd/extra/net-snmp/statPPP.c
+++ b/accel-pppd/extra/net-snmp/statPPP.c
@@ -6,9 +6,22 @@
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+#include "ap_session.h"
#include "statPPP.h"
-#include "ppp.h"
+static int handle_statPPPStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
+static int handle_statPPPActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
+static int handle_statPPPFinishing(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
/*
* Our initialization routine, called automatically by the agent
@@ -17,77 +30,115 @@
void
init_statPPP(void)
{
- netsnmp_handler_registration *reg;
- netsnmp_watcher_info *winfo;
-
static oid statPPPStarting_oid[] = { 1,3,6,1,4,1,8072,100,1,2,1 };
static oid statPPPActive_oid[] = { 1,3,6,1,4,1,8072,100,1,2,2 };
static oid statPPPFinishing_oid[] = { 1,3,6,1,4,1,8072,100,1,2,3 };
- /*
- * a debugging statement. Run the agent with -DstatPPP to see
- * the output of this debugging statement.
- */
DEBUGMSGTL(("statPPP", "Initializing the statPPP module\n"));
-
- /*
- * Register scalar watchers for each of the MIB objects.
- * The ASN type and RO/RW status are taken from the MIB definition,
- * but can be adjusted if needed.
- *
- * In most circumstances, the scalar watcher will handle all
- * of the necessary processing. But the NULL parameter in the
- * netsnmp_create_handler_registration() call can be used to
- * supply a user-provided handler if necessary.
- *
- * This approach can also be used to handle Counter64, string-
- * and OID-based watched scalars (although variable-sized writeable
- * objects will need some more specialised initialisation).
- */
DEBUGMSGTL(("statPPP",
"Initializing statPPPStarting scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statPPPStarting", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statPPPStarting", handle_statPPPStarting,
statPPPStarting_oid, OID_LENGTH(statPPPStarting_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- &ap_session_stat.starting, sizeof(ap_session_stat.starting),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statPPPStarting" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statPPPStarting" );
}
DEBUGMSGTL(("statPPP",
"Initializing statPPPActive scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statPPPActive", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statPPPActive", handle_statPPPActive,
statPPPActive_oid, OID_LENGTH(statPPPActive_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- &ap_session_stat.active, sizeof(ap_session_stat.active),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statPPPActive" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statPPPActive" );
}
DEBUGMSGTL(("statPPP",
"Initializing statPPPFinishing scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statPPPFinishing", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statPPPFinishing", handle_statPPPFinishing,
statPPPFinishing_oid, OID_LENGTH(statPPPFinishing_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- &ap_session_stat.finishing, sizeof(ap_session_stat.finishing),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statPPPFinishing" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statPPPFinishing" );
}
DEBUGMSGTL(("statPPP",
"Done initalizing statPPP module\n"));
}
+
+static int handle_statPPPStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = ap_session_stat_starting();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statPPPStarting\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static int handle_statPPPActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = ap_session_stat_active();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statPPPActive\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static int handle_statPPPFinishing(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = ap_session_stat_finishing();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statPPPFinishing\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
diff --git a/accel-pppd/extra/net-snmp/statPPPOE.c b/accel-pppd/extra/net-snmp/statPPPOE.c
index 6042dc5b..64e61c3c 100644
--- a/accel-pppd/extra/net-snmp/statPPPOE.c
+++ b/accel-pppd/extra/net-snmp/statPPPOE.c
@@ -10,16 +10,17 @@
#include "triton.h"
#include "statPPPOE.h"
-/*
- * The variables we want to tie the relevant OIDs to.
- * The agent will handle all GET and (if applicable) SET requests
- * to these variables automatically, changing the values as needed.
- */
+unsigned int pppoe_stat_starting(void);
+unsigned int pppoe_stat_active(void);
-void pppoe_get_stat(unsigned int **, unsigned int **);
-
-static unsigned int *stat_starting;
-static unsigned int *stat_active;
+static int handle_statPPPOEStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
+static int handle_statPPPOEActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
/*
* Our initialization routine, called automatically by the agent
@@ -28,9 +29,6 @@ static unsigned int *stat_active;
void
init_statPPPOE(void)
{
- netsnmp_handler_registration *reg;
- netsnmp_watcher_info *winfo;
-
static oid statPPPOEStarting_oid[] = { 1,3,6,1,4,1,8072,100,1,5,1 };
static oid statPPPOEActive_oid[] = { 1,3,6,1,4,1,8072,100,1,5,2 };
@@ -43,51 +41,69 @@ init_statPPPOE(void)
if (!triton_module_loaded("pppoe"))
return;
- pppoe_get_stat(&stat_starting, &stat_active);
-
- /*
- * Register scalar watchers for each of the MIB objects.
- * The ASN type and RO/RW status are taken from the MIB definition,
- * but can be adjusted if needed.
- *
- * In most circumstances, the scalar watcher will handle all
- * of the necessary processing. But the NULL parameter in the
- * netsnmp_create_handler_registration() call can be used to
- * supply a user-provided handler if necessary.
- *
- * This approach can also be used to handle Counter64, string-
- * and OID-based watched scalars (although variable-sized writeable
- * objects will need some more specialised initialisation).
- */
DEBUGMSGTL(("statPPPOE",
"Initializing statPPPOEStarting scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statPPPOEStarting", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statPPPOEStarting", handle_statPPPOEStarting,
statPPPOEStarting_oid, OID_LENGTH(statPPPOEStarting_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- stat_starting, sizeof(*stat_starting),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statPPPOEStarting" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statPPPOEStarting" );
}
DEBUGMSGTL(("statPPPOE",
"Initializing statPPPOEActive scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statPPPOEActive", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statPPPOEActive", handle_statPPPOEActive,
statPPPOEActive_oid, OID_LENGTH(statPPPOEActive_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- stat_active, sizeof(*stat_active),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statPPPOEActive" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statPPPOEActive" );
}
DEBUGMSGTL(("statPPPOE",
- "Done initalizing statPPPOE module\n"));
+ "Done initalizing statPPPOE module\n"));
+}
+
+static int handle_statPPPOEStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = pppoe_stat_starting();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statPPPOEStarting\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static int handle_statPPPOEActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = pppoe_stat_active();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statPPPOEActive\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
}
diff --git a/accel-pppd/extra/net-snmp/statPPTP.c b/accel-pppd/extra/net-snmp/statPPTP.c
index 48642202..8a633da3 100644
--- a/accel-pppd/extra/net-snmp/statPPTP.c
+++ b/accel-pppd/extra/net-snmp/statPPTP.c
@@ -6,20 +6,19 @@
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
-#include "statPPTP.h"
#include "triton.h"
+#include "accel-pppd/ctrl/pptp/pptp.h"
+#include "statPPTP.h"
-/*
- * The variables we want to tie the relevant OIDs to.
- * The agent will handle all GET and (if applicable) SET requests
- * to these variables automatically, changing the values as needed.
- */
-
-void pptp_get_stat(unsigned int **, unsigned int **);
-
-static unsigned int *stat_starting;
-static unsigned int *stat_active;
+static int handle_statPPTPStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
+static int handle_statPPTPActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
/*
* Our initialization routine, called automatically by the agent
@@ -28,9 +27,6 @@ static unsigned int *stat_active;
void
init_statPPTP(void)
{
- netsnmp_handler_registration *reg;
- netsnmp_watcher_info *winfo;
-
static oid statPPTPStarting_oid[] = { 1,3,6,1,4,1,8072,100,1,3,1 };
static oid statPPTPActive_oid[] = { 1,3,6,1,4,1,8072,100,1,3,2 };
@@ -43,50 +39,75 @@ init_statPPTP(void)
if (!triton_module_loaded("pptp"))
return;
- pptp_get_stat(&stat_starting, &stat_active);
- /*
- * Register scalar watchers for each of the MIB objects.
- * The ASN type and RO/RW status are taken from the MIB definition,
- * but can be adjusted if needed.
- *
- * In most circumstances, the scalar watcher will handle all
- * of the necessary processing. But the NULL parameter in the
- * netsnmp_create_handler_registration() call can be used to
- * supply a user-provided handler if necessary.
- *
- * This approach can also be used to handle Counter64, string-
- * and OID-based watched scalars (although variable-sized writeable
- * objects will need some more specialised initialisation).
- */
DEBUGMSGTL(("statPPTP",
"Initializing statPPTPStarting scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statPPTPStarting", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statPPTPStarting", handle_statPPTPStarting,
statPPTPStarting_oid, OID_LENGTH(statPPTPStarting_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- stat_starting, sizeof(*stat_starting),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statPPTPStarting" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statPPTPStarting" );
}
DEBUGMSGTL(("statPPTP",
"Initializing statPPTPActive scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statPPTPActive", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statPPTPActive", handle_statPPTPActive,
statPPTPActive_oid, OID_LENGTH(statPPTPActive_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- stat_active, sizeof(*stat_active),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statPPTPActive" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statPPTPActive" );
}
DEBUGMSGTL(("statPPTP",
"Done initalizing statPPTP module\n"));
}
+
+static int handle_statPPTPStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = pptp_stat_starting();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statPPTPStarting\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static int handle_statPPTPActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = pptp_stat_active();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statPPTPActive\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
diff --git a/accel-pppd/extra/net-snmp/statSSTP.c b/accel-pppd/extra/net-snmp/statSSTP.c
index 06ba6aa1..62948acd 100644
--- a/accel-pppd/extra/net-snmp/statSSTP.c
+++ b/accel-pppd/extra/net-snmp/statSSTP.c
@@ -8,18 +8,17 @@
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "triton.h"
+#include "accel-pppd/ctrl/sstp/sstp.h"
#include "statSSTP.h"
-/*
- * The variables we want to tie the relevant OIDs to.
- * The agent will handle all GET and (if applicable) SET requests
- * to these variables automatically, changing the values as needed.
- */
-
-void sstp_get_stat(unsigned int **, unsigned int **);
-
-static unsigned int *stat_starting;
-static unsigned int *stat_active;
+static int handle_statSSTPStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
+static int handle_statSSTPActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests);
/*
* Our initialization routine, called automatically by the agent
@@ -28,9 +27,6 @@ static unsigned int *stat_active;
void
init_statSSTP(void)
{
- netsnmp_handler_registration *reg;
- netsnmp_watcher_info *winfo;
-
static oid statSSTPStarting_oid[] = { 1,3,6,1,4,1,8072,100,1,7,1 };
static oid statSSTPActive_oid[] = { 1,3,6,1,4,1,8072,100,1,7,2 };
@@ -43,51 +39,75 @@ init_statSSTP(void)
if (!triton_module_loaded("sstp"))
return;
- sstp_get_stat(&stat_starting, &stat_active);
-
- /*
- * Register scalar watchers for each of the MIB objects.
- * The ASN type and RO/RW status are taken from the MIB definition,
- * but can be adjusted if needed.
- *
- * In most circumstances, the scalar watcher will handle all
- * of the necessary processing. But the NULL parameter in the
- * netsnmp_create_handler_registration() call can be used to
- * supply a user-provided handler if necessary.
- *
- * This approach can also be used to handle Counter64, string-
- * and OID-based watched scalars (although variable-sized writeable
- * objects will need some more specialised initialisation).
- */
DEBUGMSGTL(("statSSTP",
"Initializing statSSTPStarting scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statSSTPStarting", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statSSTPStarting", handle_statSSTPStarting,
statSSTPStarting_oid, OID_LENGTH(statSSTPStarting_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- stat_starting, sizeof(*stat_starting),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statSSTPStarting" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statSSTPStarting" );
}
DEBUGMSGTL(("statSSTP",
"Initializing statSSTPActive scalar integer. Default value = %d\n",
0));
- reg = netsnmp_create_handler_registration(
- "statSSTPActive", NULL,
+ if (netsnmp_register_scalar(netsnmp_create_handler_registration(
+ "statSSTPActive", handle_statSSTPActive,
statSSTPActive_oid, OID_LENGTH(statSSTPActive_oid),
- HANDLER_CAN_RONLY);
- winfo = netsnmp_create_watcher_info(
- stat_active, sizeof(*stat_active),
- ASN_INTEGER, WATCHER_FIXED_SIZE);
- if (netsnmp_register_watched_scalar( reg, winfo ) < 0 ) {
- snmp_log( LOG_ERR, "Failed to register watched statSSTPActive" );
+ HANDLER_CAN_RONLY)) < 0 ) {
+ snmp_log( LOG_ERR, "Failed to register statSSTPActive" );
}
DEBUGMSGTL(("statSSTP",
"Done initalizing statSSTP module\n"));
}
+
+static int handle_statSSTPStarting(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = sstp_stat_starting();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statSSTPStarting\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static int handle_statSSTPActive(netsnmp_mib_handler *handler,
+ netsnmp_handler_registration *reginfo,
+ netsnmp_agent_request_info *reqinfo,
+ netsnmp_request_info *requests)
+{
+ long stat;
+
+ (void)handler;
+ (void)reginfo;
+
+ switch (reqinfo->mode) {
+ case MODE_GET:
+ stat = sstp_stat_active();
+ snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+ (u_char *)&stat, sizeof(stat));
+ break;
+ default:
+ snmp_log(LOG_ERR, "unknown mode (%d) in handle_statSSTPActive\n", reqinfo->mode);
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
diff --git a/accel-pppd/extra/pppd_compat.c b/accel-pppd/extra/pppd_compat.c
index 040e7d90..12363cd4 100644
--- a/accel-pppd/extra/pppd_compat.c
+++ b/accel-pppd/extra/pppd_compat.c
@@ -518,6 +518,10 @@ static void write_radattr(struct pppd_compat_pd *pd, struct rad_packet_t *pack)
int fd, i;
in_addr_t addr;
char ip_str[50];
+ union {
+ uint64_t ifid;
+ uint16_t u16[4];
+ } ifid_u;
if (ses->state == AP_STATE_ACTIVE) {
sprintf(fname1, "%s.%s", conf_radattr_prefix, ses->ifname);
@@ -572,6 +576,10 @@ static void write_radattr(struct pppd_compat_pd *pd, struct rad_packet_t *pack)
inet_ntop(AF_INET6, &attr->val.ipv6addr, ip_str, sizeof(ip_str));
fprintf(f, "%s\n", ip_str);
break;
+ case ATTR_TYPE_IFID:
+ ifid_u.ifid = attr->val.ifid;
+ fprintf(f, "%x:%x:%x:%x\n", ntohs(ifid_u.u16[0]), ntohs(ifid_u.u16[1]), ntohs(ifid_u.u16[2]), ntohs(ifid_u.u16[3]));
+ break;
}
}
fclose(f);
diff --git a/accel-pppd/ifcfg.c b/accel-pppd/ifcfg.c
index 3750060b..66f5cc6b 100644
--- a/accel-pppd/ifcfg.c
+++ b/accel-pppd/ifcfg.c
@@ -225,7 +225,7 @@ void __export ap_session_ifdown(struct ap_session *ses)
if (ses->ctrl->ppp) {
ifr6.ifr6_addr.s6_addr32[0] = htonl(0xfe800000);
- *(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ses->ipv6->intf_id;
+ memcpy(ifr6.ifr6_addr.s6_addr + 8, &ses->ipv6->intf_id, sizeof(ses->ipv6->intf_id));
ifr6.ifr6_prefixlen = 64;
net->sock6_ioctl(SIOCDIFADDR, &ifr6);
}
@@ -234,7 +234,7 @@ void __export ap_session_ifdown(struct ap_session *ses)
if (!a->installed)
continue;
if (a->prefix_len > 64)
- ip6route_del(ses->ifindex, &a->addr, a->prefix_len, NULL, 0, 0);
+ ip6route_del(ses->ifindex, &a->addr, a->prefix_len, NULL, 0, 0, ses->vrf_name);
else {
struct in6_addr addr;
memcpy(addr.s6_addr, &a->addr, 8);
@@ -376,4 +376,4 @@ int __export ap_session_vrf(struct ap_session *ses, const char *vrf_name, int le
return 0;
}
-#endif \ No newline at end of file
+#endif
diff --git a/accel-pppd/include/ap_session.h b/accel-pppd/include/ap_session.h
index 27471859..a0733986 100644
--- a/accel-pppd/include/ap_session.h
+++ b/accel-pppd/include/ap_session.h
@@ -85,6 +85,12 @@ struct ap_session
struct ipv4db_item_t *ipv4;
struct ipv6db_item_t *ipv6;
struct ipv6db_prefix_t *ipv6_dp;
+ /* Per session IPv6 DNS servers, NULL when none were assigned and the
+ globally configured ones ([ipv6-dns]) should be advertised instead.
+ Only addr_list, and only the addr member of its entries, is
+ meaningful here. Owned by whoever sets it, currently the radius
+ module from the DNS-Server-IPv6-Address attribute (RFC 6911) */
+ struct ipv6db_item_t *ipv6_dns;
char *ipv4_pool_name;
char *ipv6_pool_name;
char *dpv6_pool_name;
@@ -136,10 +142,14 @@ extern int ap_shutdown;
extern int sock_fd;
extern int sock6_fd;
extern int urandom_fd;
-extern struct ap_session_stat ap_session_stat;
extern int conf_max_sessions;
extern int conf_max_starting;
+void ap_session_stat_get(struct ap_session_stat *stat);
+unsigned int ap_session_stat_starting(void);
+unsigned int ap_session_stat_active(void);
+unsigned int ap_session_stat_finishing(void);
+
void ap_session_init(struct ap_session *ses);
void ap_session_set_ifindex(struct ap_session *ses);
int ap_session_starting(struct ap_session *ses);
diff --git a/accel-pppd/include/crypto.h b/accel-pppd/include/crypto.h
deleted file mode 120000
index 2f3f63cb..00000000
--- a/accel-pppd/include/crypto.h
+++ /dev/null
@@ -1 +0,0 @@
-../../crypto/crypto.h \ No newline at end of file
diff --git a/accel-pppd/include/if_pppox.h b/accel-pppd/include/if_pppox.h
deleted file mode 100644
index 0532d18f..00000000
--- a/accel-pppd/include/if_pppox.h
+++ /dev/null
@@ -1,234 +0,0 @@
-/***************************************************************************
- * Linux PPP over X - Generic PPP transport layer sockets
- * Linux PPP over Ethernet (PPPoE) Socket Implementation (RFC 2516)
- *
- * This file supplies definitions required by the PPP over Ethernet driver
- * (pppox.c). All version information wrt this file is located in pppox.c
- *
- * License:
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- */
-
-#ifndef __LINUX_IF_PPPOX_H
-#define __LINUX_IF_PPPOX_H
-
-
-#include <asm/types.h>
-#include <asm/byteorder.h>
-#include <linux/version.h>
-#include <linux/if.h>
-#include <linux/if_ether.h>
-
-#ifdef __KERNEL__
-#include <linux/in.h>
-#include <linux/netdevice.h>
-#include <linux/ppp_channel.h>
-#endif /* __KERNEL__ */
-
-/* For user-space programs to pick up these definitions
- * which they wouldn't get otherwise without defining __KERNEL__
- */
-#ifndef AF_PPPOX
-#define AF_PPPOX 24
-#define PF_PPPOX AF_PPPOX
-#endif /* !(AF_PPPOX) */
-
-struct pppoe_addr {
- __be16 sid;
- unsigned char remote[ETH_ALEN];
- char dev[IFNAMSIZ];
-};
-
-struct pptp_addr {
- __u16 call_id;
- struct in_addr sin_addr;
-};
-/************************************************************************
- * Protocols supported by AF_PPPOX
- */
-#define PX_PROTO_OE 0 /* Currently just PPPoE */
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
-#define PX_PROTO_PPTP 1
-#define PX_MAX_PROTO 2
-#else
-#define PX_PROTO_PPTP 2
-#define PX_MAX_PROTO 3
-#endif
-
-struct sockaddr_pppox {
- sa_family_t sa_family; /* address family, AF_PPPOX */
- unsigned int sa_protocol; /* protocol identifier */
- union{
- struct pppoe_addr pppoe;
- struct pptp_addr pptp;
- }sa_addr;
-}__attribute__ ((packed));
-
-
-/*********************************************************************
- *
- * ioctl interface for defining forwarding of connections
- *
- ********************************************************************/
-
-#define PPPOEIOCSFWD _IOW(0xB1 ,0, size_t)
-#define PPPOEIOCDFWD _IO(0xB1 ,1)
-/*#define PPPOEIOCGFWD _IOWR(0xB1,2, size_t)*/
-
-/* Codes to identify message types */
-#define PADI_CODE 0x09
-#define PADO_CODE 0x07
-#define PADR_CODE 0x19
-#define PADS_CODE 0x65
-#define PADT_CODE 0xa7
-struct pppoe_tag {
- __u16 tag_type;
- __u16 tag_len;
- char tag_data[0];
-} __attribute ((packed));
-
-/* Tag identifiers */
-#define PTT_EOL __constant_htons(0x0000)
-#define PTT_SRV_NAME __constant_htons(0x0101)
-#define PTT_AC_NAME __constant_htons(0x0102)
-#define PTT_HOST_UNIQ __constant_htons(0x0103)
-#define PTT_AC_COOKIE __constant_htons(0x0104)
-#define PTT_VENDOR __constant_htons(0x0105)
-#define PTT_RELAY_SID __constant_htons(0x0110)
-#define PTT_SRV_ERR __constant_htons(0x0201)
-#define PTT_SYS_ERR __constant_htons(0x0202)
-#define PTT_GEN_ERR __constant_htons(0x0203)
-
-struct pppoe_hdr {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
- __u8 ver : 4;
- __u8 type : 4;
-#elif defined(__BIG_ENDIAN_BITFIELD)
- __u8 type : 4;
- __u8 ver : 4;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- __u8 code;
- __u16 sid;
- __u16 length;
- struct pppoe_tag tag[0];
-} __attribute__ ((packed));
-
-
-/* Socket options */
-#define PPTP_SO_TIMEOUT 1
-
-
-#ifdef __KERNEL__
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
-struct pppoe_opt {
- struct net_device *dev; /* device associated with socket*/
- struct pppoe_addr pa; /* what this socket is bound to*/
- struct sockaddr_pppox relay; /* what socket data will be
- relayed to (PPPoE relaying) */
-};
-#endif
-struct pptp_opt {
- struct pptp_addr src_addr;
- struct pptp_addr dst_addr;
- int timeout;
- __u32 ack_sent, ack_recv;
- __u32 seq_sent, seq_recv;
- int ppp_flags;
- int flags;
- struct sk_buff_head skb_buf;
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- struct tq_struct buf_work; //check bufferd packets work
- struct timer_list buf_timer;
- #else
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
- struct delayed_work buf_work; //check bufferd packets work
- #else
- struct work_struct buf_work; //check bufferd packets work
- #endif
- #endif
- struct gre_statistics *stat;
- spinlock_t xmit_lock;
- spinlock_t rcv_lock;
-};
-#define PPTP_FLAG_PAUSE 0
-#define PPTP_FLAG_PROC 1
-
-#include <net/sock.h>
-
-struct pppox_sock {
- /* struct sock must be the first member of pppox_sock */
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- struct ppp_channel chan;
- struct sock *sk;
- #else
- struct sock sk;
- struct ppp_channel chan;
- #endif
- struct pppox_sock *next; /* for hash table */
- union {
- struct pppoe_opt pppoe;
- struct pptp_opt pptp;
- } proto;
- unsigned short num;
-};
-#define pppoe_dev proto.pppoe.dev
-#define pppoe_pa proto.pppoe.pa
-#define pppoe_relay proto.pppoe.relay
-
-static inline struct pppox_sock *pppox_sk(struct sock *sk)
-{
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- return (struct pppox_sock *)sk->protinfo.pppox;
- #else
- return (struct pppox_sock *)sk;
- #endif
-}
-
-static inline struct sock *sk_pppox(struct pppox_sock *po)
-{
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- return po->sk;
- #else
- return (struct sock *)po;
- #endif
-}
-
-struct module;
-
-struct pppox_proto {
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
- int (*create)(struct socket *sock);
- #else
- int (*create)(struct net *net, struct socket *sock);
- #endif
- int (*ioctl)(struct socket *sock, unsigned int cmd,
- unsigned long arg);
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
- struct module *owner;
- #endif
-};
-
-extern int register_pppox_proto(int proto_num, struct pppox_proto *pp);
-extern void unregister_pppox_proto(int proto_num);
-extern void pppox_unbind_sock(struct sock *sk);/* delete ppp-channel binding */
-extern int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
-
-/* PPPoX socket states */
-enum {
- PPPOX_NONE = 0, /* initial state */
- PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */
- PPPOX_BOUND = 2, /* bound to ppp device */
- PPPOX_RELAY = 4, /* forwarding is enabled */
- PPPOX_ZOMBIE = 8, /* dead, but still bound to ppp device */
- PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/
-};
-
-#endif /* __KERNEL__ */
-
-#endif /* !(__LINUX_IF_PPPOX_H) */
diff --git a/accel-pppd/include/ipv6_dns.h b/accel-pppd/include/ipv6_dns.h
new file mode 100644
index 00000000..b604d006
--- /dev/null
+++ b/accel-pppd/include/ipv6_dns.h
@@ -0,0 +1,48 @@
+#ifndef __IPV6_DNS_H
+#define __IPV6_DNS_H
+
+#include <netinet/in.h>
+
+#include "list.h"
+#include "ipdb.h"
+#include "ap_session.h"
+
+/*
+ * Pick the IPv6 DNS servers to advertise to a session.
+ *
+ * A session may have been assigned its own servers (currently by the radius
+ * module, from the DNS-Server-IPv6-Address attribute of RFC 6911); those take
+ * precedence. Sessions without any fall back to the globally configured ones,
+ * which is what every session got before per session servers existed.
+ *
+ * Up to 'max' addresses are written to 'dns', the number written is returned.
+ * Callers advertise nothing when that is 0.
+ */
+static inline int ipv6_dns_get(const struct ap_session *ses,
+ const struct in6_addr *conf_dns, int conf_dns_count,
+ struct in6_addr *dns, int max)
+{
+ struct ipv6db_addr_t *a;
+ int count = 0;
+
+ if (ses && ses->ipv6_dns) {
+ list_for_each_entry(a, &ses->ipv6_dns->addr_list, entry) {
+ if (count == max)
+ break;
+ dns[count++] = a->addr;
+ }
+
+ /* An empty list means "nothing assigned", not "no DNS at all" */
+ if (count)
+ return count;
+ }
+
+ while (count < conf_dns_count && count < max) {
+ dns[count] = conf_dns[count];
+ count++;
+ }
+
+ return count;
+}
+
+#endif
diff --git a/accel-pppd/ipdb.c b/accel-pppd/ipdb.c
index dd8c61c9..264f67b3 100644
--- a/accel-pppd/ipdb.c
+++ b/accel-pppd/ipdb.c
@@ -75,15 +75,23 @@ void __export ipdb_put_ipv6_prefix(struct ap_session *ses, struct ipv6db_prefix_
void __export build_ip6_addr(struct ipv6db_addr_t *a, uint64_t intf_id, struct in6_addr *addr)
{
+ uint64_t value;
+
memcpy(addr, &a->addr, sizeof(*addr));
if (a->prefix_len == 128)
return;
if (a->prefix_len <= 64)
- *(uint64_t *)(addr->s6_addr + 8) = intf_id;
- else
- *(uint64_t *)(addr->s6_addr + 8) |= intf_id & htobe64((1 << (128 - a->prefix_len)) - 1);
+ memcpy(addr->s6_addr + 8, &intf_id, sizeof(intf_id));
+ else {
+ /* prefix_len 65..127 means a shift of up to 63 bits: a plain
+ * int literal 1 is undefined behavior for shifts >= 31, so the
+ * host bits mask must be built from a 64-bit constant */
+ memcpy(&value, addr->s6_addr + 8, sizeof(value));
+ value |= intf_id & htobe64((UINT64_C(1) << (128 - a->prefix_len)) - 1);
+ memcpy(addr->s6_addr + 8, &value, sizeof(value));
+ }
}
diff --git a/accel-pppd/ipv6/dhcpv6.c b/accel-pppd/ipv6/dhcpv6.c
index 77fc32f7..a0b159cf 100644
--- a/accel-pppd/ipv6/dhcpv6.c
+++ b/accel-pppd/ipv6/dhcpv6.c
@@ -20,8 +20,10 @@
#include "log.h"
#include "ppp.h"
#include "ipdb.h"
+#include "ipv6_dns.h"
#include "events.h"
#include "iputils.h"
+#include "utils.h"
#include "dhcpv6.h"
@@ -162,7 +164,7 @@ static void ev_ses_finished(struct ap_session *ses)
if (pd->dp_active) {
struct ipv6db_addr_t *p;
list_for_each_entry(p, &ses->ipv6_dp->prefix_list, entry)
- ip6route_del(0, &p->addr, p->prefix_len, NULL, 0, 0);
+ ip6route_del(0, &p->addr, p->prefix_len, NULL, 0, 0, NULL);
}
ipdb_put_ipv6_prefix(ses, ses->ipv6_dp);
@@ -184,7 +186,7 @@ static void insert_dp_routes(struct ap_session *ses, struct dhcpv6_pd *pd, struc
addr = NULL;
list_for_each_entry(p, &ses->ipv6_dp->prefix_list, entry) {
- if (ip6route_add(ses->ifindex, &p->addr, p->prefix_len, addr, 0, 0)) {
+ if (ip6route_add(ses->ifindex, &p->addr, p->prefix_len, addr, 0, 0, NULL)) {
err = errno;
inet_ntop(AF_INET6, &p->addr, str1, sizeof(str1));
if (addr)
@@ -220,23 +222,29 @@ static void insert_status(struct dhcpv6_packet *pkt, struct dhcpv6_option *opt,
static void insert_oro(struct dhcpv6_packet *reply, struct dhcpv6_option *opt)
{
struct dhcpv6_option *opt1;
- int i, j;
- uint16_t *ptr;
- struct in6_addr addr, *addr_ptr;
-
- for (i = ntohs(opt->hdr->len) / 2, ptr = (uint16_t *)opt->hdr->data; i; i--, ptr++) {
- if (ntohs(*ptr) == D6_OPTION_DNS_SERVERS) {
- if (conf_dns_count) {
- opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DNS_SERVERS, conf_dns_count * sizeof(addr));
- for (j = 0, addr_ptr = (struct in6_addr *)opt1->hdr->data; j < conf_dns_count; j++, addr_ptr++)
- memcpy(addr_ptr, conf_dns + j, sizeof(addr));
+ int i, j, dns_count;
+ uint8_t *ptr;
+ uint16_t code;
+ struct in6_addr addr;
+ uint8_t *addr_ptr;
+ struct in6_addr dns[MAX_DNS_COUNT];
+
+ for (i = ntohs(opt->hdr->len) / 2, ptr = opt->hdr->data; i; i--, ptr += sizeof(code)) {
+ code = u_read_be16(ptr);
+ if (code == D6_OPTION_DNS_SERVERS) {
+ dns_count = ipv6_dns_get(reply->ses, conf_dns, conf_dns_count,
+ dns, MAX_DNS_COUNT);
+ if (dns_count) {
+ opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DNS_SERVERS, dns_count * sizeof(addr));
+ for (j = 0, addr_ptr = opt1->hdr->data; j < dns_count; j++, addr_ptr += sizeof(addr))
+ memcpy(addr_ptr, dns + j, sizeof(addr));
}
- } else if (ntohs(*ptr) == D6_OPTION_DOMAIN_LIST) {
+ } else if (code == D6_OPTION_DOMAIN_LIST) {
if (conf_dnssl_size) {
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DOMAIN_LIST, conf_dnssl_size);
memcpy(opt1->hdr->data, conf_dnssl, conf_dnssl_size);
}
- } else if (ntohs(*ptr) == D6_OPTION_AFTR_NAME) {
+ } else if (code == D6_OPTION_AFTR_NAME) {
if (conf_aftr_gw_size) {
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_AFTR_NAME, conf_aftr_gw_size);
memcpy(opt1->hdr->data, conf_aftr_gw, conf_aftr_gw_size);
@@ -752,6 +760,32 @@ static void dhcpv6_recv_rebind(struct dhcpv6_packet *req)
dhcpv6_send_reply2(req, pd, D6_REPLY);
}
+static void dhcpv6_recv_confirm(struct dhcpv6_packet *req)
+{
+ struct dhcpv6_pd *pd = req->pd;
+
+ if (!req->clientid) {
+ log_ppp_error("dhcpv6: no Client-ID option\n");
+ return;
+ }
+
+ if (req->serverid) {
+ log_ppp_error("dhcpv6: unexcpected Server-ID option\n");
+ return;
+ }
+
+ if (!pd->clientid)
+ return;
+ else if (pd->clientid->hdr.len != req->clientid->hdr.len || memcmp(pd->clientid, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len))) {
+ log_ppp_error("dhcpv6: unmatched Client-ID option\n");
+ return;
+ }
+
+ req->serverid = conf_serverid;
+
+ dhcpv6_send_reply(req, pd, D6_REPLY);
+}
+
static void dhcpv6_recv_release(struct dhcpv6_packet *pkt)
{
// don't answer
@@ -783,6 +817,9 @@ static void dhcpv6_recv_packet(struct dhcpv6_packet *pkt)
case D6_REBIND:
dhcpv6_recv_rebind(pkt);
break;
+ case D6_CONFIRM:
+ dhcpv6_recv_confirm(pkt);
+ break;
case D6_RELEASE:
dhcpv6_recv_release(pkt);
break;
@@ -841,13 +878,15 @@ static int dhcpv6_read(struct triton_md_handler_t *h)
static void add_aftr_gw(const char *val)
{
- int n = strlen(val);
+ int n;
const char *ptr;
uint8_t *buf;
- if (!val)
+ if (!val || !*val)
return;
+ n = strlen(val);
+
if (val[n - 1] == '.')
n++;
else
@@ -888,13 +927,15 @@ static void add_aftr_gw(const char *val)
static void add_dnssl(const char *val)
{
- int n = strlen(val);
+ int n;
const char *ptr;
uint8_t *buf;
- if (!val)
+ if (!val || !*val)
return;
+ n = strlen(val);
+
if (val[n - 1] == '.')
n++;
else
diff --git a/accel-pppd/ipv6/dhcpv6_packet.c b/accel-pppd/ipv6/dhcpv6_packet.c
index 1890bc11..f1c168b6 100644
--- a/accel-pppd/ipv6/dhcpv6_packet.c
+++ b/accel-pppd/ipv6/dhcpv6_packet.c
@@ -4,6 +4,7 @@
#include "log.h"
#include "memdebug.h"
+#include "utils.h"
#include "dhcpv6.h"
@@ -13,7 +14,8 @@ struct dict_option {
int code;
const char *name;
int recv;
- int len;
+ int min_len;
+ int nested;
void (*print)(struct dhcpv6_option *, void (*)(const char *fmt, ...));
};
@@ -34,30 +36,30 @@ static void print_ia_prefix(struct dhcpv6_option *opt, void (*print)(const char
static void print_aftr_gw(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...));
static struct dict_option known_options[] = {
- { D6_OPTION_CLIENTID, "Client-ID", 1, 0, print_clientid },
- { D6_OPTION_SERVERID, "Server-ID", 0, 0, print_clientid },
- { D6_OPTION_IA_NA, "IA-NA", 1, sizeof(struct dhcpv6_opt_ia_na), print_ia_na },
- { D6_OPTION_IA_TA, "IA-TA", 1, sizeof(struct dhcpv6_opt_ia_ta), print_ia_ta },
- { D6_OPTION_IAADDR, "IA-Addr", 1, sizeof(struct dhcpv6_opt_ia_addr), print_ia_addr },
- { D6_OPTION_ORO, "Option-Request", 1, 0, print_oro },
- { D6_OPTION_PREFERENCE, "Preference", 0, 0, print_uint8 },
- { D6_OPTION_ELAPSED_TIME, "Elapsed-Time", 1, 0, print_time },
- { D6_OPTION_RELAY_MSG, "Relay-Message", 1, 0 },
- { D6_OPTION_AUTH, "Auth", 1, 0 },
- { D6_OPTION_PREFERENCE, "Server-Unicast", 0, 0, print_ipv6addr },
- { D6_OPTION_STATUS_CODE, "Status", 0, 0, print_status },
- { D6_OPTION_RAPID_COMMIT, "Rapid-Commit", 1, 0 },
- { D6_OPTION_USER_CLASS, "User-Class", 1, 0 },
- { D6_OPTION_VENDOR_CLASS, "Vendor-Class", 1, 0, print_hex_array },
- { D6_OPTION_VENDOR_SPECIFIC, "Vendor-Specific", 1, 0, print_hex_array },
- { D6_OPTION_INTERFACE_ID, "Interface-ID", 1, 0, print_hex_array },
- { D6_OPTION_RECONF_MSG, "Reconfigure", 0, 0, print_reconf },
- { D6_OPTION_RECONF_ACCEPT, "Reconfigure-Accept", 1, 0 },
- { D6_OPTION_DNS_SERVERS, "DNS", 1, 0, print_ipv6addr_array },
- { D6_OPTION_DOMAIN_LIST, "DNSSL", 1, 0, print_dnssl },
- { D6_OPTION_IA_PD, "IA-PD", 1, sizeof(struct dhcpv6_opt_ia_na), print_ia_na },
- { D6_OPTION_IAPREFIX, "IA-Prefix", 1, sizeof(struct dhcpv6_opt_ia_prefix), print_ia_prefix },
- { D6_OPTION_AFTR_NAME, "AFTR-Name", 1, 0, print_aftr_gw },
+ { D6_OPTION_CLIENTID, "Client-ID", 1, sizeof(uint16_t), 0, print_clientid },
+ { D6_OPTION_SERVERID, "Server-ID", 0, sizeof(uint16_t), 0, print_clientid },
+ { D6_OPTION_IA_NA, "IA-NA", 1, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr), 1, print_ia_na },
+ { D6_OPTION_IA_TA, "IA-TA", 1, sizeof(struct dhcpv6_opt_ia_ta) - sizeof(struct dhcpv6_opt_hdr), 1, print_ia_ta },
+ { D6_OPTION_IAADDR, "IA-Addr", 1, sizeof(struct dhcpv6_opt_ia_addr) - sizeof(struct dhcpv6_opt_hdr), 1, print_ia_addr },
+ { D6_OPTION_ORO, "Option-Request", 1, 0, 0, print_oro },
+ { D6_OPTION_PREFERENCE, "Preference", 0, sizeof(uint8_t), 0, print_uint8 },
+ { D6_OPTION_ELAPSED_TIME, "Elapsed-Time", 1, sizeof(uint16_t), 0, print_time },
+ { D6_OPTION_RELAY_MSG, "Relay-Message", 1, sizeof(struct dhcpv6_msg_hdr), 0 },
+ { D6_OPTION_AUTH, "Auth", 1, 0, 0 },
+ { D6_OPTION_UNICAST, "Server-Unicast", 0, sizeof(struct in6_addr), 0, print_ipv6addr },
+ { D6_OPTION_STATUS_CODE, "Status", 0, sizeof(uint16_t), 0, print_status },
+ { D6_OPTION_RAPID_COMMIT, "Rapid-Commit", 1, 0, 0 },
+ { D6_OPTION_USER_CLASS, "User-Class", 1, 0, 0 },
+ { D6_OPTION_VENDOR_CLASS, "Vendor-Class", 1, 0, 0, print_hex_array },
+ { D6_OPTION_VENDOR_SPECIFIC, "Vendor-Specific", 1, 0, 0, print_hex_array },
+ { D6_OPTION_INTERFACE_ID, "Interface-ID", 1, 0, 0, print_hex_array },
+ { D6_OPTION_RECONF_MSG, "Reconfigure", 0, sizeof(uint8_t), 0, print_reconf },
+ { D6_OPTION_RECONF_ACCEPT, "Reconfigure-Accept", 1, 0, 0 },
+ { D6_OPTION_DNS_SERVERS, "DNS", 1, 0, 0, print_ipv6addr_array },
+ { D6_OPTION_DOMAIN_LIST, "DNSSL", 1, 0, 0, print_dnssl },
+ { D6_OPTION_IA_PD, "IA-PD", 1, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr), 1, print_ia_na },
+ { D6_OPTION_IAPREFIX, "IA-Prefix", 1, sizeof(struct dhcpv6_opt_ia_prefix) - sizeof(struct dhcpv6_opt_hdr), 1, print_ia_prefix },
+ { D6_OPTION_AFTR_NAME, "AFTR-Name", 1, 0, 0, print_aftr_gw },
{ 0 }
};
@@ -89,9 +91,14 @@ static void *parse_option(void *ptr, void *endptr, struct list_head *opt_list)
break;
}
- if (dopt->len) {
+ if (ntohs(opth->len) < dopt->min_len) {
+ log_warn("dhcpv6: invalid packet received\n");
+ return NULL;
+ }
+
+ if (dopt->nested) {
endptr = ptr + sizeof(*opth) + ntohs(opth->len);
- ptr += dopt->len;
+ ptr += sizeof(*opth) + dopt->min_len;
while (ptr < endptr) {
ptr = parse_option(ptr, endptr, &opt->opt_list);
if (!ptr)
@@ -109,7 +116,8 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
struct dhcpv6_opt_hdr *opth;
struct dhcpv6_relay *rel;
struct dhcpv6_relay_hdr *rhdr;
- void *ptr, *endptr;
+ struct dhcpv6_msg_hdr *inner_hdr;
+ void *ptr, *endptr, *relay_end, *inner_end;
if (size < sizeof(struct dhcpv6_msg_hdr)) {
if (conf_verbose)
@@ -151,22 +159,37 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
list_add_tail(&rel->entry, &pkt->relay_list);
+ inner_hdr = NULL;
+ inner_end = NULL;
+ relay_end = endptr;
ptr = rhdr->data;
- while (ptr < endptr) {
+ while (ptr < relay_end) {
opth = ptr;
- if (ptr + sizeof(*opth) > endptr ||
- ptr + sizeof(*opth) + ntohs(opth->len) > endptr) {
+ if (ptr + sizeof(*opth) > relay_end ||
+ ptr + sizeof(*opth) + ntohs(opth->len) > relay_end) {
log_warn("dhcpv6: invalid packet received\n");
goto error;
}
if (opth->code == htons(D6_OPTION_RELAY_MSG)) {
- pkt->hdr = (struct dhcpv6_msg_hdr *)opth->data;
- endptr = opth->data + sizeof(*opth) + ntohs(opth->len);
+ if (inner_hdr || ntohs(opth->len) < sizeof(*inner_hdr)) {
+ log_warn("dhcpv6: invalid packet received\n");
+ goto error;
+ }
+ inner_hdr = (struct dhcpv6_msg_hdr *)opth->data;
+ inner_end = opth->data + ntohs(opth->len);
}
ptr += sizeof(*opth) + ntohs(opth->len);
}
+
+ if (!inner_hdr) {
+ log_warn("dhcpv6: invalid packet received\n");
+ goto error;
+ }
+
+ pkt->hdr = inner_hdr;
+ endptr = inner_end;
}
ptr = pkt->hdr->data;
@@ -455,26 +478,28 @@ static void print_ia_addr(struct dhcpv6_option *opt, void (*print)(const char *f
static void print_oro(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
{
- uint16_t *ptr = (uint16_t *)opt->hdr->data;
- uint16_t *end_ptr = ptr + ntohs(opt->hdr->len)/2;
+ uint8_t *ptr = opt->hdr->data;
+ uint8_t *end_ptr = ptr + ntohs(opt->hdr->len) / 2 * sizeof(uint16_t);
struct dict_option *dopt;
+ uint16_t code;
int f = 0;
- for (; ptr < end_ptr; ptr++) {
+ for (; ptr < end_ptr; ptr += sizeof(uint16_t)) {
if (f)
print(",");
else
print(" ");
+ code = u_read_be16(ptr);
for (dopt = known_options; dopt->code; dopt++) {
- if (ntohs(*ptr) == dopt->code)
+ if (code == dopt->code)
break;
}
if (dopt->code)
print("%s", dopt->name);
else
- print("%i", ntohs(*ptr));
+ print("%i", code);
f = 1;
}
@@ -491,12 +516,18 @@ static void print_hex_array(struct dhcpv6_option *opt, void (*print)(const char
static void print_uint8(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
{
+ if (ntohs(opt->hdr->len) < sizeof(uint8_t))
+ return;
+
print(" %i", *(uint8_t *)opt->hdr->data);
}
static void print_time(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
{
- print(" %u", *(uint32_t *)opt->hdr->data);
+ if (ntohs(opt->hdr->len) < sizeof(uint16_t))
+ return;
+
+ print(" %u", u_read_be16(opt->hdr->data));
}
static void print_ipv6addr(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
@@ -513,9 +544,9 @@ static void print_ipv6addr_array(struct dhcpv6_option *opt, void (*print)(const
char str[INET6_ADDRSTRLEN];
int i;
int f = 0;
- struct in6_addr *addr = (struct in6_addr *)opt->hdr->data;
+ uint8_t *addr = opt->hdr->data;
- for (i = ntohs(opt->hdr->len) / sizeof(*addr); i; i--, addr++) {
+ for (i = ntohs(opt->hdr->len) / sizeof(struct in6_addr); i; i--, addr += sizeof(struct in6_addr)) {
inet_ntop(AF_INET6, addr, str, sizeof(str));
print("%c%s", f ? ',' : ' ', str);
f = 1;
@@ -531,14 +562,20 @@ static void print_status(struct dhcpv6_option *opt, void (*print)(const char *fm
"NoAddrsAvail",
"NoBindings",
"NotOnLink",
- "UseMulticast"
+ "UseMulticast",
"NoPrefixAvail"
};
+ unsigned int code;
- if (ntohs(o->code) < 0 || ntohs(o->code) > sizeof(status_name))
- print(" %u", ntohs(o->code));
+ if ((unsigned int)ntohs(opt->hdr->len) < sizeof(o->code))
+ return;
+
+ code = ntohs(o->code);
+
+ if (code >= sizeof(status_name) / sizeof(status_name[0]))
+ print(" %u", code);
else
- print(" %s", status_name[ntohs(o->code)]);
+ print(" %s", status_name[code]);
}
static void print_reconf(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
@@ -551,21 +588,40 @@ static void print_dnssl(struct dhcpv6_option *opt, void (*print)(const char *fmt
}
-static void print_aftr_gw(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...)) {
+static void print_aftr_gw(struct dhcpv6_option *opt, void (*print)(const char *fmt, ...))
+{
int len = ntohs(opt->hdr->len);
int offset = 0;
- char domain[255];
+ int pos = 0;
+ int done = 0;
+ char domain[256];
uint8_t label_len;
- memset(domain, 0, 255);
+ memset(domain, 0, sizeof(domain));
while (offset < len) {
- label_len = opt->hdr->data[offset];
- if (label_len == 0)
+ label_len = opt->hdr->data[offset++];
+ if (label_len == 0) {
+ done = 1;
break;
- memcpy(&domain[offset], &opt->hdr->data[offset + 1], label_len);
+ }
+
+ if (label_len > 63 || offset + label_len > len ||
+ pos + label_len + 1 >= sizeof(domain)) {
+ print(" <invalid>");
+ return;
+ }
+
+ memcpy(&domain[pos], &opt->hdr->data[offset], label_len);
offset += label_len;
- domain[offset++] = '.';
+ pos += label_len;
+ domain[pos++] = '.';
+ }
+
+ if (!done || offset != len) {
+ print(" <invalid>");
+ return;
}
+
print(" %s", domain);
}
@@ -577,4 +633,3 @@ static void print_ia_prefix(struct dhcpv6_option *opt, void (*print)(const char
inet_ntop(AF_INET6, &o->prefix, str, sizeof(str));
print(" %s/%i pref_lifetime=%i valid_lifetime=%i", str, o->prefix_len, ntohl(o->pref_lifetime), ntohl(o->valid_lifetime));
}
-
diff --git a/accel-pppd/ipv6/ipv6_dns_test.c b/accel-pppd/ipv6/ipv6_dns_test.c
new file mode 100644
index 00000000..72b4751c
--- /dev/null
+++ b/accel-pppd/ipv6/ipv6_dns_test.c
@@ -0,0 +1,157 @@
+/*
+ * Standalone test for the IPv6 DNS server selection shared by the ipv6_nd and
+ * ipv6_dhcp modules.
+ *
+ * Not part of the cmake build. Compile and run from the top of the tree, with
+ * a configured build directory around for config.h:
+ * gcc -O2 -Wall -D_GNU_SOURCE -DAP_SESSIONID_LEN=16 \
+ * -I accel-pppd/include -I accel-pppd/triton -I build \
+ * -o /tmp/ipv6_dns_test accel-pppd/ipv6/ipv6_dns_test.c && /tmp/ipv6_dns_test
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "ipv6_dns.h"
+
+static int failures;
+#define CHECK(cond) do { if (!(cond)) { \
+ fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); failures++; } } while (0)
+
+#define MAX_DNS_COUNT 3 /* as in nd.c and dhcpv6.c */
+
+static struct in6_addr a6(const char *str)
+{
+ struct in6_addr addr;
+
+ if (inet_pton(AF_INET6, str, &addr) != 1) {
+ fprintf(stderr, "bad address %s\n", str);
+ exit(1);
+ }
+
+ return addr;
+}
+
+static int is(const struct in6_addr *addr, const char *str)
+{
+ struct in6_addr expect = a6(str);
+
+ return memcmp(addr, &expect, sizeof(expect)) == 0;
+}
+
+/* A session carrying 'count' DNS servers taken from 'str' */
+static struct ap_session *session_with(const char **str, int count)
+{
+ struct ap_session *ses = calloc(1, sizeof(*ses));
+ struct ipv6db_item_t *item = calloc(1, sizeof(*item));
+ int i;
+
+ INIT_LIST_HEAD(&item->addr_list);
+ for (i = 0; i < count; i++) {
+ struct ipv6db_addr_t *a = calloc(1, sizeof(*a));
+
+ a->addr = a6(str[i]);
+ a->prefix_len = 128;
+ list_add_tail(&a->entry, &item->addr_list);
+ }
+
+ ses->ipv6_dns = item;
+
+ return ses;
+}
+
+static void session_free(struct ap_session *ses)
+{
+ if (ses->ipv6_dns) {
+ while (!list_empty(&ses->ipv6_dns->addr_list)) {
+ struct ipv6db_addr_t *a = list_entry(ses->ipv6_dns->addr_list.next,
+ typeof(*a), entry);
+
+ list_del(&a->entry);
+ free(a);
+ }
+ free(ses->ipv6_dns);
+ }
+
+ free(ses);
+}
+
+int main(void)
+{
+ static const char *four[] = { "2001:db8::1", "2001:db8::2",
+ "2001:db8::3", "2001:db8::4" };
+ struct in6_addr conf_dns[MAX_DNS_COUNT];
+ struct in6_addr dns[MAX_DNS_COUNT];
+ struct ap_session *ses;
+ int n;
+
+ conf_dns[0] = a6("fc00::53");
+ conf_dns[1] = a6("fc00::54");
+
+ /* No session at all: the configured servers, as before the feature */
+ n = ipv6_dns_get(NULL, conf_dns, 2, dns, MAX_DNS_COUNT);
+ CHECK(n == 2);
+ CHECK(is(&dns[0], "fc00::53"));
+ CHECK(is(&dns[1], "fc00::54"));
+
+ /* Session without assigned servers: same fallback */
+ ses = calloc(1, sizeof(*ses));
+ n = ipv6_dns_get(ses, conf_dns, 2, dns, MAX_DNS_COUNT);
+ CHECK(n == 2);
+ CHECK(is(&dns[0], "fc00::53"));
+ free(ses);
+
+ /* Nothing configured and nothing assigned: advertise nothing */
+ n = ipv6_dns_get(NULL, conf_dns, 0, dns, MAX_DNS_COUNT);
+ CHECK(n == 0);
+
+ /* Assigned servers win over the configured ones */
+ ses = session_with(four, 2);
+ n = ipv6_dns_get(ses, conf_dns, 2, dns, MAX_DNS_COUNT);
+ CHECK(n == 2);
+ CHECK(is(&dns[0], "2001:db8::1"));
+ CHECK(is(&dns[1], "2001:db8::2"));
+
+ /* ... and win even when nothing is configured */
+ n = ipv6_dns_get(ses, conf_dns, 0, dns, MAX_DNS_COUNT);
+ CHECK(n == 2);
+ CHECK(is(&dns[0], "2001:db8::1"));
+
+ session_free(ses);
+
+ /* An empty assigned list is "nothing assigned", not "no DNS" */
+ ses = session_with(four, 0);
+ n = ipv6_dns_get(ses, conf_dns, 2, dns, MAX_DNS_COUNT);
+ CHECK(n == 2);
+ CHECK(is(&dns[0], "fc00::53"));
+ session_free(ses);
+
+ /* More assigned than fit: keep the first max, never overrun */
+ ses = session_with(four, 4);
+ memset(dns, 0, sizeof(dns));
+ n = ipv6_dns_get(ses, conf_dns, 2, dns, MAX_DNS_COUNT);
+ CHECK(n == MAX_DNS_COUNT);
+ CHECK(is(&dns[0], "2001:db8::1"));
+ CHECK(is(&dns[1], "2001:db8::2"));
+ CHECK(is(&dns[2], "2001:db8::3"));
+
+ /* Same for the configured ones, should they ever exceed max */
+ n = ipv6_dns_get(NULL, conf_dns, 99, dns, MAX_DNS_COUNT);
+ CHECK(n == MAX_DNS_COUNT);
+
+ /* A caller with no room gets nothing rather than a stomped buffer */
+ n = ipv6_dns_get(ses, conf_dns, 2, dns, 0);
+ CHECK(n == 0);
+
+ session_free(ses);
+
+ if (failures) {
+ fprintf(stderr, "%d failure(s)\n", failures);
+ return 1;
+ }
+
+ printf("all tests passed\n");
+
+ return 0;
+}
diff --git a/accel-pppd/ipv6/nd.c b/accel-pppd/ipv6/nd.c
index 297e4d63..8b22da14 100644
--- a/accel-pppd/ipv6/nd.c
+++ b/accel-pppd/ipv6/nd.c
@@ -17,6 +17,7 @@
#include "events.h"
#include "mempool.h"
#include "ipdb.h"
+#include "ipv6_dns.h"
#include "iputils.h"
#include "memdebug.h"
@@ -107,7 +108,8 @@ static void ipv6_nd_send_ra(struct ipv6_nd_handler_t *h, struct sockaddr_in6 *ds
//struct nd_opt_mtu *mtu;
struct ipv6db_addr_t *a;
struct in6_addr addr, peer_addr;
- int i, prefix_len;
+ struct in6_addr dns[MAX_DNS_COUNT];
+ int i, prefix_len, dns_count;
if (!buf) {
log_emerg("out of memory\n");
@@ -174,15 +176,17 @@ static void ipv6_nd_send_ra(struct ipv6_nd_handler_t *h, struct sockaddr_in6 *ds
rinfo++;
}*/
- if (conf_dns_count) {
+ dns_count = ipv6_dns_get(ses, conf_dns, conf_dns_count, dns, MAX_DNS_COUNT);
+
+ if (dns_count) {
rdnssinfo = (struct nd_opt_rdnss_info_local *)pinfo;
memset(rdnssinfo, 0, sizeof(*rdnssinfo));
rdnssinfo->nd_opt_rdnssi_type = ND_OPT_RDNSS_INFORMATION;
- rdnssinfo->nd_opt_rdnssi_len = 1 + 2 * conf_dns_count;
+ rdnssinfo->nd_opt_rdnssi_len = 1 + 2 * dns_count;
rdnssinfo->nd_opt_rdnssi_lifetime = htonl(conf_rdnss_lifetime);
rdnss_addr = (struct in6_addr *)rdnssinfo->nd_opt_rdnssi;
- for (i = 0; i < conf_dns_count; i++) {
- memcpy(rdnss_addr, &conf_dns[i], sizeof(*rdnss_addr));
+ for (i = 0; i < dns_count; i++) {
+ memcpy(rdnss_addr, &dns[i], sizeof(*rdnss_addr));
rdnss_addr++;
}
} else
@@ -412,13 +416,15 @@ static void ev_ses_finishing(struct ap_session *ses)
static void add_dnssl(const char *val)
{
- int n = strlen(val);
+ int n;
const char *ptr;
uint8_t *buf;
- if (!val)
+ if (!val || !*val)
return;
+ n = strlen(val);
+
if (val[n - 1] == '.')
n++;
else
diff --git a/accel-pppd/libnetlink/genl.c b/accel-pppd/libnetlink/genl.c
index 0c758077..8cdfce89 100644
--- a/accel-pppd/libnetlink/genl.c
+++ b/accel-pppd/libnetlink/genl.c
@@ -82,12 +82,14 @@ int __export genl_resolve_mcg(const char *family, const char *name, int *fam_id)
goto out;
}
- if (!tb[CTRL_ATTR_MCAST_GROUPS])
- goto out;
-
+ /* report the family id even if the group lookup below fails, the
+ * caller may still have use for it */
if (fam_id)
*fam_id = *(uint16_t *)(RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]));
+ if (!tb[CTRL_ATTR_MCAST_GROUPS])
+ goto out;
+
parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS, tb[CTRL_ATTR_MCAST_GROUPS]);
for (i = 1; i < GENL_MAX_FAM_GRPS; i++) {
diff --git a/accel-pppd/libnetlink/iputils.c b/accel-pppd/libnetlink/iputils.c
index 23325fcd..a5e3bc51 100644
--- a/accel-pppd/libnetlink/iputils.c
+++ b/accel-pppd/libnetlink/iputils.c
@@ -11,10 +11,13 @@
#include <errno.h>
#include <time.h>
#include <sys/uio.h>
-//#include <linux/if_link.h>
+#ifdef HAVE_VRF
+#include <linux/if_link.h>
+#endif
//#include <linux/if_addr.h>
//#include <linux/rtnetlink.h>
#include <linux/fib_rules.h>
+#include <net/if.h>
#include "log.h"
@@ -213,7 +216,8 @@ int __export iplink_vlan_add(const char *ifname, int ifindex, int vid)
data = NLMSG_TAIL(&req.n);
addattr_l(&req.n, 4096, IFLA_INFO_DATA, NULL, 0);
- addattr_l(&req.n, 4096, IFLA_VLAN_ID, &vid, 2);
+ uint16_t vid_16b = (uint16_t)vid;
+ addattr_l(&req.n, 4096, IFLA_VLAN_ID, &vid_16b, 2);
data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
@@ -457,7 +461,89 @@ int __export ipaddr_del_peer(int ifindex, in_addr_t addr, in_addr_t peer)
return r;
}
-int __export iproute_add(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw, int proto, int mask, uint32_t prio)
+#ifdef HAVE_VRF
+static int ipvrf_get_table(uint32_t *tb_id, const char *vrf_name)
+{
+ struct iplink_req {
+ struct nlmsghdr n;
+ struct ifinfomsg i;
+ char buf[4096];
+ } req;
+ struct rtattr *tb[IFLA_MAX+1];
+ struct rtattr *li[IFLA_INFO_MAX+1];
+ struct rtattr *vrf_attr[IFLA_VRF_MAX + 1];
+ struct ifinfomsg *ifi;
+ struct rtnl_handle *rth;
+ int len;
+ int r = -1;
+ *tb_id = RT_TABLE_MAIN;
+
+ if (!vrf_name)
+ return 0;
+
+ log_ppp_info2("utils: getting route table for %s\n", vrf_name);
+
+ rth = net->rtnl_get();
+ if (!rth)
+ return -1;
+
+ memset(&req, 0, sizeof(req) - 4096);
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETLINK;
+ req.i.ifi_family = AF_UNSPEC;
+
+ addattr_l(&req.n, 4096, IFLA_IFNAME, vrf_name, strnlen(vrf_name, IFNAMSIZ));
+
+ r = rtnl_talk(rth, &req.n, 0, 0, &req.n, NULL, NULL, 0);
+ if (r < 0) {
+ log_ppp_error("BUG: route table \"%s\" not found\n", vrf_name);
+ goto out;
+ }
+
+ ifi = NLMSG_DATA(&req.n);
+
+ len = req.n.nlmsg_len;
+
+ len -= NLMSG_LENGTH(sizeof(*ifi));
+ if (len < 0) {
+ goto error;
+ }
+
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+
+ if (!tb[IFLA_LINKINFO])
+ goto error;
+
+ parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
+
+ if (!li[IFLA_INFO_KIND] || !li[IFLA_INFO_DATA])
+ goto error;
+
+ if (strncmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf", 4)) {
+ log_ppp_error("BUG: link \"%s\" is not VRF\n", vrf_name);
+ goto error;
+ }
+
+ parse_rtattr_nested(vrf_attr, IFLA_VRF_MAX, li[IFLA_INFO_DATA]);
+ if (vrf_attr[IFLA_VRF_TABLE])
+ *tb_id = *(uint32_t *)RTA_DATA(vrf_attr[IFLA_VRF_TABLE]);
+
+ if (*tb_id == 0)
+ log_ppp_error("BUG: VRF %s is missing table id\n", vrf_name);
+
+ r = 0;
+ goto out;
+error:
+ r = -1;
+out:
+ net->rtnl_put(rth);
+ return r;
+}
+#endif
+
+int __export iproute_add(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw, int proto, int mask, uint32_t prio, const char *vrf_name)
{
struct ipaddr_req {
struct nlmsghdr n;
@@ -472,16 +558,26 @@ int __export iproute_add(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw
memset(&req, 0, sizeof(req) - 4096);
+ uint32_t rt_table = RT_TABLE_MAIN;
+#ifdef HAVE_VRF
+ if (ipvrf_get_table(&rt_table, vrf_name) < 0) {
+ r = -1;
+ goto out;
+ }
+#endif
+
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
req.n.nlmsg_type = RTM_NEWROUTE;
req.i.rtm_family = AF_INET;
- req.i.rtm_table = RT_TABLE_MAIN;
+ req.i.rtm_table = rt_table;
req.i.rtm_scope = gw ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK;
req.i.rtm_protocol = proto;
req.i.rtm_type = RTN_UNICAST;
req.i.rtm_dst_len = mask;
+ if (rt_table != RT_TABLE_MAIN)
+ addattr32(&req.n, sizeof(req), RTA_TABLE, rt_table);
if (ifindex)
addattr32(&req.n, sizeof(req), RTA_OIF, ifindex);
if (src)
@@ -495,12 +591,13 @@ int __export iproute_add(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw
if (rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL, 0) < 0)
r = -1;
+out:
net->rtnl_put(rth);
return r;
}
-int __export iproute_del(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw, int proto, int mask, uint32_t prio)
+int __export iproute_del(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw, int proto, int mask, uint32_t prio, const char *vrf_name)
{
struct ipaddr_req {
struct nlmsghdr n;
@@ -515,16 +612,26 @@ int __export iproute_del(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw
memset(&req, 0, sizeof(req) - 4096);
+ uint32_t rt_table = RT_TABLE_MAIN;
+#ifdef HAVE_VRF
+ if (ipvrf_get_table(&rt_table, vrf_name) < 0) {
+ r = -1;
+ goto out;
+ }
+#endif
+
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST;
req.n.nlmsg_type = RTM_DELROUTE;
req.i.rtm_family = AF_INET;
- req.i.rtm_table = RT_TABLE_MAIN;
+ req.i.rtm_table = rt_table;
req.i.rtm_scope = gw ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK;
req.i.rtm_protocol = proto;
req.i.rtm_type = RTN_UNICAST;
req.i.rtm_dst_len = mask;
+ if (rt_table != RT_TABLE_MAIN)
+ addattr32(&req.n, sizeof(req), RTA_TABLE, rt_table);
if (ifindex)
addattr32(&req.n, sizeof(req), RTA_OIF, ifindex);
if (src)
@@ -538,12 +645,13 @@ int __export iproute_del(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw
if (rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL, 0) < 0)
r = -1;
+out:
net->rtnl_put(rth);
return r;
}
-int __export ip6route_add(int ifindex, const struct in6_addr *dst, int pref_len, const struct in6_addr *gw, int proto, uint32_t prio)
+int __export ip6route_add(int ifindex, const struct in6_addr *dst, int pref_len, const struct in6_addr *gw, int proto, uint32_t prio, const char *vrf_name)
{
struct ipaddr_req {
struct nlmsghdr n;
@@ -558,17 +666,27 @@ int __export ip6route_add(int ifindex, const struct in6_addr *dst, int pref_len,
memset(&req, 0, sizeof(req) - 4096);
+ uint32_t rt_table = RT_TABLE_MAIN;
+#ifdef HAVE_VRF
+ if (ipvrf_get_table(&rt_table, vrf_name) < 0) {
+ r = -1;
+ goto out;
+ }
+#endif
+
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
req.n.nlmsg_type = RTM_NEWROUTE;
req.i.rtm_family = AF_INET6;
- req.i.rtm_table = RT_TABLE_MAIN;
+ req.i.rtm_table = rt_table;
req.i.rtm_scope = RT_SCOPE_UNIVERSE;
req.i.rtm_protocol = proto;
req.i.rtm_type = RTN_UNICAST;
req.i.rtm_dst_len = pref_len;
addattr_l(&req.n, sizeof(req), RTA_DST, dst, sizeof(*dst));
+ if (rt_table != RT_TABLE_MAIN)
+ addattr32(&req.n, sizeof(req), RTA_TABLE, rt_table);
if (ifindex)
addattr32(&req.n, sizeof(req), RTA_OIF, ifindex);
if (gw)
@@ -579,12 +697,13 @@ int __export ip6route_add(int ifindex, const struct in6_addr *dst, int pref_len,
if (rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL, 0) < 0)
r = -1;
+out:
net->rtnl_put(rth);
return r;
}
-int __export ip6route_del(int ifindex, const struct in6_addr *dst, int pref_len, const struct in6_addr *gw, int proto, uint32_t prio)
+int __export ip6route_del(int ifindex, const struct in6_addr *dst, int pref_len, const struct in6_addr *gw, int proto, uint32_t prio, const char *vrf_name)
{
struct ipaddr_req {
struct nlmsghdr n;
@@ -599,17 +718,27 @@ int __export ip6route_del(int ifindex, const struct in6_addr *dst, int pref_len,
memset(&req, 0, sizeof(req) - 4096);
+ uint32_t rt_table = RT_TABLE_MAIN;
+#ifdef HAVE_VRF
+ if (ipvrf_get_table(&rt_table, vrf_name) < 0) {
+ r = -1;
+ goto out;
+ }
+#endif
+
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST;
req.n.nlmsg_type = RTM_DELROUTE;
req.i.rtm_family = AF_INET6;
- req.i.rtm_table = RT_TABLE_MAIN;
+ req.i.rtm_table = rt_table;
req.i.rtm_scope = RT_SCOPE_UNIVERSE;
req.i.rtm_protocol = proto;
req.i.rtm_type = RTN_UNICAST;
req.i.rtm_dst_len = pref_len;
addattr_l(&req.n, sizeof(req), RTA_DST, dst, sizeof(*dst));
+ if (rt_table != RT_TABLE_MAIN)
+ addattr32(&req.n, sizeof(req), RTA_TABLE, rt_table);
if (ifindex)
addattr32(&req.n, sizeof(req), RTA_OIF, ifindex);
if (gw)
@@ -620,6 +749,7 @@ int __export ip6route_del(int ifindex, const struct in6_addr *dst, int pref_len,
if (rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL, 0) < 0)
r = -1;
+out:
net->rtnl_put(rth);
return r;
diff --git a/accel-pppd/libnetlink/iputils.h b/accel-pppd/libnetlink/iputils.h
index 9292cea2..86500882 100644
--- a/accel-pppd/libnetlink/iputils.h
+++ b/accel-pppd/libnetlink/iputils.h
@@ -4,6 +4,7 @@
#include <linux/if_link.h>
#include <netinet/in.h>
#include <stdint.h>
+#include <config.h>
typedef int (*iplink_list_func)(int index, int flags, const char *name, int iflink, int vid, void *arg);
@@ -20,12 +21,12 @@ int ipaddr_add_peer(int ifindex, in_addr_t addr, in_addr_t peer_addr);
int ipaddr_del(int ifindex, in_addr_t addr, int mask);
int ipaddr_del_peer(int ifindex, in_addr_t addr, in_addr_t peer);
-int iproute_add(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw, int proto, int mask, uint32_t prio);
-int iproute_del(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw, int proto, int mask, uint32_t prio);
+int iproute_add(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw, int proto, int mask, uint32_t prio, const char *vrf_name);
+int iproute_del(int ifindex, in_addr_t src, in_addr_t dst, in_addr_t gw, int proto, int mask, uint32_t prio, const char *vrf_name);
in_addr_t iproute_get(in_addr_t dst, in_addr_t *gw);
-int ip6route_add(int ifindex, const struct in6_addr *dst, int pref_len, const struct in6_addr *gw, int proto, uint32_t prio);
-int ip6route_del(int ifindex, const struct in6_addr *dst, int pref_len, const struct in6_addr *gw, int proto, uint32_t prio);
+int ip6route_add(int ifindex, const struct in6_addr *dst, int pref_len, const struct in6_addr *gw, int proto, uint32_t prio, const char *vrf_name);
+int ip6route_del(int ifindex, const struct in6_addr *dst, int pref_len, const struct in6_addr *gw, int proto, uint32_t prio, const char *vrf_name);
int ip6addr_add(int ifindex, struct in6_addr *addr, int prefix_len);
int ip6addr_add_peer(int ifindex, struct in6_addr *addr, struct in6_addr *peer_addr);
int ip6addr_del(int ifindex, struct in6_addr *addr, int prefix_len);
diff --git a/accel-pppd/log.c b/accel-pppd/log.c
index e4a1e6ed..7fac66df 100644
--- a/accel-pppd/log.c
+++ b/accel-pppd/log.c
@@ -11,15 +11,12 @@
#include "triton/mempool.h"
#include "events.h"
#include "ppp.h"
+#include "utils.h"
#include "log.h"
#include "memdebug.h"
-#ifndef min
-#define min(x,y) ((x)<(y)?(x):(y))
-#endif
-
#define LOG_MSG 0
#define LOG_ERROR 1
#define LOG_WARN 2
diff --git a/accel-pppd/logs/CMakeLists.txt b/accel-pppd/logs/CMakeLists.txt
index 4d8a631a..0052e1ae 100644
--- a/accel-pppd/logs/CMakeLists.txt
+++ b/accel-pppd/logs/CMakeLists.txt
@@ -33,7 +33,15 @@ IF(LOG_SYSLOG)
)
ENDIF(LOG_SYSLOG)
+# log_pgsql is deprecated and scheduled for removal, the build flag was
+# renamed so that nobody keeps building it without noticing
IF(LOG_PGSQL)
+ message(FATAL_ERROR "${Esc}[31mLOG_PGSQL is deprecated.\n The log_pgsql module is scheduled for removal in a future release.\n Build with -DLOG_PGSQL_DEPRECATED=TRUE if you still need it, and please say so at https://github.com/accel-ppp/accel-ppp/issues so that it is kept${Esc}[m")
+ENDIF(LOG_PGSQL)
+
+IF(LOG_PGSQL_DEPRECATED)
+ message(WARNING "${Esc}[33mBuilding deprecated module log_pgsql, it is scheduled for removal in a future release.\n Please say so at https://github.com/accel-ppp/accel-ppp/issues if you need it kept${Esc}[m")
+
find_library(PGSQL_LIBRARY pq
HINTS ${CMAKE_SOURCE_DIR}/lib/linux/gcc/${BIT}/lib
PATHS ${CMAKE_SOURCE_DIR}/lib/linux/gcc/${BIT}/lib
@@ -47,5 +55,5 @@ IF(LOG_PGSQL)
INSTALL(TARGETS log_pgsql
LIBRARY DESTINATION lib${LIB_SUFFIX}/accel-ppp
)
-ENDIF(LOG_PGSQL)
+ENDIF(LOG_PGSQL_DEPRECATED)
diff --git a/accel-pppd/logs/log_file.c b/accel-pppd/logs/log_file.c
index 481afa1e..26dd4422 100644
--- a/accel-pppd/logs/log_file.c
+++ b/accel-pppd/logs/log_file.c
@@ -385,7 +385,12 @@ static void general_reopen(void)
{
const char *fname = conf_get_opt("log", "log-file");
int old_fd = -1;
- int fd = open(fname, O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC, S_IRUSR | S_IWUSR);
+ int fd;
+
+ if (!fname)
+ return;
+
+ fd = open(fname, O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd < 0) {
log_emerg("log_file: open '%s': %s\n", fname, strerror(errno));
return;
@@ -422,7 +427,7 @@ static void free_lpd(struct log_file_pd_t *lpd)
if (lpd->lf.fd != -1)
close(lpd->lf.fd);
if (lpd->lf.new_fd != -1)
- close(lpd->lf.fd);
+ close(lpd->lf.new_fd);
spin_unlock(&lpd->lf.lock);
mempool_free(lpd);
}
@@ -710,7 +715,8 @@ static void init(void)
if (opt && atoi(opt) > 0)
conf_copy = 1;
- log_register_target(&general_target);
+ if (log_file)
+ log_register_target(&general_target);
if (conf_per_user_dir) {
log_register_target(&per_user_target);
diff --git a/accel-pppd/logs/log_pgsql.c b/accel-pppd/logs/log_pgsql.c
index e4b5a9e9..6589da0e 100644
--- a/accel-pppd/logs/log_pgsql.c
+++ b/accel-pppd/logs/log_pgsql.c
@@ -1,3 +1,13 @@
+/*
+ * DEPRECATED
+ *
+ * This module is scheduled for removal in a future release. It is built
+ * only when the deprecated LOG_PGSQL_DEPRECATED build flag is given.
+ * If you depend on it, please object at
+ * https://github.com/accel-ppp/accel-ppp/issues, otherwise it will be
+ * deleted.
+ */
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -9,6 +19,7 @@
#include "log.h"
#include "list.h"
#include "ap_session.h"
+#include "utils.h"
#include "memdebug.h"
@@ -57,20 +68,28 @@ static void unpack_msg(struct log_msg_t *msg)
static void set_hdr(struct log_msg_t *msg, struct ap_session *ses)
{
+ const char *username = ses && ses->username ? ses->username : "";
+ const char *sessionid = ses && ses->username ? ses->sessionid : "";
struct tm tm;
+ int pos, len, avail;
localtime_r(&msg->timestamp.tv_sec, &tm);
strftime(msg->hdr->msg, LOG_CHUNK_SIZE, "%Y-%m-%d %H:%M:%S", &tm);
- msg->hdr->len = strlen(msg->hdr->msg) + 1;
- if (ses && ses->username) {
- strcpy(msg->hdr->msg + msg->hdr->len, ses->username);
- msg->hdr->len += strlen(ses->username) + 1;
- strcpy(msg->hdr->msg + msg->hdr->len, ses->sessionid);
- msg->hdr->len += strlen(ses->sessionid) + 1;
- } else
- memset(msg->hdr->msg + msg->hdr->len, 0, 2);
+ pos = strlen(msg->hdr->msg) + 1;
+
+ /* username is peer supplied and may be up to 255 bytes long,
+ * truncate it to what is left of the chunk, keeping one byte
+ * for the terminator of the sessionid */
+ avail = LOG_CHUNK_SIZE - pos - 1;
+ len = snprintf(msg->hdr->msg + pos, avail, "%s", username);
+ pos += min(len, avail - 1) + 1;
+ avail = LOG_CHUNK_SIZE - pos;
+ len = snprintf(msg->hdr->msg + pos, avail, "%s", sessionid);
+ pos += min(len, avail - 1) + 1;
+
+ msg->hdr->len = pos;
}
static void write_next_msg(void)
@@ -284,6 +303,9 @@ static void init(void)
{
char *opt;
+ log_warn("log_pgsql: this module is deprecated and is scheduled for removal,"
+ " please object at https://github.com/accel-ppp/accel-ppp/issues if you need it\n");
+
spinlock_init(&queue_lock);
opt = conf_get_opt("log-pgsql", "conninfo");
diff --git a/accel-pppd/logs/log_tcp.c b/accel-pppd/logs/log_tcp.c
index e0663d34..ca803a7e 100644
--- a/accel-pppd/logs/log_tcp.c
+++ b/accel-pppd/logs/log_tcp.c
@@ -163,7 +163,7 @@ static int log_tcp_connect(struct triton_md_handler_t *h)
{
struct tcp_target_t *t = container_of(h, typeof(*t), hnd);
- if (connect(t->hnd.fd, &t->addr, sizeof(t->addr))) {
+ if (connect(t->hnd.fd, (struct sockaddr*)&t->addr, sizeof(t->addr))) {
if (errno == EAGAIN)
return 0;
if (errno == EINPROGRESS)
@@ -216,7 +216,7 @@ static void start_connect(struct tcp_target_t *t)
return;
}
- if (connect(t->hnd.fd, &t->addr, sizeof(t->addr))) {
+ if (connect(t->hnd.fd, (struct sockaddr*)&t->addr, sizeof(t->addr))) {
if (errno != EINPROGRESS) {
log_emerg("log-tcp: connect: %s\n", strerror(errno));
close(t->hnd.fd);
diff --git a/accel-pppd/main.c b/accel-pppd/main.c
index bbc36d95..221be7be 100644
--- a/accel-pppd/main.c
+++ b/accel-pppd/main.c
@@ -16,9 +16,7 @@
#include <sys/wait.h>
#include <sys/resource.h>
-#ifdef CRYPTO_OPENSSL
#include <openssl/ssl.h>
-#endif
#include "triton/triton.h"
@@ -43,7 +41,8 @@ static int term;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
-#ifdef CRYPTO_OPENSSL
+static volatile sig_atomic_t need_reload;
+
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
static pthread_mutex_t *ssl_lock_cs;
@@ -76,16 +75,14 @@ static void ssl_lock_init(void)
static void openssl_init(void)
{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
OpenSSL_add_all_digests();
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
ssl_lock_init();
#endif
}
-#endif
static void change_limits(void)
{
@@ -105,7 +102,7 @@ static void change_limits(void)
log_emerg("main: setrlimit: %s\n", strerror(errno));
}
-static void config_reload_notify(int r)
+static void config_reload_notify(int r, void *arg)
{
if (!r)
triton_event_fire(EV_CONFIG_RELOAD, NULL);
@@ -113,7 +110,7 @@ static void config_reload_notify(int r)
static void config_reload(int num)
{
- triton_conf_reload(config_reload_notify);
+ need_reload = 1;
}
static void close_all_fd(void)
@@ -393,9 +390,7 @@ int main(int _argc, char **_argv)
change_limits();
-#ifdef CRYPTO_OPENSSL
openssl_init();
-#endif
triton_register_init(0, log_version);
@@ -444,7 +439,19 @@ int main(int _argc, char **_argv)
backup_restore(internal);
#endif
- sigwait(&set, &sig);
+ {
+ struct timespec ts = { .tv_sec = 1 };
+ while (1) {
+ sig = sigtimedwait(&set, NULL, &ts);
+ if (sig > 0)
+ break;
+ if (need_reload) {
+ need_reload = 0;
+ if (triton_conf_reload(config_reload_notify, NULL))
+ log_warn("main: config reload is already in progress\n");
+ }
+ }
+ }
log_info1("terminate, sig = %i\n", sig);
ap_shutdown_soft(shutdown_cb, 1);
diff --git a/accel-pppd/memdebug.c b/accel-pppd/memdebug.c
index 5353e572..b3488aaa 100644
--- a/accel-pppd/memdebug.c
+++ b/accel-pppd/memdebug.c
@@ -42,6 +42,19 @@ struct mem_t
static LIST_HEAD(mem_list);
static spinlock_t mem_list_lock;
+static uint64_t get_tail_magic(const struct mem_t *mem)
+{
+ uint64_t magic;
+
+ memcpy(&magic, mem->data + mem->size, sizeof(magic));
+ return magic;
+}
+
+static void set_tail_magic(struct mem_t *mem)
+{
+ memcpy(mem->data + mem->size, &mem->magic2, sizeof(mem->magic2));
+}
+
static struct mem_t *_md_malloc(size_t size, const char *fname, int line)
{
struct mem_t *mem = malloc(sizeof(*mem) + size + 8);
@@ -54,7 +67,7 @@ static struct mem_t *_md_malloc(size_t size, const char *fname, int line)
mem->size = size;
mem->magic1 = MAGIC1;
mem->magic2 = (uint64_t)random() * (uint64_t)random();
- *(uint64_t*)(mem->data + size) = mem->magic2;
+ set_tail_magic(mem);
spin_lock(&mem_list_lock);
list_add_tail(&mem->entry, &mem_list);
@@ -84,7 +97,7 @@ void __export md_free(void *ptr, const char *fname, int line)
abort();
}
- if (mem->magic2 != *(uint64_t*)(mem->data + mem->size)) {
+ if (mem->magic2 != get_tail_magic(mem)) {
printf("memory corruption:\nmalloc(%zu) at %s:%i\nfree at %s:%i\n",
mem->size, mem->fname, mem->line, fname, line);
abort();
@@ -113,7 +126,7 @@ void __export *md_realloc(void *ptr, size_t size, const char *fname, int line)
abort();
}
- if (mem->magic2 != *(uint64_t*)(mem->data + mem->size)) {
+ if (mem->magic2 != get_tail_magic(mem)) {
printf("memory corruption:\nmalloc(%zu) at %s:%i\nfree at %s:%i\n",
mem->size, mem->fname, mem->line, fname, line);
abort();
@@ -217,7 +230,7 @@ static void siginfo2(int num)
spin_lock(&mem_list_lock);
list_for_each_entry(mem, &mem_list, entry) {
- if (mem->magic1 != MAGIC1 || mem->magic2 != *(uint64_t*)(mem->data + mem->size))
+ if (mem->magic1 != MAGIC1 || mem->magic2 != get_tail_magic(mem))
printf("%s:%i %lu\n", mem->fname, mem->line, (long unsigned)mem->size);
}
spin_unlock(&mem_list_lock);
@@ -233,7 +246,7 @@ void __export md_check(void *ptr)
if (mem->magic1 != MAGIC1)
abort();
- if (mem->magic2 != *(uint64_t*)(mem->data + mem->size))
+ if (mem->magic2 != get_tail_magic(mem))
abort();
}
diff --git a/accel-pppd/ppp/ccp_mppe.c b/accel-pppd/ppp/ccp_mppe.c
index cb41c0da..5042c8a0 100644
--- a/accel-pppd/ppp/ccp_mppe.c
+++ b/accel-pppd/ppp/ccp_mppe.c
@@ -10,6 +10,7 @@
#include "ppp_ccp.h"
#include "log.h"
#include "events.h"
+#include "utils.h"
#include "memdebug.h"
@@ -106,7 +107,7 @@ static int setup_mppe_key(int fd, int transmit, uint8_t *key)
memset(buf, 0, sizeof(buf));
buf[0] = CI_MPPE;
buf[1] = 6;
- *(uint32_t*)(buf + 2) = htonl(MPPE_S | MPPE_H);
+ u_write_be32(buf + 2, MPPE_S | MPPE_H);
if (key)
memcpy(buf + 6, key, 16);
diff --git a/accel-pppd/ppp/ipv6cp_opt_intfid.c b/accel-pppd/ppp/ipv6cp_opt_intfid.c
index fff74160..5de88932 100644
--- a/accel-pppd/ppp/ipv6cp_opt_intfid.c
+++ b/accel-pppd/ppp/ipv6cp_opt_intfid.c
@@ -1,6 +1,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
+#include <endian.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
@@ -25,9 +26,16 @@
static int conf_check_exists;
static int conf_intf_id = INTF_ID_FIXED;
-static uint64_t conf_intf_id_val = 1;
+/* Fixed intf-id values are stored in network byte order: the rest of
+ * the code (build_ip6_addr(), ifcfg.c, nd.c, ...) copies them into the
+ * low 8 bytes of the IPv6 address as-is, and parse_intfid() already
+ * produces network byte order. The old host-order defaults (1 and 2)
+ * therefore yielded ::100:0:0:0 / ::200:0:0:0 instead of ::1 / ::2 on
+ * little-endian hosts. Defaults are assigned in init() because
+ * htobe64() is not a constant expression on all libcs (e.g. musl). */
+static uint64_t conf_intf_id_val;
static int conf_peer_intf_id = INTF_ID_FIXED;
-static uint64_t conf_peer_intf_id_val = 2;
+static uint64_t conf_peer_intf_id_val;
static int conf_accept_peer_intf_id;
static struct ipv6cp_option_t *ipaddr_init(struct ppp_ipv6cp_t *ipv6cp);
@@ -276,12 +284,14 @@ static void ipaddr_print(void (*print)(const char *fmt,...), struct ipv6cp_optio
{
struct ipaddr_option_t *ipaddr_opt = container_of(opt, typeof(*ipaddr_opt), opt);
struct ipv6cp_opt64_t *opt64 = (struct ipv6cp_opt64_t *)ptr;
- struct in6_addr a;
+ struct in6_addr a = {};
+ uint64_t intf_id;
if (ptr)
- *(uint64_t *)(a.s6_addr + 8) = opt64->val;
+ intf_id = opt64->val;
else
- *(uint64_t *)(a.s6_addr + 8) = ipaddr_opt->ppp->ses.ipv6->intf_id;
+ intf_id = ipaddr_opt->ppp->ses.ipv6->intf_id;
+ memcpy(a.s6_addr + 8, &intf_id, sizeof(intf_id));
print("<addr %x:%x:%x:%x>", ntohs(a.s6_addr16[4]), ntohs(a.s6_addr16[5]), ntohs(a.s6_addr16[6]), ntohs(a.s6_addr16[7]));
}
@@ -354,6 +364,11 @@ static void load_config(void)
static void init()
{
+ /* network byte order, so the resulting addresses are ::1 and ::2
+ * regardless of host endianness */
+ conf_intf_id_val = htobe64(1);
+ conf_peer_intf_id_val = htobe64(2);
+
if (sock6_fd < 0)
return;
@@ -363,4 +378,3 @@ static void init()
}
DEFINE_INIT(5, init);
-
diff --git a/accel-pppd/ppp/ppp.c b/accel-pppd/ppp/ppp.c
index 8a4cce7c..cd726e17 100644
--- a/accel-pppd/ppp/ppp.c
+++ b/accel-pppd/ppp/ppp.c
@@ -12,8 +12,6 @@
#include <sys/ioctl.h>
#include "linux_ppp.h"
-#include "crypto.h"
-
#include "triton.h"
#include "ap_session.h"
@@ -59,7 +57,7 @@ static mempool_t uc_pool;
static int ppp_chan_read(struct triton_md_handler_t*);
static int ppp_unit_read(struct triton_md_handler_t*);
-static void init_layers(struct ppp_t *);
+static int init_layers(struct ppp_t *);
static void _free_layers(struct ppp_t *);
static void start_first_layer(struct ppp_t *);
static int setup_ppp_mru(struct ppp_t *ppp);
@@ -103,7 +101,8 @@ int __export establish_ppp(struct ppp_t *ppp)
goto exit_close_chan;
}
- init_layers(ppp);
+ if (init_layers(ppp))
+ goto exit_close_chan;
if (list_empty(&ppp->layers)) {
log_ppp_error("no layers to start\n");
goto exit_close_chan;
@@ -145,15 +144,13 @@ int __export connect_ppp_channel(struct ppp_t *ppp)
return 0;
}
- if (uc_size) {
- pthread_mutex_lock(&uc_lock);
- if (!list_empty(&uc_list)) {
- uc = list_entry(uc_list.next, typeof(*uc), entry);
- list_del(&uc->entry);
- --uc_size;
- }
- pthread_mutex_unlock(&uc_lock);
+ pthread_mutex_lock(&uc_lock);
+ if (!list_empty(&uc_list)) {
+ uc = list_entry(uc_list.next, typeof(*uc), entry);
+ list_del(&uc->entry);
+ --uc_size;
}
+ pthread_mutex_unlock(&uc_lock);
if (uc) {
ppp->unit_fd = uc->fd;
@@ -272,10 +269,11 @@ static void destablish_ppp(struct ppp_t *ppp)
if (conf_unit_cache) {
struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
if (ppp->ses.net != def_net) {
if (net->move_link(def_net, ppp->ses.ifindex)) {
log_ppp_warn("failed to attach to default namespace\n");
- triton_md_unregister_handler(&ppp->unit_hnd, 1);
+ triton_md_unregister_handler(&ppp->unit_hnd, 0);
goto skip;
}
ppp->ses.net = def_net;
@@ -288,7 +286,7 @@ static void destablish_ppp(struct ppp_t *ppp)
strncpy(ifr.ifr_name, ppp->ses.ifname, IFNAMSIZ);
if (net->sock_ioctl(SIOCSIFNAME, &ifr)) {
log_ppp_warn("failed to rename ppp to default name\n");
- triton_md_unregister_handler(&ppp->unit_hnd, 1);
+ triton_md_unregister_handler(&ppp->unit_hnd, 0);
goto skip;
}
}
@@ -301,11 +299,18 @@ static void destablish_ppp(struct ppp_t *ppp)
uc->fd = ppp->unit_fd;
uc->unit_idx = ppp->ses.unit_idx;
} else
- triton_md_unregister_handler(&ppp->unit_hnd, 1);
+ triton_md_unregister_handler(&ppp->unit_hnd, 0);
skip:
ap_session_finished(&ppp->ses);
+ /* The unit fd is closed only after session cleanup (ip-down scripts,
+ * radattr removal) has finished, so the kernel cannot give the same
+ * unit index (and thus ifname) to a new session while cleanup still
+ * references the ifname. */
+ if (!uc)
+ close(ppp->unit_fd);
+
ppp->unit_fd = -1;
destroy_ppp_channel(ppp);
@@ -673,7 +678,7 @@ void __export ppp_unregister_layer(struct ppp_layer_t *layer)
list_del(&layer->entry);
}
-static void init_layers(struct ppp_t *ppp)
+static int init_layers(struct ppp_t *ppp)
{
struct layer_node_t *n, *n1;
struct ppp_layer_t *l;
@@ -686,12 +691,18 @@ static void init_layers(struct ppp_t *ppp)
list_add_tail(&n1->entry, &ppp->layers);
list_for_each_entry(l, &n->items, entry) {
d = l->init(ppp);
+ if (!d) {
+ log_ppp_error("ppp layer init failed\n");
+ _free_layers(ppp);
+ return -1;
+ }
d->layer = l;
d->started = 0;
d->node = n1;
list_add_tail(&d->entry, &n1->items);
}
}
+ return 0;
}
static void _free_layers(struct ppp_t *ppp)
diff --git a/accel-pppd/ppp/ppp_ccp.c b/accel-pppd/ppp/ppp_ccp.c
index f9e05e89..30ef97b9 100644
--- a/accel-pppd/ppp/ppp_ccp.c
+++ b/accel-pppd/ppp/ppp_ccp.c
@@ -13,6 +13,8 @@
#include "ppp.h"
#include "ppp_ccp.h"
+#include "ppp_ipcp.h"
+#include "ppp_ipv6cp.h"
#include "memdebug.h"
@@ -202,6 +204,9 @@ static void ccp_layer_up(struct ppp_fsm_t *fsm)
return;
}
ppp_layer_started(ccp->ppp, &ccp->ld);
+
+ ipcp_ccp_started(ccp->ppp);
+ ipv6cp_ccp_started(ccp->ppp);
}
}
@@ -211,9 +216,12 @@ static void ccp_layer_finished(struct ppp_fsm_t *fsm)
log_ppp_debug("ccp_layer_finished\n");
- if (!ccp->started)
+ if (!ccp->started) {
ppp_layer_passive(ccp->ppp, &ccp->ld);
- else if (!ccp->ppp->ses.terminating)
+
+ ipcp_ccp_started(ccp->ppp);
+ ipv6cp_ccp_started(ccp->ppp);
+ } else if (!ccp->ppp->ses.terminating)
ap_session_terminate(&ccp->ppp->ses, TERM_USER_ERROR, 0);
fsm->fsm_state = FSM_Closed;
@@ -387,10 +395,18 @@ static int ccp_recv_conf_req(struct ppp_ccp_t *ccp, uint8_t *data, int size)
ccp->ropt_len = size;
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ log_ppp_warn("CCP: ConfReq: truncated option header (%i bytes left)\n", size);
+ return CCP_OPT_FAIL;
+ }
+
hdr = (struct ccp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
- break;
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ log_ppp_warn("CCP: ConfReq: invalid length %i of option %i (%i bytes left)\n",
+ hdr->len, hdr->id, size);
+ return CCP_OPT_FAIL;
+ }
ropt = _malloc(sizeof(*ropt));
memset(ropt, 0, sizeof(*ropt));
@@ -482,10 +498,17 @@ static int ccp_recv_conf_rej(struct ppp_ccp_t *ccp, uint8_t *data, int size)
}*/
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct ccp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &ccp->options, entry) {
if (lopt->id == hdr->id) {
@@ -523,10 +546,17 @@ static int ccp_recv_conf_nak(struct ppp_ccp_t *ccp, uint8_t *data, int size)
}*/
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct ccp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &ccp->options, entry) {
if (lopt->id == hdr->id) {
@@ -566,10 +596,17 @@ static int ccp_recv_conf_ack(struct ppp_ccp_t *ccp, uint8_t *data, int size)
}*/
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct ccp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &ccp->options, entry) {
if (lopt->id == hdr->id) {
@@ -647,7 +684,7 @@ static void ccp_recv(struct ppp_handler_t*h)
}
hdr = (struct ccp_hdr_t *)ccp->ppp->buf;
- if (ntohs(hdr->len) < PPP_HEADERLEN) {
+ if (ntohs(hdr->len) < PPP_HEADERLEN || ntohs(hdr->len) > ccp->ppp->buf_size - 2) {
log_ppp_warn("CCP: short packet received\n");
return;
}
@@ -695,8 +732,10 @@ static void ccp_recv(struct ppp_handler_t*h)
ppp_fsm_recv_conf_ack(&ccp->fsm);
break;
case CONFNAK:
- ccp_recv_conf_nak(ccp, (uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN);
- ppp_fsm_recv_conf_rej(&ccp->fsm);
+ if (ccp_recv_conf_nak(ccp, (uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN))
+ ap_session_terminate(&ccp->ppp->ses, TERM_USER_ERROR, 0);
+ else
+ ppp_fsm_recv_conf_rej(&ccp->fsm);
break;
case CONFREJ:
if (ccp_recv_conf_rej(ccp, (uint8_t*)(hdr + 1),ntohs(hdr->len) - PPP_HDRLEN))
diff --git a/accel-pppd/ppp/ppp_ipcp.c b/accel-pppd/ppp/ppp_ipcp.c
index 416fba93..1beccf8c 100644
--- a/accel-pppd/ppp/ppp_ipcp.c
+++ b/accel-pppd/ppp/ppp_ipcp.c
@@ -173,6 +173,9 @@ void ipcp_layer_free(struct ppp_layer_data_t *ld)
if (ipcp->timeout.tpd)
triton_timer_del(&ipcp->timeout);
+ if (ipcp->delay_ack_buf)
+ _free(ipcp->delay_ack_buf);
+
_free(ipcp);
}
@@ -292,7 +295,11 @@ static void send_conf_ack(struct ppp_fsm_t *fsm)
struct ipcp_hdr_t *hdr = (struct ipcp_hdr_t*)ipcp->ppp->buf;
if (ipcp->delay_ack) {
- send_term_ack(fsm);
+ /* CCP is still negotiating, withhold the ack until it settles */
+ if (ipcp->delay_ack_buf)
+ _free(ipcp->delay_ack_buf);
+ ipcp->delay_ack_buf = _malloc(ntohs(hdr->len) + 2);
+ memcpy(ipcp->delay_ack_buf, hdr, ntohs(hdr->len) + 2);
return;
}
@@ -391,10 +398,18 @@ static int ipcp_recv_conf_req(struct ppp_ipcp_t *ipcp, uint8_t *data, int size)
ipcp->ropt_len = size;
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ log_ppp_warn("IPCP: ConfReq: truncated option header (%i bytes left)\n", size);
+ return IPCP_OPT_FAIL;
+ }
+
hdr = (struct ipcp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
- break;
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ log_ppp_warn("IPCP: ConfReq: invalid length %i of option %i (%i bytes left)\n",
+ hdr->len, hdr->id, size);
+ return IPCP_OPT_FAIL;
+ }
ropt = _malloc(sizeof(*ropt));
memset(ropt, 0, sizeof(*ropt));
@@ -503,10 +518,17 @@ static int ipcp_recv_conf_rej(struct ppp_ipcp_t *ipcp, uint8_t *data, int size)
}*/
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct ipcp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &ipcp->options, entry) {
if (lopt->id == hdr->id) {
@@ -544,10 +566,17 @@ static int ipcp_recv_conf_nak(struct ppp_ipcp_t *ipcp, uint8_t *data, int size)
}*/
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct ipcp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &ipcp->options, entry) {
if (lopt->id == hdr->id) {
@@ -587,10 +616,17 @@ static int ipcp_recv_conf_ack(struct ppp_ipcp_t *ipcp, uint8_t *data, int size)
}*/
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct ipcp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &ipcp->options, entry) {
if (lopt->id == hdr->id) {
@@ -671,7 +707,7 @@ static void ipcp_recv(struct ppp_handler_t*h)
}
hdr = (struct ipcp_hdr_t *)ipcp->ppp->buf;
- if (ntohs(hdr->len) < PPP_HEADERLEN) {
+ if (ntohs(hdr->len) < PPP_HEADERLEN || ntohs(hdr->len) > ipcp->ppp->buf_size - 2) {
log_ppp_warn("IPCP: short packet received\n");
return;
}
@@ -725,8 +761,10 @@ static void ipcp_recv(struct ppp_handler_t*h)
ppp_fsm_recv_conf_ack(&ipcp->fsm);
break;
case CONFNAK:
- ipcp_recv_conf_nak(ipcp,(uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN);
- ppp_fsm_recv_conf_rej(&ipcp->fsm);
+ if (ipcp_recv_conf_nak(ipcp,(uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN))
+ ap_session_terminate(&ipcp->ppp->ses, TERM_USER_ERROR, 0);
+ else
+ ppp_fsm_recv_conf_rej(&ipcp->fsm);
break;
case CONFREJ:
if (ipcp_recv_conf_rej(ipcp, (uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN))
@@ -781,6 +819,40 @@ int ipcp_option_register(struct ipcp_option_handler_t *h)
return 0;
}
+void ipcp_ccp_started(struct ppp_t *ppp)
+{
+ struct ppp_layer_data_t *ld = ppp_find_layer_data(ppp, &ipcp_layer);
+ struct ppp_ipcp_t *ipcp;
+ struct ipcp_hdr_t *hdr;
+
+ if (!ld)
+ return;
+
+ ipcp = container_of(ld, typeof(*ipcp), ld);
+
+ if (!ipcp->delay_ack)
+ return;
+
+ ipcp->delay_ack = 0;
+
+ if (ipcp->fsm.fsm_state == FSM_Opened)
+ __ipcp_layer_up(ipcp);
+
+ if (!ipcp->delay_ack_buf)
+ return;
+
+ hdr = (struct ipcp_hdr_t *)ipcp->delay_ack_buf;
+ hdr->code = CONFACK;
+
+ if (conf_ppp_verbose)
+ log_ppp_info2("send [IPCP ConfAck id=%x]\n", hdr->id);
+
+ ppp_unit_send(ipcp->ppp, hdr, ntohs(hdr->len) + 2);
+
+ _free(ipcp->delay_ack_buf);
+ ipcp->delay_ack_buf = NULL;
+}
+
struct ipcp_option_t *ipcp_find_option(struct ppp_t *ppp, struct ipcp_option_handler_t *h)
{
struct ppp_ipcp_t *ipcp = container_of(ppp_find_layer_data(ppp, &ipcp_layer), typeof(*ipcp), ld);
diff --git a/accel-pppd/ppp/ppp_ipcp.h b/accel-pppd/ppp/ppp_ipcp.h
index 036f0a4d..8cd7790f 100644
--- a/accel-pppd/ppp/ppp_ipcp.h
+++ b/accel-pppd/ppp/ppp_ipcp.h
@@ -92,12 +92,15 @@ struct ppp_ipcp_t
struct list_head ropt_list; // last received ConfReq
int ropt_len;
+ void *delay_ack_buf; // ConfAck withheld until CCP finishes
+
int conf_req_len;
unsigned int starting:1;
unsigned int started:1;
unsigned int delay_ack:1;
};
+void ipcp_ccp_started(struct ppp_t *ppp);
int ipcp_option_register(struct ipcp_option_handler_t *h);
struct ipcp_option_t *ipcp_find_option(struct ppp_t *ppp, struct ipcp_option_handler_t *h);
diff --git a/accel-pppd/ppp/ppp_ipv6cp.c b/accel-pppd/ppp/ppp_ipv6cp.c
index 1194b314..3caa89a2 100644
--- a/accel-pppd/ppp/ppp_ipv6cp.c
+++ b/accel-pppd/ppp/ppp_ipv6cp.c
@@ -173,6 +173,9 @@ void ipv6cp_layer_free(struct ppp_layer_data_t *ld)
if (ipv6cp->timeout.tpd)
triton_timer_del(&ipv6cp->timeout);
+ if (ipv6cp->delay_ack_buf)
+ _free(ipv6cp->delay_ack_buf);
+
_free(ipv6cp);
}
@@ -205,8 +208,12 @@ static void ipv6cp_layer_finished(struct ppp_fsm_t *fsm)
ap_session_terminate(&ipv6cp->ppp->ses, TERM_USER_ERROR, 0);
else
ppp_layer_passive(ipv6cp->ppp, &ipv6cp->ld);
- } else if (!ipv6cp->ppp->ses.terminating)
- ap_session_terminate(&ipv6cp->ppp->ses, TERM_USER_ERROR, 0);
+ } else if (!ipv6cp->ppp->ses.terminating) {
+ if (conf_ipv6 == IPV6_REQUIRE)
+ ap_session_terminate(&ipv6cp->ppp->ses, TERM_USER_ERROR, 0);
+ else
+ log_ppp_info1("ipv6cp: closed, session continues without IPv6\n");
+ }
fsm->fsm_state = FSM_Closed;
}
@@ -292,7 +299,11 @@ static void send_conf_ack(struct ppp_fsm_t *fsm)
struct ipv6cp_hdr_t *hdr = (struct ipv6cp_hdr_t*)ipv6cp->ppp->buf;
if (ipv6cp->delay_ack) {
- send_term_ack(fsm);
+ /* CCP is still negotiating, withhold the ack until it settles */
+ if (ipv6cp->delay_ack_buf)
+ _free(ipv6cp->delay_ack_buf);
+ ipv6cp->delay_ack_buf = _malloc(ntohs(hdr->len) + 2);
+ memcpy(ipv6cp->delay_ack_buf, hdr, ntohs(hdr->len) + 2);
return;
}
@@ -391,10 +402,18 @@ static int ipv6cp_recv_conf_req(struct ppp_ipv6cp_t *ipv6cp, uint8_t *data, int
ipv6cp->ropt_len = size;
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ log_ppp_warn("IPV6CP: ConfReq: truncated option header (%i bytes left)\n", size);
+ return IPV6CP_OPT_FAIL;
+ }
+
hdr = (struct ipv6cp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
- break;
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ log_ppp_warn("IPV6CP: ConfReq: invalid length %i of option %i (%i bytes left)\n",
+ hdr->len, hdr->id, size);
+ return IPV6CP_OPT_FAIL;
+ }
ropt = _malloc(sizeof(*ropt));
memset(ropt, 0, sizeof(*ropt));
@@ -503,10 +522,17 @@ static int ipv6cp_recv_conf_rej(struct ppp_ipv6cp_t *ipv6cp, uint8_t *data, int
}*/
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct ipv6cp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &ipv6cp->options, entry) {
if (lopt->id == hdr->id) {
@@ -544,10 +570,17 @@ static int ipv6cp_recv_conf_nak(struct ppp_ipv6cp_t *ipv6cp, uint8_t *data, int
}*/
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct ipv6cp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &ipv6cp->options, entry) {
if (lopt->id == hdr->id) {
@@ -587,10 +620,17 @@ static int ipv6cp_recv_conf_ack(struct ppp_ipv6cp_t *ipv6cp, uint8_t *data, int
}*/
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct ipv6cp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &ipv6cp->options, entry) {
if (lopt->id == hdr->id) {
@@ -671,7 +711,7 @@ static void ipv6cp_recv(struct ppp_handler_t*h)
}
hdr = (struct ipv6cp_hdr_t *)ipv6cp->ppp->buf;
- if (ntohs(hdr->len) < PPP_HEADERLEN) {
+ if (ntohs(hdr->len) < PPP_HEADERLEN || ntohs(hdr->len) > ipv6cp->ppp->buf_size - 2) {
log_ppp_warn("IPV6CP: short packet received\n");
return;
}
@@ -725,8 +765,10 @@ static void ipv6cp_recv(struct ppp_handler_t*h)
ppp_fsm_recv_conf_ack(&ipv6cp->fsm);
break;
case CONFNAK:
- ipv6cp_recv_conf_nak(ipv6cp,(uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN);
- ppp_fsm_recv_conf_rej(&ipv6cp->fsm);
+ if (ipv6cp_recv_conf_nak(ipv6cp,(uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN))
+ ap_session_terminate(&ipv6cp->ppp->ses, TERM_USER_ERROR, 0);
+ else
+ ppp_fsm_recv_conf_rej(&ipv6cp->fsm);
break;
case CONFREJ:
if (ipv6cp_recv_conf_rej(ipv6cp, (uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN))
@@ -738,7 +780,12 @@ static void ipv6cp_recv(struct ppp_handler_t*h)
if (conf_ppp_verbose)
log_ppp_info2("recv [IPV6CP TermReq id=%x]\n", hdr->id);
ppp_fsm_recv_term_req(&ipv6cp->fsm);
- ap_session_terminate(&ipv6cp->ppp->ses, TERM_USER_REQUEST, 0);
+ /* RFC 1661 sec 3.7: closing one NCP is not sufficient reason
+ * to terminate the PPP link */
+ if (conf_ipv6 == IPV6_REQUIRE)
+ ap_session_terminate(&ipv6cp->ppp->ses, TERM_USER_REQUEST, 0);
+ else
+ ppp_layer_passive(ipv6cp->ppp, &ipv6cp->ld);
break;
case TERMACK:
if (conf_ppp_verbose)
@@ -781,6 +828,40 @@ int ipv6cp_option_register(struct ipv6cp_option_handler_t *h)
return 0;
}
+void ipv6cp_ccp_started(struct ppp_t *ppp)
+{
+ struct ppp_layer_data_t *ld = ppp_find_layer_data(ppp, &ipv6cp_layer);
+ struct ppp_ipv6cp_t *ipv6cp;
+ struct ipv6cp_hdr_t *hdr;
+
+ if (!ld)
+ return;
+
+ ipv6cp = container_of(ld, typeof(*ipv6cp), ld);
+
+ if (!ipv6cp->delay_ack)
+ return;
+
+ ipv6cp->delay_ack = 0;
+
+ if (ipv6cp->fsm.fsm_state == FSM_Opened)
+ __ipv6cp_layer_up(ipv6cp);
+
+ if (!ipv6cp->delay_ack_buf)
+ return;
+
+ hdr = (struct ipv6cp_hdr_t *)ipv6cp->delay_ack_buf;
+ hdr->code = CONFACK;
+
+ if (conf_ppp_verbose)
+ log_ppp_info2("send [IPV6CP ConfAck id=%x]\n", hdr->id);
+
+ ppp_unit_send(ipv6cp->ppp, hdr, ntohs(hdr->len) + 2);
+
+ _free(ipv6cp->delay_ack_buf);
+ ipv6cp->delay_ack_buf = NULL;
+}
+
struct ipv6cp_option_t *ipv6cp_find_option(struct ppp_t *ppp, struct ipv6cp_option_handler_t *h)
{
struct ppp_ipv6cp_t *ipv6cp = container_of(ppp_find_layer_data(ppp, &ipv6cp_layer), typeof(*ipv6cp), ld);
diff --git a/accel-pppd/ppp/ppp_ipv6cp.h b/accel-pppd/ppp/ppp_ipv6cp.h
index 6f1789ec..31bdccf6 100644
--- a/accel-pppd/ppp/ppp_ipv6cp.h
+++ b/accel-pppd/ppp/ppp_ipv6cp.h
@@ -98,12 +98,15 @@ struct ppp_ipv6cp_t
struct list_head ropt_list; // last received ConfReq
int ropt_len;
+ void *delay_ack_buf; // ConfAck withheld until CCP finishes
+
int conf_req_len;
unsigned int starting:1;
unsigned int started:1;
unsigned int delay_ack:1;
};
+void ipv6cp_ccp_started(struct ppp_t *ppp);
int ipv6cp_option_register(struct ipv6cp_option_handler_t *h);
#endif
diff --git a/accel-pppd/ppp/ppp_lcp.c b/accel-pppd/ppp/ppp_lcp.c
index 10babec8..2424ca94 100644
--- a/accel-pppd/ppp/ppp_lcp.c
+++ b/accel-pppd/ppp/ppp_lcp.c
@@ -13,6 +13,7 @@
#include "ppp_lcp.h"
#include "events.h"
#include "iputils.h"
+#include "utils.h"
#include "memdebug.h"
@@ -369,10 +370,18 @@ static int lcp_recv_conf_req(struct ppp_lcp_t *lcp, uint8_t *data, int size)
lcp->ropt_len = size;
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ log_ppp_warn("LCP: ConfReq: truncated option header (%i bytes left)\n", size);
+ return LCP_OPT_FAIL;
+ }
+
hdr = (struct lcp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
- break;
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ log_ppp_warn("LCP: ConfReq: invalid length %i of option %i (%i bytes left)\n",
+ hdr->len, hdr->id, size);
+ return LCP_OPT_FAIL;
+ }
ropt = _malloc(sizeof(*ropt));
memset(ropt, 0, sizeof(*ropt));
@@ -461,10 +470,17 @@ static int lcp_recv_conf_rej(struct ppp_lcp_t *lcp, uint8_t *data, int size)
}
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct lcp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &lcp->options, entry) {
if (lopt->id == hdr->id) {
@@ -506,10 +522,17 @@ static int lcp_recv_conf_nak(struct ppp_lcp_t *lcp, uint8_t *data, int size)
}
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct lcp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt,&lcp->options,entry) {
if (lopt->id == hdr->id) {
@@ -549,10 +572,17 @@ static int lcp_recv_conf_ack(struct ppp_lcp_t *lcp, uint8_t *data, int size)
}
while (size > 0) {
+ if (size < sizeof(*hdr)) {
+ res = -1;
+ break;
+ }
+
hdr = (struct lcp_opt_hdr_t *)data;
- if (!hdr->len || hdr->len > size)
+ if (hdr->len < sizeof(*hdr) || hdr->len > size) {
+ res = -1;
break;
+ }
list_for_each_entry(lopt, &lcp->options, entry) {
if (lopt->id == hdr->id) {
@@ -586,7 +616,7 @@ static void lcp_recv_echo_repl(struct ppp_lcp_t *lcp, uint8_t *data, int size)
if (conf_ppp_verbose)
log_ppp_debug("recv [LCP EchoRep id=%x]\n", lcp->fsm.recv_id);
} else {
- magic = ntohl(*(uint32_t *)data);
+ magic = u_read_be32(data);
if (conf_ppp_verbose)
log_ppp_debug("recv [LCP EchoRep id=%x <magic %08x>]\n", lcp->fsm.recv_id, magic);
@@ -610,12 +640,13 @@ static void send_echo_reply(struct ppp_lcp_t *lcp)
lcp->last_echo_ts = _time();
hdr->code = ECHOREP;
- *(uint32_t *)(hdr + 1) = htonl(lcp->magic);
+ uint32_t magic = htonl(lcp->magic);
+ memcpy((char *)hdr + sizeof(struct lcp_hdr_t), &magic, sizeof(magic));
if (conf_ppp_verbose)
log_ppp_debug("send [LCP EchoRep id=%x <magic %08x>]\n", hdr->id, lcp->magic);
- ppp_chan_send(lcp->ppp, hdr, ntohs(hdr->len) + 2);
+ ppp_chan_send(lcp->ppp, hdr, min(ntohs(hdr->len), lcp->ppp->mtu) + 2);
}
static void send_echo_request(struct triton_timer_t *t)
@@ -733,6 +764,8 @@ static void lcp_recv(struct ppp_handler_t*h)
struct ppp_lcp_t *lcp = container_of(h, typeof(*lcp), hnd);
int r;
char *term_msg;
+ uint16_t len;
+ int buf_len;
if (lcp->ppp->buf_size < PPP_HEADERLEN + 2) {
log_ppp_warn("LCP: short packet received\n");
@@ -740,7 +773,9 @@ static void lcp_recv(struct ppp_handler_t*h)
}
hdr = (struct lcp_hdr_t *)lcp->ppp->buf;
- if (ntohs(hdr->len) < PPP_HEADERLEN) {
+ len = ntohs(hdr->len);
+ buf_len = lcp->ppp->buf_size;
+ if (len < PPP_HEADERLEN || len > lcp->ppp->buf_size - 2) {
log_ppp_warn("LCP: short packet received\n");
return;
}
@@ -796,7 +831,10 @@ static void lcp_recv(struct ppp_handler_t*h)
}
break;
case CONFNAK:
- lcp_recv_conf_nak(lcp, (uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN);
+ if (lcp_recv_conf_nak(lcp, (uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN)) {
+ ap_session_terminate(&lcp->ppp->ses, TERM_USER_ERROR, 0);
+ break;
+ }
if (lcp->fsm.recv_id != lcp->fsm.id)
break;
ppp_fsm_recv_conf_rej(&lcp->fsm);
@@ -827,25 +865,53 @@ static void lcp_recv(struct ppp_handler_t*h)
ppp_fsm_recv_code_rej_bad(&lcp->fsm);
break;
case ECHOREQ:
+ if (len < PPP_HDRLEN + 4 || buf_len < (int)(sizeof(*hdr) + 4)) {
+ log_ppp_warn("LCP: short EchoReq received\n");
+ break;
+ }
if (conf_ppp_verbose)
- log_ppp_debug("recv [LCP EchoReq id=%x <magic %08x>]\n", hdr->id, ntohl(*(uint32_t*)(hdr + 1)));
+ log_ppp_debug("recv [LCP EchoReq id=%x <magic %08x>]\n", hdr->id, u_read_be32(hdr + 1));
send_echo_reply(lcp);
break;
case ECHOREP:
+ if (len < PPP_HDRLEN + 4 || buf_len < (int)(sizeof(*hdr) + 4)) {
+ log_ppp_warn("LCP: short EchoRep received\n");
+ break;
+ }
lcp_recv_echo_repl(lcp, (uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN);
break;
case PROTOREJ:
- if (conf_ppp_verbose)
- log_ppp_info2("recv [LCP ProtoRej id=%x <%04x>]\n", hdr->id, ntohs(*(uint16_t*)(hdr + 1)));
- ppp_recv_proto_rej(lcp->ppp, ntohs(*(uint16_t *)(hdr + 1)));
+ if (conf_ppp_verbose) {
+ if (len < PPP_HDRLEN + 2 || buf_len < (int)(sizeof(*hdr) + 2)) {
+ log_ppp_warn("LCP: short ProtoRej received\n");
+ break;
+ }
+ log_ppp_info2("recv [LCP ProtoRej id=%x <%04x>]\n", hdr->id, u_read_be16(hdr + 1));
+ }
+ if (len < PPP_HDRLEN + 2 || buf_len < (int)(sizeof(*hdr) + 2))
+ break;
+ ppp_recv_proto_rej(lcp->ppp, u_read_be16(hdr + 1));
break;
case DISCARDREQ:
- if (conf_ppp_verbose)
- log_ppp_info2("recv [LCP DiscardReq id=%x <magic %08x>]\n", hdr->id, ntohl(*(uint32_t*)(hdr + 1)));
+ if (conf_ppp_verbose) {
+ if (len < PPP_HDRLEN + 4 || buf_len < (int)(sizeof(*hdr) + 4)) {
+ log_ppp_warn("LCP: short DiscardReq received\n");
+ break;
+ }
+ log_ppp_info2("recv [LCP DiscardReq id=%x <magic %08x>]\n", hdr->id, u_read_be32(hdr + 1));
+ }
break;
case IDENT:
if (conf_ppp_verbose) {
- term_msg = _strndup((char*)(hdr + 1) + 4, ntohs(hdr->len) - 4 - 4);
+ int id_len;
+ if (len < PPP_HDRLEN + 4 || buf_len < (int)(sizeof(*hdr) + 4)) {
+ log_ppp_warn("LCP: short Ident received\n");
+ break;
+ }
+ id_len = len - PPP_HDRLEN - 4;
+ if (buf_len < (int)(sizeof(*hdr) + 4 + id_len))
+ id_len = buf_len - sizeof(*hdr) - 4;
+ term_msg = _strndup((char*)(hdr + 1) + 4, id_len);
log_ppp_info2("recv [LCP Ident id=%x <%s>]\n", hdr->id, term_msg);
_free(term_msg);
}
diff --git a/accel-pppd/radius/CMakeLists.txt b/accel-pppd/radius/CMakeLists.txt
index a0c0d534..3ce72648 100644
--- a/accel-pppd/radius/CMakeLists.txt
+++ b/accel-pppd/radius/CMakeLists.txt
@@ -22,6 +22,7 @@ ENDIF ()
ADD_DEFINITIONS(-DDICTIONARY="${CMAKE_INSTALL_PREFIX}/share/accel-ppp/radius/dictionary")
ADD_LIBRARY(radius SHARED ${sources})
+TARGET_LINK_LIBRARIES(radius ${crypto_lib})
set_property(TARGET radius PROPERTY CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set_property(TARGET radius PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/accel-ppp)
diff --git a/accel-pppd/radius/acct.c b/accel-pppd/radius/acct.c
index 1cbd9296..0448fc95 100644
--- a/accel-pppd/radius/acct.c
+++ b/accel-pppd/radius/acct.c
@@ -5,43 +5,49 @@
#include <unistd.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
-#include "linux_ppp.h"
-#include "crypto.h"
+#include <openssl/md5.h>
+
+#include "linux_ppp.h"
#include "log.h"
#include "backup.h"
#include "ap_session_backup.h"
#include "iputils.h"
+#include "utils.h"
#include "radius_p.h"
#include "memdebug.h"
-#ifndef max
-#define max(x,y) ((x) > (y) ? (x) : (y))
-#endif
-
#define INTERIM_SAFE_TIME 10
-static int req_set_RA(struct rad_req_t *req, const char *secret)
+static int req_set_RA(struct rad_req_t *req)
{
+ char *secret;
MD5_CTX ctx;
- if (rad_packet_build(req->pack, req->RA))
+ secret = rad_server_secret_dup(req->serv);
+ if (!secret)
return -1;
+ if (rad_packet_build(req->pack, req->RA)) {
+ _free(secret);
+ return -1;
+ }
+
MD5_Init(&ctx);
MD5_Update(&ctx, req->pack->buf, req->pack->len);
MD5_Update(&ctx, secret, strlen(secret));
MD5_Final(req->pack->buf + 4, &ctx);
+ _free(secret);
+
return 0;
}
static int req_set_stat(struct rad_req_t *req, struct ap_session *ses)
{
- struct rtnl_link_stats64 stats;
struct timespec ts;
int ret = 0;
@@ -50,15 +56,15 @@ static int req_set_stat(struct rad_req_t *req, struct ap_session *ses)
else
clock_gettime(CLOCK_MONOTONIC, &ts);
- if (ap_session_read_stats(ses, &stats) == 0) {
- rad_packet_change_int(req->pack, NULL, "Acct-Input-Octets", (int) (stats.rx_bytes & UINT32_MAX));
- rad_packet_change_int(req->pack, NULL, "Acct-Output-Octets", (int) (stats.tx_bytes & UINT32_MAX));
- rad_packet_change_int(req->pack, NULL, "Acct-Input-Packets", (int) (stats.rx_packets & UINT32_MAX));
- rad_packet_change_int(req->pack, NULL, "Acct-Output-Packets", (int) (stats.tx_packets & UINT32_MAX));
- rad_packet_change_int(req->pack, NULL, "Acct-Input-Gigawords", (int) (stats.rx_bytes >> (sizeof(uint32_t) * 8)));
- rad_packet_change_int(req->pack, NULL, "Acct-Output-Gigawords", (int) (stats.tx_bytes >> (sizeof(uint32_t) * 8)));
- } else
- ret = -1;
+ if (!ses->terminating)
+ ret = ap_session_read_stats(ses, NULL);
+
+ rad_packet_change_int(req->pack, NULL, "Acct-Input-Octets", (int) (ses->acct_rx_bytes & UINT32_MAX));
+ rad_packet_change_int(req->pack, NULL, "Acct-Output-Octets", (int) (ses->acct_tx_bytes & UINT32_MAX));
+ rad_packet_change_int(req->pack, NULL, "Acct-Input-Packets", (int) (ses->acct_rx_packets & UINT32_MAX));
+ rad_packet_change_int(req->pack, NULL, "Acct-Output-Packets", (int) (ses->acct_tx_packets & UINT32_MAX));
+ rad_packet_change_int(req->pack, NULL, "Acct-Input-Gigawords", (int) (ses->acct_rx_bytes >> (sizeof(uint32_t) * 8)));
+ rad_packet_change_int(req->pack, NULL, "Acct-Output-Gigawords", (int) (ses->acct_tx_bytes >> (sizeof(uint32_t) * 8)));
rad_packet_change_int(req->pack, NULL, "Acct-Session-Time", ts.tv_sec - ses->start_time);
@@ -70,7 +76,7 @@ static void rad_acct_sent(struct rad_req_t *req, int res)
if (res)
return;
- __sync_add_and_fetch(&req->serv->stat_interim_sent, 1);
+ rad_server_stat_interim_sent(req->serv);
if (!req->hnd.tpd)
triton_md_register_handler(req->rpd->ses->ctrl->ctx, &req->hnd);
@@ -88,8 +94,7 @@ static void rad_acct_recv(struct rad_req_t *req)
int dt = (req->reply->tv.tv_sec - req->pack->tv.tv_sec) * 1000 +
(req->reply->tv.tv_nsec - req->pack->tv.tv_nsec) / 1000000;
- stat_accm_add(req->serv->stat_interim_query_1m, dt);
- stat_accm_add(req->serv->stat_interim_query_5m, dt);
+ rad_server_stat_interim_query(req->serv, dt);
if (req->timeout.tpd)
triton_timer_del(&req->timeout);
@@ -109,9 +114,7 @@ static void rad_acct_timeout(struct triton_timer_t *t)
rad_server_req_exit(req);
rad_server_timeout(req->serv);
- __sync_add_and_fetch(&req->serv->stat_interim_lost, 1);
- stat_accm_add(req->serv->stat_interim_lost_1m, 1);
- stat_accm_add(req->serv->stat_interim_lost_5m, 1);
+ rad_server_stat_interim_lost(req->serv);
if (conf_acct_timeout == 0) {
triton_timer_del(t);
@@ -183,7 +186,7 @@ static void rad_acct_interim_update(struct triton_timer_t *t)
rpd->acct_req->pack->id++;
if (!rpd->acct_req->before_send)
- req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret);
+ req_set_RA(rpd->acct_req);
rpd->acct_req->timeout.expire_tv.tv_sec = conf_timeout;
rpd->acct_req->try = 0;
@@ -215,7 +218,7 @@ static int rad_acct_before_send(struct rad_req_t *req)
clock_gettime(CLOCK_MONOTONIC, &ts);
rad_packet_change_int(req->pack, NULL, "Acct-Delay-Time", ts.tv_sec - req->ts + conf_acct_delay_start);
- req_set_RA(req, req->serv->secret);
+ req_set_RA(req);
return 0;
}
@@ -227,7 +230,7 @@ static void rad_acct_start_sent(struct rad_req_t *req, int res)
return;
}
- __sync_add_and_fetch(&req->serv->stat_acct_sent, 1);
+ rad_server_stat_acct_sent(req->serv);
if (!req->hnd.tpd)
triton_md_register_handler(req->rpd->ses->ctrl->ctx, &req->hnd);
@@ -246,8 +249,7 @@ static void rad_acct_start_recv(struct rad_req_t *req)
int dt = (req->reply->tv.tv_sec - req->pack->tv.tv_sec) * 1000 +
(req->reply->tv.tv_nsec - req->pack->tv.tv_nsec) / 1000000;
- stat_accm_add(req->serv->stat_acct_query_1m, dt);
- stat_accm_add(req->serv->stat_acct_query_5m, dt);
+ rad_server_stat_acct_query(req->serv, dt);
triton_timer_del(&req->timeout);
@@ -289,9 +291,7 @@ static void rad_acct_start_timeout(struct triton_timer_t *t)
rad_server_timeout(req->serv);
- __sync_add_and_fetch(&req->serv->stat_acct_lost, 1);
- stat_accm_add(req->serv->stat_acct_lost_1m, 1);
- stat_accm_add(req->serv->stat_acct_lost_5m, 1);
+ rad_server_stat_acct_lost(req->serv);
if (req->before_send)
req->pack->id++;
@@ -314,7 +314,7 @@ static int __rad_acct_start(struct radius_pd_t *rpd)
if (conf_acct_delay_time)
req->before_send = rad_acct_before_send;
- else if (req_set_RA(req, req->serv->secret))
+ else if (req_set_RA(req))
goto out_err;
req->recv = rad_acct_start_recv;
@@ -370,11 +370,21 @@ static void rad_acct_stop_sent(struct rad_req_t *req, int res)
rpd->acct_req = NULL;
} else if (req->rpd)
rad_acct_stop_defer(req->rpd);
+ else {
+ /* deferred request: the timeout timer is one-shot and
+ * nobody else references this request, re-arm it to
+ * retry later, otherwise the request and its socket
+ * leak */
+ if (req->timeout.tpd)
+ triton_timer_mod(&req->timeout, 0);
+ else
+ triton_timer_add(NULL, &req->timeout, 0);
+ }
return;
}
- __sync_add_and_fetch(&req->serv->stat_acct_sent, 1);
+ rad_server_stat_acct_sent(req->serv);
if (!req->hnd.tpd)
triton_md_register_handler(req->rpd ? req->rpd->ses->ctrl->ctx : NULL, &req->hnd);
@@ -393,8 +403,7 @@ static void rad_acct_stop_recv(struct rad_req_t *req)
int dt = (req->reply->tv.tv_sec - req->pack->tv.tv_sec) * 1000 +
(req->reply->tv.tv_nsec - req->pack->tv.tv_nsec) / 1000000;
- stat_accm_add(req->serv->stat_acct_query_1m, dt);
- stat_accm_add(req->serv->stat_acct_query_5m, dt);
+ rad_server_stat_acct_query(req->serv, dt);
rad_req_free(req);
@@ -415,25 +424,34 @@ static void rad_acct_stop_timeout(struct triton_timer_t *t)
rad_server_timeout(req->serv);
rad_server_req_exit(req);
- __sync_add_and_fetch(&req->serv->stat_acct_lost, 1);
- stat_accm_add(req->serv->stat_acct_lost_1m, 1);
- stat_accm_add(req->serv->stat_acct_lost_5m, 1);
+ rad_server_stat_acct_lost(req->serv);
if (req->before_send)
req->pack->id++;
}
- if (req->try == conf_max_try) {
+ if (req->try >= conf_max_try) {
+ if (req->rpd)
+ req->rpd->acct_req = NULL;
rad_req_free(req);
return;
}
if (rad_req_send(req)) {
if (ap_shutdown) {
+ if (req->rpd)
+ req->rpd->acct_req = NULL;
rad_req_free(req);
return;
}
- req->try = 0;
+ /* no server available at the moment; the timeout timer is
+ * one-shot, re-arm it to retry later, otherwise the request
+ * and its socket leak; failed attempts count towards
+ * conf_max_try so the request is freed above eventually */
+ if (req->timeout.tpd)
+ triton_timer_mod(&req->timeout, 0);
+ else
+ triton_timer_add(req->rpd ? req->rpd->ses->ctrl->ctx : NULL, &req->timeout, 0);
}
}
@@ -529,10 +547,10 @@ int rad_acct_stop(struct radius_pd_t *rpd)
rad_packet_change_val(req->pack, NULL, "Acct-Status-Type", "Stop");
req_set_stat(req, rpd->ses);
- req_set_RA(req, req->serv->secret);
+ req_set_RA(req);
req->recv = rad_acct_stop_recv;
- req->timeout.expire = rad_acct_start_timeout;
+ req->timeout.expire = rad_acct_stop_timeout;
req->timeout.expire_tv.tv_sec = conf_timeout;
req->sent = rad_acct_stop_sent;
req->log = conf_verbose ? log_ppp_info1 : NULL;
@@ -544,4 +562,3 @@ int rad_acct_stop(struct radius_pd_t *rpd)
return 0;
}
-
diff --git a/accel-pppd/radius/auth.c b/accel-pppd/radius/auth.c
index 05058547..d2cb9803 100644
--- a/accel-pppd/radius/auth.c
+++ b/accel-pppd/radius/auth.c
@@ -1,7 +1,8 @@
#include <stdlib.h>
#include <string.h>
-#include "crypto.h"
+#include <openssl/md5.h>
+#include <openssl/sha.h>
#include "triton.h"
#include "mempool.h"
@@ -21,6 +22,7 @@ static int decrypt_chap_mppe_keys(struct rad_req_t *req, struct rad_attr_t *attr
uint8_t md5[MD5_DIGEST_LENGTH];
uint8_t sha1[SHA_DIGEST_LENGTH];
uint8_t plain[32];
+ char *secret;
int i;
if (attr->len != 32) {
@@ -28,10 +30,14 @@ static int decrypt_chap_mppe_keys(struct rad_req_t *req, struct rad_attr_t *attr
return -1;
}
+ secret = rad_server_secret_dup(req->serv);
+ if (!secret)
+ return -1;
+
memcpy(plain, attr->val.octets, 32);
MD5_Init(&md5_ctx);
- MD5_Update(&md5_ctx, req->serv->secret, strlen(req->serv->secret));
+ MD5_Update(&md5_ctx, secret, strlen(secret));
MD5_Update(&md5_ctx, req->pack->buf + 4, 16);
MD5_Final(md5, &md5_ctx);
@@ -39,7 +45,7 @@ static int decrypt_chap_mppe_keys(struct rad_req_t *req, struct rad_attr_t *attr
plain[i] ^= md5[i];
MD5_Init(&md5_ctx);
- MD5_Update(&md5_ctx, req->serv->secret, strlen(req->serv->secret));
+ MD5_Update(&md5_ctx, secret, strlen(secret));
MD5_Update(&md5_ctx, attr->val.octets, 16);
MD5_Final(md5, &md5_ctx);
@@ -53,6 +59,7 @@ static int decrypt_chap_mppe_keys(struct rad_req_t *req, struct rad_attr_t *attr
SHA1_Final(sha1, &sha1_ctx);
memcpy(key, sha1, 16);
+ _free(secret);
return 0;
}
@@ -62,6 +69,7 @@ static int decrypt_mppe_key(struct rad_req_t *req, struct rad_attr_t *attr, uint
MD5_CTX md5_ctx;
uint8_t md5[16];
uint8_t plain[32];
+ char *secret;
int i;
if (attr->len != 34) {
@@ -74,8 +82,12 @@ static int decrypt_mppe_key(struct rad_req_t *req, struct rad_attr_t *attr, uint
return -1;
}
+ secret = rad_server_secret_dup(req->serv);
+ if (!secret)
+ return -1;
+
MD5_Init(&md5_ctx);
- MD5_Update(&md5_ctx, req->serv->secret, strlen(req->serv->secret));
+ MD5_Update(&md5_ctx, secret, strlen(secret));
MD5_Update(&md5_ctx, req->pack->buf + 4, 16);
MD5_Update(&md5_ctx, attr->val.octets, 2);
MD5_Final(md5, &md5_ctx);
@@ -87,17 +99,19 @@ static int decrypt_mppe_key(struct rad_req_t *req, struct rad_attr_t *attr, uint
if (plain[0] != 16) {
log_ppp_warn("radius: %s: incorrect key length (%i)\n", attr->attr->name, plain[0]);
+ _free(secret);
return -1;
}
MD5_Init(&md5_ctx);
- MD5_Update(&md5_ctx, req->serv->secret, strlen(req->serv->secret));
+ MD5_Update(&md5_ctx, secret, strlen(secret));
MD5_Update(&md5_ctx, attr->val.octets + 2, 16);
MD5_Final(md5, &md5_ctx);
plain[16] ^= md5[0];
memcpy(key, plain + 1, 16);
+ _free(secret);
return 0;
}
@@ -171,8 +185,7 @@ static void rad_auth_recv(struct rad_req_t *req)
triton_timer_del(&req->timeout);
dt = (req->reply->tv.tv_sec - req->pack->tv.tv_sec) * 1000 + (req->reply->tv.tv_nsec - req->pack->tv.tv_nsec) / 1000000;
- stat_accm_add(req->serv->stat_auth_query_1m, dt);
- stat_accm_add(req->serv->stat_auth_query_5m, dt);
+ rad_server_stat_auth_query(req->serv, dt);
if (pack->code == CODE_ACCESS_ACCEPT) {
if (rad_proc_attrs(req)) {
@@ -207,9 +220,7 @@ static void rad_auth_timeout(struct triton_timer_t *t)
rad_server_timeout(req->serv);
- __sync_add_and_fetch(&req->serv->stat_auth_lost, 1);
- stat_accm_add(req->serv->stat_auth_lost_1m, 1);
- stat_accm_add(req->serv->stat_auth_lost_5m, 1);
+ rad_server_stat_auth_lost(req->serv);
if (rad_req_send(req))
rad_auth_finalize(req->rpd, PWDB_DENIED);
@@ -222,7 +233,7 @@ static void rad_auth_sent(struct rad_req_t *req, int res)
return;
}
- __sync_add_and_fetch(&req->serv->stat_auth_sent, 1);
+ rad_server_stat_auth_sent(req->serv);
if (!req->hnd.tpd)
triton_md_register_handler(req->rpd->ses->ctrl->ctx, &req->hnd);
@@ -277,11 +288,17 @@ int rad_auth_pap(struct radius_pd_t *rpd, const char *username, va_list args)
const char *passwd = va_arg(args, const char *);
uint8_t *epasswd;
int epasswd_len;
+ char *secret;
if (!req)
return PWDB_DENIED;
- epasswd = encrypt_password(passwd, req->serv->secret, req->RA, &epasswd_len);
+ secret = rad_server_secret_dup(req->serv);
+ if (!secret)
+ return PWDB_DENIED;
+
+ epasswd = encrypt_password(passwd, secret, req->RA, &epasswd_len);
+ _free(secret);
if (!epasswd)
return PWDB_DENIED;
@@ -433,7 +450,10 @@ static int rad_auth_mschap_v2_recv(struct rad_req_t *req)
if (req->reply->code == CODE_ACCESS_ACCEPT) {
ra = rad_packet_find_attr(req->reply, "Microsoft", "MS-CHAP2-Success");
if (!ra) {
- log_error("radius:auth:mschap-v2: 'MS-CHAP-Success' not found in radius response\n");
+ log_error("radius:auth:mschap-v2: 'MS-CHAP2-Success' not found in radius response\n");
+ return -1;
+ } else if (ra->len < 43) {
+ log_error("radius:auth:mschap-v2: 'MS-CHAP2-Success' too short (%i)\n", ra->len);
return -1;
} else
memcpy(rpd->auth_ctx->authenticator, ra->val.octets + 3, 40);
@@ -508,4 +528,3 @@ int rad_auth_null(struct radius_pd_t *rpd, const char *username, va_list args)
return PWDB_WAIT;
}
-
diff --git a/accel-pppd/radius/backup.c b/accel-pppd/radius/backup.c
index 6d4db636..bfca0e49 100644
--- a/accel-pppd/radius/backup.c
+++ b/accel-pppd/radius/backup.c
@@ -6,6 +6,7 @@
#include "log.h"
#include "memdebug.h"
+#include "utils.h"
#include "backup.h"
#include "ap_session_backup.h"
@@ -22,6 +23,7 @@
#define RAD_TAG_ACCT_SERVER_ADDR 9
#define RAD_TAG_ACCT_SERVER_PORT 10
#define RAD_TAG_IDLE_TIMEOUT 11
+
#define RAD_TAG_ACCT_USERNAME 12
@@ -94,10 +96,10 @@ static void restore_ipv4_addr(struct ap_session *ses)
list_for_each_entry(tag, &m->tag_list, entry) {
switch (tag->id) {
case SES_TAG_IPV4_ADDR:
- ses->ipv4->addr = *(in_addr_t *)tag->data;
+ ses->ipv4->addr = u_read_native32(tag->data);
break;
case SES_TAG_IPV4_PEER_ADDR:
- ses->ipv4->peer_addr = *(in_addr_t *)tag->data;
+ ses->ipv4->peer_addr = u_read_native32(tag->data);
break;
}
}
@@ -121,16 +123,16 @@ void radius_restore_session(struct ap_session *ses, struct radius_pd_t *rpd)
list_for_each_entry(tag, &m->tag_list, entry) {
switch (tag->id) {
case RAD_TAG_INTERIM_INTERVAL:
- rpd->acct_interim_interval = *(uint32_t *)tag->data;
+ rpd->acct_interim_interval = u_read_native32(tag->data);
break;
case RAD_TAG_INTERIM_JITTER:
- rpd->acct_interim_jitter = *(uint32_t *)tag->data;
+ rpd->acct_interim_jitter = u_read_native32(tag->data);
break;
case RAD_TAG_SESSION_TIMEOUT:
- rpd->session_timeout.expire_tv.tv_sec = *(uint64_t *)tag->data - ses->start_time;
+ rpd->session_timeout.expire_tv.tv_sec = u_read_native64(tag->data) - ses->start_time;
break;
case RAD_TAG_IDLE_TIMEOUT:
- rpd->idle_timeout.period = (*(uint32_t *)tag->data) * 1000;
+ rpd->idle_timeout.period = u_read_native32(tag->data) * 1000;
break;
case RAD_TAG_IPV4_ADDR:
ses->ipv4 = &rpd->ipv4_addr;
@@ -150,16 +152,16 @@ void radius_restore_session(struct ap_session *ses, struct radius_pd_t *rpd)
rpd->attr_state_len = tag->size;
break;
case RAD_TAG_TERMINATION_ACTION:
- rpd->termination_action = *(uint32_t *)tag->data;
+ rpd->termination_action = u_read_native32(tag->data);
break;
case RAD_TAG_ACCT_USERNAME:
rpd->acct_username = _strndup(tag->data, tag->size);
break;
case RAD_TAG_ACCT_SERVER_ADDR:
- acct_addr = *(in_addr_t *)tag->data;
+ acct_addr = u_read_native32(tag->data);
break;
case RAD_TAG_ACCT_SERVER_PORT:
- acct_port = *(uint16_t *)tag->data;
+ acct_port = u_read_native16(tag->data);
break;
}
}
@@ -182,4 +184,3 @@ static void init(void)
}
DEFINE_INIT(100, init);
-
diff --git a/accel-pppd/radius/dict.c b/accel-pppd/radius/dict.c
index 145c5da2..c181c7cd 100644
--- a/accel-pppd/radius/dict.c
+++ b/accel-pppd/radius/dict.c
@@ -92,7 +92,7 @@ static int dict_load(const char *fname)
continue;
r = split(buf, ptr);
- if (*ptr[r - 1] == '#')
+ if (r > 0 && *ptr[r - 1] == '#')
r--;
if (!strcmp(buf, "VENDOR")) {
diff --git a/accel-pppd/radius/dm_coa.c b/accel-pppd/radius/dm_coa.c
index e88cc347..df8cdf3e 100644
--- a/accel-pppd/radius/dm_coa.c
+++ b/accel-pppd/radius/dm_coa.c
@@ -10,7 +10,7 @@
#include <netinet/in.h>
#include <sys/socket.h>
-#include "crypto.h"
+#include <openssl/md5.h>
#include "triton.h"
#include "events.h"
@@ -106,6 +106,7 @@ static int dm_coa_send_nak(int fd, struct rad_packet_t *req, struct sockaddr_in
if (err_code)
rad_packet_add_int(reply, NULL, "Error-Cause", err_code);
+ // TODO: We need to add Message-Authenticator attribute here
if (rad_packet_build(reply, RA)) {
rad_packet_free(reply);
return -1;
@@ -266,6 +267,15 @@ static int dm_coa_read(struct triton_md_handler_t *h)
rad_packet_print(pack, NULL, log_debug);
}
+ if (rad_dae_src_check(addr.sin_addr.s_addr)) {
+ char ipbuf[INET_ADDRSTRLEN];
+ const char *ipstr;
+
+ ipstr = inet_ntop(AF_INET, &addr.sin_addr, ipbuf, sizeof(ipbuf));
+ log_warn("radius:dm_coa: source %s not allowed\n", ipstr ? ipstr : "unknown");
+ goto out_err_no_reply;
+ }
+
if (dm_coa_check_RA(pack, conf_dm_coa_secret)) {
log_warn("radius:dm_coa: RA validation failed\n");
goto out_err_no_reply;
diff --git a/accel-pppd/radius/packet.c b/accel-pppd/radius/packet.c
index 79007036..4a0ab244 100644
--- a/accel-pppd/radius/packet.c
+++ b/accel-pppd/radius/packet.c
@@ -9,13 +9,29 @@
#include <linux/mman.h>
#include <arpa/inet.h>
+/*
+ * Suppress OpenSSL 3.0 deprecation warnings for the HMAC 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/hmac.h>
+#include <openssl/evp.h>
+
#include "log.h"
#include "mempool.h"
-
#include "radius_p.h"
+#include "attr_defs.h"
+#include "utils.h"
#include "memdebug.h"
+#define HMAC_MD5_LEN 16
+/* Radius header + attribute: type + length */
+#define PACKET_SIGNED_OFFSET (20 + 2)
+
static mempool_t packet_pool;
static mempool_t attr_pool;
static mempool_t buf_pool;
@@ -47,6 +63,34 @@ void print_buf(uint8_t *buf,int size)
printf("\n");
}
+
+int hmac_md5(const uint8_t *key, size_t key_len,
+ const uint8_t *data, size_t data_len,
+ uint8_t out[HMAC_MD5_LEN])
+{
+ unsigned int len = 0;
+ HMAC_CTX *ctx = HMAC_CTX_new();
+ if (!ctx)
+ return -1;
+
+ if (HMAC_Init_ex(ctx, key, (int)key_len, EVP_md5(), NULL) != 1)
+ goto err;
+
+ if (HMAC_Update(ctx, data, data_len) != 1)
+ goto err;
+
+ if (HMAC_Final(ctx, out, &len) != 1 || len != HMAC_MD5_LEN)
+ goto err;
+
+ HMAC_CTX_free(ctx);
+ return 0;
+
+err:
+ HMAC_CTX_free(ctx);
+ return -1;
+}
+
+
int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA)
{
struct rad_attr_t *attr;
@@ -68,20 +112,20 @@ int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA)
*ptr = pack->code; ptr++;
*ptr = pack->id; ptr++;
- *(uint16_t*)ptr = htons(pack->len); ptr+= 2;
+ u_write_be16(ptr, pack->len); ptr += 2;
memcpy(ptr, RA, 16); ptr+=16;
list_for_each_entry(attr, &pack->attrs, entry) {
if (attr->vendor) {
*ptr = 26; ptr++;
*ptr = attr->len + 2 + 6; ptr++;
- *(uint32_t *)ptr = htonl(attr->vendor->id); ptr+=4;
+ u_write_be32(ptr, attr->vendor->id); ptr += 4;
}
*ptr = attr->attr->id; ptr++;
*ptr = attr->len + 2; ptr++;
switch(attr->attr->type) {
case ATTR_TYPE_INTEGER:
- *(uint32_t*)ptr = htonl(attr->val.integer);
+ u_write_be32(ptr, attr->val.integer);
break;
case ATTR_TYPE_OCTETS:
case ATTR_TYPE_STRING:
@@ -93,7 +137,7 @@ int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA)
memcpy(ptr, &attr->val, attr->len);
break;
case ATTR_TYPE_DATE:
- *(uint32_t*)ptr = htonl(attr->val.date);
+ u_write_be32(ptr, attr->val.date);
break;
case ATTR_TYPE_IPV6PREFIX:
ptr[0] = 0;
@@ -127,9 +171,8 @@ int rad_packet_recv(int fd, struct rad_packet_t **p, struct sockaddr_in *addr)
if (!pack)
return 0;
- //ptr = mmap(NULL, REQ_LENGTH_MAX, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
ptr = mempool_alloc(buf_pool);
- if (ptr == MAP_FAILED) {
+ if (!ptr) {
log_emerg("radius:packet: out of memory\n");
goto out_err;
}
@@ -139,7 +182,7 @@ int rad_packet_recv(int fd, struct rad_packet_t **p, struct sockaddr_in *addr)
while (1) {
if (addr)
- n = recvfrom(fd, pack->buf, REQ_LENGTH_MAX, 0, addr, &addr_len);
+ n = recvfrom(fd, pack->buf, REQ_LENGTH_MAX, 0, (struct sockaddr *)addr, &addr_len);
else
n = read(fd, pack->buf, REQ_LENGTH_MAX);
if (n < 0) {
@@ -162,17 +205,22 @@ int rad_packet_recv(int fd, struct rad_packet_t **p, struct sockaddr_in *addr)
pack->code = *ptr; ptr++;
pack->id = *ptr; ptr++;
- pack->len = ntohs(*(uint16_t*)ptr); ptr += 2;
+ pack->len = u_read_be16(ptr); ptr += 2;
- if (pack->len > n) {
+ if (pack->len < 20 || pack->len > n) {
log_ppp_warn("radius:packet: short packet received %i, expected %i\n", pack->len, n);
goto out_err;
}
ptr += 16;
- n -= 20;
+ n = pack->len - 20;
while (n>0) {
+ if (n < 2) {
+ log_ppp_warn("radius:packet: truncated attribute header received\n");
+ goto out_err;
+ }
+
id = *ptr; ptr++;
len = *ptr - 2; ptr++;
if (len < 0) {
@@ -184,20 +232,28 @@ int rad_packet_recv(int fd, struct rad_packet_t **p, struct sockaddr_in *addr)
goto out_err;
}
if (id == 26) {
- vendor_id = ntohl(*(uint32_t *)ptr);
+ if (len < 4) {
+ log_ppp_warn("radius:packet: vendor attribute too short (%i)\n", len);
+ goto out_err;
+ }
+ vendor_id = u_read_be32(ptr);
vendor = rad_dict_find_vendor_id(vendor_id);
if (vendor) {
+ if (len < 4 + vendor->tag + vendor->len) {
+ log_ppp_warn("radius:packet: vendor %i attribute too short (%i)\n", vendor_id, len);
+ goto out_err;
+ }
ptr += 4;
if (vendor->tag == 2)
- id = (uint16_t)ntohs(*(uint16_t *)ptr);
+ id = u_read_be16(ptr);
else
id = *ptr;
ptr += vendor->tag;
if (vendor->len == 2)
- len = (uint16_t)ntohs(*(uint16_t *)ptr);
+ len = u_read_be16(ptr);
else
len = *ptr;
@@ -245,16 +301,23 @@ int rad_packet_recv(int fd, struct rad_packet_t **p, struct sockaddr_in *addr)
attr->val.octets = ptr;
break;
case ATTR_TYPE_INTEGER:
- if (len != da->size)
+ if (len != da->size) {
log_ppp_warn("radius:packet: attribute %s has invalid length %i (must be %i)\n", da->name, len, da->size);
- case ATTR_TYPE_DATE:
+ break;
+ }
if (len == 4)
- attr->val.integer = ntohl(*(uint32_t*)ptr);
+ attr->val.integer = u_read_be32(ptr);
else if (len == 2)
- attr->val.integer = ntohs(*(uint16_t*)ptr);
+ attr->val.integer = u_read_be16(ptr);
else if (len == 1)
attr->val.integer = *ptr;
break;
+ case ATTR_TYPE_DATE:
+ if (len == 4)
+ attr->val.integer = u_read_be32(ptr);
+ else
+ log_ppp_warn("radius:packet: attribute %s has invalid length %i (must be 4)\n", da->name, len);
+ break;
case ATTR_TYPE_IPADDR:
case ATTR_TYPE_IFID:
case ATTR_TYPE_IPV6ADDR:
@@ -302,6 +365,9 @@ void rad_packet_free(struct rad_packet_t *pack)
mempool_free(pack->buf);
//munmap(pack->buf, REQ_LENGTH_MAX);
+ if (pack->secret)
+ _free(pack->secret);
+
while(!list_empty(&pack->attrs)) {
attr = list_entry(pack->attrs.next, typeof(*attr), entry);
list_del(&attr->entry);
@@ -811,9 +877,21 @@ int rad_packet_send(struct rad_packet_t *pack, int fd, struct sockaddr_in *addr)
clock_gettime(CLOCK_MONOTONIC, &pack->tv);
+ if (pack->secret && pack->message_authenticator) {
+ uint8_t hmac[HMAC_MD5_LEN];
+ uint8_t *ptr = pack->buf;
+ uint8_t *hmac_ptr = ptr + PACKET_SIGNED_OFFSET;
+ if (hmac_md5((const uint8_t *)pack->secret, strlen((const char *)pack->secret), pack->buf, pack->len, hmac) < 0) {
+ log_emerg("radius:packet: failed to calculate HMAC\n");
+ return -1;
+ }
+ memcpy(hmac_ptr, hmac, HMAC_MD5_LEN);
+ }
+
+
while (1) {
if (addr)
- n = sendto(fd, pack->buf, pack->len, 0, addr, sizeof(*addr));
+ n = sendto(fd, pack->buf, pack->len, 0, (struct sockaddr *)addr, sizeof(*addr));
else
n = write(fd, pack->buf, pack->len);
if (n < 0) {
diff --git a/accel-pppd/radius/radius.c b/accel-pppd/radius/radius.c
index 2406ba04..fa77a916 100644
--- a/accel-pppd/radius/radius.c
+++ b/accel-pppd/radius/radius.c
@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
+#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
@@ -58,102 +59,314 @@ static int conf_strip_realm;
const char *conf_attr_tunnel_type;
int conf_acct_delay_start;
+int conf_blast_protection;
+int conf_framed_route_strict;
static LIST_HEAD(sessions);
static pthread_rwlock_t sessions_lock = PTHREAD_RWLOCK_INITIALIZER;
+struct dae_allow_range {
+ struct list_head entry;
+ uint32_t begin;
+ uint32_t end;
+};
+
+static struct list_head *dae_allow_ranges;
+static pthread_rwlock_t dae_allow_lock = PTHREAD_RWLOCK_INITIALIZER;
+
static void *pd_key;
static struct ipdb_t ipdb;
static mempool_t rpd_pool;
static mempool_t auth_ctx_pool;
-static void parse_framed_route(struct radius_pd_t *rpd, const char *attr)
+static int rad_add_framed_ipv6_route(const char *str, struct radius_pd_t *rpd);
+
+static int ipv4_mask_to_prefix(struct in_addr mask, int *plen)
{
- char str[32];
- char *ptr;
- long int prio = 0;
- in_addr_t dst;
- in_addr_t gw;
- int mask;
- struct framed_route *fr;
+ uint32_t m = ntohl(mask.s_addr);
+ uint32_t inv = ~m;
+ int p = 0;
- ptr = strchr(attr, '/');
- if (ptr && ptr - attr > 16)
- goto out_err;
+ if (inv & (inv + 1))
+ return -1;
- if (ptr) {
- memcpy(str, attr, ptr - attr);
- str[ptr - attr] = 0;
- } else {
- ptr = strchr(attr, ' ');
- if (ptr) {
- memcpy(str, attr, ptr - attr);
- str[ptr - attr] = 0;
- } else
- strcpy(str, attr);
+ while (m & 0x80000000) {
+ p++;
+ m <<= 1;
}
- dst = inet_addr(str);
- if (dst == INADDR_NONE)
- goto out_err;
+ *plen = p;
+ return 0;
+}
+
+static int parse_framed_route_v4(const char *str, struct framed_route *fr)
+{
+ const char *ptr;
+ size_t len;
+ struct in_addr dst;
+ struct in_addr gw;
+ struct in_addr mask_addr;
+ uint8_t plen;
+ uint32_t prio;
+ uint32_t mask = 0;
+ uint32_t addr_hbo;
+
+ // Take a steady breath and skip leading RFC-style spaces so everything starts clean.
+ ptr = str + u_parse_spaces(str);
+
+ len = u_parse_ip4cidr(ptr, &dst, &plen);
+ if (len) {
+ // Happy path: CIDR tells us exactly what we need.
+ fr->dst = dst.s_addr;
+ fr->mask = plen;
+ mask = plen ? (0xffffffffu << (32 - plen)) : 0;
+ ptr += len;
+ } else {
+ // If CIDR didn't show up, we gently switch to plain IPv4 and optional mask.
+ len = u_parse_ip4addr(ptr, &dst);
+ if (!len)
+ return -1;
+ fr->dst = dst.s_addr;
+ ptr += len;
- if (ptr) {
if (*ptr == '/') {
- char *ptr2;
- for (ptr2 = ++ptr; *ptr2 && *ptr2 != '.' && *ptr2 != ' '; ptr2++);
- if (*ptr2 == '.' && ptr2 - ptr <= 16) {
- in_addr_t a;
- memcpy(str, ptr, ptr2 - ptr);
- str[ptr2 - ptr] = 0;
- a = ntohl(inet_addr(str));
- if (a == INADDR_NONE)
- goto out_err;
- mask = 33 - htonl(inet_addr(str));
- if (~((1<<(32 - mask)) - 1) != a)
- goto out_err;
- } else if (*ptr2 == ' ' || *ptr2 == 0) {
- char *ptr3;
- mask = strtol(ptr, &ptr3, 10);
- if (mask < 0 || mask > 32 || ptr3 != ptr2)
- goto out_err;
- } else
- goto out_err;
+ ptr++;
+ len = u_parse_ip4addr(ptr, &mask_addr);
+ if (len) {
+ if (ipv4_mask_to_prefix(mask_addr, &fr->mask))
+ return -1;
+ mask = ntohl(mask_addr.s_addr);
+ ptr += len;
+ } else {
+ len = u_parse_u8(ptr, &plen);
+ if (!len || plen > 32)
+ return -1;
+ fr->mask = plen;
+ mask = plen ? (0xffffffffu << (32 - plen)) : 0;
+ ptr += len;
+ }
} else
- mask = 32;
-
- for (++ptr; *ptr && *ptr != ' '; ptr++);
- if (*ptr == ' ')
- gw = inet_addr(ptr + 1);
- else if (*ptr == 0)
- gw = 0;
- else
- goto out_err;
-
- /* Parse priority, if any */
- if (*ptr) {
- for (++ptr; *ptr && *ptr != ' '; ptr++);
- if (*ptr == ' ')
- if (u_readlong(&prio, ptr + 1, 0, UINT32_MAX) < 0)
- goto out_err;
- }
- } else {
- mask = 32;
- gw = 0;
+ fr->mask = 32;
+ }
+
+ if (!mask)
+ mask = fr->mask ? (0xffffffffu << (32 - fr->mask)) : 0;
+
+ if (conf_framed_route_strict) {
+ addr_hbo = ntohl(fr->dst);
+ if (addr_hbo & ~mask)
+ return -1;
+ }
+
+ // If the string ends here, we can relax: no gateway or metric specified.
+ ptr += u_parse_spaces(ptr);
+ if (u_parse_endstr(ptr)) {
+ fr->gw = 0;
+ fr->prio = 0;
+ return 0;
}
- fr = _malloc(sizeof (*fr));
- fr->dst = dst;
- fr->mask = mask;
- fr->gw = gw;
+ len = u_parse_ip4addr(ptr, &gw);
+ if (!len)
+ return -1;
+ fr->gw = gw.s_addr;
+ ptr += len;
+
+ ptr += u_parse_spaces(ptr);
+ if (u_parse_endstr(ptr)) {
+ fr->prio = 0;
+ return 0;
+ }
+
+ len = u_parse_u32(ptr, &prio);
+ if (!len)
+ return -1;
+ ptr += len;
+
+ if (!u_parse_endstr(ptr))
+ return -1;
+
fr->prio = prio;
+ return 0;
+}
+
+static void parse_framed_route(struct radius_pd_t *rpd, const char *attr)
+{
+ struct framed_route *fr;
+
+ /* RFC 2865: Framed-Route is IPv4-only and uses spaces; IPv6 lives in Framed-IPv6-Route. */
+ if (strchr(attr, ':')) {
+ log_ppp_warn("radius: Framed-Route is IPv4-only per RFC 2865, use Framed-IPv6-Route for %s\n", attr);
+ return;
+ }
+
+ fr = _malloc(sizeof(*fr));
+ if (!fr)
+ goto out_err;
+ memset(fr, 0, sizeof(*fr));
+
+ if (parse_framed_route_v4(attr, fr) < 0)
+ goto out_err_free;
+
fr->next = rpd->fr;
rpd->fr = fr;
return;
+out_err_free:
+ _free(fr);
out_err:
- log_ppp_warn("radius: failed to parse Framed-Route=%s\n", attr);
+ log_ppp_warn("radius: failed to parse Framed-Route=\"%s\" (expected \"dst[/mask] gw [metric]\"; check destination, mask, gateway, and metric fields)\n", attr);
+}
+
+static char *trim_spaces(char *str)
+{
+ char *end;
+
+ while (isspace((unsigned char)*str))
+ ++str;
+
+ if (*str == '\0')
+ return str;
+
+ end = str + strlen(str) - 1;
+ while (end > str && isspace((unsigned char)*end)) {
+ *end = '\0';
+ --end;
+ }
+
+ return str;
+}
+
+static void dae_allow_clear(void)
+{
+ struct dae_allow_range *range;
+
+ if (!dae_allow_ranges)
+ return;
+
+ while (!list_empty(dae_allow_ranges)) {
+ range = list_first_entry(dae_allow_ranges, typeof(*range), entry);
+ list_del(&range->entry);
+ _free(range);
+ }
+
+ _free(dae_allow_ranges);
+ dae_allow_ranges = NULL;
+}
+
+static int dae_allow_add(uint32_t begin, uint32_t end)
+{
+ struct dae_allow_range *range;
+
+ if (!dae_allow_ranges)
+ return -1;
+
+ range = _malloc(sizeof(*range));
+ if (!range)
+ return -1;
+
+ range->begin = begin;
+ range->end = end;
+ list_add_tail(&range->entry, dae_allow_ranges);
+
+ return 0;
+}
+
+static int dae_allow_parse_token(const char *token, uint32_t *begin, uint32_t *end)
+{
+ struct in_addr base_addr;
+ uint8_t suffix;
+ size_t len;
+
+ len = u_parse_ip4cidr(token, &base_addr, &suffix);
+ if (len && token[len] == '\0') {
+ uint32_t addr_hbo = ntohl(base_addr.s_addr);
+ uint32_t mask = (uint64_t)0xffffffff << (32 - suffix);
+ uint32_t ip_min = addr_hbo & mask;
+
+ *begin = ip_min;
+ *end = addr_hbo | ~mask;
+ if (ip_min != addr_hbo) {
+ struct in_addr min_addr = { .s_addr = htonl(ip_min) };
+ char ipbuf[INET_ADDRSTRLEN];
+
+ log_warn("radius: dae-allowed network %s is equivalent to %s/%hhu\n",
+ token, u_ip4str(&min_addr, ipbuf), suffix);
+ }
+ return 0;
+ }
+
+ if (!inet_aton(token, &base_addr))
+ return -1;
+
+ *begin = ntohl(base_addr.s_addr);
+ *end = *begin;
+ return 0;
+}
+
+static int dae_allow_parse(const char *opt, int *entries)
+{
+ char *dup;
+ char *token;
+ char *saveptr;
+ int count = 0;
+
+ dup = _strdup(opt);
+ if (!dup)
+ return -1;
+
+ for (token = strtok_r(dup, ",", &saveptr); token;
+ token = strtok_r(NULL, ",", &saveptr)) {
+ char *trimmed = trim_spaces(token);
+ uint32_t begin;
+ uint32_t end;
+
+ if (*trimmed == '\0')
+ continue;
+
+ if (dae_allow_parse_token(trimmed, &begin, &end)) {
+ log_warn("radius: dae-allowed invalid entry \"%s\"\n", trimmed);
+ continue;
+ }
+
+ if (dae_allow_add(begin, end)) {
+ _free(dup);
+ return -1;
+ }
+
+ ++count;
+ }
+
+ _free(dup);
+ *entries = count;
+ return 0;
+}
+
+static int dae_allow_check(in_addr_t ipaddr)
+{
+ struct dae_allow_range *range;
+ uint32_t addr = ntohl(ipaddr);
+
+ list_for_each_entry(range, dae_allow_ranges, entry) {
+ if (addr >= range->begin && addr <= range->end)
+ return 0;
+ }
+
+ return -1;
+}
+
+int rad_dae_src_check(in_addr_t ipaddr)
+{
+ int res = 0;
+
+ pthread_rwlock_rdlock(&dae_allow_lock);
+ if (dae_allow_ranges)
+ res = dae_allow_check(ipaddr);
+ pthread_rwlock_unlock(&dae_allow_lock);
+
+ return res;
}
/* Parse a RADIUS Framed-IPv6-Route string.
@@ -275,12 +488,31 @@ err:
return -1;
}
+/*
+ * Number of IPv6 DNS servers kept per session. Matches the number of dns=
+ * options the ipv6_nd and ipv6_dhcp modules accept in [ipv6-dns], and keeps
+ * the RDNSS option of a router advertisement to a sane size.
+ */
+#define MAX_DNS6_COUNT 3
+
+static void free_ipv6_dns(struct radius_pd_t *rpd)
+{
+ struct ipv6db_addr_t *a;
+
+ while (!list_empty(&rpd->ipv6_dns.addr_list)) {
+ a = list_entry(rpd->ipv6_dns.addr_list.next, typeof(*a), entry);
+ list_del(&a->entry);
+ _free(a);
+ }
+}
+
int rad_proc_attrs(struct rad_req_t *req)
{
struct ev_wins_t wins = {};
struct ev_dns_t dns = {};
struct rad_attr_t *attr;
struct ipv6db_addr_t *a;
+ int dns6_count = -1;
int res = 0;
struct radius_pd_t *rpd = req->rpd;
@@ -389,6 +621,28 @@ int rad_proc_attrs(struct rad_req_t *req)
a->addr = attr->val.ipv6prefix.prefix;
list_add_tail(&a->entry, &rpd->ipv6_dp.prefix_list);
break;
+ case DNS_Server_IPv6_Address:
+ if (dns6_count < 0) {
+ /* This reply carries a DNS server list of
+ its own, it replaces whatever a previous
+ one assigned */
+ free_ipv6_dns(rpd);
+ dns6_count = 0;
+ }
+ if (dns6_count >= MAX_DNS6_COUNT) {
+ if (dns6_count == MAX_DNS6_COUNT)
+ log_ppp_warn("radius: ignoring DNS-Server-IPv6-Address"
+ " beyond the first %i\n", MAX_DNS6_COUNT);
+ dns6_count++;
+ break;
+ }
+ a = _malloc(sizeof(*a));
+ memset(a, 0, sizeof(*a));
+ a->prefix_len = 128;
+ a->addr = attr->val.ipv6addr;
+ list_add_tail(&a->entry, &rpd->ipv6_dns.addr_list);
+ dns6_count++;
+ break;
case NAS_Port:
rpd->ses->unit_idx = attr->val.integer;
break;
@@ -422,6 +676,11 @@ int rad_proc_attrs(struct rad_req_t *req)
if (!rpd->ses->ipv6_dp && !list_empty(&rpd->ipv6_dp.prefix_list))
rpd->ses->ipv6_dp = &rpd->ipv6_dp;
+ /* Like the IPv4 DNS servers, absent attributes leave whatever a
+ previous reply assigned in place */
+ if (!list_empty(&rpd->ipv6_dns.addr_list))
+ rpd->ses->ipv6_dns = &rpd->ipv6_dns;
+
return res;
}
@@ -586,6 +845,7 @@ static void ses_starting(struct ap_session *ses)
INIT_LIST_HEAD(&rpd->plugin_list);
INIT_LIST_HEAD(&rpd->ipv6_addr.addr_list);
INIT_LIST_HEAD(&rpd->ipv6_dp.prefix_list);
+ INIT_LIST_HEAD(&rpd->ipv6_dns.addr_list);
rpd->ipv4_addr.owner = &ipdb;
rpd->ipv6_addr.owner = &ipdb;
@@ -641,8 +901,7 @@ static void ses_started(struct ap_session *ses)
bool gw_spec = !IN6_IS_ADDR_UNSPECIFIED(&fr6->gw);
char nbuf[INET6_ADDRSTRLEN];
char gwbuf[INET6_ADDRSTRLEN];
-
- if (ip6route_add(gw_spec ? 0 : rpd->ses->ifindex, &fr6->prefix, fr6->plen, gw_spec ? &fr6->gw : NULL, 3, fr6->prio)) {
+ if (ip6route_add(gw_spec ? 0 : rpd->ses->ifindex, &fr6->prefix, fr6->plen, gw_spec ? &fr6->gw : NULL, 3, fr6->prio, rpd->ses->vrf_name)) {
log_ppp_warn("radius: failed to add route %s/%hhu %s %u\n",
u_ip6str(&fr6->prefix, nbuf), fr6->plen,
u_ip6str(&fr6->gw, gwbuf), fr6->prio);
@@ -650,7 +909,7 @@ static void ses_started(struct ap_session *ses)
}
for (fr = rpd->fr; fr; fr = fr->next) {
- if (iproute_add(fr->gw ? 0 : rpd->ses->ifindex, 0, fr->dst, fr->gw, 3, fr->mask, fr->prio)) {
+ if (iproute_add(fr->gw ? 0 : rpd->ses->ifindex, 0, fr->dst, fr->gw, 3, fr->mask, fr->prio, rpd->ses->vrf_name)) {
char dst[17], gw[17];
u_inet_ntoa(fr->dst, dst);
u_inet_ntoa(fr->gw, gw);
@@ -689,12 +948,12 @@ static void ses_finishing(struct ap_session *ses)
* when the interface is removed.
*/
if (!IN6_IS_ADDR_UNSPECIFIED(&fr6->gw))
- ip6route_del(0, &fr6->prefix, fr6->plen, &fr6->gw, 3, fr6->prio);
+ ip6route_del(0, &fr6->prefix, fr6->plen, &fr6->gw, 3, fr6->prio, rpd->ses->vrf_name);
}
for (fr = rpd->fr; fr; fr = fr->next) {
if (fr->gw)
- iproute_del(0, 0, fr->dst, fr->gw, 3, fr->mask, fr->prio);
+ iproute_del(0, 0, fr->dst, fr->gw, 3, fr->mask, fr->prio, rpd->ses->vrf_name);
}
if (rpd->acct_started || rpd->acct_req)
@@ -769,6 +1028,9 @@ static void ses_finished(struct ap_session *ses)
_free(a);
}
+ ses->ipv6_dns = NULL;
+ free_ipv6_dns(rpd);
+
fr6 = rpd->fr6;
while (fr6) {
struct framed_ip6_route *next = fr6->next;
@@ -1046,6 +1308,42 @@ static int load_config(void)
return -1;
}
+ {
+ int entries = 0;
+ int dae_allowed_present = 0;
+ int parse_rc = 0;
+
+ pthread_rwlock_wrlock(&dae_allow_lock);
+ dae_allow_clear();
+ opt = conf_get_opt("radius", "dae-allowed");
+ if (opt) {
+ dae_allowed_present = 1;
+ dae_allow_ranges = _malloc(sizeof(*dae_allow_ranges));
+ if (!dae_allow_ranges) {
+ log_emerg("radius: failed to allocate dae-allowed list\n");
+ parse_rc = -1;
+ goto dae_allow_unlock;
+ }
+ INIT_LIST_HEAD(dae_allow_ranges);
+ if (dae_allow_parse(opt, &entries)) {
+ log_emerg("radius: failed to parse dae-allowed\n");
+ dae_allow_clear();
+ parse_rc = -1;
+ goto dae_allow_unlock;
+ }
+ }
+
+ if (dae_allowed_present && entries == 0) {
+ log_warn("radius: dae-allowed has no valid entries, DAE source restrictions are disabled\n");
+ dae_allow_clear();
+ }
+
+dae_allow_unlock:
+ pthread_rwlock_unlock(&dae_allow_lock);
+ if (parse_rc)
+ return -1;
+ }
+
opt = conf_get_opt("radius", "sid-in-auth");
if (opt)
conf_sid_in_auth = atoi(opt);
@@ -1086,6 +1384,18 @@ static int load_config(void)
else
conf_acct_delay_start = 0;
+ opt = conf_get_opt("radius", "blast-protection");
+ if (opt && atoi(opt) > 0) {
+ conf_blast_protection = 1;
+ } else {
+ conf_blast_protection = 0;
+ }
+ opt = conf_get_opt("radius", "framed-route-strict");
+ if (opt && atoi(opt) > 0)
+ conf_framed_route_strict = 1;
+ else
+ conf_framed_route_strict = 0;
+
return 0;
}
diff --git a/accel-pppd/radius/radius.h b/accel-pppd/radius/radius.h
index 1925c973..c19b25c2 100644
--- a/accel-pppd/radius/radius.h
+++ b/accel-pppd/radius/radius.h
@@ -103,6 +103,8 @@ struct rad_attr_t
struct rad_packet_t
{
+ int message_authenticator; // 1 if message authenticator is required
+ uint8_t *secret; // shared secret for this packet for Message-Authenticator signature
int code;
uint8_t id;
int len;
diff --git a/accel-pppd/radius/radius_p.h b/accel-pppd/radius/radius_p.h
index 87613624..e4b84740 100644
--- a/accel-pppd/radius/radius_p.h
+++ b/accel-pppd/radius/radius_p.h
@@ -65,6 +65,7 @@ struct radius_pd_t {
struct ipv4db_item_t ipv4_addr;
struct ipv6db_item_t ipv6_addr;
struct ipv6db_prefix_t ipv6_dp;
+ struct ipv6db_item_t ipv6_dns;
int acct_interim_interval;
int acct_interim_jitter;
@@ -115,6 +116,31 @@ struct rad_req_t {
void (*log)(const char *fmt, ...);
};
+struct rad_server_stat_t {
+ unsigned long auth_sent;
+ unsigned long auth_lost;
+ unsigned long acct_sent;
+ unsigned long acct_lost;
+ unsigned long interim_sent;
+ unsigned long interim_lost;
+ unsigned long fail_cnt;
+
+ struct stat_accm_t *auth_lost_1m;
+ struct stat_accm_t *auth_lost_5m;
+ struct stat_accm_t *auth_query_1m;
+ struct stat_accm_t *auth_query_5m;
+
+ struct stat_accm_t *acct_lost_1m;
+ struct stat_accm_t *acct_lost_5m;
+ struct stat_accm_t *acct_query_1m;
+ struct stat_accm_t *acct_query_5m;
+
+ struct stat_accm_t *interim_lost_1m;
+ struct stat_accm_t *interim_lost_5m;
+ struct stat_accm_t *interim_query_1m;
+ struct stat_accm_t *interim_query_5m;
+};
+
struct rad_server_t {
struct list_head entry;
struct triton_context_t ctx;
@@ -139,28 +165,7 @@ struct rad_server_t {
int weight;
pthread_mutex_t lock;
- unsigned long stat_auth_sent;
- unsigned long stat_auth_lost;
- unsigned long stat_acct_sent;
- unsigned long stat_acct_lost;
- unsigned long stat_interim_sent;
- unsigned long stat_interim_lost;
- unsigned long stat_fail_cnt;
-
- struct stat_accm_t *stat_auth_lost_1m;
- struct stat_accm_t *stat_auth_lost_5m;
- struct stat_accm_t *stat_auth_query_1m;
- struct stat_accm_t *stat_auth_query_5m;
-
- struct stat_accm_t *stat_acct_lost_1m;
- struct stat_accm_t *stat_acct_lost_5m;
- struct stat_accm_t *stat_acct_query_1m;
- struct stat_accm_t *stat_acct_query_5m;
-
- struct stat_accm_t *stat_interim_lost_1m;
- struct stat_accm_t *stat_interim_lost_5m;
- struct stat_accm_t *stat_interim_query_1m;
- struct stat_accm_t *stat_interim_query_5m;
+ struct rad_server_stat_t stat;
unsigned int backup:1;
unsigned int starting:1;
@@ -198,6 +203,7 @@ extern int conf_acct_interim_jitter;
extern int conf_accounting;
extern const char *conf_attr_tunnel_type;
extern int conf_acct_delay_start;
+extern int conf_blast_protection;
int rad_check_nas_pack(struct rad_packet_t *pack);
struct radius_pd_t *rad_find_session(const char *sessionid, const char *username, const char *port_id, int port, in_addr_t ipaddr, const char *csid);
@@ -210,7 +216,7 @@ struct rad_req_t *rad_req_alloc(struct radius_pd_t *rpd, int code, const char *u
struct rad_req_t *rad_req_alloc2(struct radius_pd_t *rpd, int code, const char *username, in_addr_t addr, int port);
struct rad_req_t *rad_req_alloc_empty();
int rad_req_acct_fill(struct rad_req_t *);
-void rad_req_free(struct rad_req_t *);
+struct rad_server_t *rad_req_free(struct rad_req_t *);
int rad_req_send(struct rad_req_t *req);
int __rad_req_send(struct rad_req_t *req, int async);
int rad_req_read(struct triton_md_handler_t *h);
@@ -239,10 +245,12 @@ void rad_packet_print(struct rad_packet_t *pack, struct rad_server_t *s, void (*
int rad_packet_send(struct rad_packet_t *pck, int fd, struct sockaddr_in *addr);
void dm_coa_cancel(struct radius_pd_t *pd);
+int rad_dae_src_check(in_addr_t ipaddr);
struct rad_server_t *rad_server_get(int);
struct rad_server_t *rad_server_get2(int, in_addr_t, int);
-void rad_server_put(struct rad_server_t *, int);
+struct rad_server_t *rad_server_put(struct rad_server_t *, int);
+char *rad_server_secret_dup(struct rad_server_t *);
int rad_server_req_enter(struct rad_req_t *);
void rad_server_req_exit(struct rad_req_t *);
int rad_server_req_cancel(struct rad_req_t *, int full);
@@ -250,6 +258,16 @@ int rad_server_realloc(struct rad_req_t *);
void rad_server_fail(struct rad_server_t *);
void rad_server_timeout(struct rad_server_t *);
void rad_server_reply(struct rad_server_t *);
+void rad_server_stat_fail(struct rad_server_t *);
+void rad_server_stat_auth_sent(struct rad_server_t *);
+void rad_server_stat_auth_lost(struct rad_server_t *);
+void rad_server_stat_auth_query(struct rad_server_t *, unsigned int dt);
+void rad_server_stat_acct_sent(struct rad_server_t *);
+void rad_server_stat_acct_lost(struct rad_server_t *);
+void rad_server_stat_acct_query(struct rad_server_t *, unsigned int dt);
+void rad_server_stat_interim_sent(struct rad_server_t *);
+void rad_server_stat_interim_lost(struct rad_server_t *);
+void rad_server_stat_interim_query(struct rad_server_t *, unsigned int dt);
void rad_update_session_timeout(struct radius_pd_t *rpd, int timeout);
@@ -266,4 +284,3 @@ unsigned long stat_accm_get_cnt(struct stat_accm_t *);
unsigned long stat_accm_get_avg(struct stat_accm_t *);
#endif
-
diff --git a/accel-pppd/radius/req.c b/accel-pppd/radius/req.c
index be74f047..72c46b16 100644
--- a/accel-pppd/radius/req.c
+++ b/accel-pppd/radius/req.c
@@ -15,6 +15,8 @@
#include "memdebug.h"
+#define HMAC_MD5_LEN 16
+
static int make_socket(struct rad_req_t *req);
static mempool_t req_pool;
@@ -73,6 +75,19 @@ static struct rad_req_t *__rad_req_alloc(struct radius_pd_t *rpd, int code, cons
if (!req->pack)
goto out_err;
+ if (code == CODE_ACCESS_REQUEST && conf_blast_protection) {
+ uint8_t buf[HMAC_MD5_LEN] = {0};
+ req->pack->message_authenticator = 1;
+ req->pack->secret = (uint8_t *)rad_server_secret_dup(req->serv);
+ if (!req->pack->secret)
+ goto out_err;
+ if (rad_packet_add_octets(req->pack, NULL, "Message-Authenticator", buf, HMAC_MD5_LEN)) {
+ _free(req->pack->secret);
+ req->pack->secret = NULL;
+ goto out_err;
+ }
+ }
+
if (code == CODE_ACCOUNTING_REQUEST && rpd->acct_username)
username = rpd->acct_username;
@@ -242,13 +257,15 @@ int rad_req_acct_fill(struct rad_req_t *req)
return 0;
}
-void rad_req_free(struct rad_req_t *req)
+struct rad_server_t *rad_req_free(struct rad_req_t *req)
{
+ struct rad_server_t *s = NULL;
+
assert(!req->active);
assert(!req->entry.next);
if (req->serv)
- rad_server_put(req->serv, req->type);
+ s = rad_server_put(req->serv, req->type);
if (req->hnd.tpd)
triton_md_unregister_handler(&req->hnd, 1);
@@ -265,6 +282,8 @@ void rad_req_free(struct rad_req_t *req)
rad_packet_free(req->reply);
mempool_free(req);
+
+ return s;
}
static int make_socket(struct rad_req_t *req)
diff --git a/accel-pppd/radius/serv.c b/accel-pppd/radius/serv.c
index 4af6a67a..71398c82 100644
--- a/accel-pppd/radius/serv.c
+++ b/accel-pppd/radius/serv.c
@@ -11,14 +11,14 @@
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <openssl/md5.h>
+
#include "log.h"
#include "triton.h"
#include "events.h"
#include "cli.h"
#include "utils.h"
-#include "crypto.h"
-
#include "radius_p.h"
#include "memdebug.h"
@@ -93,16 +93,35 @@ struct rad_server_t *rad_server_get2(int type, in_addr_t addr, int port)
return __rad_server_get(type, NULL, addr, port);
}
-void rad_server_put(struct rad_server_t *s, int type)
+struct rad_server_t *rad_server_put(struct rad_server_t *s, int type)
{
+ int do_close = 0;
+ int do_free = 0;
+
__sync_sub_and_fetch(&s->client_cnt[type], 1);
if ((s->need_free || s->need_close) && !s->client_cnt[0] && !s->client_cnt[1]) {
- if (s->need_close)
+ if (s->need_close) {
+ do_close = 1;
triton_context_call(&s->ctx, (triton_event_func)serv_ctx_close, &s->ctx);
- else
+ } else {
+ do_free = 1;
__free_server(s);
- }
+ }
+ }
+
+ return (do_free || do_close) ? NULL : s;
+}
+
+char *rad_server_secret_dup(struct rad_server_t *s)
+{
+ char *secret;
+
+ pthread_mutex_lock(&s->lock);
+ secret = _strdup(s->secret);
+ pthread_mutex_unlock(&s->lock);
+
+ return secret;
}
static void req_wakeup(struct rad_req_t *req)
@@ -133,7 +152,20 @@ static void req_wakeup(struct rad_req_t *req)
}
pthread_mutex_unlock(&req->serv->lock);
- req->send(req, 1);
+ if (req->send(req, 1) == -2) {
+ /* socket setup failed: release the slot taken in
+ * rad_server_req_exit() and drive the failover path,
+ * otherwise the server's req_cnt leaks and the request
+ * is orphaned */
+ req->active = 0;
+ pthread_mutex_lock(&req->serv->lock);
+ req->serv->req_cnt--;
+ pthread_mutex_unlock(&req->serv->lock);
+
+ rad_server_fail(req->serv);
+
+ req->send(req, -1);
+ }
}
static void req_wakeup_failed(struct rad_req_t *req)
@@ -338,7 +370,7 @@ void rad_server_fail(struct rad_server_t *s)
}
s->queue_cnt = 0;
- s->stat_fail_cnt++;
+ rad_server_stat_fail(s);
pthread_mutex_unlock(&s->lock);
}
@@ -358,18 +390,86 @@ void rad_server_reply(struct rad_server_t *s)
s->timeout_cnt = 0;
}
-static int req_set_RA(struct rad_req_t *req, const char *secret)
+void rad_server_stat_fail(struct rad_server_t *s)
+{
+ __atomic_add_fetch(&s->stat.fail_cnt, 1, __ATOMIC_RELAXED);
+}
+
+void rad_server_stat_auth_sent(struct rad_server_t *s)
+{
+ __atomic_add_fetch(&s->stat.auth_sent, 1, __ATOMIC_RELAXED);
+}
+
+void rad_server_stat_auth_lost(struct rad_server_t *s)
+{
+ __atomic_add_fetch(&s->stat.auth_lost, 1, __ATOMIC_RELAXED);
+ stat_accm_add(s->stat.auth_lost_1m, 1);
+ stat_accm_add(s->stat.auth_lost_5m, 1);
+}
+
+void rad_server_stat_auth_query(struct rad_server_t *s, unsigned int dt)
+{
+ stat_accm_add(s->stat.auth_query_1m, dt);
+ stat_accm_add(s->stat.auth_query_5m, dt);
+}
+
+void rad_server_stat_acct_sent(struct rad_server_t *s)
+{
+ __atomic_add_fetch(&s->stat.acct_sent, 1, __ATOMIC_RELAXED);
+}
+
+void rad_server_stat_acct_lost(struct rad_server_t *s)
+{
+ __atomic_add_fetch(&s->stat.acct_lost, 1, __ATOMIC_RELAXED);
+ stat_accm_add(s->stat.acct_lost_1m, 1);
+ stat_accm_add(s->stat.acct_lost_5m, 1);
+}
+
+void rad_server_stat_acct_query(struct rad_server_t *s, unsigned int dt)
+{
+ stat_accm_add(s->stat.acct_query_1m, dt);
+ stat_accm_add(s->stat.acct_query_5m, dt);
+}
+
+void rad_server_stat_interim_sent(struct rad_server_t *s)
+{
+ __atomic_add_fetch(&s->stat.interim_sent, 1, __ATOMIC_RELAXED);
+}
+
+void rad_server_stat_interim_lost(struct rad_server_t *s)
+{
+ __atomic_add_fetch(&s->stat.interim_lost, 1, __ATOMIC_RELAXED);
+ stat_accm_add(s->stat.interim_lost_1m, 1);
+ stat_accm_add(s->stat.interim_lost_5m, 1);
+}
+
+void rad_server_stat_interim_query(struct rad_server_t *s, unsigned int dt)
+{
+ stat_accm_add(s->stat.interim_query_1m, dt);
+ stat_accm_add(s->stat.interim_query_5m, dt);
+}
+
+static int req_set_RA(struct rad_req_t *req)
{
+ char *secret;
MD5_CTX ctx;
- if (rad_packet_build(req->pack, req->RA))
+ secret = rad_server_secret_dup(req->serv);
+ if (!secret)
return -1;
+ if (rad_packet_build(req->pack, req->RA)) {
+ _free(secret);
+ return -1;
+ }
+
MD5_Init(&ctx);
MD5_Update(&ctx, req->pack->buf, req->pack->len);
MD5_Update(&ctx, secret, strlen(secret));
MD5_Final(req->pack->buf + 4, &ctx);
+ _free(secret);
+
return 0;
}
@@ -383,13 +483,14 @@ static void acct_on_sent(struct rad_req_t *req, int res)
static void acct_on_recv(struct rad_req_t *req)
{
- struct rad_server_t *s = req->serv;
+ struct rad_server_t *s = rad_req_free(req);
- rad_req_free(req);
+ if (!s)
+ return;
- if (req->serv->starting) {
- req->serv->starting = 0;
- req->serv->acct_on = 1;
+ if (s->starting) {
+ s->starting = 0;
+ s->acct_on = 1;
} else
__free_server(s);
}
@@ -397,12 +498,15 @@ static void acct_on_recv(struct rad_req_t *req)
static void acct_on_timeout(struct triton_timer_t *t)
{
struct rad_req_t *req = container_of(t, typeof(*req), timeout);
- struct rad_server_t *s = req->serv;
log_switch(triton_context_self(), NULL);
if (req->try++ == conf_max_try) {
- rad_req_free(req);
+ struct rad_server_t *s = rad_req_free(req);
+
+ if (!s)
+ return;
+
if (s->starting)
s->starting = 0;
else
@@ -416,6 +520,8 @@ static void acct_on_timeout(struct triton_timer_t *t)
static void send_acct_on(struct rad_server_t *s)
{
struct rad_req_t *req = rad_req_alloc_empty();
+ if (!req)
+ return;
log_switch(triton_context_self(), NULL);
@@ -450,7 +556,7 @@ static void send_acct_on(struct rad_server_t *s)
if (rad_packet_add_ipaddr(req->pack, NULL, "NAS-IP-Address", conf_nas_ip_address))
goto out_err;
- if (req_set_RA(req, s->secret))
+ if (req_set_RA(req))
goto out_err;
__rad_req_send(req, 0);
@@ -483,10 +589,66 @@ static void serv_ctx_close(struct triton_context_t *ctx)
}
}
+struct rad_server_stat_snapshot_t {
+ int req_cnt;
+ int queue_cnt;
+ unsigned long auth_sent;
+ unsigned long auth_lost;
+ unsigned long auth_lost_1m;
+ unsigned long auth_lost_5m;
+ unsigned long auth_query_1m;
+ unsigned long auth_query_5m;
+ unsigned long acct_sent;
+ unsigned long acct_lost;
+ unsigned long acct_lost_1m;
+ unsigned long acct_lost_5m;
+ unsigned long acct_query_1m;
+ unsigned long acct_query_5m;
+ unsigned long interim_sent;
+ unsigned long interim_lost;
+ unsigned long interim_lost_1m;
+ unsigned long interim_lost_5m;
+ unsigned long interim_query_1m;
+ unsigned long interim_query_5m;
+ unsigned long fail_cnt;
+};
+
+static void rad_server_stat_get(struct rad_server_t *s, struct rad_server_stat_snapshot_t *stat)
+{
+ pthread_mutex_lock(&s->lock);
+ stat->req_cnt = s->req_cnt;
+ stat->queue_cnt = s->queue_cnt;
+ pthread_mutex_unlock(&s->lock);
+
+ stat->auth_sent = __atomic_load_n(&s->stat.auth_sent, __ATOMIC_RELAXED);
+ stat->auth_lost = __atomic_load_n(&s->stat.auth_lost, __ATOMIC_RELAXED);
+ stat->auth_lost_1m = stat_accm_get_cnt(s->stat.auth_lost_1m);
+ stat->auth_lost_5m = stat_accm_get_cnt(s->stat.auth_lost_5m);
+ stat->auth_query_1m = stat_accm_get_avg(s->stat.auth_query_1m);
+ stat->auth_query_5m = stat_accm_get_avg(s->stat.auth_query_5m);
+
+ stat->acct_sent = __atomic_load_n(&s->stat.acct_sent, __ATOMIC_RELAXED);
+ stat->acct_lost = __atomic_load_n(&s->stat.acct_lost, __ATOMIC_RELAXED);
+ stat->acct_lost_1m = stat_accm_get_cnt(s->stat.acct_lost_1m);
+ stat->acct_lost_5m = stat_accm_get_cnt(s->stat.acct_lost_5m);
+ stat->acct_query_1m = stat_accm_get_avg(s->stat.acct_query_1m);
+ stat->acct_query_5m = stat_accm_get_avg(s->stat.acct_query_5m);
+
+ stat->interim_sent = __atomic_load_n(&s->stat.interim_sent, __ATOMIC_RELAXED);
+ stat->interim_lost = __atomic_load_n(&s->stat.interim_lost, __ATOMIC_RELAXED);
+ stat->interim_lost_1m = stat_accm_get_cnt(s->stat.interim_lost_1m);
+ stat->interim_lost_5m = stat_accm_get_cnt(s->stat.interim_lost_5m);
+ stat->interim_query_1m = stat_accm_get_avg(s->stat.interim_query_1m);
+ stat->interim_query_5m = stat_accm_get_avg(s->stat.interim_query_5m);
+
+ stat->fail_cnt = __atomic_load_n(&s->stat.fail_cnt, __ATOMIC_RELAXED);
+}
+
static void show_stat(struct rad_server_t *s, void *client)
{
char addr[INET6_ADDRSTRLEN]; // Sufficient size for both IPv4 and IPv6 addresses
struct timespec ts;
+ struct rad_server_stat_snapshot_t stat;
if (s->ipv4) {
u_inet_ntoa(s->addr, addr);
@@ -495,6 +657,7 @@ static void show_stat(struct rad_server_t *s, void *client)
}
clock_gettime(CLOCK_MONOTONIC, &ts);
+ rad_server_stat_get(s, &stat);
cli_sendv(client, "radius(%i, %s):\r\n", s->id, addr);
@@ -503,31 +666,31 @@ static void show_stat(struct rad_server_t *s, void *client)
else
cli_send(client, " state: active\r\n");
- cli_sendv(client, " fail count: %lu\r\n", s->stat_fail_cnt);
+ cli_sendv(client, " fail count: %lu\r\n", stat.fail_cnt);
- cli_sendv(client, " request count: %i\r\n", s->req_cnt);
- cli_sendv(client, " queue length: %i\r\n", s->queue_cnt);
+ cli_sendv(client, " request count: %i\r\n", stat.req_cnt);
+ cli_sendv(client, " queue length: %i\r\n", stat.queue_cnt);
if (s->auth_port) {
- cli_sendv(client, " auth sent: %lu\r\n", s->stat_auth_sent);
+ cli_sendv(client, " auth sent: %lu\r\n", stat.auth_sent);
cli_sendv(client, " auth lost(total/5m/1m): %lu/%lu/%lu\r\n",
- s->stat_auth_lost, stat_accm_get_cnt(s->stat_auth_lost_5m), stat_accm_get_cnt(s->stat_auth_lost_1m));
+ stat.auth_lost, stat.auth_lost_5m, stat.auth_lost_1m);
cli_sendv(client, " auth avg query time(5m/1m): %lu/%lu ms\r\n",
- stat_accm_get_avg(s->stat_auth_query_5m), stat_accm_get_avg(s->stat_auth_query_1m));
+ stat.auth_query_5m, stat.auth_query_1m);
}
if (s->acct_port) {
- cli_sendv(client, " acct sent: %lu\r\n", s->stat_acct_sent);
+ cli_sendv(client, " acct sent: %lu\r\n", stat.acct_sent);
cli_sendv(client, " acct lost(total/5m/1m): %lu/%lu/%lu\r\n",
- s->stat_acct_lost, stat_accm_get_cnt(s->stat_acct_lost_5m), stat_accm_get_cnt(s->stat_acct_lost_1m));
+ stat.acct_lost, stat.acct_lost_5m, stat.acct_lost_1m);
cli_sendv(client, " acct avg query time(5m/1m): %lu/%lu ms\r\n",
- stat_accm_get_avg(s->stat_acct_query_5m), stat_accm_get_avg(s->stat_acct_query_1m));
+ stat.acct_query_5m, stat.acct_query_1m);
- cli_sendv(client, " interim sent: %lu\r\n", s->stat_interim_sent);
+ cli_sendv(client, " interim sent: %lu\r\n", stat.interim_sent);
cli_sendv(client, " interim lost(total/5m/1m): %lu/%lu/%lu\r\n",
- s->stat_interim_lost, stat_accm_get_cnt(s->stat_interim_lost_5m), stat_accm_get_cnt(s->stat_interim_lost_1m));
+ stat.interim_lost, stat.interim_lost_5m, stat.interim_lost_1m);
cli_sendv(client, " interim avg query time(5m/1m): %lu/%lu ms\r\n",
- stat_accm_get_avg(s->stat_interim_query_5m), stat_accm_get_avg(s->stat_interim_query_1m));
+ stat.interim_query_5m, stat.interim_query_1m);
}
}
@@ -544,6 +707,7 @@ static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt,
static void __add_server(struct rad_server_t *s)
{
struct rad_server_t *s1;
+ char *old_secret;
list_for_each_entry(s1, &serv_list, entry) {
if (s1->addr == s->addr && s1->auth_port == s->auth_port && s1->acct_port == s->acct_port) {
@@ -553,6 +717,13 @@ static void __add_server(struct rad_server_t *s)
s1->need_free = 0;
s1->bind_default = s->bind_default;
strcpy(s1->bind_device, s->bind_device);
+ /* adopt the freshly parsed secret so changes take effect on reload */
+ pthread_mutex_lock(&s1->lock);
+ old_secret = s1->secret;
+ s1->secret = s->secret;
+ s->secret = NULL;
+ pthread_mutex_unlock(&s1->lock);
+ _free(old_secret);
_free(s);
return;
}
@@ -565,20 +736,20 @@ static void __add_server(struct rad_server_t *s)
list_add_tail(&s->entry, &serv_list);
s->starting = conf_acct_on;
- s->stat_auth_lost_1m = stat_accm_create(60);
- s->stat_auth_lost_5m = stat_accm_create(5 * 60);
- s->stat_auth_query_1m = stat_accm_create(60);
- s->stat_auth_query_5m = stat_accm_create(5 * 60);
+ s->stat.auth_lost_1m = stat_accm_create(60);
+ s->stat.auth_lost_5m = stat_accm_create(5 * 60);
+ s->stat.auth_query_1m = stat_accm_create(60);
+ s->stat.auth_query_5m = stat_accm_create(5 * 60);
- s->stat_acct_lost_1m = stat_accm_create(60);
- s->stat_acct_lost_5m = stat_accm_create(5 * 60);
- s->stat_acct_query_1m = stat_accm_create(60);
- s->stat_acct_query_5m = stat_accm_create(5 * 60);
+ s->stat.acct_lost_1m = stat_accm_create(60);
+ s->stat.acct_lost_5m = stat_accm_create(5 * 60);
+ s->stat.acct_query_1m = stat_accm_create(60);
+ s->stat.acct_query_5m = stat_accm_create(5 * 60);
- s->stat_interim_lost_1m = stat_accm_create(60);
- s->stat_interim_lost_5m = stat_accm_create(5 * 60);
- s->stat_interim_query_1m = stat_accm_create(60);
- s->stat_interim_query_5m = stat_accm_create(5 * 60);
+ s->stat.interim_lost_1m = stat_accm_create(60);
+ s->stat.interim_lost_5m = stat_accm_create(5 * 60);
+ s->stat.interim_query_1m = stat_accm_create(60);
+ s->stat.interim_query_5m = stat_accm_create(5 * 60);
s->ctx.close = serv_ctx_close;
@@ -593,23 +764,24 @@ static void __free_server(struct rad_server_t *s)
{
log_debug("radius: free(%i)\n", s->id);
- stat_accm_free(s->stat_auth_lost_1m);
- stat_accm_free(s->stat_auth_lost_5m);
- stat_accm_free(s->stat_auth_query_1m);
- stat_accm_free(s->stat_auth_query_5m);
+ stat_accm_free(s->stat.auth_lost_1m);
+ stat_accm_free(s->stat.auth_lost_5m);
+ stat_accm_free(s->stat.auth_query_1m);
+ stat_accm_free(s->stat.auth_query_5m);
- stat_accm_free(s->stat_acct_lost_1m);
- stat_accm_free(s->stat_acct_lost_5m);
- stat_accm_free(s->stat_acct_query_1m);
- stat_accm_free(s->stat_acct_query_5m);
+ stat_accm_free(s->stat.acct_lost_1m);
+ stat_accm_free(s->stat.acct_lost_5m);
+ stat_accm_free(s->stat.acct_query_1m);
+ stat_accm_free(s->stat.acct_query_5m);
- stat_accm_free(s->stat_interim_lost_1m);
- stat_accm_free(s->stat_interim_lost_5m);
- stat_accm_free(s->stat_interim_query_1m);
- stat_accm_free(s->stat_interim_query_5m);
+ stat_accm_free(s->stat.interim_lost_1m);
+ stat_accm_free(s->stat.interim_lost_5m);
+ stat_accm_free(s->stat.interim_query_1m);
+ stat_accm_free(s->stat.interim_query_5m);
triton_context_unregister(&s->ctx);
+ _free(s->secret);
_free(s);
}
diff --git a/accel-pppd/session.c b/accel-pppd/session.c
index c01417f3..c16f68ae 100644
--- a/accel-pppd/session.c
+++ b/accel-pppd/session.c
@@ -30,6 +30,14 @@
#define SID_SOURCE_SEQ 0
#define SID_SOURCE_URANDOM 1
+#ifndef __WORDSIZE
+#if defined(__GLIBC__) || defined(__UCLIBC__)
+#include <bits/wordsize.h>
+#else
+#include <bits/reg.h>
+#endif
+#endif
+
static int conf_sid_ucase;
static int conf_single_session = -1;
static int conf_single_session_ignore_case;
@@ -54,13 +62,58 @@ static spinlock_t seq_lock;
static long long unsigned seq;
static struct timespec seq_ts;
-struct ap_session_stat __export ap_session_stat;
+static struct ap_session_stat ap_session_stat;
static void (*shutdown_cb)(void);
static void generate_sessionid(struct ap_session *ses);
static void save_seq(void);
+void __export ap_session_stat_get(struct ap_session_stat *stat)
+{
+ stat->starting = __atomic_load_n(&ap_session_stat.starting, __ATOMIC_RELAXED);
+ stat->active = __atomic_load_n(&ap_session_stat.active, __ATOMIC_RELAXED);
+ stat->finishing = __atomic_load_n(&ap_session_stat.finishing, __ATOMIC_RELAXED);
+}
+
+unsigned int __export ap_session_stat_starting(void)
+{
+ return __atomic_load_n(&ap_session_stat.starting, __ATOMIC_RELAXED);
+}
+
+unsigned int __export ap_session_stat_active(void)
+{
+ return __atomic_load_n(&ap_session_stat.active, __ATOMIC_RELAXED);
+}
+
+unsigned int __export ap_session_stat_finishing(void)
+{
+ return __atomic_load_n(&ap_session_stat.finishing, __ATOMIC_RELAXED);
+}
+
+static void ap_session_stat_inc(unsigned int *stat)
+{
+ __atomic_add_fetch(stat, 1, __ATOMIC_RELAXED);
+}
+
+static void ap_session_stat_dec(unsigned int *stat)
+{
+ __atomic_sub_fetch(stat, 1, __ATOMIC_RELAXED);
+}
+
+static void ap_session_stat_move(unsigned int *from, unsigned int *to)
+{
+ ap_session_stat_dec(from);
+ ap_session_stat_inc(to);
+}
+
+static int ap_session_stat_idle(void)
+{
+ return !ap_session_stat_starting()
+ && !ap_session_stat_active()
+ && !ap_session_stat_finishing();
+}
+
void __export ap_session_init(struct ap_session *ses)
{
memset(ses, 0, sizeof(*ses));
@@ -105,7 +158,7 @@ int __export ap_session_starting(struct ap_session *ses)
ses->state = AP_STATE_STARTING;
}
- __sync_add_and_fetch(&ap_session_stat.starting, 1);
+ ap_session_stat_inc(&ap_session_stat.starting);
pthread_rwlock_wrlock(&ses_lock);
list_add_tail(&ses->entry, &ses_list);
@@ -148,8 +201,7 @@ void __export ap_session_activate(struct ap_session *ses)
return;
ses->state = AP_STATE_ACTIVE;
- __sync_sub_and_fetch(&ap_session_stat.starting, 1);
- __sync_add_and_fetch(&ap_session_stat.active, 1);
+ ap_session_stat_move(&ap_session_stat.starting, &ap_session_stat.active);
if (!ses->session_timeout && conf_session_timeout)
ses->session_timeout = conf_session_timeout;
@@ -178,7 +230,6 @@ void __export ap_session_finished(struct ap_session *ses)
if (!ses->down) {
ap_session_ifdown(ses);
- ap_session_read_stats(ses, NULL);
triton_event_fire(EV_SES_FINISHING, ses);
}
@@ -191,14 +242,14 @@ void __export ap_session_finished(struct ap_session *ses)
switch (ses->state) {
case AP_STATE_ACTIVE:
- __sync_sub_and_fetch(&ap_session_stat.active, 1);
+ ap_session_stat_dec(&ap_session_stat.active);
break;
case AP_STATE_RESTORE:
case AP_STATE_STARTING:
- __sync_sub_and_fetch(&ap_session_stat.starting, 1);
+ ap_session_stat_dec(&ap_session_stat.starting);
break;
case AP_STATE_FINISHING:
- __sync_sub_and_fetch(&ap_session_stat.finishing, 1);
+ ap_session_stat_dec(&ap_session_stat.finishing);
break;
}
@@ -259,7 +310,7 @@ void __export ap_session_finished(struct ap_session *ses)
ses->backup->storage->free(ses->backup);
#endif
- if (ap_shutdown && !ap_session_stat.starting && !ap_session_stat.active && !ap_session_stat.finishing) {
+ if (ap_shutdown && ap_session_stat_idle()) {
if (shutdown_cb)
shutdown_cb();
else
@@ -292,19 +343,18 @@ void __export ap_session_terminate(struct ap_session *ses, int cause, int hard)
}
if (ses->state == AP_STATE_ACTIVE)
- __sync_sub_and_fetch(&ap_session_stat.active, 1);
+ ap_session_stat_move(&ap_session_stat.active, &ap_session_stat.finishing);
else
- __sync_sub_and_fetch(&ap_session_stat.starting, 1);
+ ap_session_stat_move(&ap_session_stat.starting, &ap_session_stat.finishing);
- __sync_add_and_fetch(&ap_session_stat.finishing, 1);
ses->terminating = 1;
ses->state = AP_STATE_FINISHING;
log_ppp_debug("terminate\n");
+ ap_session_read_stats(ses, NULL);
if (ses->ctrl->terminate(ses, hard)) {
ap_session_ifdown(ses);
- ap_session_read_stats(ses, NULL);
triton_event_fire(EV_SES_FINISHING, ses);
@@ -326,7 +376,7 @@ int ap_shutdown_soft(void (*cb)(void), int term)
pthread_rwlock_rdlock(&ses_lock);
- if (!ap_session_stat.starting && !ap_session_stat.active && !ap_session_stat.finishing) {
+ if (ap_session_stat_idle()) {
pthread_rwlock_unlock(&ses_lock);
if (shutdown_cb)
shutdown_cb();
diff --git a/accel-pppd/session_backup.c b/accel-pppd/session_backup.c
index dea5b600..2873136f 100644
--- a/accel-pppd/session_backup.c
+++ b/accel-pppd/session_backup.c
@@ -59,6 +59,8 @@ static int session_save(struct ap_session *ses, struct backup_mod *m)
static int session_restore(struct ap_session *ses, struct backup_mod *m)
{
struct backup_tag *t;
+ time_t start_time;
+ uint32_t ifindex;
list_for_each_entry(t, &m->tag_list, entry) {
switch(t->id) {
@@ -79,11 +81,14 @@ static int session_restore(struct ap_session *ses, struct backup_mod *m)
ses->ifname[t->size] = 0;
break;
case SES_TAG_START_TIME:
- ses->start_time = *(time_t *)t->data;
+ memcpy(&start_time, t->data, sizeof(start_time));
+ ses->start_time = start_time;
break;
case SES_TAG_IFINDEX:
- if (ses->backup->internal)
- ses->ifindex = *(uint32_t *)t->data;
+ if (ses->backup->internal) {
+ memcpy(&ifindex, t->data, sizeof(ifindex));
+ ses->ifindex = ifindex;
+ }
break;
/*case PPP_TAG_FD:
ses->fd = *(int *)t->data;
diff --git a/accel-pppd/triton/list.h b/accel-pppd/triton/list.h
index 20b917d4..6352aa82 100644
--- a/accel-pppd/triton/list.h
+++ b/accel-pppd/triton/list.h
@@ -1,6 +1,8 @@
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
+#include <stddef.h>
+
//#if defined(__KERNEL__) || defined(_LVM_H_INCLUDE)
//#include <linux/prefetch.h>
@@ -211,7 +213,7 @@ static inline void list_splice_init(struct list_head *list,
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+ ((type *)((char *)(ptr)-offsetof(type, member)))
/**
* list_first_entry - get the first element from a list
diff --git a/accel-pppd/triton/md.c b/accel-pppd/triton/md.c
index 9b7dd81a..374e49a5 100644
--- a/accel-pppd/triton/md.c
+++ b/accel-pppd/triton/md.c
@@ -87,7 +87,7 @@ static void *md_thread(void *arg)
if (!h->pending) {
list_add_tail(&h->entry2, &h->ctx->pending_handlers);
h->pending = 1;
- __sync_add_and_fetch(&triton_stat.md_handler_pending, 1);
+ triton_stat_md_handler_pending_inc();
r = triton_queue_ctx(h->ctx);
} else
r = 0;
@@ -129,7 +129,7 @@ void __export triton_md_register_handler(struct triton_context_t *ctx, struct tr
list_add_tail(&h->entry, &h->ctx->handlers);
spin_unlock(&h->ctx->lock);
- __sync_add_and_fetch(&triton_stat.md_handler_count, 1);
+ triton_stat_md_handler_count_inc();
}
void __export triton_md_unregister_handler(struct triton_md_handler_t *ud, int c)
@@ -148,7 +148,7 @@ void __export triton_md_unregister_handler(struct triton_md_handler_t *ud, int c
list_del(&h->entry);
if (h->pending) {
list_del(&h->entry2);
- __sync_sub_and_fetch(&triton_stat.md_handler_pending, 1);
+ triton_stat_md_handler_pending_dec();
}
spin_unlock(&h->ctx->lock);
@@ -158,7 +158,7 @@ void __export triton_md_unregister_handler(struct triton_md_handler_t *ud, int c
ud->tpd = NULL;
- __sync_sub_and_fetch(&triton_stat.md_handler_count, 1);
+ triton_stat_md_handler_count_dec();
}
int __export triton_md_enable_handler(struct triton_md_handler_t *ud, int mode)
diff --git a/accel-pppd/triton/mempool.c b/accel-pppd/triton/mempool.c
index ea6d1e6d..a3c72dae 100644
--- a/accel-pppd/triton/mempool.c
+++ b/accel-pppd/triton/mempool.c
@@ -95,7 +95,7 @@ void __export *mempool_alloc(mempool_t *pool)
{
struct _mempool_t *p = (struct _mempool_t *)pool;
struct _item_t *it;
- uint32_t size = sizeof(*it) + p->size + 8;
+ size_t size = sizeof(*it) + p->size + 8;
spin_lock(&p->lock);
if (!list_empty(&p->items)) {
@@ -104,7 +104,7 @@ void __export *mempool_alloc(mempool_t *pool)
spin_unlock(&p->lock);
--p->objects;
- __sync_sub_and_fetch(&triton_stat.mempool_available, size);
+ triton_stat_mempool_available_sub(size);
return it->ptr;
}
@@ -121,10 +121,10 @@ void __export *mempool_alloc(mempool_t *pool)
it = (struct _item_t *)mmap_ptr;
mmap_ptr += size;
spin_unlock(&mmap_lock);
- __sync_sub_and_fetch(&triton_stat.mempool_available, size);
+ triton_stat_mempool_available_sub(size);
} else {
it = _malloc(size);
- __sync_add_and_fetch(&triton_stat.mempool_allocated, size);
+ triton_stat_mempool_allocated_add(size);
}
if (!it) {
@@ -140,7 +140,7 @@ void __export mempool_free(void *ptr)
{
struct _item_t *it = container_of(ptr, typeof(*it), ptr);
struct _mempool_t *p = it->owner;
- uint32_t size = sizeof(*it) + it->owner->size + 8;
+ size_t size = sizeof(*it) + it->owner->size + 8;
int need_free = 0;
#ifdef MEMDEBUG
@@ -184,9 +184,9 @@ void __export mempool_free(void *ptr)
#else
if (need_free) {
_free(it);
- __sync_sub_and_fetch(&triton_stat.mempool_allocated, size);
+ triton_stat_mempool_allocated_sub(size);
} else
- __sync_add_and_fetch(&triton_stat.mempool_available, size);
+ triton_stat_mempool_available_add(size);
#endif
}
@@ -220,7 +220,7 @@ static void mempool_clean(void)
{
struct _mempool_t *p;
struct _item_t *it;
- uint32_t size;
+ size_t size;
triton_log_error("mempool: clean");
@@ -238,8 +238,8 @@ static void mempool_clean(void)
#endif
list_del(&it->entry);
_free(it);
- __sync_sub_and_fetch(&triton_stat.mempool_allocated, size);
- __sync_sub_and_fetch(&triton_stat.mempool_available, size);
+ triton_stat_mempool_allocated_sub(size);
+ triton_stat_mempool_available_sub(size);
#ifdef VALGRIND
} else
break;
@@ -257,7 +257,7 @@ static void sigclean(int num)
static int mmap_grow(void)
{
- int size = sysconf(_SC_PAGESIZE) * (1 << PAGE_ORDER);
+ size_t size = sysconf(_SC_PAGESIZE) * (1 << PAGE_ORDER);
uint8_t *ptr;
if (mmap_endptr) {
@@ -275,8 +275,8 @@ static int mmap_grow(void)
mmap_endptr = ptr + size;
- __sync_add_and_fetch(&triton_stat.mempool_allocated, size);
- __sync_add_and_fetch(&triton_stat.mempool_available, size);
+ triton_stat_mempool_allocated_add(size);
+ triton_stat_mempool_available_add(size);
return 0;
oom:
@@ -301,4 +301,3 @@ static void __init init(void)
mmap_grow();
}
-
diff --git a/accel-pppd/triton/mempool.h b/accel-pppd/triton/mempool.h
index 9ad2c370..c4f27588 100644
--- a/accel-pppd/triton/mempool.h
+++ b/accel-pppd/triton/mempool.h
@@ -3,16 +3,10 @@
#include <stdint.h>
-struct mempool_stat_t
-{
- uint32_t allocated;
- uint32_t available;
-};
typedef void * mempool_t;
mempool_t *mempool_create(int size);
mempool_t *mempool_create2(int size);
-struct mempool_stat_t mempool_get_stat(void);
#ifdef MEMDEBUG
#include "memdebug.h"
diff --git a/accel-pppd/triton/timer.c b/accel-pppd/triton/timer.c
index 5b5d9535..ed23eb1d 100644
--- a/accel-pppd/triton/timer.c
+++ b/accel-pppd/triton/timer.c
@@ -95,7 +95,7 @@ void *timer_thread(void *arg)
if (!t->pending) {
list_add_tail(&t->entry2, &t->ctx->pending_timers);
t->pending = 1;
- __sync_add_and_fetch(&triton_stat.timer_pending, 1);
+ triton_stat_timer_pending_inc();
r = triton_queue_ctx(t->ctx);
} else
r = 0;
@@ -167,7 +167,7 @@ int __export triton_timer_add(struct triton_context_t *ctx, struct triton_timer_
goto out_err;
}
- __sync_add_and_fetch(&triton_stat.timer_count, 1);
+ triton_stat_timer_count_inc();
return 0;
@@ -206,7 +206,7 @@ void __export triton_timer_del(struct triton_timer_t *ud)
list_del(&t->entry);
if (t->pending) {
list_del(&t->entry2);
- __sync_sub_and_fetch(&triton_stat.timer_pending, 1);
+ triton_stat_timer_pending_dec();
}
spin_unlock(&t->ctx->lock);
@@ -216,6 +216,6 @@ void __export triton_timer_del(struct triton_timer_t *ud)
ud->tpd = NULL;
- __sync_sub_and_fetch(&triton_stat.timer_count, 1);
+ triton_stat_timer_count_dec();
}
diff --git a/accel-pppd/triton/triton.c b/accel-pppd/triton/triton.c
index 395a42df..105f810a 100644
--- a/accel-pppd/triton/triton.c
+++ b/accel-pppd/triton/triton.c
@@ -32,13 +32,15 @@ static LIST_HEAD(init_list);
static int terminate;
static int need_terminate;
+/* 0 - idle, 1 - reload requested, 2 - reload running */
static int need_config_reload;
-static void (*config_reload_notify)(int);
+static void (*config_reload_notify)(int, void *);
+static void *config_reload_arg;
static mempool_t *ctx_pool;
static mempool_t *call_pool;
-struct triton_stat_t __export triton_stat;
+static struct triton_stat_t triton_stat;
static struct timeval ru_utime;
static struct timeval ru_stime;
@@ -57,20 +59,167 @@ static __thread void *thread_frame;
#define log_debug2(fmt, ...)
+void __export triton_stat_get(struct triton_stat_t *stat)
+{
+ stat->mempool_allocated = __atomic_load_n(&triton_stat.mempool_allocated, __ATOMIC_RELAXED);
+ stat->mempool_available = __atomic_load_n(&triton_stat.mempool_available, __ATOMIC_RELAXED);
+ stat->thread_count = __atomic_load_n(&triton_stat.thread_count, __ATOMIC_RELAXED);
+ stat->thread_active = __atomic_load_n(&triton_stat.thread_active, __ATOMIC_RELAXED);
+ stat->context_count = __atomic_load_n(&triton_stat.context_count, __ATOMIC_RELAXED);
+ stat->context_sleeping = __atomic_load_n(&triton_stat.context_sleeping, __ATOMIC_RELAXED);
+ stat->context_pending = __atomic_load_n(&triton_stat.context_pending, __ATOMIC_RELAXED);
+ stat->md_handler_count = __atomic_load_n(&triton_stat.md_handler_count, __ATOMIC_RELAXED);
+ stat->md_handler_pending = __atomic_load_n(&triton_stat.md_handler_pending, __ATOMIC_RELAXED);
+ stat->timer_count = __atomic_load_n(&triton_stat.timer_count, __ATOMIC_RELAXED);
+ stat->timer_pending = __atomic_load_n(&triton_stat.timer_pending, __ATOMIC_RELAXED);
+ stat->start_time = __atomic_load_n(&triton_stat.start_time, __ATOMIC_RELAXED);
+ stat->cpu = __atomic_load_n(&triton_stat.cpu, __ATOMIC_RELAXED);
+}
+
+time_t __export triton_stat_start_time(void)
+{
+ return __atomic_load_n(&triton_stat.start_time, __ATOMIC_RELAXED);
+}
+
+unsigned int __export triton_stat_cpu(void)
+{
+ return __atomic_load_n(&triton_stat.cpu, __ATOMIC_RELAXED);
+}
+
+void triton_stat_mempool_allocated_add(uint64_t value)
+{
+ __atomic_add_fetch(&triton_stat.mempool_allocated, value, __ATOMIC_RELAXED);
+}
+
+void triton_stat_mempool_allocated_sub(uint64_t value)
+{
+ __atomic_sub_fetch(&triton_stat.mempool_allocated, value, __ATOMIC_RELAXED);
+}
+
+void triton_stat_mempool_available_add(uint64_t value)
+{
+ __atomic_add_fetch(&triton_stat.mempool_available, value, __ATOMIC_RELAXED);
+}
+
+void triton_stat_mempool_available_sub(uint64_t value)
+{
+ __atomic_sub_fetch(&triton_stat.mempool_available, value, __ATOMIC_RELAXED);
+}
+
+void triton_stat_thread_count_inc(void)
+{
+ __atomic_add_fetch(&triton_stat.thread_count, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_thread_active_inc(void)
+{
+ __atomic_add_fetch(&triton_stat.thread_active, 1, __ATOMIC_RELAXED);
+}
+
+unsigned int triton_stat_thread_active_dec(void)
+{
+ return __atomic_sub_fetch(&triton_stat.thread_active, 1, __ATOMIC_RELAXED);
+}
+
+static unsigned int triton_stat_thread_active(void)
+{
+ return __atomic_load_n(&triton_stat.thread_active, __ATOMIC_RELAXED);
+}
+
+void triton_stat_context_count_inc(void)
+{
+ __atomic_add_fetch(&triton_stat.context_count, 1, __ATOMIC_RELAXED);
+}
+
+unsigned int triton_stat_context_count_dec(void)
+{
+ return __atomic_sub_fetch(&triton_stat.context_count, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_context_sleeping_inc(void)
+{
+ __atomic_add_fetch(&triton_stat.context_sleeping, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_context_sleeping_dec(void)
+{
+ __atomic_sub_fetch(&triton_stat.context_sleeping, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_context_pending_inc(void)
+{
+ __atomic_add_fetch(&triton_stat.context_pending, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_context_pending_dec(void)
+{
+ __atomic_sub_fetch(&triton_stat.context_pending, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_md_handler_count_inc(void)
+{
+ __atomic_add_fetch(&triton_stat.md_handler_count, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_md_handler_count_dec(void)
+{
+ __atomic_sub_fetch(&triton_stat.md_handler_count, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_md_handler_pending_inc(void)
+{
+ __atomic_add_fetch(&triton_stat.md_handler_pending, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_md_handler_pending_dec(void)
+{
+ __atomic_sub_fetch(&triton_stat.md_handler_pending, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_timer_count_inc(void)
+{
+ __atomic_add_fetch(&triton_stat.timer_count, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_timer_count_dec(void)
+{
+ __atomic_sub_fetch(&triton_stat.timer_count, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_timer_pending_inc(void)
+{
+ __atomic_add_fetch(&triton_stat.timer_pending, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_timer_pending_dec(void)
+{
+ __atomic_sub_fetch(&triton_stat.timer_pending, 1, __ATOMIC_RELAXED);
+}
+
+void triton_stat_set_cpu(unsigned int value)
+{
+ __atomic_store_n(&triton_stat.cpu, value, __ATOMIC_RELAXED);
+}
+
+void triton_stat_set_start_time(time_t value)
+{
+ __atomic_store_n(&triton_stat.start_time, value, __ATOMIC_RELAXED);
+}
+
void triton_thread_wakeup(struct _triton_thread_t *thread)
{
log_debug2("wake up thread %p\n", thread);
pthread_kill(thread->thread, SIGUSR1);
}
-static void __config_reload(void (*notify)(int))
+static void __config_reload(void)
{
struct _triton_thread_t *t;
int r;
log_debug2("config_reload: enter\n");
r = conf_reload(NULL);
- notify(r);
+ config_reload_notify(r, config_reload_arg);
spin_lock(&threads_lock);
need_config_reload = 0;
@@ -146,7 +295,7 @@ static void* triton_thread(struct _triton_thread_t *thread)
thread->ctx->thread = thread;
thread->ctx->queued = 0;
spin_unlock(&threads_lock);
- __sync_sub_and_fetch(&triton_stat.context_pending, 1);
+ triton_stat_context_pending_dec();
}
} else {
log_debug2("thread: %p: sleeping\n", thread);
@@ -154,9 +303,10 @@ static void* triton_thread(struct _triton_thread_t *thread)
if (!terminate)
list_add(&thread->entry2, &sleep_threads);
- if (__sync_sub_and_fetch(&triton_stat.thread_active, 1) == 0 && need_config_reload) {
+ if (triton_stat_thread_active_dec() == 0 && need_config_reload == 1) {
+ need_config_reload = 2;
spin_unlock(&threads_lock);
- __config_reload(config_reload_notify);
+ __config_reload();
} else
spin_unlock(&threads_lock);
@@ -172,7 +322,7 @@ static void* triton_thread(struct _triton_thread_t *thread)
//printf("thread %p: exit sigwait\n", thread);
spin_lock(&threads_lock);
- __sync_add_and_fetch(&triton_stat.thread_active, 1);
+ triton_stat_thread_active_inc();
if (!thread->ctx) {
list_del(&thread->entry2);
spin_unlock(&threads_lock);
@@ -228,7 +378,7 @@ static void ctx_thread(struct _triton_context_t *ctx)
list_del(&t->entry2);
t->pending = 0;
spin_unlock(&ctx->lock);
- __sync_sub_and_fetch(&triton_stat.timer_pending, 1);
+ triton_stat_timer_pending_dec();
read(t->fd, &tt, sizeof(tt));
if (t->ud)
t->ud->expire(t->ud);
@@ -243,7 +393,7 @@ static void ctx_thread(struct _triton_context_t *ctx)
h->trig_epoll_events = 0;
spin_unlock(&ctx->lock);
- __sync_sub_and_fetch(&triton_stat.md_handler_pending, 1);
+ triton_stat_md_handler_pending_dec();
h->armed = 0;
@@ -320,8 +470,8 @@ struct _triton_thread_t *create_thread()
while (pthread_create(&thread->thread, &attr, (void*(*)(void*))triton_thread, thread))
sleep(1);
- __sync_add_and_fetch(&triton_stat.thread_count, 1);
- __sync_add_and_fetch(&triton_stat.thread_active, 1);
+ triton_stat_thread_count_inc();
+ triton_stat_thread_active_inc();
return thread;
}
@@ -340,7 +490,7 @@ int triton_queue_ctx(struct _triton_context_t *ctx)
spin_unlock(&threads_lock);
ctx->queued = 1;
log_debug2("ctx %p: queued\n", ctx);
- __sync_add_and_fetch(&triton_stat.context_pending, 1);
+ triton_stat_context_pending_inc();
return 0;
}
@@ -386,8 +536,8 @@ int __export triton_context_register(struct triton_context_t *ud, void *bf_arg)
list_add_tail(&ctx->entry, &ctx_list);
spin_unlock(&ctx_list_lock);
- __sync_add_and_fetch(&triton_stat.context_sleeping, 1);
- __sync_add_and_fetch(&triton_stat.context_count, 1);
+ triton_stat_context_sleeping_inc();
+ triton_stat_context_count_inc();
return 0;
}
@@ -437,7 +587,7 @@ void __export triton_context_unregister(struct triton_context_t *ud)
spin_lock(&ctx_list_lock);
list_del(&ctx->entry);
- if (__sync_sub_and_fetch(&triton_stat.context_count, 1) == 1) {
+ if (triton_stat_context_count_dec() == 1) {
if (need_terminate)
terminate = 1;
}
@@ -492,7 +642,7 @@ void __export triton_context_schedule()
volatile struct _triton_context_t *ctx = (struct _triton_context_t *)this_ctx->tpd;
log_debug2("ctx %p: enter schedule\n", ctx);
- __sync_add_and_fetch(&triton_stat.context_sleeping, 1);
+ triton_stat_context_sleeping_inc();
ctx->uc = alloc_context();
@@ -509,7 +659,7 @@ void __export triton_context_schedule()
spin_unlock(&threads_lock);
_free(ctx->uc);
ctx->uc = NULL;
- __sync_sub_and_fetch(&triton_stat.context_sleeping, 1);
+ triton_stat_context_sleeping_dec();
log_debug2("ctx %p: exit schedule\n", ctx);
} else {
ctx->asleep = 1;
@@ -527,7 +677,7 @@ void __export triton_context_wakeup(struct triton_context_t *ud)
log_debug2("ctx %p: wakeup\n", ctx);
if (ctx->init) {
- __sync_sub_and_fetch(&triton_stat.context_sleeping, 1);
+ triton_stat_context_sleeping_dec();
spin_lock(&ctx->lock);
ctx->init = 0;
if (ctx->pending)
@@ -610,7 +760,7 @@ void __export triton_collect_cpu_usage(void)
clock_gettime(CLOCK_MONOTONIC, &ru_timestamp);
ru_utime = rusage.ru_utime;
ru_stime = rusage.ru_stime;
- triton_stat.cpu = 0;
+ triton_stat_set_cpu(0);
}
}
@@ -634,7 +784,7 @@ static void ru_update(struct triton_timer_t *t)
val = (double)((rusage.ru_utime.tv_sec - ru_utime.tv_sec) * 1000000 + (rusage.ru_utime.tv_usec - ru_utime.tv_usec) +
(rusage.ru_stime.tv_sec - ru_stime.tv_sec) * 1000000 + (rusage.ru_stime.tv_usec - ru_stime.tv_usec)) / dt * 100;
- triton_stat.cpu = val;
+ triton_stat_set_cpu(val);
ru_timestamp = ts;
ru_utime = rusage.ru_utime;
@@ -709,16 +859,25 @@ int __export triton_load_modules(const char *mod_sect)
return 0;
}
-void __export triton_conf_reload(void (*notify)(int))
+int __export triton_conf_reload(void (*notify)(int, void *), void *arg)
{
spin_lock(&threads_lock);
- need_config_reload = 1;
+ if (need_config_reload) {
+ spin_unlock(&threads_lock);
+ return -1;
+ }
config_reload_notify = notify;
- if (triton_stat.thread_active == 0) {
+ config_reload_arg = arg;
+ if (triton_stat_thread_active() == 0) {
+ need_config_reload = 2;
spin_unlock(&threads_lock);
- __config_reload(notify);
- } else
+ __config_reload();
+ } else {
+ need_config_reload = 1;
spin_unlock(&threads_lock);
+ }
+
+ return 0;
}
void __export triton_run()
@@ -752,7 +911,7 @@ void __export triton_run()
}
clock_gettime(CLOCK_MONOTONIC, &ts);
- triton_stat.start_time = ts.tv_sec;
+ triton_stat_set_start_time(ts.tv_sec);
md_run();
timer_run();
diff --git a/accel-pppd/triton/triton.h b/accel-pppd/triton/triton.h
index 79bbee1b..63dc9188 100644
--- a/accel-pppd/triton/triton.h
+++ b/accel-pppd/triton/triton.h
@@ -55,8 +55,8 @@ struct conf_sect_t
struct triton_stat_t
{
- unsigned int mempool_allocated;
- unsigned int mempool_available;
+ uint64_t mempool_allocated;
+ uint64_t mempool_available;
unsigned int thread_count;
unsigned int thread_active;
unsigned int context_count;
@@ -70,7 +70,9 @@ struct triton_stat_t
unsigned int cpu;
};
-extern struct triton_stat_t triton_stat;
+void triton_stat_get(struct triton_stat_t *stat);
+time_t triton_stat_start_time(void);
+unsigned int triton_stat_cpu(void);
int triton_context_register(struct triton_context_t *, void *arg);
void triton_context_unregister(struct triton_context_t *);
void triton_context_set_priority(struct triton_context_t *, int);
@@ -102,7 +104,7 @@ void triton_event_fire(int ev_id, void *arg);
struct conf_sect_t *conf_get_section(const char *name);
char *conf_get_opt(const char *sect, const char *name);
-void triton_conf_reload(void (*notify)(int));
+int triton_conf_reload(void (*notify)(int, void *), void *arg);
void triton_collect_cpu_usage(void);
void triton_stop_collect_cpu_usage(void);
diff --git a/accel-pppd/triton/triton_p.h b/accel-pppd/triton/triton_p.h
index bc39d626..7fa70b63 100644
--- a/accel-pppd/triton/triton_p.h
+++ b/accel-pppd/triton/triton_p.h
@@ -119,4 +119,28 @@ void triton_log_debug(const char *fmt, ...) __attribute__((format(gnu_printf, 1,
int load_modules(const char *name);
void triton_context_release(struct _triton_context_t *ctx);
+void triton_stat_mempool_allocated_add(uint64_t value);
+void triton_stat_mempool_allocated_sub(uint64_t value);
+void triton_stat_mempool_available_add(uint64_t value);
+void triton_stat_mempool_available_sub(uint64_t value);
+void triton_stat_thread_count_inc(void);
+void triton_stat_thread_active_inc(void);
+unsigned int triton_stat_thread_active_dec(void);
+void triton_stat_context_count_inc(void);
+unsigned int triton_stat_context_count_dec(void);
+void triton_stat_context_sleeping_inc(void);
+void triton_stat_context_sleeping_dec(void);
+void triton_stat_context_pending_inc(void);
+void triton_stat_context_pending_dec(void);
+void triton_stat_md_handler_count_inc(void);
+void triton_stat_md_handler_count_dec(void);
+void triton_stat_md_handler_pending_inc(void);
+void triton_stat_md_handler_pending_dec(void);
+void triton_stat_timer_count_inc(void);
+void triton_stat_timer_count_dec(void);
+void triton_stat_timer_pending_inc(void);
+void triton_stat_timer_pending_dec(void);
+void triton_stat_set_cpu(unsigned int value);
+void triton_stat_set_start_time(time_t value);
+
#endif
diff --git a/accel-pppd/utils.c b/accel-pppd/utils.c
index 018b6efa..f92ebe19 100644
--- a/accel-pppd/utils.c
+++ b/accel-pppd/utils.c
@@ -347,3 +347,14 @@ int __export u_randbuf(void *buf, size_t buf_len, int *err)
return 0;
}
+
+void __export u_strstrip(char *str, char c)
+{
+ char *src = str, *dst = str;
+ while (*src) {
+ if (*src != c)
+ *dst++ = *src;
+ src++;
+ }
+ *dst = '\0';
+}
diff --git a/accel-pppd/utils.h b/accel-pppd/utils.h
index 06859a6b..6c03299f 100644
--- a/accel-pppd/utils.h
+++ b/accel-pppd/utils.h
@@ -1,8 +1,89 @@
#ifndef __UTILS_H
#define __UTILS_H
+#include <endian.h>
#include <netinet/in.h>
#include <stdint.h>
+#include <string.h>
+
+#ifndef min
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef max
+#define max(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+/*
+ * Fixed-size memcpy() lets the compiler emit efficient unaligned accesses on
+ * architectures that support them without imposing alignment or aliasing
+ * requirements on callers.
+ */
+static inline uint16_t u_read_be16(const void *ptr)
+{
+ uint16_t value;
+
+ memcpy(&value, ptr, sizeof(value));
+ return ntohs(value);
+}
+
+static inline uint32_t u_read_be32(const void *ptr)
+{
+ uint32_t value;
+
+ memcpy(&value, ptr, sizeof(value));
+ return ntohl(value);
+}
+
+static inline uint64_t u_read_be64(const void *ptr)
+{
+ uint64_t value;
+
+ memcpy(&value, ptr, sizeof(value));
+ return be64toh(value);
+}
+
+static inline void u_write_be16(void *ptr, uint16_t value)
+{
+ value = htons(value);
+ memcpy(ptr, &value, sizeof(value));
+}
+
+static inline void u_write_be32(void *ptr, uint32_t value)
+{
+ value = htonl(value);
+ memcpy(ptr, &value, sizeof(value));
+}
+
+static inline void u_write_be64(void *ptr, uint64_t value)
+{
+ value = htobe64(value);
+ memcpy(ptr, &value, sizeof(value));
+}
+
+static inline uint16_t u_read_native16(const void *ptr)
+{
+ uint16_t value;
+
+ memcpy(&value, ptr, sizeof(value));
+ return value;
+}
+
+static inline uint32_t u_read_native32(const void *ptr)
+{
+ uint32_t value;
+
+ memcpy(&value, ptr, sizeof(value));
+ return value;
+}
+
+static inline uint64_t u_read_native64(const void *ptr)
+{
+ uint64_t value;
+
+ memcpy(&value, ptr, sizeof(value));
+ return value;
+}
char *u_ip6str(const struct in6_addr *addr, char *buf);
char *u_ip4str(const struct in_addr *addr, char *buf);
@@ -24,6 +105,7 @@ size_t u_parse_ip6cidr(const char *str, struct in6_addr *netp, uint8_t *plen);
size_t u_parse_ip4cidr(const char *str, struct in_addr *netp, uint8_t *plen);
size_t u_parse_ip4range(const char *str, struct in_addr *base_ip, uint8_t *max);
+void u_strstrip(char *str, char c);
int u_randbuf(void *buf, size_t buf_len, int *err);
#endif