2014-02-11 16:11:18 +04:00
/***
This file is part of systemd .
Copyright ( C ) 2013 Intel Corporation . All rights reserved .
Copyright ( C ) 2014 Tom Gundersen
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <stdlib.h>
# include <errno.h>
# include <string.h>
# include <stdio.h>
# include <net/ethernet.h>
# include <net/if_arp.h>
# include <sys/param.h>
# include "util.h"
# include "list.h"
# include "dhcp-protocol.h"
2014-02-27 04:24:05 +04:00
# include "dhcp-lease-internal.h"
2014-02-11 16:11:18 +04:00
# include "dhcp-internal.h"
2014-02-27 04:24:05 +04:00
# include "sd-dhcp-lease.h"
2014-02-11 16:11:18 +04:00
# include "sd-dhcp-client.h"
# define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
int dhcp_message_init ( DHCPMessage * message , uint8_t op , uint32_t xid ,
2014-02-24 01:07:07 +04:00
uint8_t type , uint8_t * * opt , size_t * optlen ) {
2014-02-11 16:11:18 +04:00
int err ;
2014-02-24 01:07:07 +04:00
assert ( op = = BOOTREQUEST | | op = = BOOTREPLY ) ;
2014-02-11 16:11:18 +04:00
* opt = ( uint8_t * ) ( message + 1 ) ;
if ( * optlen < 4 )
return - ENOBUFS ;
* optlen - = 4 ;
message - > op = op ;
message - > htype = ARPHRD_ETHER ;
message - > hlen = ETHER_ADDR_LEN ;
message - > xid = htobe32 ( xid ) ;
( * opt ) [ 0 ] = 0x63 ;
( * opt ) [ 1 ] = 0x82 ;
( * opt ) [ 2 ] = 0x53 ;
( * opt ) [ 3 ] = 0x63 ;
* opt + = 4 ;
err = dhcp_option_append ( opt , optlen , DHCP_OPTION_MESSAGE_TYPE , 1 ,
& type ) ;
if ( err < 0 )
return err ;
return 0 ;
}
2014-02-25 15:33:24 +04:00
uint16_t dhcp_packet_checksum ( void * buf , int len ) {
2014-02-11 16:11:18 +04:00
uint32_t sum ;
uint16_t * check ;
int i ;
uint8_t * odd ;
sum = 0 ;
check = buf ;
for ( i = 0 ; i < len / 2 ; i + + )
sum + = check [ i ] ;
if ( len & 0x01 ) {
odd = buf ;
sum + = odd [ len - 1 ] ;
}
while ( sum > > 16 )
sum = ( sum & 0xffff ) + ( sum > > 16 ) ;
return ~ sum ;
}
2014-03-10 01:51:07 +04:00
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 ) {
2014-02-11 16:11:18 +04:00
packet - > ip . version = IPVERSION ;
packet - > ip . ihl = DHCP_IP_SIZE / 4 ;
packet - > ip . tot_len = htobe16 ( len ) ;
packet - > ip . protocol = IPPROTO_UDP ;
2014-03-10 01:51:07 +04:00
packet - > ip . saddr = source_addr ;
packet - > ip . daddr = destination_addr ;
2014-02-11 16:11:18 +04:00
2014-03-10 01:51:07 +04:00
packet - > udp . source = htobe16 ( source_port ) ;
packet - > udp . dest = htobe16 ( destination_port ) ;
2014-02-11 16:11:18 +04:00
packet - > udp . len = htobe16 ( len - DHCP_IP_SIZE ) ;
packet - > ip . check = packet - > udp . len ;
2014-02-25 15:33:24 +04:00
packet - > udp . check = dhcp_packet_checksum ( & packet - > ip . ttl , len - 8 ) ;
2014-02-11 16:11:18 +04:00
packet - > ip . ttl = IPDEFTTL ;
packet - > ip . check = 0 ;
2014-02-25 15:33:24 +04:00
packet - > ip . check = dhcp_packet_checksum ( & packet - > ip , DHCP_IP_SIZE ) ;
2014-02-11 16:11:18 +04:00
}
2014-02-24 04:09:21 +04:00
int dhcp_packet_verify_headers ( DHCPPacket * packet , size_t len , bool checksum ) {
2014-02-11 16:11:18 +04:00
size_t hdrlen ;
2014-02-23 20:30:13 +04:00
assert ( packet ) ;
2014-02-23 17:15:05 +04:00
/* IP */
2014-02-11 16:11:18 +04:00
2014-02-23 17:15:05 +04:00
if ( len < DHCP_IP_SIZE ) {
log_dhcp_client ( client , " ignoring packet: packet (%zu bytes) "
" smaller than IP header (%u bytes) " , len ,
DHCP_IP_SIZE ) ;
return - EINVAL ;
}
if ( packet - > ip . ihl < 5 ) {
log_dhcp_client ( client , " ignoring packet: IPv4 IHL (%u words) invalid " ,
packet - > ip . ihl ) ;
2014-02-11 16:11:18 +04:00
return - EINVAL ;
2014-02-23 04:34:05 +04:00
}
2014-02-11 16:11:18 +04:00
hdrlen = packet - > ip . ihl * 4 ;
2014-02-23 17:15:05 +04:00
if ( hdrlen < 20 ) {
log_dhcp_client ( client , " ignoring packet: IPv4 IHL (%zu bytes) "
" smaller than minimum (20 bytes) " , hdrlen ) ;
return - EINVAL ;
}
if ( len < hdrlen ) {
log_dhcp_client ( client , " ignoring packet: packet (%zu bytes) "
" smaller than expected (%zu) by IP header " , len ,
hdrlen ) ;
2014-02-11 16:11:18 +04:00
return - EINVAL ;
2014-02-23 04:34:05 +04:00
}
2014-02-11 16:11:18 +04:00
2014-02-25 15:33:24 +04:00
if ( dhcp_packet_checksum ( & packet - > ip , hdrlen ) ) {
2014-02-23 17:15:05 +04:00
log_dhcp_client ( client , " ignoring packet: invalid IP checksum " ) ;
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 ,
DHCP_IP_UDP_SIZE ) ;
2014-02-11 16:11:18 +04:00
return - EINVAL ;
2014-02-23 04:34:05 +04:00
}
2014-02-23 22:21:50 +04:00
if ( len < hdrlen + be16toh ( packet - > udp . len ) ) {
2014-02-23 17:15:05 +04:00
log_dhcp_client ( client , " ignoring packet: packet (%zu bytes) "
" smaller than expected (%zu) by UDP header " , len ,
hdrlen + be16toh ( packet - > udp . len ) ) ;
2014-02-23 04:34:05 +04:00
return - EINVAL ;
}
2014-02-11 16:11:18 +04:00
2014-02-24 04:09:21 +04:00
if ( checksum & & packet - > udp . check ) {
2014-02-11 16:11:18 +04:00
packet - > ip . check = packet - > udp . len ;
packet - > ip . ttl = 0 ;
2014-02-25 15:33:24 +04:00
if ( dhcp_packet_checksum ( & packet - > ip . ttl ,
2014-02-23 04:34:05 +04:00
be16toh ( packet - > udp . len ) + 12 ) ) {
2014-02-23 17:15:05 +04:00
log_dhcp_client ( client , " ignoring packet: invalid UDP checksum " ) ;
2014-02-11 16:11:18 +04:00
return - EINVAL ;
2014-02-23 04:34:05 +04:00
}
2014-02-11 16:11:18 +04:00
}
2014-02-23 22:21:50 +04:00
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 ) ;
2014-02-11 16:11:18 +04:00
return - EINVAL ;
2014-02-23 04:34:05 +04:00
}
2014-02-11 16:11:18 +04:00
return 0 ;
}