diff mbox

[v2] Write client (dis)associations to a logfile

Message ID 20150112165049.GN28893@zirkel.wertarbyte.de
State Changes Requested
Headers show

Commit Message

Stefan Tomanek Jan. 12, 2015, 4:50 p.m. UTC
This change adds the configuration option "assoc_log_file" to hostapd, making
it possible to specify a log file to which clients joining and parting the
corresponding BSS are written.

Every BSS can have an individual log file, and each entry written contains a
timestamp, an indicator whether the client is associating (+) or disaccociating
(-), the client and the bss hardware addresses as well as the device and SSID
identifiers of the affected network. This makes it easy to join the file with
the leases database of an DHCP server like dnsmasq.

Signed-off-by: Stefan Tomanek <stefan.tomanek@wertarbyte.de>
---
 hostapd/config_file.c |  3 +++
 hostapd/hostapd.conf  |  3 +++
 hostapd/main.c        |  6 ++++++
 src/ap/ap_config.c    | 27 +++++++++++++++++++++++++++
 src/ap/ap_config.h    |  5 +++++
 src/ap/ap_mlme.c      | 26 ++++++++++++++++++++++++++
 src/ap/hostapd.c      |  4 ++++
 src/ap/hostapd.h      |  2 ++
 8 files changed, 76 insertions(+)

Comments

Jouni Malinen Jan. 12, 2015, 7:44 p.m. UTC | #1
On Mon, Jan 12, 2015 at 05:50:49PM +0100, Stefan Tomanek wrote:
> This change adds the configuration option "assoc_log_file" to hostapd, making
> it possible to specify a log file to which clients joining and parting the
> corresponding BSS are written.
> 
> Every BSS can have an individual log file, and each entry written contains a
> timestamp, an indicator whether the client is associating (+) or disaccociating
> (-), the client and the bss hardware addresses as well as the device and SSID
> identifiers of the affected network. This makes it easy to join the file with
> the leases database of an DHCP server like dnsmasq.

Why would there be need for yet another log file for this? Isn't this
information available through syslog messages?
Stefan Tomanek Jan. 12, 2015, 8:57 p.m. UTC | #2
Dies schrieb Jouni Malinen (j@w1.fi):

> > Every BSS can have an individual log file, and each entry written contains a
> > timestamp, an indicator whether the client is associating (+) or disaccociating
> > (-), the client and the bss hardware addresses as well as the device and SSID
> > identifiers of the affected network. This makes it easy to join the file with
> > the leases database of an DHCP server like dnsmasq.
> 
> Why would there be need for yet another log file for this? Isn't this
> information available through syslog messages?

Most embedded system do not have persistent log storage (OpenWrt devices);
therefore, the list of client actions moves out of the log buffer quite fast.
And of course syslog messages aren't easily parseble - I am using the written
logfile to correlate client associations with DHCP leases from dnsmasq. And as
a last point, the syslog message of a client associating does not include the
SSID used (which might be interesting on a system servicing multiple networks).
Using multiple files makes it easier to log each network into separate
locations; e.g. logging activity in a guest network to /tmp/assoc_guest while
keeping internal networks away from that log.
Jouni Malinen Jan. 15, 2015, 12:02 a.m. UTC | #3
On Mon, Jan 12, 2015 at 09:57:37PM +0100, Stefan Tomanek wrote:
> Most embedded system do not have persistent log storage (OpenWrt devices);
> therefore, the list of client actions moves out of the log buffer quite fast.
> And of course syslog messages aren't easily parseble - I am using the written
> logfile to correlate client associations with DHCP leases from dnsmasq. And as
> a last point, the syslog message of a client associating does not include the
> SSID used (which might be interesting on a system servicing multiple networks).
> Using multiple files makes it easier to log each network into separate
> locations; e.g. logging activity in a guest network to /tmp/assoc_guest while
> keeping internal networks away from that log.

I'd rather make the existing syslog mechanism more useful than
introduce something completely new for doing more or less the same
thing. There are too many different existing logging options already..
Stefan Tomanek Jan. 15, 2015, 10:24 a.m. UTC | #4
Dies schrieb Jouni Malinen (j@w1.fi):

