static dbus_bool_t ni_objectmodel_addrconf_static_drop(ni_dbus_object_t *object, unsigned int addrfamily, ni_dbus_message_t *reply, DBusError *error) { ni_addrconf_lease_t *lease = NULL; ni_netdev_t *dev; int rv; if (!(dev = ni_objectmodel_unwrap_netif(object, error))) return FALSE; lease = ni_addrconf_lease_new(NI_ADDRCONF_STATIC, addrfamily); lease->state = NI_ADDRCONF_STATE_RELEASED; rv = __ni_system_interface_update_lease(dev, &lease); if (lease) ni_addrconf_lease_free(lease); if (rv < 0) { dbus_set_error(error, DBUS_ERROR_FAILED, "Error dropping static %s addresses: %s", ni_addrfamily_type_to_name(addrfamily), ni_strerror(rv)); return FALSE; } /* Don't return anything */ return TRUE; }
/* * Generic lease properties */ static dbus_bool_t __ni_objectmodel_addrconf_generic_get_lease(const ni_dbus_object_t *object, ni_addrconf_mode_t mode, unsigned int addrfamily, ni_dbus_variant_t *dict, DBusError *error) { ni_netdev_t *dev; const ni_addrconf_lease_t *lease; if (!(dev = ni_objectmodel_unwrap_netif(object, error))) return FALSE; #if 0 NI_TRACE_ENTER_ARGS("dev=%s, af=%s, mode=%s", dev->name, ni_addrfamily_type_to_name(addrfamily), ni_addrconf_type_to_name(mode)); #endif if (!(lease = ni_netdev_get_lease(dev, addrfamily, mode))) return FALSE; ni_dbus_dict_add_uint32(dict, "state", lease->state); if (lease->flags) ni_dbus_dict_add_uint32(dict, "flags", lease->flags); if (!ni_uuid_is_null(&lease->uuid)) ni_dbus_dict_add_uuid(dict, "uuid", &lease->uuid); return TRUE; }
/* * Forward an addrconf drop call to a supplicant service, such as DHCP or zeroconf */ static dbus_bool_t ni_objectmodel_addrconf_forward_release(ni_dbus_addrconf_forwarder_t *forwarder, ni_netdev_t *dev, const ni_dbus_variant_t *dict, ni_dbus_message_t *reply, DBusError *error) { ni_addrconf_lease_t *lease; dbus_bool_t rv; /* If we have no lease, neither pending nor granted, there's nothing we need to do. */ if ((lease = ni_netdev_get_lease(dev, forwarder->addrfamily, forwarder->addrconf)) == NULL) return TRUE; rv = ni_objectmodel_addrconf_forwarder_call(forwarder, dev, "drop", &lease->uuid, NULL, error); if (!rv) { switch (ni_dbus_get_error(error, NULL)) { case -NI_ERROR_ADDRCONF_NO_LEASE: ni_debug_objectmodel("%s: no %s/%s lease", dev->name, ni_addrconf_type_to_name(forwarder->addrconf), ni_addrfamily_type_to_name(forwarder->addrfamily)); rv = TRUE; break; default: ni_debug_objectmodel("%s: service returned %s (%s)", forwarder->supplicant.interface, error->name, error->message); } return rv; } /* Check again whether we still have a lease for this. The addrconf supplicant may * actually be fast, so that the callback has arrived before the reply to our original * release() call. In that case, we would tell the client to wait for a release event * that has already been broadcast (and ignored). */ if ((lease = ni_netdev_get_lease(dev, forwarder->addrfamily, forwarder->addrconf)) != NULL) { /* Tell the client to wait for an addressReleased event with the given uuid */ ni_debug_objectmodel("%s/%s: found lease, waiting for drop notification from supplicant", ni_addrconf_type_to_name(forwarder->addrconf), ni_addrfamily_type_to_name(forwarder->addrfamily)); rv = __ni_objectmodel_return_callback_info(reply, NI_EVENT_ADDRESS_RELEASED, &lease->uuid, error); } return rv; }
/* * Generic functions for static address configuration */ static dbus_bool_t ni_objectmodel_addrconf_static_request(ni_dbus_object_t *object, unsigned int addrfamily, unsigned int argc, const ni_dbus_variant_t *argv, ni_dbus_message_t *reply, DBusError *error) { ni_addrconf_lease_t *lease = NULL; const ni_dbus_variant_t *dict; const char *string_value; ni_netdev_t *dev; ni_address_t *ap; int rv; if (!(dev = ni_objectmodel_unwrap_netif(object, error))) return FALSE; if (argc != 1 || !ni_dbus_variant_is_dict(&argv[0])) { dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "requestLease: expected one dict argument"); return FALSE; } dict = &argv[0]; lease = ni_addrconf_lease_new(NI_ADDRCONF_STATIC, addrfamily); lease->state = NI_ADDRCONF_STATE_GRANTED; ni_uuid_generate(&lease->uuid); if (!__ni_objectmodel_set_address_dict(&lease->addrs, dict, error) || !__ni_objectmodel_set_route_dict(&lease->routes, dict, error) || !__ni_objectmodel_set_resolver_dict(&lease->resolver, dict, error)) { ni_addrconf_lease_free(lease); return FALSE; } if (__ni_objectmodel_get_domain_string(dict, "hostname", &string_value)) ni_string_dup(&lease->hostname, string_value); /* mark all addresses tentative, causing to verify them */ for (ap = lease->addrs; ap; ap = ap->next) ni_address_set_tentative(ap, TRUE); rv = __ni_system_interface_update_lease(dev, &lease); if (lease) ni_addrconf_lease_free(lease); if (rv < 0) { dbus_set_error(error, DBUS_ERROR_FAILED, "Error configuring static %s addresses: %s", ni_addrfamily_type_to_name(addrfamily), ni_strerror(rv)); return FALSE; } /* Don't return anything. */ return TRUE; }
static const char * __ni_addrconf_lease_file_path(char **path, const char *dir, const char *ifname, int type, int family) { const char *t = ni_addrconf_type_to_name(type); const char *f = ni_addrfamily_type_to_name(family); if (!path || ni_string_empty(dir) || ni_string_empty(ifname) || !t || !f) return NULL; return ni_string_printf(path, "%s/lease-%s-%s-%s.xml", dir, ifname, t, f); }
int __ni_addrconf_lease_info_to_xml(const ni_addrconf_lease_t *lease, xml_node_t *node) { char hex[32] = { '\0' }; xml_node_new_element("family", node, ni_addrfamily_type_to_name(lease->family)); xml_node_new_element("type", node, ni_addrconf_type_to_name(lease->type)); if (!ni_string_empty(lease->owner)) xml_node_new_element("owner", node, lease->owner); if (!ni_uuid_is_null(&lease->uuid)) xml_node_new_element("uuid", node, ni_uuid_print(&lease->uuid)); xml_node_new_element("state", node, ni_addrconf_state_to_name(lease->state)); snprintf(hex, sizeof(hex), "0x%08x", lease->update); xml_node_new_element("update", node, hex); xml_node_new_element_uint("acquired", node, lease->time_acquired); return 0; }
static xml_node_t * __ni_compat_generate_static_address_list(xml_node_t *ifnode, ni_address_t *addr_list, unsigned int af) { ni_address_t *ap; const char *afname; xml_node_t *aconf = NULL; afname = ni_addrfamily_type_to_name(af); if (!afname) { ni_error("%s: unknown address family %u", __func__, af); return NULL; } for (ap = addr_list; ap; ap = ap->next) { xml_node_t *anode; if (ap->family != af) continue; if (aconf == NULL) { char buffer[64]; snprintf(buffer, sizeof(buffer), "%s:static", afname); aconf = xml_node_create(ifnode, buffer); } anode = xml_node_new("address", aconf); xml_node_new_element("local", anode, ni_sockaddr_prefix_print(&ap->local_addr, ap->prefixlen)); if (ap->peer_addr.ss_family != AF_UNSPEC) xml_node_new_element("peer", anode, ni_sockaddr_print(&ap->peer_addr)); if (ap->bcast_addr.ss_family != AF_UNSPEC) xml_node_new_element("broadcast", anode, ni_sockaddr_print(&ap->bcast_addr)); if (ap->label) xml_node_new_element("label", anode, ap->label); } return aconf; }
/* * Callback from addrconf supplicant whenever it acquired, released or lost a lease. * * FIXME SECURITY: * Is it good enough to check for the sender interface to avoid that someone is sending * us spoofed lease messages?! */ void ni_objectmodel_addrconf_signal_handler(ni_dbus_connection_t *conn, ni_dbus_message_t *msg, void *user_data) { ni_dbus_addrconf_forwarder_t *forwarder = user_data; const char *signal_name = dbus_message_get_member(msg); ni_netdev_t *ifp; ni_addrconf_lease_t *lease = NULL; ni_dbus_variant_t argv[16]; ni_uuid_t uuid = NI_UUID_INIT; ni_event_t ifevent; int argc, optind = 0; memset(argv, 0, sizeof(argv)); argc = ni_dbus_message_get_args_variants(msg, argv, 16); if (argc < 0) { ni_error("%s: cannot parse arguments for signal %s", __func__, signal_name); goto done; } ifp = ni_objectmodel_addrconf_path_to_device(dbus_message_get_path(msg)); if (ifp == NULL) { ni_debug_dbus("%s: received signal %s for unknown interface %s", __func__, signal_name, dbus_message_get_path(msg)); goto done; } lease = ni_addrconf_lease_new(forwarder->addrconf, forwarder->addrfamily); if (argc != 1 && argc != 2) { ni_warn("%s: ignoring %s event from %s: bad number of arguments (%u)", __func__, signal_name, dbus_message_get_path(msg), argc); goto done; } if (argc == 2 && !ni_dbus_variant_get_uuid(&argv[optind++], &uuid)) { ni_debug_dbus("%s: unable to parse uuid argument", __func__); goto done; } if (!ni_objectmodel_set_addrconf_lease(lease, &argv[optind++])) { ni_error("%s: unable to parse lease argument received from %s", __func__, dbus_message_get_sender(msg)); goto done; } ni_debug_dbus("received signal %s for interface %s (ifindex %d), lease %s:%s, uuid=%s, update=0x%x, flags=0x%x", signal_name, ifp->name, ifp->link.ifindex, ni_addrfamily_type_to_name(lease->family), ni_addrconf_type_to_name(lease->type), ni_uuid_print(&uuid), lease->update, lease->flags); if (!strcmp(signal_name, NI_OBJECTMODEL_LEASE_ACQUIRED_SIGNAL)) { if (lease->state != NI_ADDRCONF_STATE_GRANTED) { ni_error("%s: unexpected lease state in signal %s", __func__, signal_name); goto done; } ifevent = NI_EVENT_ADDRESS_ACQUIRED; if (!__ni_addrconf_should_update(lease->update, NI_ADDRCONF_UPDATE_DEFAULT_ROUTE)) { ni_route_table_t *tab; ni_route_t *rp; unsigned int i; for (tab = lease->routes; tab; tab = tab->next) { for (i = 0; i < tab->routes.count; ++i) { if (!(rp = tab->routes.data[i])) continue; if (ni_sockaddr_is_specified(&rp->destination)) continue; if (ni_route_array_delete(&tab->routes, i)) i--; } } } } else if (!strcmp(signal_name, NI_OBJECTMODEL_LEASE_RELEASED_SIGNAL)) { lease->state = NI_ADDRCONF_STATE_RELEASED; ifevent = NI_EVENT_ADDRESS_RELEASED; } else if (!strcmp(signal_name, NI_OBJECTMODEL_LEASE_LOST_SIGNAL)) { lease->state = NI_ADDRCONF_STATE_FAILED; ifevent = NI_EVENT_ADDRESS_LOST; } else { /* Ignore unknown signal */ goto done; } /* * The following call updates the system with the information given in * the lease. This includes setting all addresses, as well as updating * resolver and hostname, if provided. * When a lease is dropped, we either fall back to the config information * from the next best lease, or if there is none, we restore the original * system settings. * * Note, lease may be NULL after this, as the interface object * takes ownership of it. */ __ni_system_interface_update_lease(ifp, &lease); /* Potentially, there's a client somewhere waiting for that event. * We use the UUID that's passed back and forth to make sure we * really match the event we were expecting to match. */ { ni_dbus_object_t *object; object = ni_objectmodel_get_netif_object(__ni_objectmodel_server, ifp); if (object) ni_objectmodel_send_netif_event(__ni_objectmodel_server, object, ifevent, ni_uuid_is_null(&uuid)? NULL : &uuid); } done: while (argc--) ni_dbus_variant_destroy(&argv[argc]); if (lease) ni_addrconf_lease_free(lease); }
/* * Write a lease to a file */ int ni_addrconf_lease_file_write(const char *ifname, ni_addrconf_lease_t *lease) { char tempname[PATH_MAX] = {'\0'}; ni_bool_t fallback = FALSE; char *filename = NULL; xml_node_t *xml = NULL; FILE *fp = NULL; int ret = -1; int fd; if (lease->state == NI_ADDRCONF_STATE_RELEASED) { ni_addrconf_lease_file_remove(ifname, lease->type, lease->family); return 0; } if (!__ni_addrconf_lease_file_path(&filename, ni_config_storedir(), ifname, lease->type, lease->family)) { ni_error("Cannot construct lease file name: %m"); return -1; } ni_debug_dhcp("Preparing xml lease data for '%s'", filename); if ((ret = ni_addrconf_lease_to_xml(lease, &xml)) != 0) { if (ret > 0) { ni_debug_dhcp("Skipped, %s:%s leases are disabled", ni_addrfamily_type_to_name(lease->family), ni_addrconf_type_to_name(lease->type)); } else { ni_error("Unable to represent %s:%s lease as XML", ni_addrfamily_type_to_name(lease->family), ni_addrconf_type_to_name(lease->type)); } goto failed; } snprintf(tempname, sizeof(tempname), "%s.XXXXXX", filename); if ((fd = mkstemp(tempname)) < 0) { if (errno == EROFS && __ni_addrconf_lease_file_path(&filename, ni_config_statedir(), ifname, lease->type, lease->family)) { ni_debug_dhcp("Read-only filesystem, try fallback to %s", filename); snprintf(tempname, sizeof(tempname), "%s.XXXXXX", filename); fd = mkstemp(tempname); fallback = TRUE; } if (fd < 0) { ni_error("Cannot create temporary lease file '%s': %m", tempname); tempname[0] = '\0'; ret = -1; goto failed; } } if ((fp = fdopen(fd, "we")) == NULL) { ret = -1; close(fd); ni_error("Cannot reopen temporary lease file '%s': %m", tempname); goto failed; } ni_debug_dhcp("Writing lease to temporary file for '%s'", filename); xml_node_print(xml, fp); fclose(fp); xml_node_free(xml); if ((ret = rename(tempname, filename)) != 0) { ni_error("Unable to rename temporary lease file '%s' to '%s': %m", tempname, filename); goto failed; } else if (!fallback) { __ni_addrconf_lease_file_remove(ni_config_statedir(), ifname, lease->type, lease->family); } ni_debug_dhcp("Lease written to file '%s'", filename); ni_string_free(&filename); return 0; failed: if (fp) fclose(fp); if (xml) xml_node_free(xml); if (tempname[0]) unlink(tempname); ni_string_free(&filename); return -1; }
/* * Remove information from a lease which has been released and already detached * from a device. */ static ni_bool_t ni_system_updater_remove(ni_updater_t *updater, const ni_addrconf_lease_t *lease, const char *ifname) { ni_string_array_t arguments = NI_STRING_ARRAY_INIT; ni_bool_t result = FALSE; ni_debug_ifconfig("Removing system %s settings from %s %s/%s lease", ni_updater_name(updater->kind), ifname, ni_addrconf_type_to_name(lease->type), ni_addrfamily_type_to_name(lease->family)); if (!updater->proc_remove) return TRUE; ni_string_array_append(&arguments, "-i"); ni_string_array_append(&arguments, ifname); ni_string_array_append(&arguments, "-t"); ni_string_array_append(&arguments, ni_addrconf_type_to_name(lease->type)); ni_string_array_append(&arguments, "-f"); ni_string_array_append(&arguments, ni_addrfamily_type_to_name(lease->family)); switch (updater->kind) { case NI_ADDRCONF_UPDATER_GENERIC: switch (updater->format) { case NI_ADDRCONF_UPDATER_FORMAT_INFO: ni_leaseinfo_remove(ifname, lease->type, lease->family); break; default: ni_error("Unsupported %s updater data format.", ni_updater_name(updater->kind)); break; } break; case NI_ADDRCONF_UPDATER_RESOLVER: case NI_ADDRCONF_UPDATER_HOSTNAME: break; default: ni_error("cannot remove old %s settings - file format not understood", ni_updater_name(updater->kind)); goto done; } if (!ni_system_updater_run(updater->proc_remove, &arguments)) { ni_error("failed to remove %s settings", ni_updater_name(updater->kind)); goto done; } result = TRUE; switch (updater->kind) { case NI_ADDRCONF_UPDATER_RESOLVER: if (ni_global.other_event) ni_global.other_event(NI_EVENT_RESOLVER_UPDATED); break; case NI_ADDRCONF_UPDATER_HOSTNAME: if (ni_global.other_event) ni_global.other_event(NI_EVENT_HOSTNAME_UPDATED); break; case NI_ADDRCONF_UPDATER_GENERIC: if (ni_global.other_event) ni_global.other_event(NI_EVENT_GENERIC_UPDATED); break; default: break; } done: ni_string_array_destroy(&arguments); return result; }
/* * Install information from a lease, and remember that we did */ static ni_bool_t ni_system_updater_install(ni_updater_t *updater, const ni_addrconf_lease_t *lease, const char *ifname) { ni_string_array_t arguments = NI_STRING_ARRAY_INIT; const char *statedir = NULL; char *file = NULL; ni_bool_t result = FALSE; int rv = 0; ni_debug_ifconfig("Updating system %s settings from %s/%s lease", ni_updater_name(updater->kind), ni_addrconf_type_to_name(lease->type), ni_addrfamily_type_to_name(lease->family)); if (!updater->proc_install) return TRUE; if (!ifname || (!updater->have_backup && !ni_system_updater_backup(updater, ifname))) return FALSE; ni_string_array_append(&arguments, "-i"); ni_string_array_append(&arguments, ifname); ni_string_array_append(&arguments, "-t"); ni_string_array_append(&arguments, ni_addrconf_type_to_name(lease->type)); ni_string_array_append(&arguments, "-f"); ni_string_array_append(&arguments, ni_addrfamily_type_to_name(lease->family)); switch (updater->kind) { case NI_ADDRCONF_UPDATER_GENERIC: switch (updater->format) { case NI_ADDRCONF_UPDATER_FORMAT_INFO: ni_leaseinfo_dump(NULL, lease, ifname, NULL); if (!(file = ni_leaseinfo_path(ifname, lease->type, lease->family))) { ni_error("Unable to determine leaseinfo file path."); goto done; } ni_string_array_append(&arguments, file); break; default: ni_error("Unsupported %s updater data format.", ni_updater_name(updater->kind)); goto done; } ni_string_array_append(&arguments, ni_updater_format_name(updater->format)); break; case NI_ADDRCONF_UPDATER_RESOLVER: statedir = ni_extension_statedir(ni_updater_name(updater->kind)); if (!statedir) { ni_error("failed to get %s statedir", ni_updater_name(updater->kind)); goto done; } ni_string_printf(&file, "%s/resolv.conf.%s.%s.%s", statedir, ifname, ni_addrconf_type_to_name(lease->type), ni_addrfamily_type_to_name(lease->family)); ni_string_array_append(&arguments, file); if ((rv = ni_resolver_write_resolv_conf(file, lease->resolver, NULL)) < 0) { ni_error("failed to write resolver info to file: %s", ni_strerror(rv)); goto done; } break; case NI_ADDRCONF_UPDATER_HOSTNAME: if (!ni_string_empty(lease->hostname)) { ni_string_array_append(&arguments, lease->hostname); } else { const ni_address_t *ap; char *name = NULL; unsigned int count; /* bnc#861476 workaround */ if (!can_try_reverse_lookup(lease)) goto done; for (count = 0, ap = lease->addrs; ap; ap = ap->next) { if (!ni_sockaddr_is_specified(&ap->local_addr)) continue; if (!ni_resolve_reverse_timed(&ap->local_addr, &name, NI_UPDATER_REVERSE_TIMEOUT)) break; ni_info("Unable to resolve %s to hostname", ni_sockaddr_print(&ap->local_addr)); if (++count >= NI_UPDATER_REVERSE_MAX_CNT) break; } if (ni_string_empty(name)) { ni_note("Skipping hostname update, none available"); goto done; } ni_string_array_append(&arguments, name); ni_string_free(&name); } break; default: ni_error("cannot install new %s settings - file format not understood", ni_updater_name(updater->kind)); goto done; } if (!ni_system_updater_run(updater->proc_install, &arguments)) { ni_error("failed to install %s settings", ni_updater_name(updater->kind)); goto done; } result = TRUE; switch (updater->kind) { case NI_ADDRCONF_UPDATER_RESOLVER: if (ni_global.other_event) ni_global.other_event(NI_EVENT_RESOLVER_UPDATED); break; case NI_ADDRCONF_UPDATER_HOSTNAME: if (ni_global.other_event) ni_global.other_event(NI_EVENT_HOSTNAME_UPDATED); break; case NI_ADDRCONF_UPDATER_GENERIC: if (ni_global.other_event) ni_global.other_event(NI_EVENT_GENERIC_UPDATED); break; default: break; } done: if (file) free(file); ni_string_array_destroy(&arguments); return result; }