static ni_bool_t ni_ifup_start_policies(ni_ifworker_array_t *array, ni_bool_t set_persistent) { unsigned int i; ni_bool_t rv = TRUE; for (i = 0; i < array->count; i++) { ni_ifworker_t *w = array->data[i]; if (set_persistent) ni_client_state_set_persistent(w->config.node); if (!ni_ifup_hire_nanny(w)) { ni_error("%s: unable to apply configuration to nanny", w->name); rv = FALSE; } else ni_info("%s: configuration applied to nanny", w->name); } if (0 == array->count) printf("ifup: no matching interfaces\n"); return rv; }
static int ni_dhcp4_process_offer(ni_dhcp4_device_t *dev, ni_addrconf_lease_t *lease) { char abuf1[INET_ADDRSTRLEN]; char abuf2[INET_ADDRSTRLEN]; /* TBD: We should be smarter here. * * - track "bad" leases, and blacklist them for a while. * (eg addresses that fail the ARP check). * * - try to detect if we woke up in a different network * environment; in that case there's no point in attempting * to renew the same old lease forever. Some MS based DHCP * servers in airports and hotels never seem to send NAKs * in such as case. */ inet_ntop(AF_INET, &lease->dhcp4.address, abuf1, sizeof(abuf1)); inet_ntop(AF_INET, &lease->dhcp4.server_id, abuf2, sizeof(abuf2)); ni_info("%s: Received offer for %s from %s", dev->ifname, abuf1, abuf2); if (dev->config->dry_run == NI_DHCP4_RUN_OFFER) { ni_dhcp4_send_event(NI_DHCP4_EVENT_ACQUIRED, dev, lease); ni_dhcp4_fsm_restart(dev); ni_dhcp4_device_stop(dev); } else { ni_dhcp4_fsm_request(dev, lease); } return 0; }
int ni_ethtool_set_priv_flags(const char *ifname, ni_ethtool_t *ethtool, const ni_ethtool_priv_flags_t *pflags) { static const ni_ethtool_cmd_info_t NI_ETHTOOL_CMD_SPFLAGS = { ETHTOOL_SPFLAGS, "set priv-flags" }; struct ethtool_value ecmd; unsigned int i, bit; const char *name; ni_bool_t enabled; int ret; if (!pflags || !pflags->names.count) return 1; /* nothing to set */ if (!ethtool->priv_flags && (ret = ni_ethtool_get_priv_flags(ifname, ethtool)) < 0) return ret; if (!ethtool->priv_flags || !ethtool->priv_flags->names.count) return -EOPNOTSUPP; memset(&ecmd, 0, sizeof(ecmd)); ecmd.data = ethtool->priv_flags->bitmap; /* set every single bit separately in case one fails? */ for (i = 0; i < pflags->names.count; ++i) { name = pflags->names.data[i]; if (ni_string_empty(name)) continue; enabled = !!(pflags->bitmap & NI_BIT(i)); bit = ni_string_array_index(ðtool->priv_flags->names, name); if (bit == -1U) { ni_info("%s: unable to set unknown driver private flag '%s'", ifname, name); continue; } ni_debug_verbose(NI_LOG_DEBUG1, NI_TRACE_IFCONFIG, "%s: setting driver private flag '%s' to %s", ifname, name, ni_format_boolean(enabled)); if (enabled) ecmd.data |= NI_BIT(bit); else ecmd.data &= ~NI_BIT(bit); } if (ecmd.data == ethtool->priv_flags->bitmap) return 0; ret = ni_ethtool_call(ifname, &NI_ETHTOOL_CMD_SPFLAGS, &ecmd, NULL); ni_ethtool_set_supported(ethtool, NI_ETHTOOL_SUPP_SET_PRIV_FLAGS, ret != -EOPNOTSUPP); if (ret < 0) return ret; return 0; }
int ni_dhcp4_fsm_renewal(ni_dhcp4_device_t *dev) { int rv; ni_info("%s: Initiating renewal of DHCPv4 lease", dev->ifname); dev->fsm.state = NI_DHCP4_STATE_RENEWING; rv = ni_dhcp4_device_send_message_unicast(dev, DHCP4_REQUEST, dev->lease); ni_dhcp4_fsm_set_deadline(dev, dev->lease->time_acquired + dev->lease->dhcp4.rebind_time); return rv; }
int ni_dhcp4_fsm_rebind(ni_dhcp4_device_t *dev) { int rv; ni_info("%s: Initiating rebind of DHCPv4 lease", dev->ifname); dev->lease->dhcp4.server_id.s_addr = 0; dev->fsm.state = NI_DHCP4_STATE_REBINDING; rv = ni_dhcp4_device_send_message(dev, DHCP4_REQUEST, dev->lease); ni_dhcp4_fsm_set_deadline(dev, dev->lease->time_acquired + dev->lease->dhcp4.lease_time); return rv; }
int ni_dhcp4_fsm_request(ni_dhcp4_device_t *dev, const ni_addrconf_lease_t *lease) { int rv; ni_info("%s: Requesting DHCPv4 lease with timeout %d sec", dev->ifname, dev->config->request_timeout); dev->fsm.state = NI_DHCP4_STATE_REQUESTING; rv = ni_dhcp4_device_send_message(dev, DHCP4_REQUEST, lease); /* Ignore the return value; sending the request may actually * fail transiently */ ni_dhcp4_fsm_set_timeout(dev, dev->config->request_timeout); return rv; }
int ni_dhcp4_fsm_arp_validate(ni_dhcp4_device_t *dev) { struct in_addr null = { 0 }; struct in_addr claim; if (!dev || !dev->lease) return -1; claim = dev->lease->dhcp4.address; if (dev->arp.handle == NULL) { dev->arp.handle = ni_arp_socket_open(&dev->system, ni_dhcp4_fsm_process_arp_packet, dev); if (!dev->arp.handle->user_data) { ni_error("%s: unable to create ARP handle", dev->ifname); return -1; } } if (dev->arp.nprobes) { ni_debug_dhcp("%s: arp validate: probing for %s", dev->ifname, inet_ntoa(claim)); ni_arp_send_request(dev->arp.handle, null, claim); dev->arp.nprobes--; } else if (dev->arp.nclaims) { ni_debug_dhcp("%s: arp validate: claiming %s", dev->ifname, inet_ntoa(claim)); ni_arp_send_grat_request(dev->arp.handle, claim); dev->arp.nclaims--; } else { /* Wow, we're done! */ ni_info("%s: Successfully validated DHCPv4 address %s", dev->ifname, inet_ntoa(claim)); ni_dhcp4_fsm_commit_lease(dev, dev->lease); ni_dhcp4_device_arp_close(dev); return 0; } ni_dhcp4_fsm_set_timeout_msec(dev, NI_DHCP4_ARP_TIMEOUT); return 0; }
int ni_dhcp4_fsm_validate_lease(ni_dhcp4_device_t *dev, ni_addrconf_lease_t *lease) { /* * When the address is already set on the link, we * don't need to validate it and just commit it. */ if (__ni_dhcp4_address_on_link(dev, lease->dhcp4.address)) { ni_debug_dhcp("%s: address %s is on link, omit validation", dev->ifname, inet_ntoa(lease->dhcp4.address)); ni_dhcp4_fsm_commit_lease(dev, lease); return 0; } ni_info("%s: Validating DHCPv4 address %s", dev->ifname, inet_ntoa(lease->dhcp4.address)); /* For ARP validations, we will send 3 ARP queries with a timeout * of 200ms each. * The "claims" part is really for IPv4LL */ dev->arp.nprobes = 3; dev->arp.nclaims = 0; /* dhcp4cd source code says: * IEEE1394 cannot set ARP target address according to RFC2734 */ if (dev->system.hwaddr.type == ARPHRD_IEEE1394) dev->arp.nclaims = 0; if (ni_dhcp4_fsm_arp_validate(dev) < 0) { ni_debug_dhcp("%s: unable to validate lease", dev->ifname); return -1; } dev->fsm.state = NI_DHCP4_STATE_VALIDATING; return 0; }
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; }
void ni_config_parse_update_targets(unsigned int *update_mask, const xml_node_t *node) { const xml_node_t *child; *update_mask = __NI_ADDRCONF_UPDATE_NONE; for (child = node->children; child; child = child->next) { unsigned int target; if (!strcmp(child->name, "all")) { *update_mask = ni_config_addrconf_update_mask_all(); } else if (!strcmp(child->name, "none")) { *update_mask = __NI_ADDRCONF_UPDATE_NONE; } else if (ni_addrconf_update_name_to_flag(child->name, &target)) { ni_addrconf_update_set(update_mask, target, TRUE); } else { ni_info("ignoring unknown addrconf update target \"%s\"", child->name); } } }
static ni_socket_t * __ni_rtevent_sock_open(void) { unsigned int recv_buff_len = __ni_rtevent_config_recv_buff_len(); unsigned int mesg_buff_len = __ni_rtevent_config_mesg_buff_len(); ni_rtevent_handle_t *handle; ni_socket_t *sock; int fd, ret; if (!(handle = __ni_rtevent_handle_new())) { ni_error("Unable to allocate rtnetlink event handle: %m"); return NULL; } if (!(handle->nlsock = nl_socket_alloc())) { ni_error("Cannot allocate rtnetlink event socket: %m"); __ni_rtevent_handle_free(handle); return NULL; } /* * Modify the callback for processing valid messages... * We may pass some kind of data (event filter?) too... */ nl_socket_modify_cb(handle->nlsock, NL_CB_VALID, NL_CB_CUSTOM, __ni_rtevent_process_cb, NULL); /* Required to receive async event notifications */ nl_socket_disable_seq_check(handle->nlsock); if ((ret = nl_connect(handle->nlsock, NETLINK_ROUTE)) < 0) { ni_error("Cannot open rtnetlink: %s", nl_geterror(ret)); __ni_rtevent_handle_free(handle); return NULL; } /* Enable non-blocking processing */ nl_socket_set_nonblocking(handle->nlsock); fd = nl_socket_get_fd(handle->nlsock); if (!(sock = ni_socket_wrap(fd, SOCK_DGRAM))) { ni_error("Cannot wrap rtnetlink event socket: %m"); __ni_rtevent_handle_free(handle); return NULL; } if (recv_buff_len) { if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, (char *)&recv_buff_len, sizeof(recv_buff_len)) && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buff_len, sizeof(recv_buff_len))) { ni_warn("Unable to set netlink event receive buffer to %u bytes: %m", recv_buff_len); } else { ni_info("Using netlink event receive buffer of %u bytes", recv_buff_len); } } if (mesg_buff_len) { if (nl_socket_set_msg_buf_size(handle->nlsock, mesg_buff_len)) { ni_warn("Unable to set netlink event message buffer to %u bytes", mesg_buff_len); } else { ni_info("Using netlink event message buffer of %u bytes", mesg_buff_len); } } sock->user_data = handle; sock->receive = __ni_rtevent_receive; sock->close = __ni_rtevent_close; sock->handle_error = __ni_rtevent_sock_error_handler; sock->release_user_data = __ni_rtevent_sock_release_data; return sock; }
static void __do_arp_validate_process(ni_arp_socket_t *sock, const ni_arp_packet_t *pkt, void *user_data) { struct arp_handle *handle = user_data; ni_netconfig_t *nc = ni_global_state_handle(0); const ni_netdev_t *ifp; ni_bool_t false_alarm = FALSE; ni_bool_t found_addr = FALSE; const ni_address_t *ap; ni_sockaddr_t addr; if (!pkt || pkt->op != ARPOP_REPLY || !handle->replies) return; /* Is it about the address we're validating at all? */ if (pkt->sip.s_addr != handle->ipaddr.sin.sin_addr.s_addr) { ni_debug_application("%s: report about different address", handle->ifname); return; } /* Ignore any ARP replies that seem to come from our own * MAC address. Some helpful switches seem to generate * these. */ if (ni_link_address_equal(&sock->dev_info.hwaddr, &pkt->sha)) { ni_debug_application("%s: adress in use by ourself", handle->ifname); return; } /* As well as ARP replies that seem to come from our own * host: dup if same address, not a dup if there are two * interfaces connected to the same broadcast domain. */ ni_sockaddr_set_ipv4(&addr, pkt->sip, 0); for (ifp = ni_netconfig_devlist(nc); ifp; ifp = ifp->next) { if (ifp->link.ifindex == sock->dev_info.ifindex) continue; if (!ni_netdev_link_is_up(ifp)) continue; if (!ni_link_address_equal(&ifp->link.hwaddr, &pkt->sha)) continue; /* OK, we have an interface matching the hwaddr, * which will answer arp requests when it is on * the same broadcast domain and causes a false * alarm, except it really has the IP assigned. */ false_alarm = TRUE; for (ap = ifp->addrs; !found_addr && ap; ap = ap->next) { if (ap->family != AF_INET) continue; if (ni_sockaddr_equal(&ap->local_addr, &addr)) found_addr = TRUE; } } if (false_alarm && !found_addr) { ni_debug_application("%s: reply from one of our interfaces", handle->ifname); return; } ni_info("%s: adress %s in use by %s reported", handle->ifname, inet_ntoa(pkt->sip), ni_link_address_print(&pkt->sha)); handle->hwaddr = pkt->sha; }
/* * 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; }
static int __ni_dhcp4_build_msg_put_our_hostname(const ni_dhcp4_device_t *dev, ni_buffer_t *msgbuf) { const ni_dhcp4_config_t *options = dev->config; size_t len = ni_string_len(options->hostname); if (!len) return 1; /* skipped hint */ if (options->fqdn == FQDN_DISABLE) { char hname[64] = {'\0'}, *end; /* * Truncate the domain part if fqdn to avoid attempts * to update DNS with foo.bar + update-domain. */ strncat(hname, options->hostname, sizeof(hname)-1); if ((end = strchr(hname, '.'))) *end = '\0'; len = ni_string_len(hname); if (ni_check_domain_name(hname, len, 0)) { ni_dhcp4_option_puts(msgbuf, DHCP4_HOSTNAME, hname); ni_debug_verbose(NI_LOG_DEBUG1, NI_TRACE_DHCP, "%s: using hostname: %s", dev->ifname, hname); } else { ni_info("%s: not sending suspect hostname: '%s'", dev->ifname, ni_print_suspect(hname, len)); return 1; } } else if (ni_check_domain_name(options->hostname, len, 0)) { /* IETF DHC-FQDN option(81) * http://tools.ietf.org/html/rfc4702#section-2.1 * * Flags: 0000NEOS * S: 1 => Client requests Server to update * a RR in DNS as well as PTR * O: 1 => Server indicates to client that * DNS has been updated * E: 1 => Name data is DNS format * N: 1 => Client requests Server to not * update DNS */ ni_buffer_putc(msgbuf, DHCP4_FQDN); ni_buffer_putc(msgbuf, len + 3); ni_buffer_putc(msgbuf, options->fqdn & 0x9); ni_buffer_putc(msgbuf, 0); /* from server for PTR RR */ ni_buffer_putc(msgbuf, 0); /* from server for A RR if S=1 */ ni_buffer_put(msgbuf, options->hostname, len); ni_debug_verbose(NI_LOG_DEBUG1, NI_TRACE_DHCP, "%s: using fqdn: %s", dev->ifname, options->hostname); } else { ni_info("%s: not sending suspect fqdn: '%s'", dev->ifname, ni_print_suspect(options->hostname, len)); return 1; } return 0; }