> > Most embedded system do not have persistent log storage (OpenWrt devices);
> > therefore, the list of client actions moves out of the log buffer quite fast.
> > And of course syslog messages aren't easily parseble - I am using the written
> > logfile to correlate client associations with DHCP leases from dnsmasq. And as
> > a last point, the syslog message of a client associating does not include the
> > SSID used (which might be interesting on a system servicing multiple networks).
> > Using multiple files makes it easier to log each network into separate
> > locations; e.g. logging activity in a guest network to /tmp/assoc_guest while
> > keeping internal networks away from that log.
> 
> I'd rather make the existing syslog mechanism more useful than
> introduce something completely new for doing more or less the same
> thing. There are too many different existing logging options already..

That might be the case, but I still find it extremely convenient to have a
well-arranged summary listing of the clients using my AP - especially if I can
simply parse it using awk and merge it with other data (like the dnsmasq leases
file). Grabbing that data from syslog isn't that easy, especially if no
persistent syslog storage is in play.
diff mbox

Patch

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index e30efbe..145fad1 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1881,6 +1881,9 @@  static int hostapd_config_fill(struct hostapd_config *conf,
 		bss->logger_syslog = atoi(pos);
 	} else if (os_strcmp(buf, "logger_stdout") == 0) {
 		bss->logger_stdout = atoi(pos);
+	} else if (os_strcmp(buf, "assoc_log_file") == 0) {
+		os_free(bss->assoc_log_file);
+		bss->assoc_log_file = os_strdup(pos);
 	} else if (os_strcmp(buf, "dump_file") == 0) {
 		wpa_printf(MSG_INFO, "Line %d: DEPRECATED: 'dump_file' configuration variable is not used anymore",
 			   line);
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 2f6126c..6635d9b 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -54,6 +54,9 @@  logger_syslog_level=2
 logger_stdout=-1
 logger_stdout_level=2
 
+# Clients joining or departing the AP can be logged to a separate file.
+assoc_log_file=/tmp/assoc_log-wlan0
+
 # Interface for separate control program. If this is specified, hostapd
 # will create this directory and a UNIX domain socket for listening to requests
 # from external programs (CLI/GUI, etc.) for status information and
diff --git a/hostapd/main.c b/hostapd/main.c
index 3ecd009..40dba1d 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -265,6 +265,12 @@  hostapd_interface_init(struct hapd_interfaces *interfaces,
 		return NULL;
 	}
 
+	for (k = 0; k < iface->conf->num_bss; k++) {
+		int r = hostapd_open_assoc_log(iface->bss[k]);
+		if (r < 0)
+			return NULL;
+	}
+
 	return iface;
 }
 
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 1c0ed7a..cc1ff46 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -6,6 +6,8 @@ 
  * See README for more details.
  */
 
+#include <fcntl.h>
+
 #include "utils/includes.h"
 
 #include "utils/common.h"
@@ -18,6 +20,7 @@ 
 #include "wpa_auth.h"
 #include "sta_info.h"
 #include "ap_config.h"
+#include "hostapd.h"
 
 
 static void hostapd_config_free_vlan(struct hostapd_bss_config *bss)
@@ -201,6 +204,30 @@  int hostapd_mac_comp_empty(const void *a)
 	return os_memcmp(a, empty, sizeof(macaddr));
 }
 
