#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
- uint8_t type, uint16_t secs, uint8_t **opt,
- size_t *optlen) {
+ uint8_t type, uint8_t **opt, size_t *optlen) {
int err;
+ assert(op == BOOTREQUEST || op == BOOTREPLY);
+
*opt = (uint8_t *)(message + 1);
if (*optlen < 4)
message->hlen = ETHER_ADDR_LEN;
message->xid = htobe32(xid);
- /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
- refuse to issue an DHCP lease if 'secs' is set to zero */
- message->secs = htobe16(secs);
-
(*opt)[0] = 0x63;
(*opt)[1] = 0x82;
(*opt)[2] = 0x53;
return ~sum;
}
-void dhcp_packet_append_ip_headers(DHCPPacket *packet, uint8_t op,
- uint16_t len) {
- assert(op == BOOTREQUEST || op == BOOTREPLY);
-
+void dhcp_packet_append_ip_headers(DHCPPacket *packet, uint16_t len) {
packet->ip.version = IPVERSION;
packet->ip.ihl = DHCP_IP_SIZE / 4;
packet->ip.tot_len = htobe16(len);
packet->ip.saddr = INADDR_ANY;
packet->ip.daddr = INADDR_BROADCAST;
- switch (op) {
- case BOOTREQUEST:
- packet->udp.source = htobe16(DHCP_PORT_CLIENT);
- packet->udp.dest = htobe16(DHCP_PORT_SERVER);
- break;
- case BOOTREPLY:
- packet->udp.source = htobe16(DHCP_PORT_SERVER);
- packet->udp.dest = htobe16(DHCP_PORT_CLIENT);
- break;
- }
+ packet->udp.source = htobe16(DHCP_PORT_CLIENT);
+ packet->udp.dest = htobe16(DHCP_PORT_SERVER);
packet->udp.len = htobe16(len - DHCP_IP_SIZE);
packet->ip.check = dhcp_checksum(&packet->ip, DHCP_IP_SIZE);
}
-int dhcp_packet_verify_headers(DHCPPacket *packet, uint8_t op, size_t len) {
+int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
size_t hdrlen;
- assert(op == BOOTREQUEST || op == BOOTREPLY);
+ assert(packet);
+
+ /* IP */
+
+ if (len < DHCP_IP_SIZE) {
+ log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
+ " smaller than IP header (%u bytes)", len,
+ DHCP_IP_SIZE);
+ return -EINVAL;
+ }
- if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE)) {
- log_dhcp_client(client, "ignoring packet: packet too small");
+ if (packet->ip.ihl < 5) {
+ log_dhcp_client(client, "ignoring packet: IPv4 IHL (%u words) invalid",
+ packet->ip.ihl);
return -EINVAL;
}
hdrlen = packet->ip.ihl * 4;
- if (hdrlen < 20 || hdrlen > len) {
- log_dhcp_client(client, "ignoring packet: header with wrong size");
+ if (hdrlen < 20) {
+ log_dhcp_client(client, "ignoring packet: IPv4 IHL (%zu bytes) "
+ "smaller than minimum (20 bytes)", hdrlen);
+ return -EINVAL;
+ }
+
+ if (len < hdrlen) {
+ log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
+ "smaller than expected (%zu) by IP header", len,
+ hdrlen);
return -EINVAL;
}
if (dhcp_checksum(&packet->ip, hdrlen)) {
- log_dhcp_client(client, "ignoring packet: invalid ip checksum");
+ log_dhcp_client(client, "ignoring packet: invalid IP checksum");
+ return -EINVAL;
+ }
+
+ /* UDP */
+
+ if (len < DHCP_IP_UDP_SIZE) {
+ log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
+ " smaller than IP+UDP header (%u bytes)", len,
+ DHCP_IP_UDP_SIZE);
return -EINVAL;
}
- if (hdrlen + be16toh(packet->udp.len) > len) {
- log_dhcp_client(client, "ignoring packet: packet too small (udp.len=%u)",
- be16toh(packet->udp.len));
+ if (len < hdrlen + be16toh(packet->udp.len)) {
+ log_dhcp_client(client, "ignoring packet: packet (%zu bytes) "
+ "smaller than expected (%zu) by UDP header", len,
+ hdrlen + be16toh(packet->udp.len));
return -EINVAL;
}
- if (packet->udp.check) {
+ if (checksum && packet->udp.check) {
packet->ip.check = packet->udp.len;
packet->ip.ttl = 0;
if (dhcp_checksum(&packet->ip.ttl,
be16toh(packet->udp.len) + 12)) {
- log_dhcp_client(client, "ignoring packet: invalid udp checksum");
+ log_dhcp_client(client, "ignoring packet: invalid UDP checksum");
return -EINVAL;
}
}
- if (packet->dhcp.op != op) {
- log_dhcp_client(client, "ignoring packet: wrong operation");
+ if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
+ log_dhcp_client(client, "ignoring packet: to port %u, which "
+ "is not the DHCP client port (%u)",
+ be16toh(packet->udp.dest), DHCP_PORT_CLIENT);
return -EINVAL;
}
- switch (op) {
- case BOOTREQUEST:
- if (be16toh(packet->udp.source) != DHCP_PORT_CLIENT ||
- be16toh(packet->udp.dest) != DHCP_PORT_SERVER) {
- log_dhcp_client(client, "ignoring packet: wrong ports");
- return -EINVAL;
- }
- break;
- case BOOTREPLY:
- if (be16toh(packet->udp.source) != DHCP_PORT_SERVER ||
- be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
- log_dhcp_client(client, "ignoring packet: wrong ports");
- return -EINVAL;
- }
- break;
- }
-
return 0;
}