X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-network%2Fdhcp-packet.c;h=b012e63d1f498a3c534ee1503e3beeea1db2648e;hb=7739a40b45fcc00874f8760be3d26534ed76cd45;hp=95c4277f8c45cf425c125f6a304d3a77c2c9e784;hpb=fe8db0c5ee3365a2fc80ee7ebffa238f9a0a2ae2;p=elogind.git diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c index 95c4277f8..b012e63d1 100644 --- a/src/libsystemd-network/dhcp-packet.c +++ b/src/libsystemd-network/dhcp-packet.c @@ -43,23 +43,13 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, 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; + *opt = (uint8_t *)(message + 1); err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1, &type); @@ -69,21 +59,48 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, return 0; } -static uint16_t dhcp_checksum(void *buf, int len) { - uint32_t sum; - uint16_t *check; - int i; - uint8_t *odd; +uint16_t dhcp_packet_checksum(void *buf, size_t len) { + uint64_t *buf_64 = buf; + uint64_t *end_64 = (uint64_t*)buf + (len / sizeof(uint64_t)); + uint32_t *buf_32; + uint16_t *buf_16; + uint8_t *buf_8; + uint64_t sum = 0; + + while (buf_64 < end_64) { + sum += *buf_64; + if (sum < *buf_64) + sum++; + + buf_64 ++; + } + + buf_32 = (uint32_t*)buf_64; + + if (len & sizeof(uint32_t)) { + sum += *buf_32; + if (sum < *buf_32) + sum++; + + buf_32 ++; + } + + buf_16 = (uint16_t*)buf_32; + + if (len & sizeof(uint16_t)) { + sum += *buf_16; + if (sum < *buf_16) + sum ++; - sum = 0; - check = buf; + buf_16 ++; + } - for (i = 0; i < len / 2 ; i++) - sum += check[i]; + buf_8 = (uint8_t*)buf_16; - if (len & 0x01) { - odd = buf; - sum += odd[len - 1]; + if (len & sizeof(uint8_t)) { + sum += *buf_8; + if (sum < *buf_8) + sum++; } while (sum >> 16) @@ -92,26 +109,28 @@ static uint16_t dhcp_checksum(void *buf, int len) { return ~sum; } -void dhcp_packet_append_ip_headers(DHCPPacket *packet, uint16_t len) { +void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr, + uint16_t source_port, be32_t destination_addr, + uint16_t destination_port, uint16_t len) { packet->ip.version = IPVERSION; packet->ip.ihl = DHCP_IP_SIZE / 4; packet->ip.tot_len = htobe16(len); packet->ip.protocol = IPPROTO_UDP; - packet->ip.saddr = INADDR_ANY; - packet->ip.daddr = INADDR_BROADCAST; + packet->ip.saddr = source_addr; + packet->ip.daddr = destination_addr; - packet->udp.source = htobe16(DHCP_PORT_CLIENT); - packet->udp.dest = htobe16(DHCP_PORT_SERVER); + packet->udp.source = htobe16(source_port); + packet->udp.dest = htobe16(destination_port); packet->udp.len = htobe16(len - DHCP_IP_SIZE); packet->ip.check = packet->udp.len; - packet->udp.check = dhcp_checksum(&packet->ip.ttl, len - 8); + packet->udp.check = dhcp_packet_checksum(&packet->ip.ttl, len - 8); packet->ip.ttl = IPDEFTTL; packet->ip.check = 0; - packet->ip.check = dhcp_checksum(&packet->ip, DHCP_IP_SIZE); + packet->ip.check = dhcp_packet_checksum(&packet->ip, DHCP_IP_SIZE); } int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) { @@ -121,51 +140,58 @@ 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); + log_debug("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"); + /* UDP */ + + if (packet->ip.protocol != IPPROTO_UDP) { + log_debug("ignoring packet: not UDP"); return -EINVAL; } - /* UDP */ + 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 < 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 (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; } - 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)); + /* checksums - computing these is relatively expensive, so only do it + if all the other checks have passed + */ + + if (dhcp_packet_checksum(&packet->ip, hdrlen)) { + log_debug("ignoring packet: invalid IP checksum"); return -EINVAL; } @@ -173,19 +199,12 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) { packet->ip.check = packet->udp.len; packet->ip.ttl = 0; - if (dhcp_checksum(&packet->ip.ttl, + if (dhcp_packet_checksum(&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; }