+int hostapd_open_assoc_log(struct hostapd_data *bss)
+{
+	char *alog = bss->conf->assoc_log_file;
+	if (alog) {
+		int fd = open(alog, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600);
+		if (fd < 0) {
+			wpa_printf(MSG_ERROR, "Error opening association log file '%s'\n", alog);
+			return -1;
+		}
+		bss->assoc_log_fd = fd;
+		return 1;
+	} else {
+		bss->assoc_log_fd = -1;
+	}
+	return 0;
+}
+
+void hostapd_close_assoc_log(struct hostapd_data *bss)
+{
+	if (bss->assoc_log_fd != -1) {
+		close(bss->assoc_log_fd);
+		bss->assoc_log_fd = -1;
+	}
+}
 
 static int hostapd_config_read_wpa_psk(const char *fname,
 				       struct hostapd_ssid *ssid)
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 58af6cb..e0e4fe8 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -221,6 +221,8 @@  struct hostapd_bss_config {
 	unsigned int logger_syslog; /* module bitfield */
 	unsigned int logger_stdout; /* module bitfield */
 
+	char *assoc_log_file;
+
 	int max_num_sta; /* maximum number of STAs in station table */
 
 	int dtim_period;
@@ -640,6 +642,9 @@  struct hostapd_config {
 #endif /* CONFIG_ACS */
 };
 
+struct hostapd_data;
+int hostapd_open_assoc_log(struct hostapd_data *bss);
+void hostapd_close_assoc_log(struct hostapd_data *bss);
 
 int hostapd_mac_comp(const void *a, const void *b);
 int hostapd_mac_comp_empty(const void *a);
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index 13604ed..2dd7d72 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -35,6 +35,23 @@  static const char * mlme_auth_alg_str(int alg)
 }
 #endif /* CONFIG_NO_HOSTAPD_LOGGER */
 
+static void write_assoc_log(struct hostapd_data *hapd, struct sta_info *sta, char op) {
+	struct os_time now;
+	int fd = hapd->assoc_log_fd;
+	if (fd >= 0) {
+		os_get_time(&now);
+		char msg[100];
+		sprintf(msg,
+		        "%lu\t" MACSTR "\t%c\t%s\t" MACSTR "\t'%s'\n",
+		        now.sec,
+		        MAC2STR(sta->addr),
+			op,
+		        hapd->conf->iface,
+		        MAC2STR(hapd->own_addr),
+		        wpa_ssid_txt(sta->ssid->ssid, sta->ssid->ssid_len));
+		write(fd, msg, strlen(msg));
+	}
+}
 
 /**
  * mlme_authenticate_indication - Report the establishment of an authentication
@@ -104,6 +121,9 @@  void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-ASSOCIATE.indication(" MACSTR ")",
 		       MAC2STR(sta->addr));
+
+	write_assoc_log(hapd, sta, '+');
+
 	if (sta->auth_alg != WLAN_AUTH_FT)
 		mlme_deletekeys_request(hapd, sta);
 }
@@ -128,6 +148,9 @@  void mlme_reassociate_indication(struct hostapd_data *hapd,
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-REASSOCIATE.indication(" MACSTR ")",
 		       MAC2STR(sta->addr));
+
+	write_assoc_log(hapd, sta, '@');
+
 	if (sta->auth_alg != WLAN_AUTH_FT)
 		mlme_deletekeys_request(hapd, sta);
 }
@@ -152,6 +175,9 @@  void mlme_disassociate_indication(struct hostapd_data *hapd,
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-DISASSOCIATE.indication(" MACSTR ", %d)",
 		       MAC2STR(sta->addr), reason_code);
+
+	write_assoc_log(hapd, sta, '-');
+
 	mlme_deletekeys_request(hapd, sta);
 }
 
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 6e4169b..a449e78 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -73,6 +73,10 @@  static void hostapd_reload_bss(struct hostapd_data *hapd)
 	radius_client_reconfig(hapd->radius, hapd->conf->radius);
 #endif /* CONFIG_NO_RADIUS */
 
+	/* close and reopen assoc_log */
+	hostapd_close_assoc_log(hapd);
+	hostapd_open_assoc_log(hapd);
+
 	ssid = &hapd->conf->ssid;
 	if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next &&
 	    ssid->wpa_passphrase_set && ssid->wpa_passphrase) {
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 8e2c70e..91018a8 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -114,6 +114,8 @@  struct hostapd_data {
 #define STA_HASH(sta) (sta[5])
 	struct sta_info *sta_hash[STA_HASH_SIZE];
 
+	int assoc_log_fd;
+
 	/*
 	 * Bitfield for indicating which AIDs are allocated. Only AID values
 	 * 1-2007 are used and as such, the bit at index 0 corresponds to AID