summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Fedoryshchenko <denys.f@collabora.com>2026-01-24 20:13:41 +0200
committerDenys Fedoryshchenko <denys.f@collabora.com>2026-01-24 20:14:44 +0200
commit027bff94665e3e9779e34117947f0e0bec1eb8b6 (patch)
tree7364fa61e399c926b22cd367fc86093cae8d33c3
parenta4c79494f2b8aefecb4d4db25f7c3a7442a4f0fd (diff)
downloadaccel-ppp-027bff94665e3e9779e34117947f0e0bec1eb8b6.tar.gz
accel-ppp-027bff94665e3e9779e34117947f0e0bec1eb8b6.zip
radius: Add optional validation of Framed-Route
Signed-off-by: Denys Fedoryshchenko <denys.f@collabora.com>
-rw-r--r--accel-pppd/accel-ppp.conf1
-rw-r--r--accel-pppd/accel-ppp.conf.55
-rw-r--r--accel-pppd/radius/radius.c20
3 files changed, 26 insertions, 0 deletions
diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf
index a812c547..329c3adc 100644
--- a/accel-pppd/accel-ppp.conf
+++ b/accel-pppd/accel-ppp.conf
@@ -209,6 +209,7 @@ 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
diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5
index 3b56b3cf..cefc74ed 100644
--- a/accel-pppd/accel-ppp.conf.5
+++ b/accel-pppd/accel-ppp.conf.5
@@ -1099,6 +1099,11 @@ If enabled, checks that the NAS-Identifier or NAS-IP-Address in DM/CoA requests
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.
diff --git a/accel-pppd/radius/radius.c b/accel-pppd/radius/radius.c
index 4710a70f..f32a30ce 100644
--- a/accel-pppd/radius/radius.c
+++ b/accel-pppd/radius/radius.c
@@ -60,6 +60,7 @@ 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;
@@ -108,6 +109,8 @@ static int parse_framed_route_v4(const char *str, struct framed_route *fr)
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);
@@ -117,6 +120,7 @@ static int parse_framed_route_v4(const char *str, struct framed_route *fr)
// 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.
@@ -132,18 +136,29 @@ static int parse_framed_route_v4(const char *str, struct framed_route *fr)
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
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)) {
@@ -1326,6 +1341,11 @@ dae_allow_unlock:
} 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;
}