[Dnsmasq-discuss] [PATCH] Add new extensible D-Bus signal
Victorien Molle
victorien.molle at wifirst.fr
Thu Feb 13 13:58:36 GMT 2020
Hello Simon,
this is not the final patch. I rewrote the way to build extensible dictionary to be able to
add a vendorclass n times when in IPv6 mode.
Moreover, as I don't really work with IPv6, I don't know if the IPv6 part of the code is correct.
I faced multiple issues:
1) I don't really know how to configure dhclient to send n vendorclass identifiers
2) when requesting an IPv6 against DNSMasq, it does not always emit a D-Bus signal,
even if I erase the content of /var/lib/misc/dnsmasq.leases
3) if there is an IPv6 entry in the leases file, DNSMasq will emit a D-Bus signal
when starting/restarting/reloading DNSMasq
About entries in the dictionary, I currently added the 'IP mode' (IPv6 or not) to
be able to correctly parse the dictionary outside of DNSMasq (by a program which would
receive the D-Bus signal). I also want to know if the method I used to create/populate
the dictionary is OK for you.
To be honest, I don't really know what I can add to this dictionary, so tell me what you
want to include in it.
Regards,
Victorien
On Thu, Feb 13, 2020 at 02:33:15PM +0100, Victorien Molle wrote:
> 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