/* * 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 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; }
void ni_addrconf_lease_destroy(ni_addrconf_lease_t *lease) { ni_addrconf_updater_free(&lease->updater); if (lease->old) { ni_addrconf_lease_free(lease->old); lease->old = NULL; } ni_string_free(&lease->owner); ni_string_free(&lease->hostname); ni_address_list_destroy(&lease->addrs); ni_route_tables_destroy(&lease->routes); if (lease->nis) { ni_nis_info_free(lease->nis); lease->nis = NULL; } if (lease->resolver) { ni_resolver_info_free(lease->resolver); lease->resolver = NULL; } ni_string_array_destroy(&lease->ntp_servers); ni_string_array_destroy(&lease->nds_servers); ni_string_array_destroy(&lease->nds_context); ni_string_free(&lease->nds_tree); ni_string_array_destroy(&lease->netbios_name_servers); ni_string_array_destroy(&lease->netbios_dd_servers); ni_string_free(&lease->netbios_scope); ni_string_array_destroy(&lease->slp_servers); ni_string_array_destroy(&lease->slp_scopes); ni_string_array_destroy(&lease->sip_servers); ni_string_array_destroy(&lease->lpr_servers); ni_string_array_destroy(&lease->log_servers); ni_string_free(&lease->posix_tz_string); ni_string_free(&lease->posix_tz_dbname); switch (lease->type) { case NI_ADDRCONF_DHCP: switch (lease->family) { case AF_INET: ni_addrconf_lease_dhcp4_destroy(&lease->dhcp4); break; case AF_INET6: ni_addrconf_lease_dhcp6_destroy(&lease->dhcp6); break; default: ; } break; default: ; } }
void ni_dhcp4_device_drop_best_offer(ni_dhcp4_device_t *dev) { dev->best_offer.weight = -1; if (dev->best_offer.lease) ni_addrconf_lease_free(dev->best_offer.lease); dev->best_offer.lease = NULL; }
int ni_netdev_unset_lease(ni_netdev_t *dev, unsigned int family, ni_addrconf_mode_t type) { ni_addrconf_lease_t *lease; if ((lease = __ni_netdev_find_lease(dev, family, type, 1)) != NULL) ni_addrconf_lease_free(lease); return 0; }
void ni_autoip_device_set_lease(ni_autoip_device_t *dev, ni_addrconf_lease_t *lease) { if (dev->lease != lease) { if (dev->lease) ni_addrconf_lease_free(dev->lease); dev->lease = lease; } }
int ni_dhcp4_fsm_recover_lease(ni_dhcp4_device_t *dev, const ni_dhcp4_request_t *req) { ni_addrconf_lease_t *lease; time_t now = time(NULL), then; /* Don't recover anything if we already have a lease attached. */ if (dev->lease != NULL) return -1; lease = ni_addrconf_lease_file_read(dev->ifname, NI_ADDRCONF_DHCP, AF_INET); if (!lease) return -1; if (lease->state != NI_ADDRCONF_STATE_GRANTED) goto discard; ni_debug_dhcp("trying to recover dhcp4 lease, now inspecting"); then = lease->time_acquired; if (now < then) { ni_debug_dhcp("%s: found time-warped lease (hi, grand-grand-pa)", __FUNCTION__); goto discard; } if (now >= then + lease->dhcp4.lease_time) { ni_debug_dhcp("%s: found expired lease", __FUNCTION__); goto discard; } if ((req->hostname && !ni_string_eq(req->hostname, dev->lease->hostname)) || (req->clientid && !ni_string_eq(req->clientid, dev->lease->dhcp4.client_id))) { ni_debug_dhcp("%s: lease doesn't match request", __FUNCTION__); goto discard; } ni_dhcp4_device_set_lease(dev, lease); if (now >= then + lease->dhcp4.rebind_time) { ni_dhcp4_fsm_rebind(dev); } else if (now >= then + lease->dhcp4.renewal_time) { ni_dhcp4_fsm_renewal(dev); } else { ni_dhcp4_fsm_set_deadline(dev, then + lease->dhcp4.renewal_time); dev->fsm.state = NI_DHCP4_STATE_BOUND; } ni_debug_dhcp("%s: recovered old lease; now in state=%s", dev->ifname, ni_dhcp4_fsm_state_name(dev->fsm.state)); dev->notify = 1; return 0; discard: ni_addrconf_lease_free(lease); return -1; }
void ni_addrconf_lease_list_destroy(ni_addrconf_lease_t **list) { ni_addrconf_lease_t *lease; while ((lease = *list) != NULL) { *list = lease->next; ni_addrconf_lease_free(lease); } }
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; }
void ni_dhcp4_device_set_best_offer(ni_dhcp4_device_t *dev, ni_addrconf_lease_t *lease, int weight) { if (dev->best_offer.lease && dev->best_offer.lease != lease) ni_addrconf_lease_free(dev->best_offer.lease); dev->best_offer.lease = lease; dev->best_offer.weight = weight; if (dev->config && lease) lease->uuid = dev->config->uuid; }
void ni_dhcp4_device_set_lease(ni_dhcp4_device_t *dev, ni_addrconf_lease_t *lease) { if (dev->lease != lease) { if (dev->lease) ni_addrconf_lease_free(dev->lease); dev->lease = lease; if (dev->config && lease) lease->uuid = dev->config->uuid; } }
void ni_dhcp4_device_drop_lease(ni_dhcp4_device_t *dev) { ni_addrconf_lease_t *lease; if ((lease = dev->lease) != NULL) { ni_addrconf_lease_free(lease); dev->lease = NULL; /* Go back to square one */ dev->fsm.state = NI_DHCP4_STATE_INIT; } }
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; }
/* * Reload an old lease from file, and see whether we can reuse it. * This is used during restart of wickedd. */ int ni_dhcp4_recover_lease(ni_dhcp4_device_t *dev) { ni_addrconf_lease_t *lease; ni_sockaddr_t addr; if (dev->lease) return 1; lease = ni_addrconf_lease_file_read(dev->ifname, NI_ADDRCONF_DHCP, AF_INET); if (!lease) return -1; if (!ni_addrconf_lease_is_valid(dev->lease)) { ni_debug_verbose(NI_LOG_DEBUG1, NI_TRACE_DHCP, "%s: discarding existing lease, not granted", dev->ifname); goto discard; } /* We cannot renew/rebind/reboot without it */ ni_sockaddr_set_ipv4(&addr, lease->dhcp4.server_id, 0); if (!ni_sockaddr_is_ipv4_specified(&addr)) { ni_debug_verbose(NI_LOG_DEBUG1, NI_TRACE_DHCP, "%s: discarding existing lease, no server-id", dev->ifname); goto discard; } ni_dhcp4_device_set_lease(dev, lease); return 0; discard: ni_addrconf_lease_free(lease); return -1; }
/* * 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); }
int ni_dhcp4_fsm_process_dhcp4_packet(ni_dhcp4_device_t *dev, ni_buffer_t *msgbuf) { ni_dhcp4_message_t *message; ni_addrconf_lease_t *lease = NULL; int msg_code; if (dev->fsm.state == NI_DHCP4_STATE_VALIDATING) { /* We arrive here, when some dhcp4 packet arrives after * we've got and processed an ACK already. Just ignore. */ ni_debug_dhcp("%s: ignoring dhcp4 packet arrived in state VALIDATING", dev->ifname); return -1; } if (!(message = ni_buffer_pull_head(msgbuf, sizeof(*message)))) { ni_debug_dhcp("short DHCP4 packet (%u bytes)", ni_buffer_count(msgbuf)); return -1; } if (dev->dhcp4.xid == 0) { ni_debug_dhcp("unexpected packet on %s", dev->ifname); return -1; } if (dev->dhcp4.xid != message->xid) { ni_debug_dhcp("ignoring packet with wrong xid 0x%x (expected 0x%x)", message->xid, dev->dhcp4.xid); return -1; } msg_code = ni_dhcp4_parse_response(message, msgbuf, &lease); if (msg_code < 0) { /* Ignore this message, time out later */ ni_error("unable to parse DHCP4 response"); return -1; } /* set reqest client-id in the response early to have it in test mode */ ni_opaque_set(&lease->dhcp4.client_id, dev->config->client_id.data, dev->config->client_id.len); ni_debug_dhcp("%s: received %s message in state %s", dev->ifname, ni_dhcp4_message_name(msg_code), ni_dhcp4_fsm_state_name(dev->fsm.state)); /* When receiving a DHCP4 OFFER, verify sender address against list of * servers to ignore, and preferred servers. */ if (msg_code == DHCP4_OFFER && dev->fsm.state == NI_DHCP4_STATE_SELECTING) { struct in_addr srv_addr = lease->dhcp4.server_id; int weight = 0; if (ni_dhcp4_config_ignore_server(srv_addr)) { ni_debug_dhcp("%s: ignoring DHCP4 offer from %s", dev->ifname, inet_ntoa(srv_addr)); goto out; } /* If we're scanning all offers, we need to decide whether * this offer is accepted, or whether we want to wait for * more. */ if (!dev->dhcp4.accept_any_offer) { /* Check if we have any preferred servers. */ weight = ni_dhcp4_config_server_preference(srv_addr); /* If we're refreshing an existing lease (eg after link disconnect * and reconnect), we accept the offer if it comes from the same * server as the original one. */ if (dev->lease && dev->lease->dhcp4.server_id.s_addr == srv_addr.s_addr) weight = 100; ni_debug_dhcp("received lease offer from %s; server weight=%d (best offer=%d)", inet_ntoa(lease->dhcp4.server_id), weight, dev->best_offer.weight); /* negative weight means never. */ if (weight < 0) goto out; /* weight between 0 and 100 means maybe. */ if (weight < 100) { if (dev->best_offer.weight < weight) { ni_dhcp4_device_set_best_offer(dev, lease, weight); return 0; } goto out; } /* If the weight has maximum value, just accept this offer. */ } ni_dhcp4_device_set_best_offer(dev, lease, weight); lease = NULL; } /* We've received a valid response; if something goes wrong now * it's nothing that could be fixed by retransmitting the message. * * The only exception would be if we ever do some filtering or * matching of OFFERs - then we would certainly want to keep * waiting for additional packets. */ ni_dhcp4_device_disarm_retransmit(dev); dev->dhcp4.xid = 0; /* move to next stage of protocol */ switch (msg_code) { case DHCP4_OFFER: if (dev->fsm.state != NI_DHCP4_STATE_SELECTING) goto ignore; /* process best offer set above */ ni_dhcp4_process_offer(dev, dev->best_offer.lease); break; case DHCP4_ACK: if (dev->fsm.state == NI_DHCP4_STATE_INIT) { /* * Received a decline ACK -- wait until * timeout before we restart from begin */ ni_dhcp4_device_drop_lease(dev); break; } if (dev->fsm.state != NI_DHCP4_STATE_REQUESTING && dev->fsm.state != NI_DHCP4_STATE_RENEWING && dev->fsm.state != NI_DHCP4_STATE_REBOOT && dev->fsm.state != NI_DHCP4_STATE_REBINDING) goto ignore; ni_dhcp4_process_ack(dev, lease); lease = NULL; break; case DHCP4_NAK: /* The RFC 2131 state diagram says, ignore NAKs in state BOUND. * I guess we also have no use for NAK replies to a DHCP4_DISCOVER */ if (dev->fsm.state == NI_DHCP4_STATE_SELECTING || dev->fsm.state == NI_DHCP4_STATE_BOUND) goto ignore; ni_dhcp4_process_nak(dev); break; default: ignore: ni_debug_dhcp("ignoring %s in state %s", ni_dhcp4_message_name(msg_code), ni_dhcp4_fsm_state_name(dev->fsm.state)); break; } out: if (lease && dev->lease != lease) ni_addrconf_lease_free(lease); /* If we received a message other than NAK, reset the NAK * backoff timer. */ if (msg_code != DHCP4_NAK) dev->dhcp4.nak_backoff = 1; return 0; }