Question about batman for ARM
by Charles Chien
Hello developers,
We’re currently trying to install batman-adv on an ARM platform but we
ran into some issues and we would appreciate your help to answer our
questions below:
Is the batman-adv module compatible with an ARM processor (Raspberry PI 4)?
I’m trying to install batman-adv onto an ARM Raspberry PI 4 with a
Debian based Linux distribution (Pop OS). However, when I run
“modprobe batman-adv”, it returns a FATAL error complaining that the
module is not found in the “/lib/modules” directory. Where can I
find/download the “.ko” for batman-adv for the ARM platform?
And if I need to recompile the .ko file for ARM, is there an online
repository where I can find the original source code for the
batman-adv driver?
--
Regards,
Charles
3 months
Re: [RFC PATCH v3 1/1] mac80211: use AQL airtime for expected throughput.
by Linus Lüssing
On Tue, May 31, 2022 at 12:09:22PM +0200, Baligh Gasmi wrote:
> Since the integration of AQL, packet TX airtime estimation is
> calculated and counted to be used for the dequeue limit.
>
> Use this estimated airtime to compute expected throughput for
> each station.
>
> It will be a generic mac80211 implementation. If the driver has
> get_expected_throughput implementation, it will be used instead.
>
> Useful for L2 routing protocols, like B.A.T.M.A.N.
>
> Signed-off-by: Baligh Gasmi <gasmibal(a)gmail.com>
Hi Baligh,
Thanks for your work, this indeed sounds very relevant for
batman-adv. Do you have some test results on how this compares to
real throughput? And maybe how it compares to other methods we
already have in the kernel, like expected throughput via
minstrel_ht rate control or the estimates performed in 802.11s
HWMP [0]?
Is there a certain minimum amount of traffic you'd suggest to have
enough samples to get a meaningful result?
I'm also wondering if we are starting to accumulate too many
places to provide wifi expected throughput calculations. Do you
see a chance that this generic mac80211 implementation could be made
good enough to be used as the sole source for both batman-adv and
802.11s HWMP, for instance? Or do you see some pros and cons
between the different methods?
Regards, Linus
[0]: https://elixir.bootlin.com/linux/v5.18/source/net/mac80211/mesh_hwmp.c#L295
8 months
[PATCH v3] alfred: notify event listener via unix socket
by Marek Lindner
The alfred server instance accepts event notification registration
via the unix socket. These notification sockets only inform
registered parties of the availability of an alfred datatype change.
The actual data itself needs to be retrieved via the existing data
retrieval mechanisms.
Unlike the update-command this event monitor allows:
- multiple parallel listeners
- programmatic access to changes without requiring multiple processes
The alfred client allows to monitor events via the newly added '-E'
(event monitor) command line option. Serving as debugging tool and
example code at the same time.
Signed-off-by: Marek Lindner <mareklindner(a)neomailbox.ch>
---
v3:
- rebase on top of master
- convert to epoll
- add source MAC address to event details
v2:
- fix typ0s
- replace list_del_init() with list_del()
- remove unnecessary INIT_LIST_HEAD()
- change --event-monitor to not require an argument
alfred.h | 13 +++++
client.c | 58 ++++++++++++++++++++++
main.c | 10 +++-
man/alfred.8 | 3 ++
packet.h | 27 ++++++++++
recv.c | 5 +-
server.c | 1 +
unix_sock.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 251 insertions(+), 2 deletions(-)
diff --git a/alfred.h b/alfred.h
index 8bea033..127b115 100644
--- a/alfred.h
+++ b/alfred.h
@@ -99,6 +99,7 @@ enum clientmode {
CLIENT_CHANGE_INTERFACE,
CLIENT_CHANGE_BAT_IFACE,
CLIENT_SERVER_STATUS,
+ CLIENT_EVENT_MONITOR,
};
struct interface {
@@ -118,8 +119,16 @@ struct interface {
struct list_head list;
};
+struct event_listener {
+ int fd;
+ struct epoll_handle epoll;
+
+ struct list_head list;
+};
+
struct globals {
struct list_head interfaces;
+ struct list_head event_listeners;
char *net_iface;
struct server *best_server; /* NULL if we are a server ourselves */
@@ -171,6 +180,7 @@ int alfred_client_modeswitch(struct globals *globals);
int alfred_client_change_interface(struct globals *globals);
int alfred_client_change_bat_iface(struct globals *globals);
int alfred_client_server_status(struct globals *globals);
+int alfred_client_event_monitor(struct globals *globals);
/* recv.c */
int recv_alfred_packet(struct globals *globals, struct interface *interface,
int recv_sock);
@@ -199,6 +209,9 @@ int unix_sock_open_client(struct globals *globals);
int unix_sock_close(struct globals *globals);
int unix_sock_req_data_finish(struct globals *globals,
struct transaction_head *head);
+void unix_sock_events_close_all(struct globals *globals);
+void unix_sock_event_notify(struct globals *globals, uint8_t type,\
+ const uint8_t source[ETH_ALEN]);
/* vis.c */
int vis_update_data(struct globals *globals);
/* netsock.c */
diff --git a/client.c b/client.c
index 81cdd7c..ace42b9 100644
--- a/client.c
+++ b/client.c
@@ -452,3 +452,61 @@ err:
unix_sock_close(globals);
return 0;
}
+
+int alfred_client_event_monitor(struct globals *globals)
+{
+ struct alfred_event_register_v0 event_register;
+ struct alfred_event_notify_v0 event_notify;
+ int ret, len;
+
+ if (unix_sock_open_client(globals))
+ return -1;
+
+ len = sizeof(event_register);
+
+ event_register.header.type = ALFRED_EVENT_REGISTER;
+ event_register.header.version = ALFRED_VERSION;
+ event_register.header.length = 0;
+
+ ret = write(globals->unix_sock, &event_register, len);
+ if (ret != len) {
+ fprintf(stderr, "%s: only wrote %d of %d bytes: %s\n",
+ __func__, ret, len, strerror(errno));
+ goto err;
+ }
+
+ while (true) {
+ len = read(globals->unix_sock, &event_notify, sizeof(event_notify));
+ if (len == 0) {
+ fprintf(stdout, "Server closed the connection\n");
+ goto err;
+ }
+
+ if (len < 0) {
+ perror("read from unix socket failed");
+ goto err;
+ }
+
+ if (len != sizeof(event_notify)) {
+ fprintf(stderr, "notify read bytes: %d (expected: %zu)\n",
+ len, sizeof(event_notify));
+ goto err;
+ }
+
+ if (event_notify.header.version != ALFRED_VERSION)
+ continue;
+
+ if (event_notify.header.type != ALFRED_EVENT_NOTIFY)
+ continue;
+
+ fprintf(stdout, "Event: type = %hhu, source = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ event_notify.type,
+ event_notify.source[0], event_notify.source[1],
+ event_notify.source[2], event_notify.source[3],
+ event_notify.source[4], event_notify.source[5]);
+ }
+
+err:
+ unix_sock_close(globals);
+ return 0;
+}
diff --git a/main.c b/main.c
index 30e18a5..3fe7b42 100644
--- a/main.c
+++ b/main.c
@@ -39,6 +39,7 @@ static void alfred_usage(void)
printf(" -I, --change-interface [interface] change to the specified interface(s)\n");
printf(" -B, --change-bat-iface [interface] change to the specified batman-adv interface\n");
printf(" -S, --server-status request server status info such as mode & interfaces\n");
+ printf(" -E, --event-monitor monitor alfred data record update events\n");
printf("\n");
printf("server mode options:\n");
printf(" -i, --interface specify the interface (or comma separated list of interfaces) to listen on\n");
@@ -164,6 +165,7 @@ static struct globals *alfred_init(int argc, char *argv[])
{"change-interface", required_argument, NULL, 'I'},
{"change-bat-iface", required_argument, NULL, 'B'},
{"server-status", no_argument, NULL, 'S'},
+ {"event-monitor", no_argument, NULL, 'E'},
{"unix-path", required_argument, NULL, 'u'},
{"update-command", required_argument, NULL, 'c'},
{"version", no_argument, NULL, 'v'},
@@ -181,6 +183,7 @@ static struct globals *alfred_init(int argc, char *argv[])
memset(globals, 0, sizeof(*globals));
INIT_LIST_HEAD(&globals->interfaces);
+ INIT_LIST_HEAD(&globals->event_listeners);
globals->net_iface = NULL;
globals->opmode = OPMODE_SECONDARY;
globals->clientmode = CLIENT_NONE;
@@ -198,7 +201,7 @@ static struct globals *alfred_init(int argc, char *argv[])
time_random_seed();
- while ((opt = getopt_long(argc, argv, "ms:r:hi:b:vV:M:I:B:Su:dc:p:4:f", long_options,
+ while ((opt = getopt_long(argc, argv, "ms:r:hi:b:vV:M:I:B:SEu:dc:p:4:f", long_options,
&opt_ind)) != -1) {
switch (opt) {
case 'r':
@@ -263,6 +266,9 @@ static struct globals *alfred_init(int argc, char *argv[])
case 'S':
globals->clientmode = CLIENT_SERVER_STATUS;
break;
+ case 'E':
+ globals->clientmode = CLIENT_EVENT_MONITOR;
+ break;
case 'u':
globals->unix_path = optarg;
break;
@@ -328,6 +334,8 @@ int main(int argc, char *argv[])
return alfred_client_change_bat_iface(globals);
case CLIENT_SERVER_STATUS:
return alfred_client_server_status(globals);
+ case CLIENT_EVENT_MONITOR:
+ return alfred_client_event_monitor(globals);
}
return 0;
diff --git a/man/alfred.8 b/man/alfred.8
index cf0eafc..ca83f02 100644
--- a/man/alfred.8
+++ b/man/alfred.8
@@ -97,6 +97,9 @@ Change the alfred server to use the new \fBbatman-adv interface\fP
.TP
\fB\-S\fP, \fB\-\-server\-status\fP
Request server status information such as mode & interfaces\fP
+.TP
+\fB\-E\fP, \fB\-\-event\-monitor\fP
+Start alfred event monitor connecting to the alfred server and reporting update events\fP
.
.SH SERVER OPTIONS
.TP
diff --git a/packet.h b/packet.h
index 0c1a2eb..b8d528e 100644
--- a/packet.h
+++ b/packet.h
@@ -59,6 +59,8 @@ struct alfred_transaction_mgmt {
* @ALFRED_STATUS_ERROR: Error was detected during the transaction
* @ALFRED_MODESWITCH: Switch between different operation modes
* @ALFRED_CHANGE_INTERFACE: Change the listening interface
+ * @ALFRED_EVENT_REGISTER: Request to be notified about alfred update events
+ * @ALFRED_EVENT_NOTIFY: Data record update has been received
*/
enum alfred_packet_type {
ALFRED_PUSH_DATA = 0,
@@ -70,6 +72,8 @@ enum alfred_packet_type {
ALFRED_CHANGE_INTERFACE = 6,
ALFRED_CHANGE_BAT_IFACE = 7,
ALFRED_SERVER_STATUS = 8,
+ ALFRED_EVENT_REGISTER = 9,
+ ALFRED_EVENT_NOTIFY = 10,
};
/* packets */
@@ -229,6 +233,29 @@ struct alfred_server_status_rep_v0 {
struct alfred_tlv header;
} __packed;
+/**
+ * struct alfred_event_register_v0 - event registration message
+ * @header: TLV header describing the complete packet
+ *
+ * Sent by the client to daemon to register for data record updates
+ */
+struct alfred_event_register_v0 {
+ struct alfred_tlv header;
+} __packed;
+
+/**
+ * struct alfred_event_notify_v0 - event notification message
+ * @header: TLV header describing the complete packet
+ * @type: Type of the data triggering the event
+ *
+ * Sent by the daemon to client on data record update
+ */
+struct alfred_event_notify_v0 {
+ struct alfred_tlv header;
+ uint8_t type;
+ uint8_t source[ETH_ALEN];
+} __packed;
+
/**
* struct alfred_status_v0 - Status info of a transaction
* @header: TLV header describing the complete packet
diff --git a/recv.c b/recv.c
index 8acad10..75e3faf 100644
--- a/recv.c
+++ b/recv.c
@@ -76,8 +76,11 @@ static int finish_alfred_push_data(struct globals *globals,
/* check that data was changed */
if (new_entry_created ||
dataset->data.header.length != data_len ||
- memcmp(dataset->buf, data->data, data_len) != 0)
+ memcmp(dataset->buf, data->data, data_len) != 0) {
changed_data_type(globals, data->header.type);
+ unix_sock_event_notify(globals, data->header.type,
+ data->source);
+ }
/* free old buffer */
if (dataset->buf) {
diff --git a/server.c b/server.c
index 52cdbe7..0d792b0 100644
--- a/server.c
+++ b/server.c
@@ -518,5 +518,6 @@ int alfred_server(struct globals *globals)
netsock_close_all(globals);
unix_sock_close(globals);
+ unix_sock_events_close_all(globals);
return 0;
}
diff --git a/unix_sock.c b/unix_sock.c
index ef72aa0..ce08a51 100644
--- a/unix_sock.c
+++ b/unix_sock.c
@@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/un.h>
@@ -26,6 +27,8 @@
static void unix_sock_read(struct globals *globals,
struct epoll_handle *handle __unused,
struct epoll_event *ev __unused);
+static int unix_sock_register_listener(struct globals *globals,
+ int client_sock);
int unix_sock_open_daemon(struct globals *globals)
{
@@ -99,6 +102,7 @@ static int unix_sock_add_data(struct globals *globals,
struct alfred_push_data_v0 *push,
int client_sock)
{
+ bool new_entry_created = false;
struct alfred_data *data;
struct dataset *dataset;
int len, data_len, ret = -1;
@@ -153,10 +157,18 @@ static int unix_sock_add_data(struct globals *globals,
free(dataset);
goto err;
}
+ new_entry_created = true;
}
dataset->data_source = SOURCE_LOCAL;
clock_gettime(CLOCK_MONOTONIC, &dataset->last_seen);
+ /* check that data was changed */
+ if (new_entry_created ||
+ dataset->data.header.length != data_len ||
+ memcmp(dataset->buf, data->data, data_len) != 0)
+ unix_sock_event_notify(globals, data->header.type,
+ data->source);
+
/* free old buffer */
free(dataset->buf);
@@ -554,6 +566,9 @@ static void unix_sock_read(struct globals *globals,
case ALFRED_SERVER_STATUS:
unix_sock_server_status(globals, client_sock);
break;
+ case ALFRED_EVENT_REGISTER:
+ unix_sock_register_listener(globals, client_sock);
+ break;
default:
/* unknown packet type */
goto err;
@@ -570,3 +585,124 @@ int unix_sock_close(struct globals *globals)
close(globals->unix_sock);
return 0;
}
+
+static void unix_sock_event_listener_free(struct event_listener *listener)
+{
+ list_del(&listener->list);
+ close(listener->fd);
+ free(listener);
+}
+
+static void unix_sock_event_listener_handle(struct globals *globals __unused,
+ struct epoll_handle *handle,
+ struct epoll_event *ev)
+{
+ struct event_listener *listener;
+ char buff[4];
+ int ret;
+
+ listener = container_of(handle, struct event_listener, epoll);
+
+ if (ev->events & EPOLLERR) {
+ fprintf(stderr, "Error on event listener detected: %d\n",
+ listener->fd);
+ unix_sock_event_listener_free(listener);
+ return;
+ }
+
+ if (ev->events & EPOLLIN) {
+ ret = recv(listener->fd, buff, sizeof(buff),
+ MSG_PEEK | MSG_DONTWAIT);
+ /* listener has hung up */
+ if (ret == 0)
+ unix_sock_event_listener_free(listener);
+ else if (ret > 0) {
+ fprintf(stderr, "Event listener has written to socket: %d - closing\n",
+ listener->fd);
+ unix_sock_event_listener_free(listener);
+ }
+ }
+}
+
+static int unix_sock_register_listener(struct globals *globals, int client_sock)
+{
+ struct event_listener *listener;
+ struct epoll_event ev;
+ int ret;
+
+ ret = fcntl(client_sock, F_GETFL, 0);
+ if (ret < 0) {
+ perror("failed to get file status flags");
+ goto err;
+ }
+
+ ret = fcntl(client_sock, F_SETFL, ret | O_NONBLOCK);
+ if (ret < 0) {
+ perror("failed to set file status flags");
+ goto err;
+ }
+
+ listener = malloc(sizeof(*listener));
+ if (!listener)
+ goto err;
+
+ ev.events = EPOLLIN;
+ ev.data.ptr = &listener->epoll;
+ listener->epoll.handler = unix_sock_event_listener_handle;
+
+ if (epoll_ctl(globals->epollfd, EPOLL_CTL_ADD, client_sock,
+ &ev) == -1) {
+ perror("Failed to add epoll for event listener");
+ goto err;
+ }
+
+ listener->fd = client_sock;
+ list_add_tail(&listener->list, &globals->event_listeners);
+ return 0;
+
+err:
+ close(client_sock);
+ return -1;
+}
+
+static void unix_sock_event_notify_listener(struct event_listener *listener,
+ uint8_t type,
+ const uint8_t source[ETH_ALEN])
+{
+ struct alfred_event_notify_v0 notify;
+ int ret;
+
+ notify.header.type = ALFRED_EVENT_NOTIFY;
+ notify.header.version = ALFRED_VERSION;
+ notify.header.length = FIXED_TLV_LEN(notify);
+ notify.type = type;
+ memcpy(¬ify.source, source, ETH_ALEN);
+
+ ret = write(listener->fd, ¬ify, sizeof(notify));
+ if (ret == sizeof(notify))
+ return;
+
+ unix_sock_event_listener_free(listener);
+}
+
+void unix_sock_events_close_all(struct globals *globals)
+{
+ struct event_listener *listener, *tmp;
+
+ list_for_each_entry_safe(listener, tmp,
+ &globals->event_listeners, list) {
+ unix_sock_event_listener_free(listener);
+ }
+}
+
+void unix_sock_event_notify(struct globals *globals, uint8_t type,
+ const uint8_t source[ETH_ALEN])
+{
+ struct event_listener *listener, *tmp;
+
+ /* if event notify is unsuccessful, listener socket is closed */
+ list_for_each_entry_safe(listener, tmp,
+ &globals->event_listeners, list) {
+ unix_sock_event_notify_listener(listener, type, source);
+ }
+}
--
2.32.0.rc0
8 months, 2 weeks
[PATCH v2] alfred: notify event listener via unix socket
by Marek Lindner
The alfred server instance accepts event notification registration
via the unix socket. These notification sockets only inform
registered parties of the availability of an alfred datatype change.
The actual data itself needs to be retrieved via the existing data
retrieval mechanisms.
Unlike the update-command this event monitor allows:
- multiple parallel listeners
- programmatic access to changes without requiring multiple processes
The alfred client allows to monitor events via the newly added '-E'
(event monitor) command line option. Serving as debugging tool and
example code at the same time.
Signed-off-by: Marek Lindner <mareklindner(a)neomailbox.ch>
---
v2:
- fix typ0s
- replace list_del_init() with list_del()
- remove unnecessary INIT_LIST_HEAD()
- change --event-monitor to not require an argument
alfred.h | 15 ++++++
client.c | 54 ++++++++++++++++++++
main.c | 10 +++-
man/alfred.8 | 3 ++
packet.h | 26 ++++++++++
recv.c | 4 +-
server.c | 5 ++
unix_sock.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 256 insertions(+), 2 deletions(-)
diff --git a/alfred.h b/alfred.h
index 2d98a30..f442c48 100644
--- a/alfred.h
+++ b/alfred.h
@@ -94,6 +94,7 @@ enum clientmode {
CLIENT_CHANGE_INTERFACE,
CLIENT_CHANGE_BAT_IFACE,
CLIENT_SERVER_STATUS,
+ CLIENT_EVENT_MONITOR,
};
struct interface {
@@ -110,8 +111,15 @@ struct interface {
struct list_head list;
};
+struct event_listener {
+ int fd;
+
+ struct list_head list;
+};
+
struct globals {
struct list_head interfaces;
+ struct list_head event_listeners;
char *net_iface;
struct server *best_server; /* NULL if we are a server ourselves */
@@ -157,6 +165,7 @@ int alfred_client_modeswitch(struct globals *globals);
int alfred_client_change_interface(struct globals *globals);
int alfred_client_change_bat_iface(struct globals *globals);
int alfred_client_server_status(struct globals *globals);
+int alfred_client_event_monitor(struct globals *globals);
/* recv.c */
int recv_alfred_packet(struct globals *globals, struct interface *interface,
int recv_sock);
@@ -186,6 +195,12 @@ int unix_sock_open_client(struct globals *globals);
int unix_sock_close(struct globals *globals);
int unix_sock_req_data_finish(struct globals *globals,
struct transaction_head *head);
+int unix_sock_events_select_prepare(struct globals *globals, fd_set *fds,
+ fd_set *errfds, int maxsock);
+void unix_sock_events_select_handle(struct globals *globals,
+ fd_set *fds, fd_set *errfds);
+void unix_sock_events_close_all(struct globals *globals);
+void unix_sock_event_notify(struct globals *globals, uint8_t type);
/* vis.c */
int vis_update_data(struct globals *globals);
/* netsock.c */
diff --git a/client.c b/client.c
index 81cdd7c..d86d23c 100644
--- a/client.c
+++ b/client.c
@@ -452,3 +452,57 @@ err:
unix_sock_close(globals);
return 0;
}
+
+int alfred_client_event_monitor(struct globals *globals)
+{
+ struct alfred_event_register_v0 event_register;
+ struct alfred_event_notify_v0 event_notify;
+ int ret, len;
+
+ if (unix_sock_open_client(globals))
+ return -1;
+
+ len = sizeof(event_register);
+
+ event_register.header.type = ALFRED_EVENT_REGISTER;
+ event_register.header.version = ALFRED_VERSION;
+ event_register.header.length = 0;
+
+ ret = write(globals->unix_sock, &event_register, len);
+ if (ret != len) {
+ fprintf(stderr, "%s: only wrote %d of %d bytes: %s\n",
+ __func__, ret, len, strerror(errno));
+ goto err;
+ }
+
+ while (true) {
+ len = read(globals->unix_sock, &event_notify, sizeof(event_notify));
+ if (len == 0) {
+ fprintf(stdout, "Server closed the connection\n");
+ goto err;
+ }
+
+ if (len < 0) {
+ perror("read from unix socket failed");
+ goto err;
+ }
+
+ if (len != sizeof(event_notify)) {
+ fprintf(stderr, "notify read bytes: %d (expected: %zu)\n",
+ len, sizeof(event_notify));
+ goto err;
+ }
+
+ if (event_notify.header.version != ALFRED_VERSION)
+ continue;
+
+ if (event_notify.header.type != ALFRED_EVENT_NOTIFY)
+ continue;
+
+ fprintf(stdout, "Event: type = %hhu\n", event_notify.type);
+ }
+
+err:
+ unix_sock_close(globals);
+ return 0;
+}
diff --git a/main.c b/main.c
index 68d6efd..6dfb1e7 100644
--- a/main.c
+++ b/main.c
@@ -39,6 +39,7 @@ static void alfred_usage(void)
printf(" -I, --change-interface [interface] change to the specified interface(s)\n");
printf(" -B, --change-bat-iface [interface] change to the specified batman-adv interface\n");
printf(" -S, --server-status request server status info such as mode & interfaces\n");
+ printf(" -E, --event-monitor monitor alfred data record update events\n");
printf("\n");
printf("server mode options:\n");
printf(" -i, --interface specify the interface (or comma separated list of interfaces) to listen on\n");
@@ -164,6 +165,7 @@ static struct globals *alfred_init(int argc, char *argv[])
{"change-interface", required_argument, NULL, 'I'},
{"change-bat-iface", required_argument, NULL, 'B'},
{"server-status", required_argument, NULL, 'S'},
+ {"event-monitor", no_argument, NULL, 'E'},
{"unix-path", required_argument, NULL, 'u'},
{"update-command", required_argument, NULL, 'c'},
{"version", no_argument, NULL, 'v'},
@@ -181,6 +183,7 @@ static struct globals *alfred_init(int argc, char *argv[])
memset(globals, 0, sizeof(*globals));
INIT_LIST_HEAD(&globals->interfaces);
+ INIT_LIST_HEAD(&globals->event_listeners);
globals->net_iface = NULL;
globals->opmode = OPMODE_SECONDARY;
globals->clientmode = CLIENT_NONE;
@@ -198,7 +201,7 @@ static struct globals *alfred_init(int argc, char *argv[])
time_random_seed();
- while ((opt = getopt_long(argc, argv, "ms:r:hi:b:vV:M:I:B:Su:dc:p:4:f", long_options,
+ while ((opt = getopt_long(argc, argv, "ms:r:hi:b:vV:M:I:B:SEu:dc:p:4:f", long_options,
&opt_ind)) != -1) {
switch (opt) {
case 'r':
@@ -263,6 +266,9 @@ static struct globals *alfred_init(int argc, char *argv[])
case 'S':
globals->clientmode = CLIENT_SERVER_STATUS;
break;
+ case 'E':
+ globals->clientmode = CLIENT_EVENT_MONITOR;
+ break;
case 'u':
globals->unix_path = optarg;
break;
@@ -328,6 +334,8 @@ int main(int argc, char *argv[])
return alfred_client_change_bat_iface(globals);
case CLIENT_SERVER_STATUS:
return alfred_client_server_status(globals);
+ case CLIENT_EVENT_MONITOR:
+ return alfred_client_event_monitor(globals);
}
return 0;
diff --git a/man/alfred.8 b/man/alfred.8
index cf0eafc..ca83f02 100644
--- a/man/alfred.8
+++ b/man/alfred.8
@@ -97,6 +97,9 @@ Change the alfred server to use the new \fBbatman-adv interface\fP
.TP
\fB\-S\fP, \fB\-\-server\-status\fP
Request server status information such as mode & interfaces\fP
+.TP
+\fB\-E\fP, \fB\-\-event\-monitor\fP
+Start alfred event monitor connecting to the alfred server and reporting update events\fP
.
.SH SERVER OPTIONS
.TP
diff --git a/packet.h b/packet.h
index 0c1a2eb..84b027f 100644
--- a/packet.h
+++ b/packet.h
@@ -59,6 +59,8 @@ struct alfred_transaction_mgmt {
* @ALFRED_STATUS_ERROR: Error was detected during the transaction
* @ALFRED_MODESWITCH: Switch between different operation modes
* @ALFRED_CHANGE_INTERFACE: Change the listening interface
+ * @ALFRED_EVENT_REGISTER: Request to be notified about alfred update events
+ * @ALFRED_EVENT_NOTIFY: Data record update has been received
*/
enum alfred_packet_type {
ALFRED_PUSH_DATA = 0,
@@ -70,6 +72,8 @@ enum alfred_packet_type {
ALFRED_CHANGE_INTERFACE = 6,
ALFRED_CHANGE_BAT_IFACE = 7,
ALFRED_SERVER_STATUS = 8,
+ ALFRED_EVENT_REGISTER = 9,
+ ALFRED_EVENT_NOTIFY = 10,
};
/* packets */
@@ -229,6 +233,28 @@ struct alfred_server_status_rep_v0 {
struct alfred_tlv header;
} __packed;
+/**
+ * struct alfred_event_register_v0 - event registration message
+ * @header: TLV header describing the complete packet
+ *
+ * Sent by the client to daemon to register for data record updates
+ */
+struct alfred_event_register_v0 {
+ struct alfred_tlv header;
+} __packed;
+
+/**
+ * struct alfred_event_notify_v0 - event notification message
+ * @header: TLV header describing the complete packet
+ * @type: Type of the data triggering the event
+ *
+ * Sent by the daemon to client on data record update
+ */
+struct alfred_event_notify_v0 {
+ struct alfred_tlv header;
+ uint8_t type;
+} __packed;
+
/**
* struct alfred_status_v0 - Status info of a transaction
* @header: TLV header describing the complete packet
diff --git a/recv.c b/recv.c
index 8acad10..36b3a49 100644
--- a/recv.c
+++ b/recv.c
@@ -76,8 +76,10 @@ static int finish_alfred_push_data(struct globals *globals,
/* check that data was changed */
if (new_entry_created ||
dataset->data.header.length != data_len ||
- memcmp(dataset->buf, data->data, data_len) != 0)
+ memcmp(dataset->buf, data->data, data_len) != 0) {
changed_data_type(globals, data->header.type);
+ unix_sock_event_notify(globals, data->header.type);
+ }
/* free old buffer */
if (dataset->buf) {
diff --git a/server.c b/server.c
index bfc37bc..4078fe6 100644
--- a/server.c
+++ b/server.c
@@ -442,6 +442,8 @@ int alfred_server(struct globals *globals)
maxsock = netsock_prepare_select(globals, &fds, maxsock);
maxsock = netsock_prepare_select(globals, &errfds, maxsock);
+ maxsock = unix_sock_events_select_prepare(globals, &fds,
+ &errfds, maxsock);
ret = pselect(maxsock + 1, &fds, NULL, &errfds, &tv, NULL);
@@ -450,6 +452,8 @@ int alfred_server(struct globals *globals)
} else if (ret) {
netsock_check_error(globals, &errfds);
+ unix_sock_events_select_handle(globals, &fds, &errfds);
+
if (FD_ISSET(globals->unix_sock, &fds)) {
unix_sock_read(globals);
continue;
@@ -478,5 +482,6 @@ int alfred_server(struct globals *globals)
netsock_close_all(globals);
unix_sock_close(globals);
+ unix_sock_events_close_all(globals);
return 0;
}
diff --git a/unix_sock.c b/unix_sock.c
index 3894736..f488763 100644
--- a/unix_sock.c
+++ b/unix_sock.c
@@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/un.h>
@@ -84,6 +85,7 @@ static int unix_sock_add_data(struct globals *globals,
struct alfred_push_data_v0 *push,
int client_sock)
{
+ bool new_entry_created = false;
struct alfred_data *data;
struct dataset *dataset;
int len, data_len, ret = -1;
@@ -138,10 +140,17 @@ static int unix_sock_add_data(struct globals *globals,
free(dataset);
goto err;
}
+ new_entry_created = true;
}
dataset->data_source = SOURCE_LOCAL;
clock_gettime(CLOCK_MONOTONIC, &dataset->last_seen);
+ /* check that data was changed */
+ if (new_entry_created ||
+ dataset->data.header.length != data_len ||
+ memcmp(dataset->buf, data->data, data_len) != 0)
+ unix_sock_event_notify(globals, data->header.type);
+
/* free old buffer */
free(dataset->buf);
@@ -472,6 +481,36 @@ err:
return ret;
}
+static int unix_sock_register_listener(struct globals *globals, int client_sock)
+{
+ struct event_listener *listener;
+ int ret;
+
+ ret = fcntl(client_sock, F_GETFL, 0);
+ if (ret < 0) {
+ perror("failed to get file status flags");
+ goto err;
+ }
+
+ ret = fcntl(client_sock, F_SETFL, ret | O_NONBLOCK);
+ if (ret < 0) {
+ perror("failed to set file status flags");
+ goto err;
+ }
+
+ listener = malloc(sizeof(*listener));
+ if (!listener)
+ goto err;
+
+ listener->fd = client_sock;
+ list_add_tail(&listener->list, &globals->event_listeners);
+ return 0;
+
+err:
+ close(client_sock);
+ return -1;
+}
+
int unix_sock_read(struct globals *globals)
{
int client_sock;
@@ -537,6 +576,9 @@ int unix_sock_read(struct globals *globals)
case ALFRED_SERVER_STATUS:
ret = unix_sock_server_status(globals, client_sock);
break;
+ case ALFRED_EVENT_REGISTER:
+ ret = unix_sock_register_listener(globals, client_sock);
+ break;
default:
/* unknown packet type */
ret = -1;
@@ -555,3 +597,102 @@ int unix_sock_close(struct globals *globals)
close(globals->unix_sock);
return 0;
}
+
+static void unix_sock_event_listener_free(struct event_listener *listener)
+{
+ list_del(&listener->list);
+ close(listener->fd);
+ free(listener);
+}
+
+static void unix_sock_event_notify_listener(struct event_listener *listener,
+ uint8_t type)
+{
+ struct alfred_event_notify_v0 notify;
+ int ret;
+
+ notify.header.type = ALFRED_EVENT_NOTIFY;
+ notify.header.version = ALFRED_VERSION;
+ notify.header.length = FIXED_TLV_LEN(notify);
+ notify.type = type;
+
+ ret = write(listener->fd, ¬ify, sizeof(notify));
+ if (ret == sizeof(notify))
+ return;
+
+ unix_sock_event_listener_free(listener);
+}
+
+int unix_sock_events_select_prepare(struct globals *globals, fd_set *fds,
+ fd_set *errfds, int maxsock)
+{
+ struct event_listener *listener;
+
+ list_for_each_entry(listener, &globals->event_listeners, list) {
+ if (listener->fd < 0)
+ continue;
+
+ FD_SET(listener->fd, fds);
+ FD_SET(listener->fd, errfds);
+
+ if (maxsock < listener->fd)
+ maxsock = listener->fd;
+ }
+
+ return maxsock;
+}
+
+void unix_sock_events_select_handle(struct globals *globals,
+ fd_set *fds, fd_set *errfds)
+{
+ struct event_listener *listener, *tmp;
+ char buff[4];
+ int ret;
+
+ list_for_each_entry_safe(listener, tmp,
+ &globals->event_listeners, list) {
+ if (FD_ISSET(listener->fd, fds)) {
+ ret = recv(listener->fd, buff, sizeof(buff),
+ MSG_PEEK | MSG_DONTWAIT);
+ /* listener has hung up */
+ if (ret == 0)
+ unix_sock_event_listener_free(listener);
+ else if (ret > 0) {
+ fprintf(stderr, "Event listener has written to socket: %d - closing\n",
+ listener->fd);
+ unix_sock_event_listener_free(listener);
+ }
+
+ if (ret >= 0)
+ continue;
+ }
+
+ if (FD_ISSET(listener->fd, errfds)) {
+ fprintf(stderr, "Error on event listener detected: %d\n",
+ listener->fd);
+ unix_sock_event_listener_free(listener);
+ continue;
+ }
+ }
+}
+
+void unix_sock_events_close_all(struct globals *globals)
+{
+ struct event_listener *listener, *tmp;
+
+ list_for_each_entry_safe(listener, tmp,
+ &globals->event_listeners, list) {
+ unix_sock_event_listener_free(listener);
+ }
+}
+
+void unix_sock_event_notify(struct globals *globals, uint8_t type)
+{
+ struct event_listener *listener, *tmp;
+
+ /* if event notify is unsuccessful, listener socket is closed */
+ list_for_each_entry_safe(listener, tmp,
+ &globals->event_listeners, list) {
+ unix_sock_event_notify_listener(listener, type);
+ }
+}
--
2.32.0.rc0
8 months, 2 weeks
[PATCH net 00/12] net: iflink and link-netnsid fixes
by Sabrina Dubroca
In a lot of places, we use this kind of comparison to detect if a
device has a lower link:
dev->ifindex != dev_get_iflink(dev)
This seems to be a leftover of the pre-netns days, when the ifindex
was unique over the whole system. Nowadays, with network namespaces,
it's very easy to create a device with the same ifindex as its lower
link:
ip netns add main
ip netns add peer
ip -net main link add dummy0 type dummy
ip -net main link add link dummy0 macvlan0 netns peer type macvlan
ip -net main link show type dummy
9: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop ...
ip -net peer link show type macvlan
9: macvlan0@if9: <BROADCAST,MULTICAST> mtu 1500 qdisc noop ...
To detect if a device has a lower link, we can simply check the
existence of the dev->netdev_ops->ndo_get_iflink operation, instead of
checking its return value. In particular, I attempted to fix one of
these checks in commit feadc4b6cf42 ("rtnetlink: always put IFLA_LINK
for links with a link-netnsid"), but this patch isn't correct, since
tunnel devices can export IFLA_LINK_NETNSID without IFLA_LINK. That
patch needs to be reverted.
This series will fix all those bogus comparisons, and export missing
IFLA_LINK_NETNSID attributes in bridge and ipv6 dumps.
ipvlan and geneve are also missing the get_link_net operation, so
userspace can't know when those device are cross-netns. There are a
couple of other device types that have an ndo_get_iflink op but no
get_link_net (virt_wifi, ipoib), and should probably also have a
get_link_net.
Sabrina Dubroca (12):
ipvlan: add get_link_net
geneve: add get_link_net
Revert "rtnetlink: always put IFLA_LINK for links with a link-netnsid"
rtnetlink: always put IFLA_LINK for links with ndo_get_iflink
bridge: always put IFLA_LINK for ports with ndo_get_iflink
bridge: advertise IFLA_LINK_NETNSID when dumping bridge ports
ipv6: always put IFLA_LINK for devices with ndo_get_iflink
ipv6: advertise IFLA_LINK_NETNSID when dumping ipv6 addresses
net: link_watch: fix operstate when the link has the same index as the
device
net: link_watch: fix detection of urgent events
batman-adv: fix iflink detection in batadv_is_on_batman_iface
batman-adv: fix detection of lower link in batadv_get_real_netdevice
drivers/net/can/vxcan.c | 2 +-
drivers/net/geneve.c | 8 ++++++++
drivers/net/ipvlan/ipvlan_main.c | 9 +++++++++
drivers/net/veth.c | 2 +-
include/net/rtnetlink.h | 4 ++++
net/batman-adv/hard-interface.c | 4 ++--
net/bridge/br_netlink.c | 4 +++-
net/core/link_watch.c | 4 ++--
net/core/rtnetlink.c | 25 ++++++++++++-------------
net/ipv6/addrconf.c | 11 ++++++++++-
10 files changed, 52 insertions(+), 21 deletions(-)
--
2.28.0
8 months, 3 weeks
[PATCH 0/1] pull request for net: batman-adv 2022-05-08
by Simon Wunderlich
Hi David, hi Jakub,
here is a bugfix for batman-adv which we would like to have integrated into net.
Please pull or let me know of any problem!
Thank you,
Simon
The following changes since commit 3123109284176b1532874591f7c81f3837bbdc17:
Linux 5.18-rc1 (2022-04-03 14:08:21 -0700)
are available in the Git repository at:
git://git.open-mesh.org/linux-merge.git tags/batadv-net-pullrequest-20220508
for you to fetch changes up to a063f2fba3fa633a599253b62561051ac185fa99:
batman-adv: Don't skb_split skbuffs with frag_list (2022-04-17 23:41:44 +0200)
----------------------------------------------------------------
Here is a batman-adv bugfix:
- Don't skb_split skbuffs with frag_list, by Sven Eckelmann
----------------------------------------------------------------
Sven Eckelmann (1):
batman-adv: Don't skb_split skbuffs with frag_list
net/batman-adv/fragmentation.c | 11 +++++++++++
1 file changed, 11 insertions(+)
8 months, 3 weeks
[PATCH 0/2] pull request for net-next: batman-adv 2022-05-08
by Simon Wunderlich
Hi Jakub, hi David,
here is a very little cleanup pull request of batman-adv to go into net-next.
Please pull or let me know of any problem!
Thank you,
Simon
The following changes since commit 3123109284176b1532874591f7c81f3837bbdc17:
Linux 5.18-rc1 (2022-04-03 14:08:21 -0700)
are available in the Git repository at:
git://git.open-mesh.org/linux-merge.git tags/batadv-next-pullrequest-20220508
for you to fetch changes up to 8864d2fcf04385cabb8c8bb159f1f2ba5790cf71:
batman-adv: remove unnecessary type castings (2022-04-22 11:23:46 +0200)
----------------------------------------------------------------
This cleanup patchset includes the following patches:
- bump version strings, by Simon Wunderlich
- remove unnecessary type castings, by Yu Zhe
----------------------------------------------------------------
Simon Wunderlich (1):
batman-adv: Start new development cycle
Yu Zhe (1):
batman-adv: remove unnecessary type castings
net/batman-adv/bridge_loop_avoidance.c | 4 ++--
net/batman-adv/main.h | 2 +-
net/batman-adv/translation-table.c | 12 ++++++------
3 files changed, 9 insertions(+), 9 deletions(-)
8 months, 4 weeks
[PATCH] alfred: Drop argument requirement from --server-status
by Sven Eckelmann
The short form -S doesn't require a parameter but the --server-status
requires one (which no one reads). If none is supplied, following is
printed:
./alfred: option '--server-status' requires an argument
and the execution stops. Just drop this requirement because it is not used
and no where documented.
Fixes: 5a7d28b1866d ("alfred: introduce 'server status' IPC call")
Signed-off-by: Sven Eckelmann <sven(a)narfation.org>
---
main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/main.c b/main.c
index 68d6efd..30e18a5 100644
--- a/main.c
+++ b/main.c
@@ -163,7 +163,7 @@ static struct globals *alfred_init(int argc, char *argv[])
{"modeswitch", required_argument, NULL, 'M'},
{"change-interface", required_argument, NULL, 'I'},
{"change-bat-iface", required_argument, NULL, 'B'},
- {"server-status", required_argument, NULL, 'S'},
+ {"server-status", no_argument, NULL, 'S'},
{"unix-path", required_argument, NULL, 'u'},
{"update-command", required_argument, NULL, 'c'},
{"version", no_argument, NULL, 'v'},
--
2.34.1
9 months
[PATCH 1/2] alfred: Stabilize synchronization period using timerfd
by Sven Eckelmann
The current way of scheduling the synchronization related events tends to
cause drift from a perfect periodic timer. This happens because it doesn't
calculate the next event based on a fixed start time + period * the number
of past synchronization periods. Instead, the next event was scheduled
based on the time before the last select() - ignoring that non-zero time
was spend processing events.
For a 10 second period, this usually looks somthing like:
[24.043904208] announce primary ...
[34.044216187] announce primary ...
[44.053485658] announce primary ...
[54.063562062] announce primary ...
[64.073517069] announce primary ...
To avoid this drift, just use timerfd as rather stable periodic timer event
sources. It also has the benefit of making it easier to use multiple
periodic timers with different periods.
Only some small jitter can be seen with this external timer implementation:
[12.673756426] announce primary ...
[22.673779811] announce primary ...
[32.673778362] announce primary ...
[42.673775216] announce primary ...
Signed-off-by: Sven Eckelmann <sven(a)narfation.org>
---
alfred.h | 2 ++
server.c | 89 +++++++++++++++++++++++++++++++++++---------------------
2 files changed, 58 insertions(+), 33 deletions(-)
diff --git a/alfred.h b/alfred.h
index 2d98a30..2679515 100644
--- a/alfred.h
+++ b/alfred.h
@@ -124,6 +124,8 @@ struct globals {
uint8_t ipv4mode:1;
uint8_t force:1;
+ int check_timerfd;
+
int unix_sock;
const char *unix_path;
diff --git a/server.c b/server.c
index bfc37bc..b5ec7b2 100644
--- a/server.c
+++ b/server.c
@@ -20,6 +20,7 @@
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
+#include <sys/timerfd.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
@@ -366,17 +367,44 @@ static void execute_update_command(struct globals *globals)
free(command);
}
+static int create_sync_period_timer(struct globals *globals)
+{
+ struct itimerspec sync_timer;
+ int ret;
+
+ globals->check_timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ if (globals->check_timerfd < 0) {
+ perror("Failed to create periodic timer");
+ return -1;
+ }
+
+ sync_timer.it_value = globals->sync_period;
+ sync_timer.it_interval = globals->sync_period;
+
+ ret = timerfd_settime(globals->check_timerfd, 0, &sync_timer, NULL);
+ if (ret < 0) {
+ perror("Failed to arm synchronization timer");
+ return -1;
+ }
+
+ return 0;
+}
+
int alfred_server(struct globals *globals)
{
int maxsock, ret, recvs;
- struct timespec last_check, now, tv;
+ struct timespec now;
fd_set fds, errfds;
size_t num_interfaces;
+ uint64_t timer_exp;
int num_socks;
if (create_hashes(globals))
return -1;
+ if (create_sync_period_timer(globals))
+ return -1;
+
if (unix_sock_open_daemon(globals))
return -1;
@@ -414,25 +442,10 @@ int alfred_server(struct globals *globals)
return -1;
}
- clock_gettime(CLOCK_MONOTONIC, &last_check);
- globals->if_check = last_check;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ globals->if_check = now;
while (1) {
- clock_gettime(CLOCK_MONOTONIC, &now);
-
- /* subtract the synchronization period from the current time
- * NOTE: this is an atypical usage of time_diff as it ignores the return
- * value and store the result back into now, essentially performing the
- * operation:
- * now -= globals->sync_period;
- */
- time_diff(&now, &globals->sync_period, &now);
-
- if (!time_diff(&last_check, &now, &tv)) {
- tv.tv_sec = 0;
- tv.tv_nsec = 0;
- }
-
netsock_reopen(globals);
FD_ZERO(&fds);
@@ -440,10 +453,14 @@ int alfred_server(struct globals *globals)
FD_SET(globals->unix_sock, &fds);
maxsock = globals->unix_sock;
+ FD_SET(globals->check_timerfd, &fds);
+ if (maxsock < globals->check_timerfd)
+ maxsock = globals->check_timerfd;
+
maxsock = netsock_prepare_select(globals, &fds, maxsock);
maxsock = netsock_prepare_select(globals, &errfds, maxsock);
- ret = pselect(maxsock + 1, &fds, NULL, &errfds, &tv, NULL);
+ ret = pselect(maxsock + 1, &fds, NULL, &errfds, NULL, NULL);
if (ret == -1) {
perror("main loop select failed ...");
@@ -459,21 +476,27 @@ int alfred_server(struct globals *globals)
continue;
}
}
- clock_gettime(CLOCK_MONOTONIC, &last_check);
-
- if (globals->opmode == OPMODE_PRIMARY) {
- /* we are a primary */
- printf("[%ld.%09ld] announce primary ...\n", last_check.tv_sec, last_check.tv_nsec);
- announce_primary(globals);
- sync_data(globals);
- } else {
- /* send local data to server */
- update_server_info(globals);
- push_local_data(globals);
+
+ if (FD_ISSET(globals->check_timerfd, &fds)) {
+ read(globals->check_timerfd, &timer_exp,
+ sizeof(timer_exp));
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ if (globals->opmode == OPMODE_PRIMARY) {
+ /* we are a primary */
+ printf("[%ld.%09ld] announce primary ...\n",
+ now.tv_sec, now.tv_nsec);
+ announce_primary(globals);
+ sync_data(globals);
+ } else {
+ /* send local data to server */
+ update_server_info(globals);
+ push_local_data(globals);
+ }
+ purge_data(globals);
+ check_if_sockets(globals);
+ execute_update_command(globals);
}
- purge_data(globals);
- check_if_sockets(globals);
- execute_update_command(globals);
}
netsock_close_all(globals);
--
2.30.2
9 months
[PATCH] alfred: notify event listener via unix socket
by Marek Lindner
The alfred server instance accepts event notification registration
via the unix socket. These notification sockets only inform
registered parties of the availibility of an alfred datatype change.
The actual data itself needs to be retrieved via the existing data
retrieval mechanisms.
Unlike the update-command this event monitor allows:
- multiple parallel listeners
- programmatic access to changes without requiring multiple processes
The alfred client allows to monitor events via the newly added '-E'
(event monitor) command line option. Serving as debugging tool and
example code at the same time.
Signed-off-by: Marek Lindner <mareklindner(a)neomailbox.ch>
---
alfred.h | 15 ++++++
client.c | 54 ++++++++++++++++++++
main.c | 10 +++-
man/alfred.8 | 3 ++
packet.h | 26 ++++++++++
recv.c | 4 +-
server.c | 5 ++
unix_sock.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 257 insertions(+), 2 deletions(-)
diff --git a/alfred.h b/alfred.h
index 2d98a30..f442c48 100644
--- a/alfred.h
+++ b/alfred.h
@@ -94,6 +94,7 @@ enum clientmode {
CLIENT_CHANGE_INTERFACE,
CLIENT_CHANGE_BAT_IFACE,
CLIENT_SERVER_STATUS,
+ CLIENT_EVENT_MONITOR,
};
struct interface {
@@ -110,8 +111,15 @@ struct interface {
struct list_head list;
};
+struct event_listener {
+ int fd;
+
+ struct list_head list;
+};
+
struct globals {
struct list_head interfaces;
+ struct list_head event_listeners;
char *net_iface;
struct server *best_server; /* NULL if we are a server ourselves */
@@ -157,6 +165,7 @@ int alfred_client_modeswitch(struct globals *globals);
int alfred_client_change_interface(struct globals *globals);
int alfred_client_change_bat_iface(struct globals *globals);
int alfred_client_server_status(struct globals *globals);
+int alfred_client_event_monitor(struct globals *globals);
/* recv.c */
int recv_alfred_packet(struct globals *globals, struct interface *interface,
int recv_sock);
@@ -186,6 +195,12 @@ int unix_sock_open_client(struct globals *globals);
int unix_sock_close(struct globals *globals);
int unix_sock_req_data_finish(struct globals *globals,
struct transaction_head *head);
+int unix_sock_events_select_prepare(struct globals *globals, fd_set *fds,
+ fd_set *errfds, int maxsock);
+void unix_sock_events_select_handle(struct globals *globals,
+ fd_set *fds, fd_set *errfds);
+void unix_sock_events_close_all(struct globals *globals);
+void unix_sock_event_notify(struct globals *globals, uint8_t type);
/* vis.c */
int vis_update_data(struct globals *globals);
/* netsock.c */
diff --git a/client.c b/client.c
index 81cdd7c..9e88f47 100644
--- a/client.c
+++ b/client.c
@@ -452,3 +452,57 @@ err:
unix_sock_close(globals);
return 0;
}
+
+int alfred_client_event_monitor(struct globals *globals)
+{
+ struct alfred_event_register_v0 event_register;
+ struct alfred_event_notify_v0 event_notify;
+ int ret, len;
+
+ if (unix_sock_open_client(globals))
+ return -1;
+
+ len = sizeof(event_register);
+
+ event_register.header.type = ALFRED_EVENT_REGISTER;
+ event_register.header.version = ALFRED_VERSION;
+ event_register.header.length = 0;
+
+ ret = write(globals->unix_sock, &event_register, len);
+ if (ret != len) {
+ fprintf(stderr, "%s: only wrote %d of %d bytes: %s\n",
+ __func__, ret, len, strerror(errno));
+ goto err;
+ }
+
+ while (true) {
+ len = read(globals->unix_sock, &event_notify, sizeof(event_notify));
+ if (len == 0) {
+ fprintf(stdout, "Server closed the connection\n");
+ goto err;
+ }
+
+ if (len < 0) {
+ perror("read from unix socket failed");
+ goto err;
+ }
+
+ if (len != sizeof(event_notify)) {
+ fprintf(stderr, "notify read bytes: %d (expected: %zu)\n",
+ len, sizeof(event_notify));
+ goto err;
+ }
+
+ if (event_notify.header.version != ALFRED_VERSION)
+ continue;
+
+ if (event_notify.header.type != ALFRED_EVENT_NOTIFY)
+ continue;
+
+ fprintf(stdout, "Event: type = %d\n", event_notify.type);
+ }
+
+err:
+ unix_sock_close(globals);
+ return 0;
+}
diff --git a/main.c b/main.c
index 68d6efd..98bf64d 100644
--- a/main.c
+++ b/main.c
@@ -39,6 +39,7 @@ static void alfred_usage(void)
printf(" -I, --change-interface [interface] change to the specified interface(s)\n");
printf(" -B, --change-bat-iface [interface] change to the specified batman-adv interface\n");
printf(" -S, --server-status request server status info such as mode & interfaces\n");
+ printf(" -E, --event-monitor monitor alfred data record update eventss\n");
printf("\n");
printf("server mode options:\n");
printf(" -i, --interface specify the interface (or comma separated list of interfaces) to listen on\n");
@@ -164,6 +165,7 @@ static struct globals *alfred_init(int argc, char *argv[])
{"change-interface", required_argument, NULL, 'I'},
{"change-bat-iface", required_argument, NULL, 'B'},
{"server-status", required_argument, NULL, 'S'},
+ {"event-monitor", required_argument, NULL, 'E'},
{"unix-path", required_argument, NULL, 'u'},
{"update-command", required_argument, NULL, 'c'},
{"version", no_argument, NULL, 'v'},
@@ -181,6 +183,7 @@ static struct globals *alfred_init(int argc, char *argv[])
memset(globals, 0, sizeof(*globals));
INIT_LIST_HEAD(&globals->interfaces);
+ INIT_LIST_HEAD(&globals->event_listeners);
globals->net_iface = NULL;
globals->opmode = OPMODE_SECONDARY;
globals->clientmode = CLIENT_NONE;
@@ -198,7 +201,7 @@ static struct globals *alfred_init(int argc, char *argv[])
time_random_seed();
- while ((opt = getopt_long(argc, argv, "ms:r:hi:b:vV:M:I:B:Su:dc:p:4:f", long_options,
+ while ((opt = getopt_long(argc, argv, "ms:r:hi:b:vV:M:I:B:SEu:dc:p:4:f", long_options,
&opt_ind)) != -1) {
switch (opt) {
case 'r':
@@ -263,6 +266,9 @@ static struct globals *alfred_init(int argc, char *argv[])
case 'S':
globals->clientmode = CLIENT_SERVER_STATUS;
break;
+ case 'E':
+ globals->clientmode = CLIENT_EVENT_MONITOR;
+ break;
case 'u':
globals->unix_path = optarg;
break;
@@ -328,6 +334,8 @@ int main(int argc, char *argv[])
return alfred_client_change_bat_iface(globals);
case CLIENT_SERVER_STATUS:
return alfred_client_server_status(globals);
+ case CLIENT_EVENT_MONITOR:
+ return alfred_client_event_monitor(globals);
}
return 0;
diff --git a/man/alfred.8 b/man/alfred.8
index cf0eafc..ca83f02 100644
--- a/man/alfred.8
+++ b/man/alfred.8
@@ -97,6 +97,9 @@ Change the alfred server to use the new \fBbatman-adv interface\fP
.TP
\fB\-S\fP, \fB\-\-server\-status\fP
Request server status information such as mode & interfaces\fP
+.TP
+\fB\-E\fP, \fB\-\-event\-monitor\fP
+Start alfred event monitor connecting to the alfred server and reporting update events\fP
.
.SH SERVER OPTIONS
.TP
diff --git a/packet.h b/packet.h
index 0c1a2eb..84b027f 100644
--- a/packet.h
+++ b/packet.h
@@ -59,6 +59,8 @@ struct alfred_transaction_mgmt {
* @ALFRED_STATUS_ERROR: Error was detected during the transaction
* @ALFRED_MODESWITCH: Switch between different operation modes
* @ALFRED_CHANGE_INTERFACE: Change the listening interface
+ * @ALFRED_EVENT_REGISTER: Request to be notified about alfred update events
+ * @ALFRED_EVENT_NOTIFY: Data record update has been received
*/
enum alfred_packet_type {
ALFRED_PUSH_DATA = 0,
@@ -70,6 +72,8 @@ enum alfred_packet_type {
ALFRED_CHANGE_INTERFACE = 6,
ALFRED_CHANGE_BAT_IFACE = 7,
ALFRED_SERVER_STATUS = 8,
+ ALFRED_EVENT_REGISTER = 9,
+ ALFRED_EVENT_NOTIFY = 10,
};
/* packets */
@@ -229,6 +233,28 @@ struct alfred_server_status_rep_v0 {
struct alfred_tlv header;
} __packed;
+/**
+ * struct alfred_event_register_v0 - event registration message
+ * @header: TLV header describing the complete packet
+ *
+ * Sent by the client to daemon to register for data record updates
+ */
+struct alfred_event_register_v0 {
+ struct alfred_tlv header;
+} __packed;
+
+/**
+ * struct alfred_event_notify_v0 - event notification message
+ * @header: TLV header describing the complete packet
+ * @type: Type of the data triggering the event
+ *
+ * Sent by the daemon to client on data record update
+ */
+struct alfred_event_notify_v0 {
+ struct alfred_tlv header;
+ uint8_t type;
+} __packed;
+
/**
* struct alfred_status_v0 - Status info of a transaction
* @header: TLV header describing the complete packet
diff --git a/recv.c b/recv.c
index 8acad10..36b3a49 100644
--- a/recv.c
+++ b/recv.c
@@ -76,8 +76,10 @@ static int finish_alfred_push_data(struct globals *globals,
/* check that data was changed */
if (new_entry_created ||
dataset->data.header.length != data_len ||
- memcmp(dataset->buf, data->data, data_len) != 0)
+ memcmp(dataset->buf, data->data, data_len) != 0) {
changed_data_type(globals, data->header.type);
+ unix_sock_event_notify(globals, data->header.type);
+ }
/* free old buffer */
if (dataset->buf) {
diff --git a/server.c b/server.c
index bfc37bc..4078fe6 100644
--- a/server.c
+++ b/server.c
@@ -442,6 +442,8 @@ int alfred_server(struct globals *globals)
maxsock = netsock_prepare_select(globals, &fds, maxsock);
maxsock = netsock_prepare_select(globals, &errfds, maxsock);
+ maxsock = unix_sock_events_select_prepare(globals, &fds,
+ &errfds, maxsock);
ret = pselect(maxsock + 1, &fds, NULL, &errfds, &tv, NULL);
@@ -450,6 +452,8 @@ int alfred_server(struct globals *globals)
} else if (ret) {
netsock_check_error(globals, &errfds);
+ unix_sock_events_select_handle(globals, &fds, &errfds);
+
if (FD_ISSET(globals->unix_sock, &fds)) {
unix_sock_read(globals);
continue;
@@ -478,5 +482,6 @@ int alfred_server(struct globals *globals)
netsock_close_all(globals);
unix_sock_close(globals);
+ unix_sock_events_close_all(globals);
return 0;
}
diff --git a/unix_sock.c b/unix_sock.c
index 3894736..c45564d 100644
--- a/unix_sock.c
+++ b/unix_sock.c
@@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/un.h>
@@ -84,6 +85,7 @@ static int unix_sock_add_data(struct globals *globals,
struct alfred_push_data_v0 *push,
int client_sock)
{
+ bool new_entry_created = false;
struct alfred_data *data;
struct dataset *dataset;
int len, data_len, ret = -1;
@@ -138,10 +140,17 @@ static int unix_sock_add_data(struct globals *globals,
free(dataset);
goto err;
}
+ new_entry_created = true;
}
dataset->data_source = SOURCE_LOCAL;
clock_gettime(CLOCK_MONOTONIC, &dataset->last_seen);
+ /* check that data was changed */
+ if (new_entry_created ||
+ dataset->data.header.length != data_len ||
+ memcmp(dataset->buf, data->data, data_len) != 0)
+ unix_sock_event_notify(globals, data->header.type);
+
/* free old buffer */
free(dataset->buf);
@@ -472,6 +481,37 @@ err:
return ret;
}
+static int unix_sock_register_listener(struct globals *globals, int client_sock)
+{
+ struct event_listener *listener;
+ int ret;
+
+ ret = fcntl(client_sock, F_GETFL, 0);
+ if (ret < 0) {
+ perror("failed to get file status flags");
+ goto err;
+ }
+
+ ret = fcntl(client_sock, F_SETFL, ret | O_NONBLOCK);
+ if (ret < 0) {
+ perror("failed to set file status flags");
+ goto err;
+ }
+
+ listener = malloc(sizeof(*listener));
+ if (!listener)
+ goto err;
+
+ listener->fd = client_sock;
+ INIT_LIST_HEAD(&listener->list);
+ list_add_tail(&listener->list, &globals->event_listeners);
+ return 0;
+
+err:
+ close(client_sock);
+ return -1;
+}
+
int unix_sock_read(struct globals *globals)
{
int client_sock;
@@ -537,6 +577,9 @@ int unix_sock_read(struct globals *globals)
case ALFRED_SERVER_STATUS:
ret = unix_sock_server_status(globals, client_sock);
break;
+ case ALFRED_EVENT_REGISTER:
+ ret = unix_sock_register_listener(globals, client_sock);
+ break;
default:
/* unknown packet type */
ret = -1;
@@ -555,3 +598,102 @@ int unix_sock_close(struct globals *globals)
close(globals->unix_sock);
return 0;
}
+
+static void unix_sock_event_listener_free(struct event_listener *listener)
+{
+ list_del_init(&listener->list);
+ close(listener->fd);
+ free(listener);
+}
+
+static void unix_sock_event_notify_listener(struct event_listener *listener,
+ uint8_t type)
+{
+ struct alfred_event_notify_v0 notify;
+ int ret;
+
+ notify.header.type = ALFRED_EVENT_NOTIFY;
+ notify.header.version = ALFRED_VERSION;
+ notify.header.length = FIXED_TLV_LEN(notify);
+ notify.type = type;
+
+ ret = write(listener->fd, ¬ify, sizeof(notify));
+ if (ret == sizeof(notify))
+ return;
+
+ unix_sock_event_listener_free(listener);
+}
+
+int unix_sock_events_select_prepare(struct globals *globals, fd_set *fds,
+ fd_set *errfds, int maxsock)
+{
+ struct event_listener *listener;
+
+ list_for_each_entry(listener, &globals->event_listeners, list) {
+ if (listener->fd < 0)
+ continue;
+
+ FD_SET(listener->fd, fds);
+ FD_SET(listener->fd, errfds);
+
+ if (maxsock < listener->fd)
+ maxsock = listener->fd;
+ }
+
+ return maxsock;
+}
+
+void unix_sock_events_select_handle(struct globals *globals,
+ fd_set *fds, fd_set *errfds)
+{
+ struct event_listener *listener, *tmp;
+ char buff[4];
+ int ret;
+
+ list_for_each_entry_safe(listener, tmp,
+ &globals->event_listeners, list) {
+ if (FD_ISSET(listener->fd, fds)) {
+ ret = recv(listener->fd, buff, sizeof(buff),
+ MSG_PEEK | MSG_DONTWAIT);
+ /* listener has hung up */
+ if (ret == 0)
+ unix_sock_event_listener_free(listener);
+ else if (ret > 0) {
+ fprintf(stderr, "Event listener has written to socket: %d - closing\n",
+ listener->fd);
+ unix_sock_event_listener_free(listener);
+ }
+
+ if (ret >= 0)
+ continue;
+ }
+
+ if (FD_ISSET(listener->fd, errfds)) {
+ fprintf(stderr, "Error on event listener detected: %d\n",
+ listener->fd);
+ unix_sock_event_listener_free(listener);
+ continue;
+ }
+ }
+}
+
+void unix_sock_events_close_all(struct globals *globals)
+{
+ struct event_listener *listener, *tmp;
+
+ list_for_each_entry_safe(listener, tmp,
+ &globals->event_listeners, list) {
+ unix_sock_event_listener_free(listener);
+ }
+}
+
+void unix_sock_event_notify(struct globals *globals, uint8_t type)
+{
+ struct event_listener *listener, *tmp;
+
+ /* if event notify is unsuccessful, listener socket is closed */
+ list_for_each_entry_safe(listener, tmp,
+ &globals->event_listeners, list) {
+ unix_sock_event_notify_listener(listener, type);
+ }
+}
--
2.32.0.rc0
9 months