LCOV - code coverage report
Current view: top level - libsystemd-network - dhcp6-option.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 109 150 72.7 %
Date: 2015-07-29 18:47:03 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright (C) 2014 Intel Corporation. All rights reserved.
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <netinet/in.h>
      23             : #include <errno.h>
      24             : #include <string.h>
      25             : 
      26             : #include "sparse-endian.h"
      27             : #include "unaligned.h"
      28             : #include "util.h"
      29             : 
      30             : #include "dhcp6-internal.h"
      31             : #include "dhcp6-protocol.h"
      32             : 
      33             : #define DHCP6_OPTION_IA_NA_LEN                  12
      34             : #define DHCP6_OPTION_IA_TA_LEN                  4
      35             : 
      36             : typedef struct DHCP6Option {
      37             :         be16_t code;
      38             :         be16_t len;
      39             :         uint8_t data[];
      40             : } _packed_ DHCP6Option;
      41             : 
      42          16 : static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
      43             :                              size_t optlen) {
      44          16 :         DHCP6Option *option = (DHCP6Option*) *buf;
      45             : 
      46          16 :         assert_return(buf, -EINVAL);
      47          16 :         assert_return(*buf, -EINVAL);
      48          16 :         assert_return(buflen, -EINVAL);
      49             : 
      50          16 :         if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option))
      51           0 :                 return -ENOBUFS;
      52             : 
      53          16 :         option->code = htobe16(optcode);
      54          16 :         option->len = htobe16(optlen);
      55             : 
      56          16 :         *buf += sizeof(DHCP6Option);
      57          16 :         *buflen -= sizeof(DHCP6Option);
      58             : 
      59          16 :         return 0;
      60             : }
      61             : 
      62          13 : int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
      63             :                         size_t optlen, const void *optval) {
      64             :         int r;
      65             : 
      66          13 :         assert_return(optval || optlen == 0, -EINVAL);
      67             : 
      68          13 :         r = option_append_hdr(buf, buflen, code, optlen);
      69          13 :         if (r < 0)
      70           0 :                 return r;
      71             : 
      72          13 :         if (optval)
      73          12 :                 memcpy(*buf, optval, optlen);
      74             : 
      75          13 :         *buf += optlen;
      76          13 :         *buflen -= optlen;
      77             : 
      78          13 :         return 0;
      79             : }
      80             : 
      81           2 : int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
      82             :         uint16_t len;
      83             :         uint8_t *ia_hdr;
      84           2 :         size_t ia_buflen, ia_addrlen = 0;
      85             :         DHCP6Address *addr;
      86             :         int r;
      87             : 
      88           2 :         assert_return(buf && *buf && buflen && ia, -EINVAL);
      89             : 
      90           2 :         switch (ia->type) {
      91             :         case DHCP6_OPTION_IA_NA:
      92           2 :                 len = DHCP6_OPTION_IA_NA_LEN;
      93           2 :                 break;
      94             : 
      95             :         case DHCP6_OPTION_IA_TA:
      96           0 :                 len = DHCP6_OPTION_IA_TA_LEN;
      97           0 :                 break;
      98             : 
      99             :         default:
     100           0 :                 return -EINVAL;
     101             :         }
     102             : 
     103           2 :         if (*buflen < len)
     104           0 :                 return -ENOBUFS;
     105             : 
     106           2 :         ia_hdr = *buf;
     107           2 :         ia_buflen = *buflen;
     108             : 
     109           2 :         *buf += sizeof(DHCP6Option);
     110           2 :         *buflen -= sizeof(DHCP6Option);
     111             : 
     112           2 :         memcpy(*buf, &ia->id, len);
     113             : 
     114           2 :         *buf += len;
     115           2 :         *buflen -= len;
     116             : 
     117           3 :         LIST_FOREACH(addresses, addr, ia->addresses) {
     118           1 :                 r = option_append_hdr(buf, buflen, DHCP6_OPTION_IAADDR,
     119             :                                       sizeof(addr->iaaddr));
     120           1 :                 if (r < 0)
     121           0 :                         return r;
     122             : 
     123           1 :                 memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr));
     124             : 
     125           1 :                 *buf += sizeof(addr->iaaddr);
     126           1 :                 *buflen -= sizeof(addr->iaaddr);
     127             : 
     128           1 :                 ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr);
     129             :         }
     130             : 
     131           2 :         r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
     132           2 :         if (r < 0)
     133           0 :                 return r;
     134             : 
     135           2 :         return 0;
     136             : }
     137             : 
     138             : 
     139          60 : static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
     140          60 :         DHCP6Option *option = (DHCP6Option*) *buf;
     141             :         uint16_t len;
     142             : 
     143          60 :         assert_return(buf, -EINVAL);
     144          60 :         assert_return(optcode, -EINVAL);
     145          60 :         assert_return(optlen, -EINVAL);
     146             : 
     147          60 :         if (*buflen < sizeof(DHCP6Option))
     148          12 :                 return -ENOMSG;
     149             : 
     150          48 :         len = be16toh(option->len);
     151             : 
     152          48 :         if (len > *buflen)
     153           0 :                 return -ENOMSG;
     154             : 
     155          48 :         *optcode = be16toh(option->code);
     156          48 :         *optlen = len;
     157             : 
     158          48 :         *buf += 4;
     159          48 :         *buflen -= 4;
     160             : 
     161          48 :         return 0;
     162             : }
     163             : 
     164          49 : int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
     165             :                        size_t *optlen, uint8_t **optvalue) {
     166             :         int r;
     167             : 
     168          49 :         assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
     169             : 
     170          49 :         r = option_parse_hdr(buf, buflen, optcode, optlen);
     171          49 :         if (r < 0)
     172           8 :                 return r;
     173             : 
     174          41 :         if (*optlen > *buflen)
     175           0 :                 return -ENOBUFS;
     176             : 
     177          41 :         *optvalue = *buf;
     178          41 :         *buflen -= *optlen;
     179          41 :         *buf += *optlen;
     180             : 
     181          41 :         return 0;
     182             : }
     183             : 
     184           4 : int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
     185             :                           DHCP6IA *ia) {
     186             :         int r;
     187             :         uint16_t opt, status;
     188             :         size_t optlen;
     189             :         size_t iaaddr_offset;
     190             :         DHCP6Address *addr;
     191           4 :         uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0;
     192             : 
     193           4 :         assert_return(ia, -EINVAL);
     194           4 :         assert_return(!ia->addresses, -EINVAL);
     195             : 
     196           4 :         switch (iatype) {
     197             :         case DHCP6_OPTION_IA_NA:
     198             : 
     199           4 :                 if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
     200             :                     sizeof(addr->iaaddr)) {
     201           0 :                         r = -ENOBUFS;
     202           0 :                         goto error;
     203             :                 }
     204             : 
     205           4 :                 iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
     206           4 :                 memcpy(&ia->id, *buf, iaaddr_offset);
     207             : 
     208           4 :                 lt_t1 = be32toh(ia->lifetime_t1);
     209           4 :                 lt_t2 = be32toh(ia->lifetime_t2);
     210             : 
     211           4 :                 if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
     212           0 :                         log_dhcp6_client(client, "IA T1 %ds > T2 %ds",
     213             :                                          lt_t1, lt_t2);
     214           0 :                         r = -EINVAL;
     215           0 :                         goto error;
     216             :                 }
     217             : 
     218           4 :                 break;
     219             : 
     220             :         case DHCP6_OPTION_IA_TA:
     221           0 :                 if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
     222             :                     sizeof(addr->iaaddr)) {
     223           0 :                         r = -ENOBUFS;
     224           0 :                         goto error;
     225             :                 }
     226             : 
     227           0 :                 iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
     228           0 :                 memcpy(&ia->id, *buf, iaaddr_offset);
     229             : 
     230           0 :                 ia->lifetime_t1 = 0;
     231           0 :                 ia->lifetime_t2 = 0;
     232             : 
     233           0 :                 break;
     234             : 
     235             :         default:
     236           0 :                 r = -ENOMSG;
     237           0 :                 goto error;
     238             :         }
     239             : 
     240           4 :         ia->type = iatype;
     241             : 
     242           4 :         *buflen -= iaaddr_offset;
     243           4 :         *buf += iaaddr_offset;
     244             : 
     245          15 :         while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
     246             : 
     247           7 :                 switch (opt) {
     248             :                 case DHCP6_OPTION_IAADDR:
     249             : 
     250           4 :                         addr = new0(DHCP6Address, 1);
     251           4 :                         if (!addr) {
     252           0 :                                 r = -ENOMEM;
     253           0 :                                 goto error;
     254             :                         }
     255             : 
     256           4 :                         LIST_INIT(addresses, addr);
     257             : 
     258           4 :                         memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr));
     259             : 
     260           4 :                         lt_valid = be32toh(addr->iaaddr.lifetime_valid);
     261           4 :                         lt_pref = be32toh(addr->iaaddr.lifetime_valid);
     262             : 
     263           4 :                         if (!lt_valid || lt_pref > lt_valid) {
     264           0 :                                 log_dhcp6_client(client, "IA preferred %ds > valid %ds",
     265             :                                                  lt_pref, lt_valid);
     266           0 :                                 free(addr);
     267             :                         } else {
     268           4 :                                 LIST_PREPEND(addresses, ia->addresses, addr);
     269           4 :                                 if (lt_valid < lt_min)
     270           4 :                                         lt_min = lt_valid;
     271             :                         }
     272             : 
     273           4 :                         break;
     274             : 
     275             :                 case DHCP6_OPTION_STATUS_CODE:
     276           3 :                         if (optlen < sizeof(status))
     277           0 :                                 break;
     278             : 
     279           3 :                         status = (*buf)[0] << 8 | (*buf)[1];
     280           3 :                         if (status) {
     281           0 :                                 log_dhcp6_client(client, "IA status %d",
     282             :                                                  status);
     283           0 :                                 r = -EINVAL;
     284           0 :                                 goto error;
     285             :                         }
     286             : 
     287           3 :                         break;
     288             : 
     289             :                 default:
     290           0 :                         log_dhcp6_client(client, "Unknown IA option %d", opt);
     291           0 :                         break;
     292             :                 }
     293             : 
     294           7 :                 *buflen -= optlen;
     295           7 :                 *buf += optlen;
     296             :         }
     297             : 
     298           4 :         if (r == -ENOMSG)
     299           4 :                 r = 0;
     300             : 
     301           4 :         if (!ia->lifetime_t1 && !ia->lifetime_t2) {
     302           0 :                 lt_t1 = lt_min / 2;
     303           0 :                 lt_t2 = lt_min / 10 * 8;
     304           0 :                 ia->lifetime_t1 = htobe32(lt_t1);
     305           0 :                 ia->lifetime_t2 = htobe32(lt_t2);
     306             : 
     307           0 :                 log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero",
     308             :                                  lt_t1, lt_t2);
     309             :         }
     310             : 
     311           4 :         if (*buflen)
     312           0 :                 r = -ENOMSG;
     313             : 
     314             : error:
     315           4 :         *buf += *buflen;
     316           4 :         *buflen = 0;
     317             : 
     318           4 :         return r;
     319             : }

Generated by: LCOV version 1.11