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-05-21 17:55:02 +04:00
uint8_t type , size_t optlen , size_t * optoffset ) {
2014-05-20 13:04:50 +04:00
size_t offset = 0 ;
int r ;
2014-02-11 16:11:18 +04:00
2014-02-24 01:07:07 +04:00
assert ( op = = BOOTREQUEST | | op = = BOOTREPLY ) ;
2014-02-11 16:11:18 +04:00
message - > op = op ;
message - > htype = ARPHRD_ETHER ;
message - > hlen = ETHER_ADDR_LEN ;
message - > xid = htobe32 ( xid ) ;
2014-04-06 16:05:32 +04:00
message - > magic = htobe32 ( DHCP_MAGIC_COOKIE ) ;
2014-02-11 16:11:18 +04:00
2014-05-21 17:55:02 +04:00
r = dhcp_option_append ( message , optlen , & offset , 0 ,
DHCP_OPTION_MESSAGE_TYPE , 1 , & type ) ;
2014-05-20 13:04:50 +04:00
if ( r < 0 )
return r ;
2014-02-11 16:11:18 +04:00
2014-05-20 13:04:50 +04:00
* optoffset = offset ;
2014-02-11 16:11:18 +04:00
return 0 ;
}
2014-06-16 17:24:28 +04:00
uint16_t dhcp_packet_checksum ( uint8_t * buf , size_t len ) {
uint64_t * buf_64 = ( uint64_t * ) buf ;
uint64_t * end_64 = buf_64 + ( len / sizeof ( uint64_t ) ) ;
2014-04-02 12:00:31 +04:00
uint64_t sum = 0 ;
2014-06-16 17:24:28 +04:00
/* See RFC1071 */
2014-04-02 12:00:31 +04:00
while ( buf_64 < end_64 ) {
sum + = * buf_64 ;
if ( sum < * buf_64 )
2014-06-16 17:24:28 +04:00
/* wrap around in one's complement */
2014-04-02 12:00:31 +04:00
sum + + ;
buf_64 + + ;
}
2014-06-16 17:24:28 +04:00
if ( len % sizeof ( uint64_t ) ) {
/* If the buffer is not aligned to 64-bit, we need
to zero - pad the last few bytes and add them in */
uint64_t buf_tail = 0 ;
2014-02-11 16:11:18 +04:00
2014-06-16 17:24:28 +04:00
memcpy ( & buf_tail , buf_64 , len % sizeof ( uint64_t ) ) ;
2014-02-11 16:11:18 +04:00
2014-06-16 17:24:28 +04:00
sum + = buf_tail ;
if ( sum < buf_tail )
/* wrap around */
2014-04-02 12:00:31 +04:00
sum + + ;
2014-02-11 16:11:18 +04:00
}
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 ) ;
2014-05-07 00:02:14 +04:00
packet - > ip . tos = IPTOS_CLASS_CS6 ;
2014-02-11 16:11:18 +04:00
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-06-16 17:24:28 +04:00
packet - > udp . check = dhcp_packet_checksum ( ( uint8_t * ) & packet - > ip . ttl , len - 8 ) ;
2014-02-11 16:11:18 +04:00
packet - > ip . ttl = IPDEFTTL ;
packet - > ip . check = 0 ;
2014-06-16 17:24:28 +04:00
packet - > ip . check = dhcp_packet_checksum ( ( uint8_t * ) & 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-03-30 22:33:57 +04:00
if ( packet - > ip . version ! = IPVERSION ) {
2014-04-07 22:00:53 +04:00
log_debug ( " ignoring packet: not IPv4 " ) ;
2014-03-30 22:33:57 +04:00
return - EINVAL ;
}
2014-02-23 17:15:05 +04:00
if ( packet - > ip . ihl < 5 ) {
2014-04-07 22:00:53 +04:00
log_debug ( " 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 ) {
2014-04-07 22:00:53 +04:00
log_debug ( " ignoring packet: IPv4 IHL (%zu bytes) "
" smaller than minimum (20 bytes) " , hdrlen ) ;
2014-02-23 17:15:05 +04:00
return - EINVAL ;
}
if ( len < hdrlen ) {
2014-04-07 22:00:53 +04:00
log_debug ( " 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-23 17:15:05 +04:00
/* UDP */
2014-03-30 21:09:14 +04:00
if ( packet - > ip . protocol ! = IPPROTO_UDP ) {
2014-04-07 22:00:53 +04:00
log_debug ( " ignoring packet: not UDP " ) ;
2014-03-30 21:09:14 +04:00
return - EINVAL ;
}
2014-02-23 22:21:50 +04:00
if ( len < hdrlen + be16toh ( packet - > udp . len ) ) {
2014-04-07 22:00:53 +04:00
log_debug ( " 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-03-30 22:36:44 +04:00
if ( be16toh ( packet - > udp . dest ) ! = DHCP_PORT_CLIENT ) {
2014-04-07 22:00:53 +04:00
log_debug ( " ignoring packet: to port %u, which "
" is not the DHCP client port (%u) " ,
be16toh ( packet - > udp . dest ) , DHCP_PORT_CLIENT ) ;
2014-03-30 22:36:44 +04:00
return - EINVAL ;
}
/* checksums - computing these is relatively expensive, so only do it
if all the other checks have passed
*/
2014-06-16 17:24:28 +04:00
if ( dhcp_packet_checksum ( ( uint8_t * ) & packet - > ip , hdrlen ) ) {
2014-04-07 22:00:53 +04:00
log_debug ( " ignoring packet: invalid IP checksum " ) ;
2014-03-30 22:36:44 +04:00
return - EINVAL ;
}
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-06-16 17:24:28 +04:00
if ( dhcp_packet_checksum ( ( uint8_t * ) & packet - > ip . ttl ,
2014-02-23 04:34:05 +04:00
be16toh ( packet - > udp . len ) + 12 ) ) {
2014-04-07 22:00:53 +04:00
log_debug ( " 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
}
return 0 ;
}