Przeglądaj źródła

dhcpclient: compute and verify UDP checksums

ambrop7 15 lat temu
rodzic
commit
a3e9e14484
3 zmienionych plików z 63 dodań i 0 usunięć
  1. 10 0
      dhcpclient/DHCPIpUdpDecoder.c
  2. 4 0
      dhcpclient/DHCPIpUdpEncoder.c
  3. 49 0
      misc/udp_proto.h

+ 10 - 0
dhcpclient/DHCPIpUdpDecoder.c

@@ -74,6 +74,16 @@ static void input_handler_send (DHCPIpUdpDecoder *o, uint8_t *data, int data_len
         goto fail;
     }
     
+    if (ntoh16(udph->checksum) != 0) {
+        uint16_t checksum_in_packet = udph->checksum;
+        udph->checksum = 0;
+        uint16_t checksum_computed = udp_checksum((uint8_t *)udph, udph_length, iph->source_address, iph->destination_address);
+        udph->checksum = checksum_in_packet;
+        if (checksum_in_packet != checksum_computed) {
+            goto fail;
+        }
+    }
+    
     // pass payload to output
     PacketPassInterface_Sender_Send(o->output, (uint8_t *)(udph + 1), udph_length - sizeof(*udph));
     

+ 4 - 0
dhcpclient/DHCPIpUdpEncoder.c

@@ -77,6 +77,10 @@ static void input_handler_done (DHCPIpUdpEncoder *o, int data_len)
     udph->length = hton16(sizeof(*udph) + data_len);
     udph->checksum = hton16(0);
     
+    // compute checksum
+    checksum = udp_checksum((uint8_t *)udph, sizeof(*udph) + data_len, iph->source_address, iph->destination_address);
+    udph->checksum = checksum;
+    
     // finish packet
     PacketRecvInterface_Done(&o->output, sizeof(struct combined_header) + data_len);
 }

+ 49 - 0
misc/udp_proto.h

@@ -29,6 +29,9 @@
 
 #include <stdint.h>
 
+#include <misc/debug.h>
+#include <misc/byteorder.h>
+
 struct udp_header {
     uint16_t source_port;
     uint16_t dest_port;
@@ -36,4 +39,50 @@ struct udp_header {
     uint16_t checksum;
 } __attribute__((packed));
 
+static uint32_t udp_checksum_summer (uint8_t *data, uint16_t len)
+{
+    ASSERT(len % 2 == 0)
+    
+    struct ipv4_short *s = (void *)data;
+    
+    uint32_t t = 0;
+    
+    for (uint16_t i = 0; i < len / 2; i++) {
+        t += ntoh16(s[i].v);
+    }
+    
+    return t;
+}
+
+static uint16_t udp_checksum (uint8_t *udp, uint16_t len, uint32_t source_addr, uint32_t dest_addr)
+{
+    uint32_t t = 0;
+    
+    t += udp_checksum_summer((uint8_t *)&source_addr, sizeof(source_addr));
+    t += udp_checksum_summer((uint8_t *)&dest_addr, sizeof(dest_addr));
+    
+    uint16_t x;
+    x = hton16(IPV4_PROTOCOL_UDP);
+    t += udp_checksum_summer((uint8_t *)&x, sizeof(x));
+    x = hton16(len);
+    t += udp_checksum_summer((uint8_t *)&x, sizeof(x));
+    
+    if (len % 2 == 0) {
+        t += udp_checksum_summer(udp, len);
+    } else {
+        t += udp_checksum_summer(udp, len - 1);
+        
+        x = hton16((udp[len - 1] << 8));
+        t += udp_checksum_summer((uint8_t *)&x, sizeof(x));
+    }
+    
+    t = (t&0xFFFF) + (t >> 16);
+    
+    if (t == 0) {
+        t = ~t;
+    }
+    
+    return hton16(~t);
+}
+
 #endif