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 <sys/types.h>
# include <sys/socket.h>
# include <string.h>
# include <linux/if_packet.h>
# include <net/ethernet.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>
# include <unistd.h>
2014-03-31 11:54:18 +04:00
# include <linux/filter.h>
2013-12-10 01:43:18 +04:00
# include "socket-util.h"
# include "dhcp-internal.h"
2014-05-07 00:02:32 +04:00
int dhcp_network_bind_raw_socket ( int index , union sockaddr_union * link ,
uint32_t xid ) {
2014-03-31 11:54:18 +04:00
struct sock_filter filter [ ] = {
2014-04-06 21:35:36 +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 */
2014-04-06 21:23:33 +04:00
/* TODO: match ip.version */
2014-03-31 11:54:18 +04:00
BPF_STMT ( BPF_LD + BPF_B + BPF_ABS , offsetof ( DHCPPacket , ip . protocol ) ) , /* A <- IP protocol */
2014-04-06 21:35:36 +04:00
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , IPPROTO_UDP , 1 , 0 ) , /* IP protocol == UDP ? */
2014-03-31 11:54:18 +04:00
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
BPF_STMT ( BPF_LD + BPF_H + BPF_ABS , offsetof ( DHCPPacket , udp . dest ) ) , /* A <- UDP destination port */
2014-04-06 21:35:36 +04:00
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , DHCP_PORT_CLIENT , 1 , 0 ) , /* UDP destination port == DHCP client port ? */
2014-03-31 11:54:18 +04:00
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
2014-04-06 21:23:33 +04:00
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 */
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , ARPHRD_ETHER , 1 , 0 ) , /* header type == ARPHRD_ETHER ? */
BPF_STMT ( BPF_RET + BPF_K , 0 ) , /* ignore */
BPF_STMT ( BPF_LD + BPF_B + BPF_ABS , offsetof ( DHCPPacket , dhcp . hlen ) ) , /* A <- mac address length */
BPF_JUMP ( BPF_JMP + BPF_JEQ + BPF_K , ETHER_ADDR_LEN , 1 , 0 ) , /* address length == ETHER_ADDR_LEN ? */
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 */
/* TODO: match chaddr */
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 */
2014-03-31 11:54:18 +04:00
BPF_STMT ( BPF_RET + BPF_K , 65535 ) , /* return all */
} ;
struct sock_fprog fprog = {
. len = ELEMENTSOF ( filter ) ,
. filter = filter
} ;
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
2013-12-18 13:52:22 +04:00
assert ( index > 0 ) ;
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-05-24 17:48:47 +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 ;
link - > ll . sll_protocol = htons ( ETH_P_IP ) ;
link - > ll . sll_ifindex = index ;
link - > ll . sll_halen = ETH_ALEN ;
memset ( link - > ll . sll_addr , 0xff , ETH_ALEN ) ;
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-05-07 00:02:32 +04:00
int dhcp_network_bind_udp_socket ( 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 ;
int r , 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 17:48:47 +04:00
if ( address = = INADDR_ANY ) {
int on = 1 ;
r = setsockopt ( s , SOL_SOCKET , SO_REUSEADDR , & on , sizeof ( on ) ) ;
if ( r < 0 )
return - errno ;
r = setsockopt ( s , IPPROTO_IP , IP_PKTINFO , & on , sizeof ( on ) ) ;
if ( r < 0 )
return - errno ;
}
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 ;
}