[Dnsmasq-discuss] [PATCH] Add new extensible D-Bus signal
Victorien Molle
victorien.molle at wifirst.fr
Thu Feb 13 13:33:15 GMT 2020
For our usage, we need to have more informations sent over D-Bus such as the interface
name, the vendor class identifier and the lease expiration time.
To achieve this, we add a new D-Bus signal "DhcpLeaseNotification" which exports the
requested informations as a dictionnary.
It also has the advantage to be flexible if someone wants to add a new entry in the
future.
Note: in order to get leases extradata be populated, we enabled this feature if D-Bus
is enabled in configuration file (this was enabled in the past only if a script was
ran on leases updates).
Here is an example of the obtained result with a Python3 program:
''' Define our D-Bus callback '''
def cb(action, ipaddr, hwaddr, hostname, info):
print(f'Action: {action}')
print(f'IP: {ipaddr}')
print(f'Hostname: {hostname}')
for k in info:
print(f'{k}: {info.get(k)}')
''' Connect to signal DhcpLeaseNotification on interface uk.org.thekelleys.dnsmasq '''
DNSMasq.listen('DhcpLeaseNotification', callback=cb)
''' Run GLib loop '''
GLib.MainLoop().run()
''' When DNSMasq receives a DHCP request, here is the result: '''
Action: DhcpLeaseAdded
IP: 192.168.1.100
Hostname: Nucleus.nucle.us
interface: br-mgmt
expiration: 43200
ipv6: 0
vendor_class: LaGrosseBiche
Signed-off-by: Victorien Molle <victorien.molle at wifirst.fr>
---
dbus/DBus-interface | 9 ++
src/dbus.c | 215 +++++++++++++++++++++++++++++++++++++++++++-
src/dnsmasq.h | 4 +-
src/lease.c | 6 +-
src/rfc2131.c | 12 +--
5 files changed, 233 insertions(+), 13 deletions(-)
diff --git a/dbus/DBus-interface b/dbus/DBus-interface
index 954c5b9..ed42551 100644
--- a/dbus/DBus-interface
+++ b/dbus/DBus-interface
@@ -273,14 +273,23 @@ DhcpLeaseAdded
---------------
This signal is emitted when a DHCP lease for a given IP address is created.
+This will also trigger the DhcpLeaseNotification signal.
DhcpLeaseDeleted
----------------
This signal is emitted when a DHCP lease for a given IP address is deleted.
+This will also trigger the DhcpLeaseNotification signal.
DhcpLeaseUpdated
----------------
This signal is emitted when a DHCP lease for a given IP address is updated.
+This will also trigger the DhcpLeaseNotification signal.
+
+DhcpLeaseNotification
+---------------------
+
+This signal is emitted when a DHCP lease action is triggered. It exports,
+as a dictionnary, more informations than the other signals.
diff --git a/src/dbus.c b/src/dbus.c
index c0ce903..468f393 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -55,6 +55,7 @@ const char* introspection_xml_template =
" <method name=\"SetBogusPrivOption\">\n"
" <arg name=\"boguspriv\" direction=\"in\" type=\"b\"/>\n"
" </method>\n"
+#ifdef HAVE_DHCP
" <signal name=\"DhcpLeaseAdded\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"hwaddr\" type=\"s\"/>\n"
@@ -70,7 +71,13 @@ const char* introspection_xml_template =
" <arg name=\"hwaddr\" type=\"s\"/>\n"
" <arg name=\"hostname\" type=\"s\"/>\n"
" </signal>\n"
-#ifdef HAVE_DHCP
+" <signal name=\"DhcpLeaseNotification\">\n"
+" <arg name=\"notification_type\" type=\"s\"/>\n"
+" <arg name=\"ipaddr\" type=\"s\"/>\n"
+" <arg name=\"hwaddr\" type=\"s\"/>\n"
+" <arg name=\"hostname\" type=\"s\"/>\n"
+" <arg name=\"lease_info\" type=\"a{sv}\"/>\n"
+" </signal>\n"
" <method name=\"AddDhcpLease\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"hwaddr\" type=\"s\"/>\n"
@@ -98,6 +105,13 @@ struct watch {
struct watch *next;
};
+struct lease_info {
+ char *key;
+ char *fmt;
+ char dbus_type;
+ DBusBasicValue value;
+ struct lease_info *next;
+};
static dbus_bool_t add_watch(DBusWatch *watch, void *data)
{
@@ -137,6 +151,48 @@ static void remove_watch(DBusWatch *watch, void *data)
w = data; /* no warning */
}
+static struct lease_info* add_lease_info(struct lease_info *parent, char *key, char *fmt, char dbus_type, DBusBasicValue value)
+{
+ struct lease_info *info, *it;
+
+ /* Allocate new struct */
+ if (!(info = whine_malloc(sizeof(struct lease_info))))
+ return parent;
+
+ /* Populate struct */
+ info->key = key;
+ info->fmt = fmt;
+ info->dbus_type = dbus_type;
+ info->value = value;
+ info->next = NULL;
+
+ /*
+ Assign newly allocated struct to parent.
+ It implies this is the first lease info we want to add
+ */
+ if (!parent)
+ parent = info;
+ else
+ {
+ for (it = parent; it->next != NULL; it = it->next);
+ it->next = info;
+ }
+
+ return parent;
+}
+
+static void destroy_lease_infos(struct lease_info *infos)
+{
+ struct lease_info *it;
+
+ while (infos)
+ {
+ it = infos->next;
+ free(infos);
+ infos = it;
+ }
+}
+
static void dbus_read_servers(DBusMessage *message)
{
DBusMessageIter iter;
@@ -828,7 +884,159 @@ void check_dbus_listeners()
}
#ifdef HAVE_DHCP
-void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
+/*
+ As this function is called by emit_dbus_signal, we already have access to ipaddr, hwaddr and hostname attributes
+ NOTE: connection attribute is currently not NULL as it is verified by emit_dbus_signal
+*/
+void emit_dhcplease_notification(DBusConnection *connection, char *action, struct dhcp_lease *lease, char *ipaddr, char *hwaddr, char *hostname, time_t now)
+{
+ DBusMessageIter array, dict, iter, variant;
+ unsigned char *buf = lease->extradata;
+ int fd = daemon->dhcpfd, cpt, is6;
+ struct lease_info *infos, *it;
+ char *vendor_class, sfmt[24];
+ char interface[IF_NAMESIZE];
+ DBusMessage* message = NULL;
+ DBusBasicValue value;
+
+#ifdef HAVE_DHCP6
+ if (!daemon->dhcp)
+ fd = daemon->dhcp6fd;
+#endif
+
+ /* Get interface name */
+ if (!indextoname(fd, lease->last_interface, interface))
+ interface[0] = 0;
+
+ /* Check if working with IPv6 */
+ is6 = !!(lease->flags & (LEASE_TA | LEASE_NA));
+
+ /* Start building dictionary with interface */
+ value.str = interface;
+ infos = add_lease_info(NULL, "interface", "s", DBUS_TYPE_STRING, value);
+
+ /* Add expiration time */
+ if (lease->expires != 0)
+ value.u64 = (dbus_uint64_t)difftime(lease->expires, now);
+ else
+ value.u64 = 0;
+ infos = add_lease_info(infos, "expiration", "t", DBUS_TYPE_UINT64, value);
+
+ /* Add flag to know if we use IPv6 or not */
+ value.bool_val = is6 == 1 ? TRUE : FALSE;
+ infos = add_lease_info(infos, "ipv6", "b", DBUS_TYPE_BOOLEAN, value);
+
+ vendor_class = "";
+ if (buf && *buf != 0)
+ {
+ if (!is6)
+ {
+ /* As defined in rfc2131.c, the first data is the vendor class even if it is empty */
+ vendor_class = (char*)lease->extradata;
+ value.str = vendor_class;
+ infos = add_lease_info(infos, "vendor_class", "s", DBUS_TYPE_STRING, value);
+ }
+#ifdef HAVE_DHCP6
+ else
+ {
+ value.i32 = (dbus_int32_t)lease->vendorclass_count;
+ infos = add_lease_info(infos, "vendorclass_count", "i", DBUS_TYPE_INT32, value);
+ if (lease->vendorclass_count != 0)
+ {
+ /* Add VENDOR-ID string */
+ value.str = (char*)lease->extradata;
+ infos = add_lease_info(infos, "vendor_id", "s", DBUS_TYPE_STRING, value);
+
+ /* Set pointer to the right offset */
+ vendor_class = (char*)(lease->extradata + strlen(value.str) + 1);
+
+ /* Add vendor_class strings */
+ for (cpt = 0; cpt < lease->vendorclass_count; ++cpt)
+ {
+ snprintf(sfmt, sizeof(sfmt), "vendor_class%d", cpt);
+ value.str = vendor_class;
+ infos = add_lease_info(infos, sfmt, "s", DBUS_TYPE_STRING, value);
+
+ /* Update vendor_class pointer */
+ vendor_class += strlen(value.str) + 1;
+ }
+ }
+#endif
+ }
+ }
+
+ if (!(message = dbus_message_new_signal(DNSMASQ_PATH, daemon->dbus_name, "DhcpLeaseNotification")))
+ goto out;
+
+ dbus_message_iter_init_append(message, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &action);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ipaddr);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &hwaddr);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &hostname);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array);
+ for (it = infos; it; it = it->next) {
+ dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
+ dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &it->key);
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, it->fmt, &variant);
+ switch (it->dbus_type)
+ {
+ case DBUS_TYPE_BOOLEAN:
+ dbus_message_iter_append_basic(&variant, it->dbus_type, &it->value.bool_val);
+ break;
+
+ case DBUS_TYPE_INT16:
+ dbus_message_iter_append_basic(&variant, it->dbus_type, &it->value.i16);
+ break;
+
+ case DBUS_TYPE_UINT16:
+ dbus_message_iter_append_basic(&variant, it->dbus_type, &it->value.u16);
+ break;
+
+ case DBUS_TYPE_INT32:
+ dbus_message_iter_append_basic(&variant, it->dbus_type, &it->value.i32);
+ break;
+
+ case DBUS_TYPE_UINT32:
+ dbus_message_iter_append_basic(&variant, it->dbus_type, &it->value.u32);
+ break;
+
+ case DBUS_TYPE_INT64:
+ dbus_message_iter_append_basic(&variant, it->dbus_type, &it->value.i64);
+ break;
+
+ case DBUS_TYPE_UINT64:
+ dbus_message_iter_append_basic(&variant, it->dbus_type, &it->value.u64);
+ break;
+
+ case DBUS_TYPE_DOUBLE:
+ dbus_message_iter_append_basic(&variant, it->dbus_type, &it->value.dbl);
+ break;
+
+ case DBUS_TYPE_STRING:
+ dbus_message_iter_append_basic(&variant, it->dbus_type, &it->value.str);
+ break;
+
+ default:
+ dbus_message_iter_abandon_container(&dict, &variant);
+ dbus_message_iter_abandon_container_if_open(&array, &dict);
+ dbus_message_iter_abandon_container_if_open(&iter, &array);
+ dbus_message_unref(message);
+ my_syslog(LOG_WARNING, _("Unknown D-Bus type specified for key '%s'. This will completely disable 'DhcpLeaseNotification' signal"),
+ it->key);
+ goto out;
+ }
+ dbus_message_iter_close_container(&dict, &variant);
+ dbus_message_iter_close_container(&array, &dict);
+ }
+ dbus_message_iter_close_container(&iter, &array);
+ dbus_connection_send(connection, message, NULL);
+ dbus_message_unref(message);
+
+out:
+ destroy_lease_infos(infos);
+}
+
+void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname, time_t now)
{
DBusConnection *connection = (DBusConnection *)daemon->dbus;
DBusMessage* message = NULL;
@@ -878,6 +1086,9 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
dbus_connection_send(connection, message, NULL);
dbus_message_unref(message);
+
+ /* Now emit a notification with more informations */
+ emit_dhcplease_notification(connection, action_str, lease, daemon->addrbuff, mac, hostname, now);
}
#endif
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 8e047fc..d53fe38 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1415,7 +1415,7 @@ void lease_update_from_configs(void);
int do_script_run(time_t now);
void rerun_scripts(void);
void lease_find_interfaces(time_t now);
-#ifdef HAVE_SCRIPT
+#if defined(HAVE_SCRIPT) || defined(HAVE_DBUS)
void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data,
unsigned int len, int delim);
#endif
@@ -1465,7 +1465,7 @@ char *dbus_init(void);
void check_dbus_listeners(void);
void set_dbus_listeners(void);
# ifdef HAVE_DHCP
-void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
+void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname, time_t now);
# endif
#endif
diff --git a/src/lease.c b/src/lease.c
index 081d90e..97e7b3e 100644
--- a/src/lease.c
+++ b/src/lease.c
@@ -1109,7 +1109,7 @@ int do_script_run(time_t now)
queue_script(ACTION_DEL, lease, lease->old_hostname, now);
#endif
#ifdef HAVE_DBUS
- emit_dbus_signal(ACTION_DEL, lease, lease->old_hostname);
+ emit_dbus_signal(ACTION_DEL, lease, lease->old_hostname, now);
#endif
old_leases = lease->next;
@@ -1144,7 +1144,7 @@ int do_script_run(time_t now)
#endif
#ifdef HAVE_DBUS
emit_dbus_signal((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease,
- lease->fqdn ? lease->fqdn : lease->hostname);
+ lease->fqdn ? lease->fqdn : lease->hostname, now);
#endif
lease->flags &= ~(LEASE_NEW | LEASE_CHANGED | LEASE_AUX_CHANGED);
@@ -1158,7 +1158,7 @@ int do_script_run(time_t now)
return 0; /* nothing to do */
}
-#ifdef HAVE_SCRIPT
+#if defined(HAVE_SCRIPT) || defined(HAVE_DBUS)
/* delim == -1 -> delim = 0, but embedded 0s, creating extra records, are OK. */
void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim)
{
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 0058747..d4e9802 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -21,7 +21,7 @@
#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
-#ifdef HAVE_SCRIPT
+#if defined(HAVE_SCRIPT) || defined(HAVE_DBUS)
static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt);
#endif
@@ -97,7 +97,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
struct dhcp_opt *o;
unsigned char pxe_uuid[17];
unsigned char *oui = NULL, *serial = NULL;
-#ifdef HAVE_SCRIPT
+#if defined(HAVE_SCRIPT) || defined(HAVE_DBUS)
unsigned char *class = NULL;
#endif
@@ -163,7 +163,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
unsigned char *y = option_ptr(opt, offset + elen + 5);
oui = option_find1(x, y, 1, 1);
serial = option_find1(x, y, 2, 1);
-#ifdef HAVE_SCRIPT
+#if defined(HAVE_SCRIPT) || defined(HAVE_DBUS)
class = option_find1(x, y, 3, 1);
#endif
/* If TR069-id is present set the tag "cpewan-id" to facilitate echoing
@@ -1365,8 +1365,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
/* pick up INIT-REBOOT events. */
lease->flags |= LEASE_CHANGED;
-#ifdef HAVE_SCRIPT
- if (daemon->lease_change_command)
+#if defined(HAVE_SCRIPT) || defined(HAVE_DBUS)
+ if (daemon->lease_change_command || daemon->dbus)
{
struct dhcp_netid *n;
@@ -1661,7 +1661,7 @@ static int sanitise(unsigned char *opt, char *buf)
return 1;
}
-#ifdef HAVE_SCRIPT
+#if defined(HAVE_SCRIPT) || defined(HAVE_DBUS)
static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
{
if (!opt)
--
2.24.0
More information about the Dnsmasq-discuss
mailing list