Use 64-bit TX/RX byte counters for statistics
If the driver supports 64-bit TX/RX byte counters, use them directly. The old 32-bit counter extension is maintained for backwards compatibility with older drivers. For nl80211 driver interface, the newer NL80211_STA_INFO_RX_BYTES64 and NL80211_STA_INFO_TX_BYTES64 attributes are used when available. This resolves the race vulnerable 32-bit value wrap/overflow. Rework RADIUS accounting to use these for Acct-Input-Octets, Acct-Input-Gigawords, Acct-Output-Octets, and Acct-Output-Gigawords, these values are often used for billing purposes. Signed-off-by: Nick Lowe <nick.lowe@lugatech.com>
This commit is contained in:
parent
3f81ac0762
commit
43022abdb9
5 changed files with 64 additions and 38 deletions
|
@ -167,19 +167,25 @@ static int accounting_sta_update_stats(struct hostapd_data *hapd,
|
|||
if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
|
||||
return -1;
|
||||
|
||||
if (sta->last_rx_bytes > data->rx_bytes)
|
||||
sta->acct_input_gigawords++;
|
||||
if (sta->last_tx_bytes > data->tx_bytes)
|
||||
sta->acct_output_gigawords++;
|
||||
sta->last_rx_bytes = data->rx_bytes;
|
||||
sta->last_tx_bytes = data->tx_bytes;
|
||||
if (!data->bytes_64bit) {
|
||||
/* Extend 32-bit counters from the driver to 64-bit counters */
|
||||
if (sta->last_rx_bytes_lo > data->rx_bytes)
|
||||
sta->last_rx_bytes_hi++;
|
||||
sta->last_rx_bytes_lo = data->rx_bytes;
|
||||
|
||||
if (sta->last_tx_bytes_lo > data->tx_bytes)
|
||||
sta->last_tx_bytes_hi++;
|
||||
sta->last_tx_bytes_lo = data->tx_bytes;
|
||||
}
|
||||
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
|
||||
HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
|
||||
"Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
|
||||
"Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
|
||||
sta->last_rx_bytes, sta->acct_input_gigawords,
|
||||
sta->last_tx_bytes, sta->acct_output_gigawords);
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
|
||||
data->rx_bytes, sta->last_rx_bytes_hi,
|
||||
sta->last_rx_bytes_lo,
|
||||
data->tx_bytes, sta->last_tx_bytes_hi,
|
||||
sta->last_tx_bytes_lo,
|
||||
data->bytes_64bit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -224,8 +230,10 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
|
|||
(unsigned long long) sta->acct_session_id);
|
||||
|
||||
os_get_reltime(&sta->acct_session_start);
|
||||
sta->last_rx_bytes = sta->last_tx_bytes = 0;
|
||||
sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
|
||||
sta->last_rx_bytes_hi = 0;
|
||||
sta->last_rx_bytes_lo = 0;
|
||||
sta->last_tx_bytes_hi = 0;
|
||||
sta->last_tx_bytes_lo = 0;
|
||||
hostapd_drv_sta_clear_stats(hapd, sta->addr);
|
||||
|
||||
if (!hapd->conf->radius->acct_server)
|
||||
|
@ -254,7 +262,7 @@ static void accounting_sta_report(struct hostapd_data *hapd,
|
|||
int cause = sta->acct_terminate_cause;
|
||||
struct hostap_sta_driver_data data;
|
||||
struct os_reltime now_r, diff;
|
||||
u32 gigawords;
|
||||
u64 bytes;
|
||||
|
||||
if (!hapd->conf->radius->acct_server)
|
||||
return;
|
||||
|
@ -288,37 +296,37 @@ static void accounting_sta_report(struct hostapd_data *hapd,
|
|||
wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
|
||||
goto fail;
|
||||
}
|
||||
if (data.bytes_64bit)
|
||||
bytes = data.rx_bytes;
|
||||
else
|
||||
bytes = ((u64) sta->last_rx_bytes_hi << 32) |
|
||||
sta->last_rx_bytes_lo;
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_INPUT_OCTETS,
|
||||
data.rx_bytes)) {
|
||||
(u32) bytes)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
|
||||
goto fail;
|
||||
}
|
||||
gigawords = sta->acct_input_gigawords;
|
||||
#if __WORDSIZE == 64
|
||||
gigawords += data.rx_bytes >> 32;
|
||||
#endif
|
||||
if (gigawords &&
|
||||
!radius_msg_add_attr_int32(
|
||||
msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
|
||||
gigawords)) {
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
|
||||
(u32) (bytes >> 32))) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
|
||||
goto fail;
|
||||
}
|
||||
if (data.bytes_64bit)
|
||||
bytes = data.tx_bytes;
|
||||
else
|
||||
bytes = ((u64) sta->last_tx_bytes_hi << 32) |
|
||||
sta->last_tx_bytes_lo;
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
|
||||
data.tx_bytes)) {
|
||||
(u32) bytes)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
|
||||
goto fail;
|
||||
}
|
||||
gigawords = sta->acct_output_gigawords;
|
||||
#if __WORDSIZE == 64
|
||||
gigawords += data.tx_bytes >> 32;
|
||||
#endif
|
||||
if (gigawords &&
|
||||
!radius_msg_add_attr_int32(
|
||||
msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
|
||||
gigawords)) {
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
|
||||
(u32) (bytes >> 32))) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
|
|||
return 0;
|
||||
|
||||
ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
|
||||
"rx_bytes=%lu\ntx_bytes=%lu\n",
|
||||
"rx_bytes=%llu\ntx_bytes=%llu\n",
|
||||
data.rx_packets, data.tx_packets,
|
||||
data.rx_bytes, data.tx_bytes);
|
||||
if (os_snprintf_error(buflen, ret))
|
||||
|
|
|
@ -109,10 +109,11 @@ struct sta_info {
|
|||
int acct_terminate_cause; /* Acct-Terminate-Cause */
|
||||
int acct_interim_interval; /* Acct-Interim-Interval */
|
||||
|
||||
unsigned long last_rx_bytes;
|
||||
unsigned long last_tx_bytes;
|
||||
u32 acct_input_gigawords; /* Acct-Input-Gigawords */
|
||||
u32 acct_output_gigawords; /* Acct-Output-Gigawords */
|
||||
/* For extending 32-bit driver counters to 64-bit counters */
|
||||
u32 last_rx_bytes_hi;
|
||||
u32 last_rx_bytes_lo;
|
||||
u32 last_tx_bytes_hi;
|
||||
u32 last_tx_bytes_lo;
|
||||
|
||||
u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
|
||||
|
||||
|
|
|
@ -1373,7 +1373,9 @@ struct wpa_driver_capa {
|
|||
struct hostapd_data;
|
||||
|
||||
struct hostap_sta_driver_data {
|
||||
unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes;
|
||||
unsigned long rx_packets, tx_packets;
|
||||
unsigned long long rx_bytes, tx_bytes;
|
||||
int bytes_64bit; /* whether 64-bit byte counters are supported */
|
||||
unsigned long current_tx_rate;
|
||||
unsigned long inactive_msec;
|
||||
unsigned long flags;
|
||||
|
|
|
@ -5381,6 +5381,8 @@ static int get_sta_handler(struct nl_msg *msg, void *arg)
|
|||
[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
|
||||
[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
|
||||
[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
|
||||
[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
|
||||
[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
|
||||
};
|
||||
|
||||
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||
|
@ -5406,10 +5408,23 @@ static int get_sta_handler(struct nl_msg *msg, void *arg)
|
|||
if (stats[NL80211_STA_INFO_INACTIVE_TIME])
|
||||
data->inactive_msec =
|
||||
nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
|
||||
/* For backwards compatibility, fetch the 32-bit counters first. */
|
||||
if (stats[NL80211_STA_INFO_RX_BYTES])
|
||||
data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]);
|
||||
if (stats[NL80211_STA_INFO_TX_BYTES])
|
||||
data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]);
|
||||
if (stats[NL80211_STA_INFO_RX_BYTES64] &&
|
||||
stats[NL80211_STA_INFO_TX_BYTES64]) {
|
||||
/*
|
||||
* The driver supports 64-bit counters, so use them to override
|
||||
* the 32-bit values.
|
||||
*/
|
||||
data->rx_bytes =
|
||||
nla_get_u64(stats[NL80211_STA_INFO_RX_BYTES64]);
|
||||
data->tx_bytes =
|
||||
nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]);
|
||||
data->bytes_64bit = 1;
|
||||
}
|
||||
if (stats[NL80211_STA_INFO_RX_PACKETS])
|
||||
data->rx_packets =
|
||||
nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
|
||||
|
|
Loading…
Reference in a new issue