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:
Nick Lowe 2016-02-19 15:22:25 +00:00 committed by Jouni Malinen
parent 3f81ac0762
commit 43022abdb9
5 changed files with 64 additions and 38 deletions

View file

@ -167,19 +167,25 @@ static int accounting_sta_update_stats(struct hostapd_data *hapd,
if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
return -1; return -1;
if (sta->last_rx_bytes > data->rx_bytes) if (!data->bytes_64bit) {
sta->acct_input_gigawords++; /* Extend 32-bit counters from the driver to 64-bit counters */
if (sta->last_tx_bytes > data->tx_bytes) if (sta->last_rx_bytes_lo > data->rx_bytes)
sta->acct_output_gigawords++; sta->last_rx_bytes_hi++;
sta->last_rx_bytes = data->rx_bytes; sta->last_rx_bytes_lo = data->rx_bytes;
sta->last_tx_bytes = data->tx_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_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " HOSTAPD_LEVEL_DEBUG,
"Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
"Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", data->rx_bytes, sta->last_rx_bytes_hi,
sta->last_rx_bytes, sta->acct_input_gigawords, sta->last_rx_bytes_lo,
sta->last_tx_bytes, sta->acct_output_gigawords); data->tx_bytes, sta->last_tx_bytes_hi,
sta->last_tx_bytes_lo,
data->bytes_64bit);
return 0; 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); (unsigned long long) sta->acct_session_id);
os_get_reltime(&sta->acct_session_start); os_get_reltime(&sta->acct_session_start);
sta->last_rx_bytes = sta->last_tx_bytes = 0; sta->last_rx_bytes_hi = 0;
sta->acct_input_gigawords = sta->acct_output_gigawords = 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); hostapd_drv_sta_clear_stats(hapd, sta->addr);
if (!hapd->conf->radius->acct_server) 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; int cause = sta->acct_terminate_cause;
struct hostap_sta_driver_data data; struct hostap_sta_driver_data data;
struct os_reltime now_r, diff; struct os_reltime now_r, diff;
u32 gigawords; u64 bytes;
if (!hapd->conf->radius->acct_server) if (!hapd->conf->radius->acct_server)
return; return;
@ -288,37 +296,37 @@ static void accounting_sta_report(struct hostapd_data *hapd,
wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
goto fail; 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, if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_INPUT_OCTETS, RADIUS_ATTR_ACCT_INPUT_OCTETS,
data.rx_bytes)) { (u32) bytes)) {
wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
goto fail; goto fail;
} }
gigawords = sta->acct_input_gigawords; if (!radius_msg_add_attr_int32(msg,
#if __WORDSIZE == 64 RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
gigawords += data.rx_bytes >> 32; (u32) (bytes >> 32))) {
#endif
if (gigawords &&
!radius_msg_add_attr_int32(
msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
gigawords)) {
wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
goto fail; 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, if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_OUTPUT_OCTETS, RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
data.tx_bytes)) { (u32) bytes)) {
wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
goto fail; goto fail;
} }
gigawords = sta->acct_output_gigawords; if (!radius_msg_add_attr_int32(msg,
#if __WORDSIZE == 64 RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
gigawords += data.tx_bytes >> 32; (u32) (bytes >> 32))) {
#endif
if (gigawords &&
!radius_msg_add_attr_int32(
msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
gigawords)) {
wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
goto fail; goto fail;
} }

View file

@ -35,7 +35,7 @@ static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
return 0; return 0;
ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" 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_packets, data.tx_packets,
data.rx_bytes, data.tx_bytes); data.rx_bytes, data.tx_bytes);
if (os_snprintf_error(buflen, ret)) if (os_snprintf_error(buflen, ret))

View file

@ -109,10 +109,11 @@ struct sta_info {
int acct_terminate_cause; /* Acct-Terminate-Cause */ int acct_terminate_cause; /* Acct-Terminate-Cause */
int acct_interim_interval; /* Acct-Interim-Interval */ int acct_interim_interval; /* Acct-Interim-Interval */
unsigned long last_rx_bytes; /* For extending 32-bit driver counters to 64-bit counters */
unsigned long last_tx_bytes; u32 last_rx_bytes_hi;
u32 acct_input_gigawords; /* Acct-Input-Gigawords */ u32 last_rx_bytes_lo;
u32 acct_output_gigawords; /* Acct-Output-Gigawords */ u32 last_tx_bytes_hi;
u32 last_tx_bytes_lo;
u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */

View file

@ -1373,7 +1373,9 @@ struct wpa_driver_capa {
struct hostapd_data; struct hostapd_data;
struct hostap_sta_driver_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 current_tx_rate;
unsigned long inactive_msec; unsigned long inactive_msec;
unsigned long flags; unsigned long flags;

View file

@ -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_RX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_FAILED] = { .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), 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]) if (stats[NL80211_STA_INFO_INACTIVE_TIME])
data->inactive_msec = data->inactive_msec =
nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]); 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]) if (stats[NL80211_STA_INFO_RX_BYTES])
data->rx_bytes = nla_get_u32(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]) if (stats[NL80211_STA_INFO_TX_BYTES])
data->tx_bytes = nla_get_u32(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]) if (stats[NL80211_STA_INFO_RX_PACKETS])
data->rx_packets = data->rx_packets =
nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);