/* * Run the netif firmware discovery scripts and return their output * as an XML document. * The optional from parameter allow to specify the firmware extension * type (e.g. ibft) and a firmware type specific path (e.g. ethernet0), * passed as last argument to the discovery script. */ xml_document_t * ni_netconfig_firmware_discovery(const char *root, const char *from) { ni_buffer_t *buffer; xml_document_t *doc; char *path = NULL; char *type = NULL; /* sanity adjustments... */ if (ni_string_empty(root)) root = NULL; if (ni_string_empty(from)) from = NULL; else { ni_string_dup(&type, from); if ((path = strchr(type, ':'))) *path++ = '\0'; if (ni_string_empty(path)) path = NULL; } buffer = __ni_netconfig_firmware_discovery(root, type, path); if (buffer == NULL) { ni_string_free(&type); return NULL; } ni_debug_ifconfig("%s: %s%sbuffer has %u bytes", __func__, (from ? from : ""), (from ? " ": ""), ni_buffer_count(buffer)); doc = xml_document_from_buffer(buffer, from); ni_buffer_free(buffer); ni_string_free(&type); if (doc == NULL) ni_error("%s: error processing document", __func__); return doc; }
/* * DHCP_STATICROUTE * List of network/gateway pairs. */ static int ni_dhcp_decode_static_routes(ni_buffer_t *bp, ni_route_array_t *routes) { while (ni_buffer_count(bp) && !bp->underflow) { ni_sockaddr_t destination, gateway; ni_route_t *rp; if (ni_dhcp_option_get_sockaddr(bp, &destination) < 0 || ni_dhcp_option_get_sockaddr(bp, &gateway) < 0) return -1; rp = ni_route_create(guess_prefix_len_sockaddr(&destination), &destination, &gateway, 0, NULL); ni_route_array_append(routes, rp); } return 0; }
static int ni_dhcp_option_get_netbios_type(ni_buffer_t *bp, unsigned int *type) { unsigned int len = ni_buffer_count(bp); if (len != 1) return -1; *type = (unsigned int)ni_buffer_getc(bp); switch (*type) { case 0x1: /* B-node */ case 0x2: /* P-node */ case 0x4: /* M-node */ case 0x8: /* H-node */ return 0; default: break; } *type = 0; return -1; }
int ni_file_write_safe(FILE *fp, const ni_buffer_t *wbuf) { const unsigned char *string = ni_buffer_head(wbuf); unsigned int left = ni_buffer_count(wbuf); mbstate_t mbstate; size_t written = 0; ni_bool_t saw_newline = TRUE; if (left == 0) return 0; memset(&mbstate, 0, sizeof(mbstate)); while (left != 0) { unsigned int nbytes; wchar_t wc; /* Scan until we hit the first non-printable character. * Assume the test node uses the same locale as we do */ for (nbytes = 0; nbytes < left; ) { ssize_t n; saw_newline = FALSE; n = mbrtowc(&wc, (char *) string + nbytes, left - nbytes, &mbstate); if (n < 0) { /* We hit a bad path of string. * If this is preceded by one or more printable * chars, write those out first, then come back here. */ if (nbytes != 0) break; /* write one byte, and try to re-sync */ wc = ((unsigned char *) string)[nbytes++]; if (__ni_file_write_wchar_escaped(fp, wc) < 0) return -1; memset(&mbstate, 0, sizeof(mbstate)); goto consumed_some; } if (!iswprint(wc) && !iswspace(wc)) { /* If this is preceded by one or more printable * chars, write those out first */ if (nbytes != 0) break; if (__ni_file_write_wchar_escaped(fp, wc) < 0) return -1; nbytes += n; goto consumed_some; } if (wc == '\n') saw_newline = TRUE; nbytes += n; } ni_assert(nbytes); if (__ni_file_write_partial(fp, string, nbytes) < 0) return -1; consumed_some: string += nbytes; left -= nbytes; } /* Make sure we're writing a newline at the end of our output */ if (!saw_newline) fputc('\n', fp); fflush(fp); return written; }
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; }
/* * 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)); }
/* * Decode an RFC3397 DNS search order option. */ static int ni_dhcp_decode_dnssearch(ni_buffer_t *optbuf, ni_string_array_t *list, const char *what) { ni_stringbuf_t namebuf = NI_STRINGBUF_INIT_DYNAMIC; unsigned char *base = ni_buffer_head(optbuf); unsigned int base_offset = optbuf->head; size_t len; ni_string_array_destroy(list); while (ni_buffer_count(optbuf) && !optbuf->underflow) { ni_buffer_t *bp = optbuf; ni_buffer_t jumpbuf; while (1) { unsigned int pos = bp->head - base_offset; unsigned int pointer; char label[64]; int length; if ((length = ni_buffer_getc(bp)) < 0) goto failure; /* unexpected EOF */ if (length == 0) break; /* end of this name */ switch (length & 0xC0) { case 0: /* Plain name component */ if (ni_buffer_get(bp, label, length) < 0) goto failure; label[length] = '\0'; if (!ni_stringbuf_empty(&namebuf)) ni_stringbuf_putc(&namebuf, '.'); ni_stringbuf_puts(&namebuf, label); break; case 0xC0: /* Pointer */ pointer = (length & 0x3F) << 8; if ((length = ni_buffer_getc(bp)) < 0) goto failure; pointer |= length; if (pointer >= pos) goto failure; ni_buffer_init_reader(&jumpbuf, base, pos); jumpbuf.head = pointer; bp = &jumpbuf; break; default: goto failure; } } if (!ni_stringbuf_empty(&namebuf)) { len = ni_string_len(namebuf.string); if (ni_check_domain_name(namebuf.string, len, 0)) { ni_string_array_append(list, namebuf.string); } else { ni_debug_dhcp("Discarded suspect %s: %s", what, ni_print_suspect(namebuf.string, len)); } } ni_stringbuf_destroy(&namebuf); } return 0; failure: ni_stringbuf_destroy(&namebuf); ni_string_array_destroy(list); return -1; }