2013-12-10 01:43:18 +04:00
/***
This file is part of systemd .
Copyright ( C ) 2013 Intel Corporation . All rights reserved .
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 <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>
# include <sys/socket.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"
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 ,
uint32_t xid , const uint8_t * mac_addr ,
size_t mac_addr_len ,
const uint8_t * bcast_addr ,
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 */
2015-02-09 16:18:57 +03:00
BPF_STMT ( BPF_LD + BPF_B + BPF_ABS , offsetof ( DHCPPacket , dhcp . hlen ) ) , /* A <- MAC address length */
2014-10-08 23:15:45 +04:00
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , dhcp_hlen , 1 , 0 ) , /* address length == dhcp_hlen ? */
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 */
2014-10-08 23:15:45 +04:00
BPF_STMT ( BPF_LD + BPF_IMM , htobe32 ( * ( ( unsigned int * ) eth_mac ) ) ) , /* A <- 4 bytes of client's MAC */
2014-07-30 01:49:54 +04:00
BPF_STMT ( BPF_MISC + BPF_TAX , 0 ) , /* X <- A */
BPF_STMT ( BPF_LD + BPF_W + BPF_ABS , offsetof ( DHCPPacket , dhcp . chaddr ) ) , /* A <- 4 bytes of MAC from dhcp.chaddr */
BPF_STMT ( BPF_ALU + BPF_XOR + BPF_X , 0 ) , /* A xor X */
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , 0 , 1 , 0 ) , /* A == 0 ? */
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
2014-10-08 23:15:45 +04:00
BPF_STMT ( BPF_LD + BPF_IMM , htobe16 ( * ( ( unsigned short * ) ( ( ( char * ) eth_mac ) + 4 ) ) ) ) , /* A <- remainder of client's MAC */
2014-07-30 01:49:54 +04:00
BPF_STMT ( BPF_MISC + BPF_TAX , 0 ) , /* X <- A */
BPF_STMT ( BPF_LD + BPF_H + BPF_ABS , offsetof ( DHCPPacket , dhcp . chaddr ) + 4 ) , /* A <- remainder of MAC from dhcp.chaddr */
BPF_STMT ( BPF_ALU + BPF_XOR + BPF_X , 0 ) , /* A xor X */
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_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 */
BPF_STMT ( BPF_RET + BPF_K , 65535 ) , /* return all */
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 ;
2014-05-24 17:48:47 +04:00
int r , on = 1 ;
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 ;
2014-07-30 01:49:54 +04:00
r = setsockopt ( s , SOL_PACKET , PACKET_AUXDATA , & on , sizeof ( on ) ) ;
2014-05-07 00:02:32 +04:00
if ( r < 0 )
return - errno ;
r = setsockopt ( s , SOL_SOCKET , SO_ATTACH_FILTER , & fprog , sizeof ( fprog ) ) ;
if ( r < 0 )
return - errno ;
2013-12-10 01:43:26 +04:00
link - > ll . sll_family = AF_PACKET ;
2016-06-15 02:26:01 +03:00
link - > ll . sll_protocol = htobe16 ( ETH_P_IP ) ;
2014-07-30 01:49:54 +04:00
link - > ll . sll_ifindex = ifindex ;
2016-06-15 02:26:01 +03:00
link - > ll . sll_hatype = htobe16 ( arp_type ) ;
2014-10-08 23:15:45 +04:00
link - > ll . sll_halen = mac_addr_len ;
memcpy ( link - > ll . sll_addr , bcast_addr , mac_addr_len ) ;
2013-12-10 01:43:18 +04:00
2014-05-07 00:02:32 +04:00
r = bind ( s , & link - > sa , sizeof ( link - > ll ) ) ;
if ( r < 0 )
2014-03-31 11:54:18 +04:00
return - errno ;
2014-05-07 00:02:32 +04:00
r = s ;
s = - 1 ;
2013-12-10 01:43:18 +04:00
2014-05-07 00:02:32 +04:00
return r ;
2013-12-10 01:43:26 +04:00
}
2014-10-08 23:15:45 +04:00
int dhcp_network_bind_raw_socket ( int ifindex , union sockaddr_union * link ,
uint32_t xid , const uint8_t * mac_addr ,
2016-11-11 02:34:19 +03:00
size_t mac_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
} ;
struct ether_addr eth_mac = { { 0 , 0 , 0 , 0 , 0 , 0 } } ;
const uint8_t * bcast_addr = NULL ;
uint8_t dhcp_hlen = 0 ;
assert_return ( mac_addr_len > 0 , - EINVAL ) ;
if ( arp_type = = ARPHRD_ETHER ) {
assert_return ( mac_addr_len = = ETH_ALEN , - EINVAL ) ;
memcpy ( & eth_mac , mac_addr , ETH_ALEN ) ;
bcast_addr = eth_bcast ;
dhcp_hlen = ETH_ALEN ;
} else if ( arp_type = = ARPHRD_INFINIBAND ) {
assert_return ( mac_addr_len = = INFINIBAND_ALEN , - EINVAL ) ;
bcast_addr = ib_bcast ;
} else
return - EINVAL ;
return _bind_raw_socket ( ifindex , link , xid , mac_addr , mac_addr_len ,
2016-11-11 02:34:19 +03:00
bcast_addr , & eth_mac , arp_type , dhcp_hlen , port ) ;
2014-10-08 23:15:45 +04:00
}
2016-12-07 04:00:05 +03:00
int dhcp_network_bind_udp_socket ( int ifindex , be32_t address , uint16_t port ) {
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 ;
2016-12-07 04:00:05 +03:00
char ifname [ IF_NAMESIZE ] = " " ;
2014-07-25 16:43:16 +04:00
int r , on = 1 , tos = IPTOS_CLASS_CS6 ;
2013-12-20 19:16:17 +04:00
s = socket ( AF_INET , SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK , 0 ) ;
if ( s < 0 )
return - errno ;
2014-05-07 00:02:32 +04:00
r = setsockopt ( s , IPPROTO_IP , IP_TOS , & tos , sizeof ( tos ) ) ;
if ( r < 0 )
2014-05-07 00:02:14 +04:00
return - errno ;
2014-05-24 20:48:41 +04:00
2014-07-25 16:43:16 +04:00
r = setsockopt ( s , SOL_SOCKET , SO_REUSEADDR , & on , sizeof ( on ) ) ;
if ( r < 0 )
return - errno ;
2014-05-24 17:48:47 +04:00
2016-12-07 04:00:05 +03:00
if ( ifindex > 0 ) {
if ( if_indextoname ( ifindex , ifname ) = = 0 )
return - errno ;
r = setsockopt ( s , SOL_SOCKET , SO_BINDTODEVICE , ifname , strlen ( ifname ) ) ;
if ( r < 0 )
return - errno ;
}
2014-07-25 16:43:16 +04:00
if ( address = = INADDR_ANY ) {
2014-05-24 17:48:47 +04:00
r = setsockopt ( s , IPPROTO_IP , IP_PKTINFO , & on , sizeof ( on ) ) ;
if ( r < 0 )
return - errno ;
2014-06-21 16:39:36 +04:00
r = setsockopt ( s , SOL_SOCKET , SO_BROADCAST , & on , sizeof ( on ) ) ;
if ( r < 0 )
return - errno ;
2016-12-07 04:00:05 +03:00
2014-07-25 16:43:16 +04:00
} else {
r = setsockopt ( s , IPPROTO_IP , IP_FREEBIND , & on , sizeof ( on ) ) ;
if ( r < 0 )
return - errno ;
2014-05-24 17:48:47 +04:00
}
2014-05-07 00:02:14 +04:00
2014-05-07 00:02:32 +04:00
r = bind ( s , & src . sa , sizeof ( src . in ) ) ;
if ( r < 0 )
2013-12-20 19:16:17 +04:00
return - errno ;
2014-05-07 00:02:32 +04:00
r = s ;
s = - 1 ;
return r ;
2013-12-20 19:16:17 +04:00
}
2013-12-10 01:43:26 +04:00
int dhcp_network_send_raw_socket ( int s , const union sockaddr_union * link ,
2014-05-07 00:02:32 +04:00
const void * packet , size_t len ) {
int r ;
2013-12-18 13:52:22 +04:00
assert ( link ) ;
assert ( packet ) ;
assert ( len ) ;
2014-05-07 00:02:32 +04:00
r = sendto ( s , packet , len , 0 , & link - > sa , sizeof ( link - > ll ) ) ;
if ( r < 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
2014-02-14 20:23:58 +04:00
int dhcp_network_send_udp_socket ( int s , be32_t address , uint16_t port ,
2014-05-07 00:02:32 +04:00
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
int r ;
assert ( s > = 0 ) ;
assert ( packet ) ;
assert ( len ) ;
2013-12-20 19:16:17 +04:00
2014-05-07 00:02:32 +04:00
r = sendto ( s , packet , len , 0 , & dest . sa , sizeof ( dest . in ) ) ;
if ( r < 0 )
2013-12-20 19:16:17 +04:00
return - errno ;
return 0 ;
}