/* * Inline functions for setting/retrieving options from a buffer */ static inline void ni_dhcp_option_put(ni_buffer_t *bp, int code, const void *data, size_t len) { ni_buffer_putc(bp, code); ni_buffer_putc(bp, len); ni_buffer_put(bp, data, len); }
int ni_arp_send(ni_arp_socket_t *arph, const ni_arp_packet_t *packet) { unsigned int hwlen, pktlen; struct arphdr *arp; ni_buffer_t buf; int rv; hwlen = ni_link_address_length(arph->dev_info.hwaddr.type); pktlen = sizeof(*arp) + 2 * hwlen + 2 * 4; arp = calloc(1, pktlen); ni_buffer_init(&buf, arp, pktlen); arp = ni_buffer_push_tail(&buf, sizeof(*arp)); arp->ar_hrd = htons(arph->dev_info.hwaddr.type); arp->ar_pro = htons(ETHERTYPE_IP); arp->ar_hln = hwlen; arp->ar_pln = 4; arp->ar_op = htons(packet->op); if (packet->sha.len == hwlen) { ni_buffer_put(&buf, packet->sha.data, packet->sha.len); } else { ni_buffer_put(&buf, NULL, hwlen); } ni_buffer_put(&buf, &packet->sip, 4); if (packet->tha.len == hwlen) { ni_buffer_put(&buf, packet->tha.data, packet->tha.len); } else { ni_buffer_put(&buf, NULL, hwlen); } ni_buffer_put(&buf, &packet->tip, 4); rv = ni_capture_send(arph->capture, &buf, NULL); free(buf.base); return rv; }
int ni_dhcp_build_message(const ni_dhcp_device_t *dev, unsigned int msg_code, const ni_addrconf_lease_t *lease, ni_buffer_t *msgbuf) { const ni_dhcp_config_t *options = dev->config; struct in_addr src_addr, dst_addr; ni_dhcp_message_t *message = NULL; if (!options || !lease) return -1; if (IN_LINKLOCAL(ntohl(lease->dhcp.address.s_addr))) { ni_error("cannot request a link local address"); goto failed; } src_addr.s_addr = dst_addr.s_addr = 0; switch (msg_code) { case DHCP_DISCOVER: if (lease->dhcp.serveraddress.s_addr != 0) return -1; break; case DHCP_REQUEST: case DHCP_RELEASE: case DHCP_INFORM: if (lease->dhcp.address.s_addr == 0 || lease->dhcp.serveraddress.s_addr == 0) return -1; src_addr = lease->dhcp.address; dst_addr = lease->dhcp.serveraddress; break; } /* Reserve some room for the IP and UDP header */ ni_buffer_reserve_head(msgbuf, sizeof(struct ip) + sizeof(struct udphdr)); /* Build the message */ message = ni_buffer_push_tail(msgbuf, sizeof(*message)); message->op = DHCP_BOOTREQUEST; message->hwtype = dev->system.arp_type; message->xid = dev->dhcp.xid; message->cookie = htonl(MAGIC_COOKIE); message->secs = htons(ni_dhcp_device_uptime(dev, 0xFFFF)); if (dev->fsm.state == NI_DHCP_STATE_BOUND || dev->fsm.state == NI_DHCP_STATE_RENEWING || dev->fsm.state == NI_DHCP_STATE_REBINDING) message->ciaddr = lease->dhcp.address.s_addr; switch (dev->system.arp_type) { case ARPHRD_ETHER: case ARPHRD_IEEE802: if (dev->system.hwaddr.len > sizeof(message->chaddr)) { ni_error("dhcp cannot handle hwaddress length %u", dev->system.hwaddr.len); goto failed; } message->hwlen = dev->system.hwaddr.len; memcpy(&message->chaddr, dev->system.hwaddr.data, dev->system.hwaddr.len); break; case ARPHRD_IEEE1394: case ARPHRD_INFINIBAND: message->hwlen = 0; if (message->ciaddr == 0) message->flags = htons(BROADCAST_FLAG); break; default: ni_error("dhcp: unknown hardware type %d", dev->system.arp_type); } ni_dhcp_option_put8(msgbuf, DHCP_MESSAGETYPE, msg_code); if (msg_code == DHCP_REQUEST) ni_dhcp_option_put16(msgbuf, DHCP_MAXMESSAGESIZE, dev->system.mtu); ni_dhcp_option_put(msgbuf, DHCP_CLIENTID, options->raw_client_id.data, options->raw_client_id.len); if (msg_code != DHCP_DECLINE && msg_code != DHCP_RELEASE) { if (options->userclass.len > 0) ni_dhcp_option_put(msgbuf, DHCP_USERCLASS, options->userclass.data, options->userclass.len); if (options->classid && options->classid[0]) ni_dhcp_option_puts(msgbuf, DHCP_CLASSID, options->classid); } if (msg_code == DHCP_DISCOVER || msg_code == DHCP_REQUEST) { if (lease->dhcp.address.s_addr) ni_dhcp_option_put_ipv4(msgbuf, DHCP_ADDRESS, lease->dhcp.address); if (lease->dhcp.lease_time != 0) ni_dhcp_option_put32(msgbuf, DHCP_LEASETIME, lease->dhcp.lease_time); } if (msg_code == DHCP_REQUEST) { if (lease->dhcp.serveraddress.s_addr) ni_dhcp_option_put_ipv4(msgbuf, DHCP_SERVERIDENTIFIER, lease->dhcp.serveraddress); } if (msg_code == DHCP_DISCOVER || msg_code == DHCP_INFORM || msg_code == DHCP_REQUEST) { unsigned int params_begin; if (options->hostname && options->hostname[0]) { if (options->fqdn == FQDN_DISABLE) { ni_dhcp_option_puts(msgbuf, DHCP_HOSTNAME, options->hostname); } else { /* 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, DHCP_FQDN); ni_buffer_putc(msgbuf, strlen(options->hostname) + 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, strlen(options->hostname)); } } params_begin = ni_dhcp_option_begin(msgbuf, DHCP_PARAMETERREQUESTLIST); if (msg_code == DHCP_DISCOVER) { /* dhcpcd says we should include just a single option * in discovery packets. * I'm not convinced this is right, but let's do it * this way. */ ni_buffer_putc(msgbuf, DHCP_DNSSERVER); } else { if (msg_code != DHCP_INFORM) { ni_buffer_putc(msgbuf, DHCP_RENEWALTIME); ni_buffer_putc(msgbuf, DHCP_REBINDTIME); } ni_buffer_putc(msgbuf, DHCP_NETMASK); ni_buffer_putc(msgbuf, DHCP_BROADCAST); if (options->flags & DHCP_DO_CSR) ni_buffer_putc(msgbuf, DHCP_CSR); if (options->flags & DHCP_DO_MSCSR) ni_buffer_putc(msgbuf, DHCP_MSCSR); /* RFC 3442 states classless static routes should be * before routers and static routes as classless static * routes override them both */ ni_buffer_putc(msgbuf, DHCP_STATICROUTE); ni_buffer_putc(msgbuf, DHCP_ROUTERS); ni_buffer_putc(msgbuf, DHCP_HOSTNAME); ni_buffer_putc(msgbuf, DHCP_DNSSEARCH); ni_buffer_putc(msgbuf, DHCP_DNSDOMAIN); ni_buffer_putc(msgbuf, DHCP_DNSSERVER); if (options->flags & DHCP_DO_NIS) { ni_buffer_putc(msgbuf, DHCP_NISDOMAIN); ni_buffer_putc(msgbuf, DHCP_NISSERVER); } if (options->flags & DHCP_DO_NTP) ni_buffer_putc(msgbuf, DHCP_NTPSERVER); ni_buffer_putc(msgbuf, DHCP_MTU); ni_buffer_putc(msgbuf, DHCP_ROOTPATH); ni_buffer_putc(msgbuf, DHCP_SIPSERVER); ni_buffer_putc(msgbuf, DHCP_LPRSERVER); ni_buffer_putc(msgbuf, DHCP_LOGSERVER); ni_buffer_putc(msgbuf, DHCP_NETBIOSNAMESERVER); ni_buffer_putc(msgbuf, DHCP_NETBIOSDDSERVER); ni_buffer_putc(msgbuf, DHCP_NETBIOSNODETYPE); ni_buffer_putc(msgbuf, DHCP_NETBIOSSCOPE); } ni_dhcp_option_end(msgbuf, params_begin); } ni_buffer_putc(msgbuf, DHCP_END); #ifdef BOOTP_MESSAGE_LENGTH_MIN ni_buffer_pad(msgbuf, BOOTP_MESSAGE_LENGTH_MIN, DHCP_PAD); #endif if (ni_capture_build_udp_header(msgbuf, src_addr, DHCP_CLIENT_PORT, dst_addr, DHCP_SERVER_PORT) < 0) { ni_error("unable to build packet header"); goto failed; } return 0; failed: return -1; }
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; }