#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
- uint8_t type, uint8_t **opt, size_t *optlen) {
- int err;
+ uint8_t type, size_t optlen, size_t *optoffset) {
+ size_t offset = 0;
+ int r;
assert(op == BOOTREQUEST || op == BOOTREPLY);
- *opt = (uint8_t *)(message + 1);
-
- if (*optlen < 4)
- return -ENOBUFS;
- *optlen -= 4;
-
message->op = op;
message->htype = ARPHRD_ETHER;
message->hlen = ETHER_ADDR_LEN;
message->xid = htobe32(xid);
+ message->magic = htobe32(DHCP_MAGIC_COOKIE);
- (*opt)[0] = 0x63;
- (*opt)[1] = 0x82;
- (*opt)[2] = 0x53;
- (*opt)[3] = 0x63;
-
- *opt += 4;
+ r = dhcp_option_append(message, optlen, &offset, 0,
+ DHCP_OPTION_MESSAGE_TYPE, 1, &type);
+ if (r < 0)
+ return r;
- err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
- &type);
- if (err < 0)
- return err;
+ *optoffset = offset;
return 0;
}
-uint16_t dhcp_packet_checksum(void *buf, int len) {
- uint32_t sum;
- uint16_t *check;
- int i;
- uint8_t *odd;
+uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
+ uint64_t *buf_64 = (uint64_t*)buf;
+ uint64_t *end_64 = buf_64 + (len / sizeof(uint64_t));
+ uint64_t sum = 0;
- sum = 0;
- check = buf;
+ /* See RFC1071 */
- for (i = 0; i < len / 2 ; i++)
- sum += check[i];
+ while (buf_64 < end_64) {
+ sum += *buf_64;
+ if (sum < *buf_64)
+ /* wrap around in one's complement */
+ sum++;
- if (len & 0x01) {
- odd = buf;
- sum += odd[len - 1];
+ buf_64 ++;
+ }
+
+ if (len % sizeof(uint64_t)) {
+ /* If the buffer is not aligned to 64-bit, we need
+ to zero-pad the last few bytes and add them in */
+ uint64_t buf_tail = 0;
+
+ memcpy(&buf_tail, buf_64, len % sizeof(uint64_t));
+
+ sum += buf_tail;
+ if (sum < buf_tail)
+ /* wrap around */
+ sum++;
}
while (sum >> 16)
packet->ip.ihl = DHCP_IP_SIZE / 4;
packet->ip.tot_len = htobe16(len);
+ packet->ip.tos = IPTOS_CLASS_CS6;
+
packet->ip.protocol = IPPROTO_UDP;
packet->ip.saddr = source_addr;
packet->ip.daddr = destination_addr;
packet->udp.len = htobe16(len - DHCP_IP_SIZE);
packet->ip.check = packet->udp.len;
- packet->udp.check = dhcp_packet_checksum(&packet->ip.ttl, len - 8);
+ packet->udp.check = dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, len - 8);
packet->ip.ttl = IPDEFTTL;
packet->ip.check = 0;
- packet->ip.check = dhcp_packet_checksum(&packet->ip, DHCP_IP_SIZE);
+ packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
}
int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
/* 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);
+ if (packet->ip.version != IPVERSION) {
+ log_debug("ignoring packet: not IPv4");
return -EINVAL;
}
if (packet->ip.ihl < 5) {
- log_dhcp_client(client, "ignoring packet: IPv4 IHL (%u words) invalid",
- packet->ip.ihl);
+ log_debug("ignoring packet: IPv4 IHL (%u words) invalid",
+ packet->ip.ihl);
return -EINVAL;
}
hdrlen = packet->ip.ihl * 4;
if (hdrlen < 20) {
- log_dhcp_client(client, "ignoring packet: IPv4 IHL (%zu bytes) "
- "smaller than minimum (20 bytes)", hdrlen);
+ log_debug("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_packet_checksum(&packet->ip, hdrlen)) {
- log_dhcp_client(client, "ignoring packet: invalid IP checksum");
+ log_debug("ignoring packet: packet (%zu bytes) "
+ "smaller than expected (%zu) by IP header", len,
+ hdrlen);
return -EINVAL;
}
/* UDP */
if (packet->ip.protocol != IPPROTO_UDP) {
- log_dhcp_client(client, "ignoring packet: not UDP");
+ log_debug("ignoring packet: not UDP");
return -EINVAL;
}
- 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);
+ if (len < hdrlen + be16toh(packet->udp.len)) {
+ log_debug("ignoring packet: packet (%zu bytes) "
+ "smaller than expected (%zu) by UDP header", len,
+ hdrlen + be16toh(packet->udp.len));
return -EINVAL;
}
- 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));
+ if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
+ log_debug("ignoring packet: to port %u, which "
+ "is not the DHCP client port (%u)",
+ be16toh(packet->udp.dest), DHCP_PORT_CLIENT);
+ return -EINVAL;
+ }
+
+ /* checksums - computing these is relatively expensive, so only do it
+ if all the other checks have passed
+ */
+
+ if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) {
+ log_debug("ignoring packet: invalid IP checksum");
return -EINVAL;
}
packet->ip.check = packet->udp.len;
packet->ip.ttl = 0;
- if (dhcp_packet_checksum(&packet->ip.ttl,
+ if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl,
be16toh(packet->udp.len) + 12)) {
- log_dhcp_client(client, "ignoring packet: invalid UDP checksum");
+ log_debug("ignoring packet: invalid UDP checksum");
return -EINVAL;
}
}
- 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;
- }
-
return 0;
}