chiark / gitweb /
sd-dhcp: avoid checksum calculation if possible
[elogind.git] / src / libsystemd-network / dhcp-packet.c
index 95c4277f8c45cf425c125f6a304d3a77c2c9e784..4f90c283a21485a686b18704dbf1227b2a058d46 100644 (file)
@@ -69,7 +69,7 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
         return 0;
 }
 
-static uint16_t dhcp_checksum(void *buf, int len) {
+uint16_t dhcp_packet_checksum(void *buf, int len) {
         uint32_t sum;
         uint16_t *check;
         int i;
@@ -92,26 +92,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) {
@@ -128,6 +130,11 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
                 return -EINVAL;
         }
 
+        if (packet->ip.version != IPVERSION) {
+                log_dhcp_client(client, "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);
@@ -148,13 +155,13 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
                 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_dhcp_client(client, "ignoring packet: not UDP");
                 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,
@@ -169,23 +176,32 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool 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;
+        }
+
+        /* 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_dhcp_client(client, "ignoring packet: invalid IP checksum");
+                return -EINVAL;
+        }
+
         if (checksum && packet->udp.check) {
                 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");
                         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;
 }