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 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; }
int ni_addrconf_lease_from_xml(ni_addrconf_lease_t **leasep, const xml_node_t *root) { const xml_node_t *node = root; ni_addrconf_lease_t *lease; int ret = -1; if (root && !ni_string_eq(root->name, NI_ADDRCONF_LEASE_XML_NODE)) node = xml_node_get_child(root, NI_ADDRCONF_LEASE_XML_NODE); if (!node || !leasep) return ret; *leasep = NULL; /* initialize... */ if (!(lease = ni_addrconf_lease_new(__NI_ADDRCONF_MAX, AF_UNSPEC))) return ret; if ((ret = __ni_addrconf_lease_info_from_xml(lease, node)) != 0) { ni_addrconf_lease_free(lease); return ret; } switch (lease->type) { case NI_ADDRCONF_STATIC: case NI_ADDRCONF_AUTOCONF: case NI_ADDRCONF_INTRINSIC: ret = __ni_addrconf_lease_static_from_xml(lease, node); break; case NI_ADDRCONF_DHCP: ret = __ni_addrconf_lease_dhcp_from_xml(lease, node); break; default: ; /* fall through error */ } if (ret) { ni_addrconf_lease_free(lease); } else { *leasep = lease; } return ret; }
/* * Forward an addrconf request to a supplicant service, such as DHCP or zeroconf * * What we do here is: * * - create an addrconf request handle, and register it with the interface * - assign a uuid * - assign an event ID * - forward the request, along with the uuid * - when we receive a lease event with the matching uuid, * broadcast a corresponding interface event (with the assigned even ID) * * Note, we do not record the contents of the addrconf request, which may be totally * free-form. We just pass it on to the respective addrconf service. */ static dbus_bool_t ni_objectmodel_addrconf_forward_request(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; ni_uuid_t req_uuid; dbus_bool_t rv, enabled; uint32_t flags = 0; /* Check whether we already have a lease on this interface. */ lease = ni_netdev_get_lease(dev, forwarder->addrfamily, forwarder->addrconf); /* Generate a uuid and assign an event ID */ ni_uuid_generate(&req_uuid); /* If the caller tells us to disable this addrconf family, we may need * to do a release() call. */ if (!ni_dbus_dict_get_bool(dict, "enabled", &enabled) || !enabled) return ni_objectmodel_addrconf_forward_release(forwarder, dev, NULL, reply, error); if (!ni_dbus_dict_get_uint32(dict, "flags", &flags)) flags = 0; if (lease == NULL) { /* We didn't have a lease for this address family and addrconf protocol yet. * Create one and track it. */ lease = ni_addrconf_lease_new(forwarder->addrconf, forwarder->addrfamily); ni_netdev_set_lease(dev, lease); } lease->uuid = req_uuid; lease->state = NI_ADDRCONF_STATE_REQUESTING; lease->flags = flags; rv = ni_objectmodel_addrconf_forwarder_call(forwarder, dev, "acquire", &req_uuid, dict, error); if (rv) { /* Tell the client to wait for an addressAcquired event with the given uuid */ rv = __ni_objectmodel_return_callback_info(reply, NI_EVENT_ADDRESS_ACQUIRED, &req_uuid, error); } return rv; }
int __ni_dhcp4_fsm_discover(ni_dhcp4_device_t *dev, int scan_offers) { ni_addrconf_lease_t *lease; int rv; ni_info("%s: Initiating DHCPv4 discovery (ifindex %d)", dev->ifname, dev->link.ifindex); /* If we already have a lease, try asking for the same. * If not, create a dummy lease with NULL fields. * Note: if DISCOVER for the old lease times out, * we should fall back to asking for anything. */ if ((lease = dev->lease) == NULL) lease = ni_addrconf_lease_new(NI_ADDRCONF_DHCP, AF_INET); lease->uuid = dev->config->uuid; dev->fsm.state = NI_DHCP4_STATE_SELECTING; rv = ni_dhcp4_device_send_message(dev, DHCP4_DISCOVER, lease); dev->dhcp4.accept_any_offer = 1; ni_debug_dhcp("valid lease: %d; have prefs: %d", ni_addrconf_lease_is_valid(dev->lease), ni_dhcp4_config_have_server_preference()); if (ni_addrconf_lease_is_valid(dev->lease) || (scan_offers && ni_dhcp4_config_have_server_preference())) { ni_dhcp4_fsm_set_timeout(dev, dev->config->initial_discovery_timeout); dev->dhcp4.accept_any_offer = 0; } else { ni_dhcp4_fsm_set_timeout(dev, dev->config->request_timeout); } ni_dhcp4_device_drop_best_offer(dev); if (lease != dev->lease) ni_addrconf_lease_free(lease); return rv; }
static dbus_bool_t __ni_objectmodel_addrconf_generic_set_lease(ni_dbus_object_t *object, ni_addrconf_mode_t mode, unsigned int addrfamily, const ni_dbus_variant_t *dict, DBusError *error) { ni_netdev_t *dev; uint32_t state; if (!(dev = ni_objectmodel_unwrap_netif(object, error))) return FALSE; if (ni_dbus_dict_get_uint32(dict, "state", &state)) { ni_addrconf_lease_t *lease; lease = ni_addrconf_lease_new(mode, addrfamily); lease->state = state; ni_dbus_dict_get_uuid(dict, "uuid", &lease->uuid); ni_dbus_dict_get_uint32(dict, "flags", &lease->flags); ni_netdev_set_lease(dev, lease); } return TRUE; }
/* * 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); }
/* * Parse a DHCP response. * FIXME: RFC2131 states that the server is allowed to split a DHCP option into * several (partial) options if the total length exceeds 255 octets. We don't * handle this yet. */ int ni_dhcp_parse_response(const ni_dhcp_message_t *message, ni_buffer_t *options, ni_addrconf_lease_t **leasep) { ni_buffer_t overload_buf; ni_addrconf_lease_t *lease; ni_route_array_t default_routes = NI_ROUTE_ARRAY_INIT; ni_route_array_t static_routes = NI_ROUTE_ARRAY_INIT; ni_route_array_t classless_routes = NI_ROUTE_ARRAY_INIT; ni_string_array_t dns_servers = NI_STRING_ARRAY_INIT; ni_string_array_t dns_search = NI_STRING_ARRAY_INIT; ni_string_array_t nis_servers = NI_STRING_ARRAY_INIT; char *nisdomain = NULL; char *dnsdomain = NULL; int opt_overload = 0; int msg_type = -1; int use_bootserver = 1; int use_bootfile = 1; lease = ni_addrconf_lease_new(NI_ADDRCONF_DHCP, AF_INET); lease->state = NI_ADDRCONF_STATE_GRANTED; lease->type = NI_ADDRCONF_DHCP; lease->family = AF_INET; lease->time_acquired = time(NULL); lease->dhcp.address.s_addr = message->yiaddr; lease->dhcp.serveraddress.s_addr = message->siaddr; lease->dhcp.address.s_addr = message->yiaddr; parse_more: /* Loop as long as we still have data in the buffer. */ while (ni_buffer_count(options) && !options->underflow) { ni_buffer_t buf; int option; option = ni_dhcp_option_next(options, &buf); //ni_debug_dhcp("handle option %s (%d)", ni_dhcp_option_name(option), option); if (option == DHCP_PAD) continue; if (option == DHCP_END) break; if (option < 0) goto error; if (ni_buffer_count(&buf) == 0) { ni_error("option %d has zero length", option); goto error; } switch (option) { case DHCP_MESSAGETYPE: msg_type = ni_buffer_getc(&buf); if (msg_type < 0) goto error; continue; case DHCP_ADDRESS: ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.address); break; case DHCP_NETMASK: ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.netmask); break; case DHCP_BROADCAST: ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.broadcast); break; case DHCP_SERVERIDENTIFIER: ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.serveraddress); break; case DHCP_LEASETIME: ni_dhcp_option_get32(&buf, &lease->dhcp.lease_time); break; case DHCP_RENEWALTIME: ni_dhcp_option_get32(&buf, &lease->dhcp.renewal_time); break; case DHCP_REBINDTIME: ni_dhcp_option_get32(&buf, &lease->dhcp.rebind_time); break; case DHCP_MTU: ni_dhcp_option_get16(&buf, &lease->dhcp.mtu); /* Minimum legal mtu is 68 accoridng to * RFC 2132. In practise it's 576 which is the * minimum maximum message size. */ if (lease->dhcp.mtu < MTU_MIN) { ni_debug_dhcp("MTU %u is too low, minimum is %d; ignoring", lease->dhcp.mtu, MTU_MIN); lease->dhcp.mtu = 0; } break; case DHCP_HOSTNAME: ni_dhcp_option_get_domain(&buf, &lease->hostname, "hostname"); break; case DHCP_DNSDOMAIN: ni_dhcp_option_get_domain(&buf, &dnsdomain, "dns-domain"); break; case DHCP_MESSAGE: ni_dhcp_option_get_printable(&buf, &lease->dhcp.message, "dhcp-message"); break; case DHCP_ROOTPATH: ni_dhcp_option_get_pathname(&buf, &lease->dhcp.rootpath, "root-path"); break; case DHCP_NISDOMAIN: ni_dhcp_option_get_domain(&buf, &nisdomain, "nis-domain"); break; case DHCP_NETBIOSNODETYPE: ni_dhcp_option_get_netbios_type(&buf, &lease->netbios_type); break; case DHCP_NETBIOSSCOPE: ni_dhcp_option_get_domain(&buf, &lease->netbios_scope, "netbios-scope"); break; case DHCP_DNSSERVER: ni_dhcp_decode_address_list(&buf, &dns_servers); break; case DHCP_NTPSERVER: ni_dhcp_decode_address_list(&buf, &lease->ntp_servers); break; case DHCP_NISSERVER: ni_dhcp_decode_address_list(&buf, &nis_servers); break; case DHCP_LPRSERVER: ni_dhcp_decode_address_list(&buf, &lease->lpr_servers); break; case DHCP_LOGSERVER: ni_dhcp_decode_address_list(&buf, &lease->log_servers); break; case DHCP_NETBIOSNAMESERVER: ni_dhcp_decode_address_list(&buf, &lease->netbios_name_servers); break; case DHCP_NETBIOSDDSERVER: ni_dhcp_decode_address_list(&buf, &lease->netbios_dd_servers); break; case DHCP_DNSSEARCH: ni_dhcp_decode_dnssearch(&buf, &dns_search, "dns-search domain"); break; case DHCP_CSR: case DHCP_MSCSR: ni_route_array_destroy(&classless_routes); if (ni_dhcp_decode_csr(&buf, &classless_routes) < 0) goto error; break; case DHCP_SIPSERVER: ni_dhcp_decode_sipservers(&buf, &lease->sip_servers); break; case DHCP_STATICROUTE: ni_route_array_destroy(&static_routes); if (ni_dhcp_decode_static_routes(&buf, &static_routes) < 0) goto error; break; case DHCP_ROUTERS: ni_route_array_destroy(&default_routes); if (ni_dhcp_decode_routers(&buf, &default_routes) < 0) goto error; break; case DHCP_OPTIONSOVERLOADED: if (options != &overload_buf) { opt_overload = ni_buffer_getc(&buf); } else { ni_debug_dhcp("DHCP: ignoring OVERLOAD option in overloaded data"); (void) ni_buffer_getc(&buf); } break; case DHCP_FQDN: /* We ignore replies about FQDN */ break; default: ni_debug_dhcp("ignoring unsupported DHCP code %u", option); break; } if (buf.underflow) { ni_debug_dhcp("unable to parse DHCP option %s: too short", ni_dhcp_option_name(option)); goto error; } else if (ni_buffer_count(&buf)) { ni_debug_dhcp("excess data in DHCP option %s - %u bytes left", ni_dhcp_option_name(option), ni_buffer_count(&buf)); } } if (options->underflow) { ni_debug_dhcp("unable to parse DHCP response: truncated packet"); goto error; } if (opt_overload) { const void *more_data = NULL; size_t size = 0; if (opt_overload & DHCP_OVERLOAD_BOOTFILE) { use_bootfile = 0; more_data = message->bootfile; size = sizeof(message->bootfile); opt_overload &= ~DHCP_OVERLOAD_BOOTFILE; } else if (opt_overload & DHCP_OVERLOAD_SERVERNAME) { use_bootserver = 0; more_data = message->servername; size = sizeof(message->servername); opt_overload &= ~DHCP_OVERLOAD_SERVERNAME; } else { opt_overload = 0; } if (more_data) { ni_buffer_init_reader(&overload_buf, (void *) more_data, size); options = &overload_buf; goto parse_more; } } if (use_bootserver && message->servername[0]) { char tmp[sizeof(message->servername)]; size_t len; assert(sizeof(lease->dhcp.servername) == sizeof(message->servername)); memcpy(tmp, message->servername, sizeof(tmp)); tmp[sizeof(tmp)-1] = '\0'; len = ni_string_len(tmp); if (ni_check_domain_name(tmp, len, 0)) { memcpy(lease->dhcp.servername, tmp, sizeof(lease->dhcp.servername)); } else { ni_debug_dhcp("Discarded suspect boot-server name: %s", ni_print_suspect(tmp, len)); } } if (use_bootfile && message->bootfile[0]) { char tmp[sizeof(message->bootfile)]; size_t len; memcpy(tmp, message->bootfile, sizeof(tmp)); tmp[sizeof(tmp)-1] = '\0'; len = ni_string_len(tmp); if (ni_check_pathname(tmp, len)) { ni_string_dup(&lease->dhcp.bootfile, tmp); } else { ni_debug_dhcp("Discarded suspect boot-file name: %s", ni_print_suspect(tmp, len)); } } /* Fill in any missing fields */ if (!lease->dhcp.netmask.s_addr) { unsigned int pfxlen = guess_prefix_len(lease->dhcp.address); lease->dhcp.netmask.s_addr = htonl(~(0xFFFFFFFF >> pfxlen)); }