diff --git a/NEWS b/NEWS index 5d606724..d9a114f2 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,5 @@ * Version 1.2.4 (unreleased) +- Get connection speed limits (traffic shaping) from RADIUS (#554) - Use "OpenConnect VPN server" as the long package name. Keep "ocserv" as a short name. - usage: open issues on GitLab to report bugs. diff --git a/doc/README-radius.md b/doc/README-radius.md index 205a9f53..88aef64a 100644 --- a/doc/README-radius.md +++ b/doc/README-radius.md @@ -141,4 +141,21 @@ ATTRIBUTE DNS-Server-IPv6-Address 169 ipv6addr # Sets IPv6 routes ATTRIBUTE Framed-IPv6-Prefix 97 ipv6prefix ATTRIBUTE Route-IPv6-Information 170 ipv6prefix + + +############################ +# Traffic shaping # +############################ + +VENDOR Roaring-Penguin 10055 + +BEGIN-VENDOR Roaring-Penguin + +# tx speed limit in kb/s +ATTRIBUTE RP-Upstream-Speed-Limit 1 integer +# rx speed limit in kb/s +ATTRIBUTE RP-Downstream-Speed-Limit 2 integer + +END-VENDOR Roaring-Penguin + ``` diff --git a/src/auth/radius.c b/src/auth/radius.c index a0c9678f..4dc6e717 100644 --- a/src/auth/radius.c +++ b/src/auth/radius.c @@ -41,9 +41,22 @@ # include #endif +#ifndef VENDOR_BIT_SIZE +# define VENDOR_BIT_SIZE 16 +# define VENDOR_MASK 0xffff +#else +# define VENDOR_MASK 0xffffffff +#endif + +#define VATTRID_SET(a,v) ((a)|((uint64_t)((v)&VENDOR_MASK)) << VENDOR_BIT_SIZE) + #define RAD_GROUP_NAME PW_CLASS -#define RAD_MS_PRIMARY_DNS_SERVER ((311<<16)|(28)) /* RFC 2548 */ -#define RAD_MS_SECONDARY_DNS_SERVER ((311<<16)|(29)) /* RFC 2548 */ +/* Microsoft - RFC 2548 */ +#define MS_PRIMARY_DNS_SERVER VATTRID_SET(28, 311) +#define MS_SECONDARY_DNS_SERVER VATTRID_SET(29, 311) +/* Roaring Penguin */ +#define RP_UPSTREAM_SPEED_LIMIT VATTRID_SET(1, 10055) +#define RP_DOWNSTREAM_SPEED_LIMIT VATTRID_SET(2, 10055) #if defined(LEGACY_RADIUS) # ifndef PW_DELEGATED_IPV6_PREFIX @@ -431,11 +444,11 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) /* Framed-IP-Netmask */ ipv4 = htonl(vp->lvalue); inet_ntop(AF_INET, &ipv4, pctx->ipv4_mask, sizeof(pctx->ipv4_mask)); - } else if (vp->attribute == RAD_MS_PRIMARY_DNS_SERVER && vp->type == PW_TYPE_IPADDR) { + } else if (vp->attribute == MS_PRIMARY_DNS_SERVER && vp->type == PW_TYPE_IPADDR) { /* MS-Primary-DNS-Server */ ipv4 = htonl(vp->lvalue); inet_ntop(AF_INET, &ipv4, pctx->ipv4_dns1, sizeof(pctx->ipv4_dns1)); - } else if (vp->attribute == RAD_MS_SECONDARY_DNS_SERVER && vp->type == PW_TYPE_IPADDR) { + } else if (vp->attribute == MS_SECONDARY_DNS_SERVER && vp->type == PW_TYPE_IPADDR) { /* MS-Secondary-DNS-Server */ ipv4 = htonl(vp->lvalue); inet_ntop(AF_INET, &ipv4, pctx->ipv4_dns2, sizeof(pctx->ipv4_dns2)); @@ -449,8 +462,13 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned pass_len) pctx->interim_interval_secs = vp->lvalue; } else if (vp->attribute == PW_SESSION_TIMEOUT && vp->type == PW_TYPE_INTEGER) { pctx->session_timeout_secs = vp->lvalue; + } else if (vp->attribute == RP_UPSTREAM_SPEED_LIMIT && vp->type == PW_TYPE_INTEGER) { + pctx->rx_per_sec = vp->lvalue; + } else if (vp->attribute == RP_DOWNSTREAM_SPEED_LIMIT && vp->type == PW_TYPE_INTEGER) { + pctx->tx_per_sec = vp->lvalue; } else { - oc_syslog(LOG_DEBUG, "radius-auth: ignoring server's value %u of type %u", (int)vp->attribute, (int)vp->type); + oc_syslog(LOG_DEBUG, "radius-auth: ignoring server's attribute (%u,%u) of type %u", + (unsigned)ATTRID(vp->attribute), (unsigned)VENDOR(vp->attribute), (unsigned)vp->type); } vp = vp->next; } diff --git a/src/auth/radius.h b/src/auth/radius.h index f715bc97..5916892e 100644 --- a/src/auth/radius.h +++ b/src/auth/radius.h @@ -61,6 +61,9 @@ struct radius_ctx_st { char ipv6_dns1[MAX_IP_STR]; char ipv6_dns2[MAX_IP_STR]; + uint32_t rx_per_sec; + uint32_t tx_per_sec; + char **routes; unsigned routes_size; diff --git a/src/sup-config/radius.c b/src/sup-config/radius.c index 6a4511b7..996206a8 100644 --- a/src/sup-config/radius.c +++ b/src/sup-config/radius.c @@ -120,6 +120,15 @@ static int get_sup_config(struct cfg_st *cfg, client_entry_st *entry, msg->config->has_ipv6_subnet_prefix = 1; } + if (pctx->rx_per_sec) { + msg->config->has_rx_per_sec = 1; + msg->config->rx_per_sec = pctx->rx_per_sec; + } + if (pctx->tx_per_sec) { + msg->config->has_tx_per_sec = 1; + msg->config->tx_per_sec = pctx->tx_per_sec; + } + return 0; } diff --git a/tests/data/raddb/users b/tests/data/raddb/users index be191467..16ab9914 100644 --- a/tests/data/raddb/users +++ b/tests/data/raddb/users @@ -132,7 +132,9 @@ testtime Cleartext-Password := "test" Framed-IP-Netmask = 255.255.255.0, Framed-MTU = 1500, Session-Timeout = 60, - Acct-Interim-Interval = 20 + Acct-Interim-Interval = 20, + RP-Upstream-Speed-Limit = 16, + RP-Downstream-Speed-Limit = 64 test1-otp Cleartext-Password := "test1-otp-stage%{string:State}", Tmp-Integer-0 := 3 Service-Type = Framed-User, diff --git a/tests/data/radiusclient/dictionary b/tests/data/radiusclient/dictionary index 03e92b81..e79bae18 100644 --- a/tests/data/radiusclient/dictionary +++ b/tests/data/radiusclient/dictionary @@ -282,3 +282,12 @@ ATTRIBUTE MS-Primary-DNS-Server 28 ipaddr ATTRIBUTE MS-Secondary-DNS-Server 29 ipaddr END-VENDOR Microsoft + +VENDOR Roaring-Penguin 10055 + +BEGIN-VENDOR Roaring-Penguin + +ATTRIBUTE RP-Upstream-Speed-Limit 1 integer +ATTRIBUTE RP-Downstream-Speed-Limit 2 integer + +END-VENDOR Roaring-Penguin diff --git a/tests/radius-config b/tests/radius-config index b49c41f7..dc5182f2 100755 --- a/tests/radius-config +++ b/tests/radius-config @@ -161,6 +161,21 @@ if test $? != 0;then exit 1 fi +grep "Limit RX: 16" ${OUTFILE} +if test $? != 0;then + ${OCCTL} -s ${OCCTL_SOCKET} show user ${USERNAME} + echo "occtl show user didn't find upstream speed limit!" + exit 1 +fi + +grep "Limit TX: 64" ${OUTFILE} +if test $? != 0;then + ${OCCTL} -s ${OCCTL_SOCKET} show user ${USERNAME} + echo "occtl show user didn't find downstream speed limit!" + exit 1 +fi + + echo "Waiting for accounting report" wait_file_contents ${RADIUSLOG} "Acct-Input-Octets = [1-9]+" 35