LCOV - code coverage report
Current view: top level - libsystemd-network - dhcp-packet.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 60 75 80.0 %
Date: 2019-08-22 15:41:25 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : /***
       3             :   Copyright © 2013 Intel Corporation. All rights reserved.
       4             : ***/
       5             : 
       6             : #include <errno.h>
       7             : #include <net/ethernet.h>
       8             : #include <net/if_arp.h>
       9             : #include <string.h>
      10             : 
      11             : #include "dhcp-internal.h"
      12             : #include "dhcp-protocol.h"
      13             : 
      14             : #define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
      15             : 
      16           3 : int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
      17             :                       uint8_t type, uint16_t arp_type, size_t optlen,
      18             :                       size_t *optoffset) {
      19           3 :         size_t offset = 0;
      20             :         int r;
      21             : 
      22           3 :         assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
      23           3 :         assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND));
      24             : 
      25           3 :         message->op = op;
      26           3 :         message->htype = arp_type;
      27           3 :         message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
      28           3 :         message->xid = htobe32(xid);
      29           3 :         message->magic = htobe32(DHCP_MAGIC_COOKIE);
      30             : 
      31           3 :         r = dhcp_option_append(message, optlen, &offset, 0,
      32             :                                SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type);
      33           3 :         if (r < 0)
      34           0 :                 return r;
      35             : 
      36           3 :         *optoffset = offset;
      37             : 
      38           3 :         return 0;
      39             : }
      40             : 
      41          10 : uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
      42          10 :         uint64_t *buf_64 = (uint64_t*)buf;
      43          10 :         uint64_t *end_64 = buf_64 + (len / sizeof(uint64_t));
      44          10 :         uint64_t sum = 0;
      45             : 
      46             :         /* See RFC1071 */
      47             : 
      48         170 :         while (buf_64 < end_64) {
      49         160 :                 sum += *buf_64;
      50         160 :                 if (sum < *buf_64)
      51             :                         /* wrap around in one's complement */
      52           6 :                         sum++;
      53             : 
      54         160 :                 buf_64++;
      55             :         }
      56             : 
      57          10 :         if (len % sizeof(uint64_t)) {
      58             :                 /* If the buffer is not aligned to 64-bit, we need
      59             :                    to zero-pad the last few bytes and add them in */
      60           8 :                 uint64_t buf_tail = 0;
      61             : 
      62           8 :                 memcpy(&buf_tail, buf_64, len % sizeof(uint64_t));
      63             : 
      64           8 :                 sum += buf_tail;
      65           8 :                 if (sum < buf_tail)
      66             :                         /* wrap around */
      67           0 :                         sum++;
      68             :         }
      69             : 
      70          36 :         while (sum >> 16)
      71          26 :                 sum = (sum & 0xffff) + (sum >> 16);
      72             : 
      73          10 :         return ~sum;
      74             : }
      75             : 
      76           2 : void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
      77             :                                    uint16_t source_port, be32_t destination_addr,
      78             :                                    uint16_t destination_port, uint16_t len) {
      79           2 :         packet->ip.version = IPVERSION;
      80           2 :         packet->ip.ihl = DHCP_IP_SIZE / 4;
      81           2 :         packet->ip.tot_len = htobe16(len);
      82             : 
      83           2 :         packet->ip.tos = IPTOS_CLASS_CS6;
      84             : 
      85           2 :         packet->ip.protocol = IPPROTO_UDP;
      86           2 :         packet->ip.saddr = source_addr;
      87           2 :         packet->ip.daddr = destination_addr;
      88             : 
      89           2 :         packet->udp.source = htobe16(source_port);
      90           2 :         packet->udp.dest = htobe16(destination_port);
      91             : 
      92           2 :         packet->udp.len = htobe16(len - DHCP_IP_SIZE);
      93             : 
      94           2 :         packet->ip.check = packet->udp.len;
      95           2 :         packet->udp.check = dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, len - 8);
      96             : 
      97           2 :         packet->ip.ttl = IPDEFTTL;
      98           2 :         packet->ip.check = 0;
      99           2 :         packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
     100           2 : }
     101             : 
     102           1 : int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port) {
     103             :         size_t hdrlen;
     104             : 
     105           1 :         assert(packet);
     106             : 
     107             :         /* IP */
     108             : 
     109           1 :         if (packet->ip.version != IPVERSION)
     110           0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
     111             :                                        "ignoring packet: not IPv4");
     112             : 
     113           1 :         if (packet->ip.ihl < 5)
     114           0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
     115             :                                        "ignoring packet: IPv4 IHL (%u words) invalid",
     116             :                                        packet->ip.ihl);
     117             : 
     118           1 :         hdrlen = packet->ip.ihl * 4;
     119           1 :         if (hdrlen < 20)
     120           0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
     121             :                                        "ignoring packet: IPv4 IHL (%zu bytes) "
     122             :                                        "smaller than minimum (20 bytes)",
     123             :                                        hdrlen);
     124             : 
     125           1 :         if (len < hdrlen)
     126           0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
     127             :                                        "ignoring packet: packet (%zu bytes) "
     128             :                                        "smaller than expected (%zu) by IP header",
     129             :                                        len, hdrlen);
     130             : 
     131             :         /* UDP */
     132             : 
     133           1 :         if (packet->ip.protocol != IPPROTO_UDP)
     134           0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
     135             :                                        "ignoring packet: not UDP");
     136             : 
     137           1 :         if (len < hdrlen + be16toh(packet->udp.len))
     138           0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
     139             :                                        "ignoring packet: packet (%zu bytes) "
     140             :                                        "smaller than expected (%zu) by UDP header",
     141             :                                        len, hdrlen + be16toh(packet->udp.len));
     142             : 
     143           1 :         if (be16toh(packet->udp.dest) != port)
     144           0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
     145             :                                        "ignoring packet: to port %u, which "
     146             :                                        "is not the DHCP client port (%u)",
     147             :                                        be16toh(packet->udp.dest), port);
     148             : 
     149             :         /* checksums - computing these is relatively expensive, so only do it
     150             :            if all the other checks have passed
     151             :          */
     152             : 
     153           1 :         if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen))
     154           0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
     155             :                                        "ignoring packet: invalid IP checksum");
     156             : 
     157           1 :         if (checksum && packet->udp.check) {
     158           0 :                 packet->ip.check = packet->udp.len;
     159           0 :                 packet->ip.ttl = 0;
     160             : 
     161           0 :                 if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl,
     162           0 :                                   be16toh(packet->udp.len) + 12))
     163           0 :                         return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
     164             :                                                "ignoring packet: invalid UDP checksum");
     165             :         }
     166             : 
     167           1 :         return 0;
     168             : }

Generated by: LCOV version 1.14