2020-11-09 07:23:58 +03:00
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2013-12-10 01:43:18 +04:00
/***
2018-06-12 18:15:23 +03:00
Copyright © 2013 Intel Corporation . All rights reserved .
2013-12-10 01:43:18 +04:00
* * */
# include <errno.h>
# include <net/ethernet.h>
2016-12-07 04:00:05 +03:00
# include <net/if.h>
2014-04-06 21:23:33 +04:00
# include <net/if_arp.h>
2013-12-10 01:43:18 +04:00
# include <stdio.h>
2015-10-25 15:14:12 +03:00
# include <string.h>
2014-03-31 11:54:18 +04:00
# include <linux/filter.h>
2015-10-25 15:14:12 +03:00
# include <linux/if_infiniband.h>
# include <linux/if_packet.h>
2013-12-10 01:43:18 +04:00
# include "dhcp-internal.h"
2015-10-25 15:14:12 +03:00
# include "fd-util.h"
# include "socket-util.h"
2017-12-17 08:41:37 +03:00
# include "unaligned.h"
2013-12-10 01:43:18 +04:00
2014-10-08 23:15:45 +04:00
static int _bind_raw_socket ( int ifindex , union sockaddr_union * link ,
2020-10-26 16:09:13 +03:00
uint32_t xid ,
2014-10-08 23:15:45 +04:00
const uint8_t * bcast_addr ,
2020-10-26 16:09:13 +03:00
size_t bcast_addr_len ,
2014-10-08 23:15:45 +04:00
const struct ether_addr * eth_mac ,
2016-11-11 02:34:19 +03:00
uint16_t arp_type , uint8_t dhcp_hlen ,
uint16_t port ) {
2014-03-31 11:54:18 +04:00
struct sock_filter filter [ ] = {
2014-07-30 01:49:54 +04:00
BPF_STMT ( BPF_LD + BPF_W + BPF_LEN , 0 ) , /* A <- packet length */
BPF_JUMP ( BPF_JMP + BPF_JGE + BPF_K , sizeof ( DHCPPacket ) , 1 , 0 ) , /* packet >= DHCPPacket ? */
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
BPF_STMT ( BPF_LD + BPF_B + BPF_ABS , offsetof ( DHCPPacket , ip . protocol ) ) , /* A <- IP protocol */
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , IPPROTO_UDP , 1 , 0 ) , /* IP protocol == UDP ? */
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
BPF_STMT ( BPF_LD + BPF_B + BPF_ABS , offsetof ( DHCPPacket , ip . frag_off ) ) , /* A <- Flags */
BPF_STMT ( BPF_ALU + BPF_AND + BPF_K , 0x20 ) , /* A <- A & 0x20 (More Fragments bit) */
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , 0 , 1 , 0 ) , /* A == 0 ? */
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
BPF_STMT ( BPF_LD + BPF_H + BPF_ABS , offsetof ( DHCPPacket , ip . frag_off ) ) , /* A <- Flags + Fragment offset */
BPF_STMT ( BPF_ALU + BPF_AND + BPF_K , 0x1fff ) , /* A <- A & 0x1fff (Fragment offset) */
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , 0 , 1 , 0 ) , /* A == 0 ? */
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
BPF_STMT ( BPF_LD + BPF_H + BPF_ABS , offsetof ( DHCPPacket , udp . dest ) ) , /* A <- UDP destination port */
2016-11-11 02:34:19 +03:00
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , port , 1 , 0 ) , /* UDP destination port == DHCP client port ? */
2014-07-30 01:49:54 +04:00
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
BPF_STMT ( BPF_LD + BPF_B + BPF_ABS , offsetof ( DHCPPacket , dhcp . op ) ) , /* A <- DHCP op */
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , BOOTREPLY , 1 , 0 ) , /* op == BOOTREPLY ? */
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
BPF_STMT ( BPF_LD + BPF_B + BPF_ABS , offsetof ( DHCPPacket , dhcp . htype ) ) , /* A <- DHCP header type */
2014-10-08 23:15:45 +04:00
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , arp_type , 1 , 0 ) , /* header type == arp_type ? */
2014-07-30 01:49:54 +04:00
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
BPF_STMT ( BPF_LD + BPF_W + BPF_ABS , offsetof ( DHCPPacket , dhcp . xid ) ) , /* A <- client identifier */
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , xid , 1 , 0 ) , /* client identifier == xid ? */
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
2019-02-06 20:13:20 +03:00
BPF_STMT ( BPF_LD + BPF_B + BPF_ABS , offsetof ( DHCPPacket , dhcp . hlen ) ) , /* A <- MAC address length */
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , dhcp_hlen , 1 , 0 ) , /* address length == dhcp_hlen ? */
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
/* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally
* compare chaddr for ETH_ALEN bytes . */
2021-10-19 14:28:57 +03:00
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , ETH_ALEN , 0 , 8 ) , /* A (the MAC address length) == ETH_ALEN ? */
BPF_STMT ( BPF_LDX + BPF_IMM , unaligned_read_be32 ( & eth_mac - > ether_addr_octet [ 0 ] ) ) , /* X <- 4 bytes of client's MAC */
2014-07-30 01:49:54 +04:00
BPF_STMT ( BPF_LD + BPF_W + BPF_ABS , offsetof ( DHCPPacket , dhcp . chaddr ) ) , /* A <- 4 bytes of MAC from dhcp.chaddr */
2021-10-19 14:28:57 +03:00
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_X , 0 , 1 , 0 ) , /* A == X ? */
2014-07-30 01:49:54 +04:00
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
2021-10-19 14:28:57 +03:00
BPF_STMT ( BPF_LDX + BPF_IMM , unaligned_read_be16 ( & eth_mac - > ether_addr_octet [ 4 ] ) ) , /* X <- remainder of client's MAC */
2014-07-30 01:49:54 +04:00
BPF_STMT ( BPF_LD + BPF_H + BPF_ABS , offsetof ( DHCPPacket , dhcp . chaddr ) + 4 ) , /* A <- remainder of MAC from dhcp.chaddr */
2021-10-19 14:28:57 +03:00
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_X , 0 , 1 , 0 ) , /* A == X ? */
2014-07-30 01:49:54 +04:00
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
2019-02-06 20:13:20 +03:00
2014-07-30 01:49:54 +04:00
BPF_STMT ( BPF_LD + BPF_W + BPF_ABS , offsetof ( DHCPPacket , dhcp . magic ) ) , /* A <- DHCP magic cookie */
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , DHCP_MAGIC_COOKIE , 1 , 0 ) , /* cookie == DHCP magic cookie ? */
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
2021-10-19 14:28:57 +03:00
BPF_STMT ( BPF_RET + BPF_K , UINT32_MAX ) , /* accept */
2014-03-31 11:54:18 +04:00
} ;
struct sock_fprog fprog = {
2014-07-30 01:49:54 +04:00
. len = ELEMENTSOF ( filter ) ,
. filter = filter
2014-03-31 11:54:18 +04:00
} ;
2014-05-07 00:02:32 +04:00
_cleanup_close_ int s = - 1 ;
2018-10-15 20:21:37 +03:00
int r ;
2013-12-10 01:43:18 +04:00
2014-07-30 01:49:54 +04:00
assert ( ifindex > 0 ) ;
2013-12-18 13:52:22 +04:00
assert ( link ) ;
2014-04-01 01:28:58 +04:00
s = socket ( AF_PACKET , SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK , 0 ) ;
2013-12-10 01:43:18 +04:00
if ( s < 0 )
return - errno ;
2018-10-18 20:48:18 +03:00
r = setsockopt_int ( s , SOL_PACKET , PACKET_AUXDATA , true ) ;
2014-05-07 00:02:32 +04:00
if ( r < 0 )
2018-10-18 20:48:18 +03:00
return r ;
2014-05-07 00:02:32 +04:00
r = setsockopt ( s , SOL_SOCKET , SO_ATTACH_FILTER , & fprog , sizeof ( fprog ) ) ;
if ( r < 0 )
return - errno ;
2017-08-08 19:55:31 +03:00
link - > ll = ( struct sockaddr_ll ) {
. sll_family = AF_PACKET ,
. sll_protocol = htobe16 ( ETH_P_IP ) ,
. sll_ifindex = ifindex ,
. sll_hatype = htobe16 ( arp_type ) ,
2020-10-26 16:09:13 +03:00
. sll_halen = bcast_addr_len ,
2017-08-08 19:55:31 +03:00
} ;
2020-11-10 17:33:28 +03:00
memcpy ( link - > ll . sll_addr , bcast_addr , bcast_addr_len ) ; /* We may overflow link->ll. link->ll_buffer ensures we have enough space. */
2013-12-10 01:43:18 +04:00
2017-08-08 19:55:31 +03:00
r = bind ( s , & link - > sa , SOCKADDR_LL_LEN ( link - > ll ) ) ;
2014-05-07 00:02:32 +04:00
if ( r < 0 )
2014-03-31 11:54:18 +04:00
return - errno ;
2018-03-22 19:04:29 +03:00
return TAKE_FD ( s ) ;
2013-12-10 01:43:26 +04:00
}
2021-10-19 14:24:09 +03:00
int dhcp_network_bind_raw_socket (
int ifindex ,
union sockaddr_union * link ,
uint32_t xid ,
const uint8_t * mac_addr ,
size_t mac_addr_len ,
const uint8_t * bcast_addr ,
size_t bcast_addr_len ,
uint16_t arp_type ,
uint16_t port ) {
2014-10-08 23:15:45 +04:00
static const uint8_t eth_bcast [ ] = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF } ;
/* Default broadcast address for IPoIB */
static const uint8_t ib_bcast [ ] = {
0x00 , 0xff , 0xff , 0xff , 0xff , 0x12 , 0x40 , 0x1b ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0xff , 0xff , 0xff , 0xff
2020-10-26 16:09:13 +03:00
} ;
2014-10-08 23:15:45 +04:00
struct ether_addr eth_mac = { { 0 , 0 , 0 , 0 , 0 , 0 } } ;
2020-10-26 16:09:13 +03:00
const uint8_t * default_bcast_addr ;
size_t expected_bcast_addr_len ;
2014-10-08 23:15:45 +04:00
uint8_t dhcp_hlen = 0 ;
if ( arp_type = = ARPHRD_ETHER ) {
assert_return ( mac_addr_len = = ETH_ALEN , - EINVAL ) ;
memcpy ( & eth_mac , mac_addr , ETH_ALEN ) ;
dhcp_hlen = ETH_ALEN ;
2020-10-26 16:09:13 +03:00
default_bcast_addr = eth_bcast ;
expected_bcast_addr_len = ETH_ALEN ;
2014-10-08 23:15:45 +04:00
} else if ( arp_type = = ARPHRD_INFINIBAND ) {
2020-10-26 16:09:13 +03:00
default_bcast_addr = ib_bcast ;
expected_bcast_addr_len = INFINIBAND_ALEN ;
2014-10-08 23:15:45 +04:00
} else
return - EINVAL ;
2020-10-26 16:09:13 +03:00
if ( bcast_addr & & bcast_addr_len > 0 )
assert_return ( bcast_addr_len = = expected_bcast_addr_len , - EINVAL ) ;
else {
bcast_addr = default_bcast_addr ;
bcast_addr_len = expected_bcast_addr_len ;
}
return _bind_raw_socket ( ifindex , link , xid , bcast_addr , bcast_addr_len ,
& eth_mac , arp_type , dhcp_hlen , port ) ;
2014-10-08 23:15:45 +04:00
}
2019-09-23 14:25:21 +03:00
int dhcp_network_bind_udp_socket ( int ifindex , be32_t address , uint16_t port , int ip_service_type ) {
2013-12-20 19:16:17 +04:00
union sockaddr_union src = {
. in . sin_family = AF_INET ,
2014-02-14 20:23:58 +04:00
. in . sin_port = htobe16 ( port ) ,
. in . sin_addr . s_addr = address ,
2013-12-20 19:16:17 +04:00
} ;
2014-05-07 00:02:32 +04:00
_cleanup_close_ int s = - 1 ;
2018-10-18 23:39:55 +03:00
int r ;
2013-12-20 19:16:17 +04:00
s = socket ( AF_INET , SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK , 0 ) ;
if ( s < 0 )
return - errno ;
2019-09-23 14:25:21 +03:00
if ( ip_service_type > = 0 )
r = setsockopt_int ( s , IPPROTO_IP , IP_TOS , ip_service_type ) ;
else
r = setsockopt_int ( s , IPPROTO_IP , IP_TOS , IPTOS_CLASS_CS6 ) ;
2014-05-07 00:02:32 +04:00
if ( r < 0 )
2018-10-18 23:39:55 +03:00
return r ;
2014-05-24 20:48:41 +04:00
2018-10-18 20:48:18 +03:00
r = setsockopt_int ( s , SOL_SOCKET , SO_REUSEADDR , true ) ;
2014-07-25 16:43:16 +04:00
if ( r < 0 )
2018-10-18 20:48:18 +03:00
return r ;
2014-05-24 17:48:47 +04:00
2016-12-07 04:00:05 +03:00
if ( ifindex > 0 ) {
2019-03-18 14:01:02 +03:00
r = socket_bind_to_ifindex ( s , ifindex ) ;
2016-12-07 04:00:05 +03:00
if ( r < 0 )
2019-03-18 14:01:02 +03:00
return r ;
2016-12-07 04:00:05 +03:00
}
2021-03-09 14:57:37 +03:00
if ( port = = DHCP_PORT_SERVER ) {
2018-10-18 20:48:18 +03:00
r = setsockopt_int ( s , SOL_SOCKET , SO_BROADCAST , true ) ;
2014-06-21 16:39:36 +04:00
if ( r < 0 )
2018-10-18 20:48:18 +03:00
return r ;
2021-03-09 14:57:37 +03:00
if ( address = = INADDR_ANY ) {
/* IP_PKTINFO filter should not be applied when packets are
allowed to enter / leave through the interface other than
DHCP server sits on ( BindToInterface option ) . */
r = setsockopt_int ( s , IPPROTO_IP , IP_PKTINFO , true ) ;
if ( r < 0 )
return r ;
}
2014-07-25 16:43:16 +04:00
} else {
2018-10-18 20:48:18 +03:00
r = setsockopt_int ( s , IPPROTO_IP , IP_FREEBIND , true ) ;
2014-07-25 16:43:16 +04:00
if ( r < 0 )
2018-10-18 20:48:18 +03:00
return r ;
2014-05-24 17:48:47 +04:00
}
2014-05-07 00:02:14 +04:00
2021-10-19 14:22:56 +03:00
if ( bind ( s , & src . sa , sizeof ( src . in ) ) < 0 )
2013-12-20 19:16:17 +04:00
return - errno ;
2018-03-22 19:04:29 +03:00
return TAKE_FD ( s ) ;
2013-12-20 19:16:17 +04:00
}
2021-10-19 14:24:09 +03:00
int dhcp_network_send_raw_socket (
int s ,
const union sockaddr_union * link ,
const void * packet ,
size_t len ) {
2014-05-07 00:02:32 +04:00
2021-10-19 14:23:47 +03:00
/* Do not add assert(s >= 0) here, as this is called in fuzz-dhcp-server, and in that case this
* function should fail with negative errno . */
2013-12-18 13:52:22 +04:00
assert ( link ) ;
assert ( packet ) ;
2021-10-19 14:23:47 +03:00
assert ( len > 0 ) ;
2013-12-18 13:52:22 +04:00
2021-10-19 14:22:56 +03:00
if ( sendto ( s , packet , len , 0 , & link - > sa , SOCKADDR_LL_LEN ( link - > ll ) ) < 0 )
2013-12-20 19:16:11 +04:00
return - errno ;
2013-12-10 01:43:18 +04:00
2013-12-20 19:16:11 +04:00
return 0 ;
2013-12-10 01:43:18 +04:00
}
2013-12-20 19:16:17 +04:00
2021-10-19 14:24:09 +03:00
int dhcp_network_send_udp_socket (
int s ,
be32_t address ,
uint16_t port ,
const void * packet ,
size_t len ) {
2013-12-20 19:16:17 +04:00
union sockaddr_union dest = {
. in . sin_family = AF_INET ,
2014-02-14 20:23:58 +04:00
. in . sin_port = htobe16 ( port ) ,
. in . sin_addr . s_addr = address ,
2013-12-20 19:16:17 +04:00
} ;
2014-05-07 00:02:32 +04:00
assert ( s > = 0 ) ;
assert ( packet ) ;
2021-10-19 14:23:47 +03:00
assert ( len > 0 ) ;
2013-12-20 19:16:17 +04:00
2021-10-19 14:22:56 +03:00
if ( sendto ( s , packet , len , 0 , & dest . sa , sizeof ( dest . in ) ) < 0 )
2013-12-20 19:16:17 +04:00
return - errno ;
return 0 ;
}