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; }
int ni_dhcp4_build_message(const ni_dhcp4_device_t *dev, unsigned int msg_code, const ni_addrconf_lease_t *lease, ni_buffer_t *msgbuf) { const ni_dhcp4_config_t *options = dev->config; struct in_addr src_addr, dst_addr; int renew = dev->fsm.state == NI_DHCP4_STATE_RENEWING && msg_code == DHCP4_REQUEST; if (!options || !lease) { ni_error("%s: %s: %s: missing %s %s", __func__, dev->ifname, ni_dhcp4_message_name(msg_code), options? "" : "options", lease ? "" : "lease"); return -1; } if (IN_LINKLOCAL(ntohl(lease->dhcp4.address.s_addr))) { ni_error("%s: cannot request a link local address", dev->ifname); goto failed; } /* Reserve some room for the IP and UDP header */ if (!renew) ni_buffer_reserve_head(msgbuf, sizeof(struct ip) + sizeof(struct udphdr)); src_addr.s_addr = dst_addr.s_addr = 0; switch (msg_code) { case DHCP4_INFORM: if (__ni_dhcp4_build_msg_inform(dev, lease, msgbuf) < 0) goto failed; break; case DHCP4_DISCOVER: if (__ni_dhcp4_build_msg_discover(dev, lease, msgbuf) < 0) goto failed; break; case DHCP4_DECLINE: if (__ni_dhcp4_build_msg_decline(dev, lease, msgbuf) < 0) goto failed; break; case DHCP4_RELEASE: if (__ni_dhcp4_build_msg_release(dev, lease, msgbuf) < 0) goto failed; break; case DHCP4_REQUEST: switch (dev->fsm.state) { case NI_DHCP4_STATE_REQUESTING: src_addr.s_addr = 0; dst_addr.s_addr = 0; if (__ni_dhcp4_build_msg_request_offer(dev, lease, msgbuf) < 0) goto failed; break; case NI_DHCP4_STATE_RENEWING: src_addr = lease->dhcp4.address; dst_addr = lease->dhcp4.server_id; if (__ni_dhcp4_build_msg_request_renew(dev, lease, msgbuf) < 0) goto failed; break; case NI_DHCP4_STATE_REBINDING: src_addr = lease->dhcp4.address; dst_addr.s_addr = 0; if (__ni_dhcp4_build_msg_request_rebind(dev, lease, msgbuf) < 0) goto failed; break; case NI_DHCP4_STATE_REBOOT: src_addr.s_addr = 0; dst_addr.s_addr = 0; if (__ni_dhcp4_build_msg_request_reboot(dev, lease, msgbuf) < 0) goto failed; break; default: goto failed; } break; default: goto failed; } ni_buffer_putc(msgbuf, DHCP4_END); #ifdef BOOTP_MESSAGE_LENGTH_MIN ni_buffer_pad(msgbuf, BOOTP_MESSAGE_LENGTH_MIN, DHCP4_PAD); #endif if (!renew && ni_capture_build_udp_header(msgbuf, src_addr, DHCP4_CLIENT_PORT, dst_addr, DHCP4_SERVER_PORT) < 0) { ni_error("%s: unable to build packet header", dev->ifname); goto failed; } return 0; failed: return -1; }