LCOV - code coverage report
Current view: top level - libsystemd-network - dhcp-packet.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 59 84 70.2 %
Date: 2015-07-29 18:47:03 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /***
       2             :   This file is part of systemd.
       3             : 
       4             :   Copyright (C) 2013 Intel Corporation. All rights reserved.
       5             :   Copyright (C) 2014 Tom Gundersen
       6             : 
       7             :   systemd is free software; you can redistribute it and/or modify it
       8             :   under the terms of the GNU Lesser General Public License as published by
       9             :   the Free Software Foundation; either version 2.1 of the License, or
      10             :   (at your option) any later version.
      11             : 
      12             :   systemd is distributed in the hope that it will be useful, but
      13             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      15             :   Lesser General Public License for more details.
      16             : 
      17             :   You should have received a copy of the GNU Lesser General Public License
      18             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      19             : ***/
      20             : 
      21             : #include <errno.h>
      22             : #include <string.h>
      23             : #include <net/ethernet.h>
      24             : #include <net/if_arp.h>
      25             : 
      26             : 
      27             : #include "dhcp-protocol.h"
      28             : #include "dhcp-internal.h"
      29             : 
      30             : #define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
      31             : 
      32           4 : int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
      33             :                       uint8_t type, uint16_t arp_type, size_t optlen,
      34             :                       size_t *optoffset) {
      35           4 :         size_t offset = 0;
      36             :         int r;
      37             : 
      38           4 :         assert(op == BOOTREQUEST || op == BOOTREPLY);
      39           4 :         assert(arp_type == ARPHRD_ETHER || arp_type == ARPHRD_INFINIBAND);
      40             : 
      41           4 :         message->op = op;
      42           4 :         message->htype = arp_type;
      43           4 :         message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
      44           4 :         message->xid = htobe32(xid);
      45           4 :         message->magic = htobe32(DHCP_MAGIC_COOKIE);
      46             : 
      47           4 :         r = dhcp_option_append(message, optlen, &offset, 0,
      48             :                                DHCP_OPTION_MESSAGE_TYPE, 1, &type);
      49           4 :         if (r < 0)
      50           0 :                 return r;
      51             : 
      52           4 :         *optoffset = offset;
      53             : 
      54           4 :         return 0;
      55             : }
      56             : 
      57          15 : uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
      58          15 :         uint64_t *buf_64 = (uint64_t*)buf;
      59          15 :         uint64_t *end_64 = buf_64 + (len / sizeof(uint64_t));
      60          15 :         uint64_t sum = 0;
      61             : 
      62             :         /* See RFC1071 */
      63             : 
      64         272 :         while (buf_64 < end_64) {
      65         242 :                 sum += *buf_64;
      66         242 :                 if (sum < *buf_64)
      67             :                         /* wrap around in one's complement */
      68           0 :                         sum++;
      69             : 
      70         242 :                 buf_64 ++;
      71             :         }
      72             : 
      73          15 :         if (len % sizeof(uint64_t)) {
      74             :                 /* If the buffer is not aligned to 64-bit, we need
      75             :                    to zero-pad the last few bytes and add them in */
      76          15 :                 uint64_t buf_tail = 0;
      77             : 
      78          15 :                 memcpy(&buf_tail, buf_64, len % sizeof(uint64_t));
      79             : 
      80          15 :                 sum += buf_tail;
      81          15 :                 if (sum < buf_tail)
      82             :                         /* wrap around */
      83           0 :                         sum++;
      84             :         }
      85             : 
      86          70 :         while (sum >> 16)
      87          40 :                 sum = (sum & 0xffff) + (sum >> 16);
      88             : 
      89          15 :         return ~sum;
      90             : }
      91             : 
      92           3 : void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
      93             :                                    uint16_t source_port, be32_t destination_addr,
      94             :                                    uint16_t destination_port, uint16_t len) {
      95           3 :         packet->ip.version = IPVERSION;
      96           3 :         packet->ip.ihl = DHCP_IP_SIZE / 4;
      97           3 :         packet->ip.tot_len = htobe16(len);
      98             : 
      99           3 :         packet->ip.tos = IPTOS_CLASS_CS6;
     100             : 
     101           3 :         packet->ip.protocol = IPPROTO_UDP;
     102           3 :         packet->ip.saddr = source_addr;
     103           3 :         packet->ip.daddr = destination_addr;
     104             : 
     105           3 :         packet->udp.source = htobe16(source_port);
     106           3 :         packet->udp.dest = htobe16(destination_port);
     107             : 
     108           3 :         packet->udp.len = htobe16(len - DHCP_IP_SIZE);
     109             : 
     110           3 :         packet->ip.check = packet->udp.len;
     111           3 :         packet->udp.check = dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, len - 8);
     112             : 
     113           3 :         packet->ip.ttl = IPDEFTTL;
     114           3 :         packet->ip.check = 0;
     115           3 :         packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
     116           3 : }
     117             : 
     118           2 : int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
     119             :         size_t hdrlen;
     120             : 
     121           2 :         assert(packet);
     122             : 
     123             :         /* IP */
     124             : 
     125           2 :         if (packet->ip.version != IPVERSION) {
     126           0 :                 log_debug("ignoring packet: not IPv4");
     127           0 :                 return -EINVAL;
     128             :         }
     129             : 
     130           2 :         if (packet->ip.ihl < 5) {
     131           0 :                 log_debug("ignoring packet: IPv4 IHL (%u words) invalid",
     132             :                           packet->ip.ihl);
     133           0 :                 return -EINVAL;
     134             :         }
     135             : 
     136           2 :         hdrlen = packet->ip.ihl * 4;
     137           2 :         if (hdrlen < 20) {
     138           0 :                 log_debug("ignoring packet: IPv4 IHL (%zu bytes) "
     139             :                           "smaller than minimum (20 bytes)", hdrlen);
     140           0 :                 return -EINVAL;
     141             :         }
     142             : 
     143           2 :         if (len < hdrlen) {
     144           0 :                 log_debug("ignoring packet: packet (%zu bytes) "
     145             :                           "smaller than expected (%zu) by IP header", len,
     146             :                           hdrlen);
     147           0 :                 return -EINVAL;
     148             :         }
     149             : 
     150             :         /* UDP */
     151             : 
     152           2 :         if (packet->ip.protocol != IPPROTO_UDP) {
     153           0 :                 log_debug("ignoring packet: not UDP");
     154           0 :                 return -EINVAL;
     155             :         }
     156             : 
     157           2 :         if (len < hdrlen + be16toh(packet->udp.len)) {
     158           0 :                 log_debug("ignoring packet: packet (%zu bytes) "
     159             :                           "smaller than expected (%zu) by UDP header", len,
     160             :                           hdrlen + be16toh(packet->udp.len));
     161           0 :                 return -EINVAL;
     162             :         }
     163             : 
     164           2 :         if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
     165           0 :                 log_debug("ignoring packet: to port %u, which "
     166             :                           "is not the DHCP client port (%u)",
     167             :                           be16toh(packet->udp.dest), DHCP_PORT_CLIENT);
     168           0 :                 return -EINVAL;
     169             :         }
     170             : 
     171             :         /* checksums - computing these is relatively expensive, so only do it
     172             :            if all the other checks have passed
     173             :          */
     174             : 
     175           2 :         if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) {
     176           0 :                 log_debug("ignoring packet: invalid IP checksum");
     177           0 :                 return -EINVAL;
     178             :         }
     179             : 
     180           2 :         if (checksum && packet->udp.check) {
     181           0 :                 packet->ip.check = packet->udp.len;
     182           0 :                 packet->ip.ttl = 0;
     183             : 
     184           0 :                 if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl,
     185           0 :                                   be16toh(packet->udp.len) + 12)) {
     186           0 :                         log_debug("ignoring packet: invalid UDP checksum");
     187           0 :                         return -EINVAL;
     188             :                 }
     189             :         }
     190             : 
     191           2 :         return 0;
     192             : }

Generated by: LCOV version 1.11