2018-06-28 13:15:37 +03:00
/*
2009-02-28 05:08:31 +03:00
ctdb system specific code to manage raw sockets on linux
Copyright ( C ) Ronnie Sahlberg 2007
Copyright ( C ) Andrew Tridgell 2007
2018-08-17 06:58:18 +03:00
Copyright ( C ) Marc Dequènes ( Duck ) 2009
Copyright ( C ) Volker Lendecke 2012
2009-02-28 05:08:31 +03:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
2018-06-28 13:15:37 +03:00
2009-02-28 05:08:31 +03:00
This program 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 General Public License for more details .
2018-06-28 13:15:37 +03:00
2009-02-28 05:08:31 +03:00
You should have received a copy of the GNU General Public License
along with this program ; if not , see < http : //www.gnu.org/licenses/>.
*/
2015-10-26 08:50:46 +03:00
# include "replace.h"
2018-06-28 14:06:58 +03:00
/*
* Use BSD struct tcphdr field names for portability . Modern glibc
* makes them available by default via < netinet / tcp . h > but older glibc
* requires __FAVOR_BSD to be defined .
*
* __FAVOR_BSD is normally defined in < features . h > if _DEFAULT_SOURCE
* ( new ) or _BSD_SOURCE ( now deprecated ) is set and _GNU_SOURCE is not
* set . Including " replace.h " above causes < features . h > to be
* indirectly included and this will not set __FAVOR_BSD because
* _GNU_SOURCE is set in Samba ' s " config.h " ( which is included by
* " replace.h " ) .
*
* Therefore , set __FAVOR_BSD by hand below .
*/
# define __FAVOR_BSD 1
2009-02-28 05:08:31 +03:00
# include "system/network.h"
2018-06-28 13:57:08 +03:00
# ifdef HAVE_NETINET_IF_ETHER_H
# include <netinet/if_ether.h>
# endif
# ifdef HAVE_NETINET_IP6_H
# include <netinet/ip6.h>
# endif
# ifdef HAVE_NETINET_ICMP6_H
# include <netinet/icmp6.h>
# endif
# ifdef HAVE_LINUX_IF_PACKET_H
# include <linux/if_packet.h>
# endif
# ifndef ETHERTYPE_IP6
# define ETHERTYPE_IP6 0x86dd
# endif
2015-10-26 08:50:46 +03:00
# include "lib/util/debug.h"
2018-06-28 14:12:04 +03:00
# include "lib/util/blocking.h"
2015-10-26 08:50:46 +03:00
2015-11-02 08:03:29 +03:00
# include "protocol/protocol.h"
2014-09-04 07:33:58 +04:00
2015-11-11 06:43:56 +03:00
# include "common/logging.h"
2018-06-28 13:15:37 +03:00
# include "common/system_socket.h"
2015-10-23 06:11:53 +03:00
2009-02-28 05:08:31 +03:00
/*
uint16 checksum for n bytes
*/
2018-06-28 14:06:58 +03:00
static uint32_t uint16_checksum ( uint16_t * data , size_t n )
2009-02-28 05:08:31 +03:00
{
uint32_t sum = 0 ;
while ( n > = 2 ) {
sum + = ( uint32_t ) ntohs ( * data ) ;
data + + ;
n - = 2 ;
}
if ( n = = 1 ) {
sum + = ( uint32_t ) ntohs ( * ( uint8_t * ) data ) ;
}
return sum ;
}
2018-06-28 13:15:37 +03:00
/*
* See if the given IP is currently on an interface
2009-02-28 05:08:31 +03:00
*/
bool ctdb_sys_have_ip ( ctdb_sock_addr * _addr )
{
int s ;
int ret ;
ctdb_sock_addr __addr = * _addr ;
ctdb_sock_addr * addr = & __addr ;
2013-07-04 11:00:23 +04:00
socklen_t addrlen = 0 ;
2009-02-28 05:08:31 +03:00
switch ( addr - > sa . sa_family ) {
case AF_INET :
addr - > ip . sin_port = 0 ;
2010-08-30 12:40:43 +04:00
addrlen = sizeof ( struct sockaddr_in ) ;
2009-02-28 05:08:31 +03:00
break ;
case AF_INET6 :
addr - > ip6 . sin6_port = 0 ;
2010-08-30 12:40:43 +04:00
addrlen = sizeof ( struct sockaddr_in6 ) ;
2009-02-28 05:08:31 +03:00
break ;
}
s = socket ( addr - > sa . sa_family , SOCK_STREAM , IPPROTO_TCP ) ;
if ( s = = - 1 ) {
return false ;
}
2010-08-30 12:40:43 +04:00
ret = bind ( s , ( struct sockaddr * ) addr , addrlen ) ;
2009-02-28 05:08:31 +03:00
close ( s ) ;
return ret = = 0 ;
}
2018-06-28 13:24:10 +03:00
2018-06-28 14:06:58 +03:00
/*
* simple TCP checksum - assumes data is multiple of 2 bytes long
*/
static uint16_t ip_checksum ( uint16_t * data , size_t n , struct ip * ip )
{
uint32_t sum = uint16_checksum ( data , n ) ;
uint16_t sum2 ;
sum + = uint16_checksum ( ( uint16_t * ) & ip - > ip_src , sizeof ( ip - > ip_src ) ) ;
sum + = uint16_checksum ( ( uint16_t * ) & ip - > ip_dst , sizeof ( ip - > ip_dst ) ) ;
sum + = ip - > ip_p + n ;
sum = ( sum & 0xFFFF ) + ( sum > > 16 ) ;
sum = ( sum & 0xFFFF ) + ( sum > > 16 ) ;
sum2 = htons ( sum ) ;
sum2 = ~ sum2 ;
if ( sum2 = = 0 ) {
return 0xFFFF ;
}
return sum2 ;
}
2018-06-28 13:57:08 +03:00
static uint16_t ip6_checksum ( uint16_t * data , size_t n , struct ip6_hdr * ip6 )
{
2018-08-13 05:18:51 +03:00
uint16_t phdr [ 3 ] ;
2018-06-28 13:57:08 +03:00
uint32_t sum = 0 ;
uint16_t sum2 ;
2018-08-13 05:18:51 +03:00
uint32_t len ;
2018-06-28 13:57:08 +03:00
sum + = uint16_checksum ( ( uint16_t * ) ( void * ) & ip6 - > ip6_src , 16 ) ;
sum + = uint16_checksum ( ( uint16_t * ) ( void * ) & ip6 - > ip6_dst , 16 ) ;
2018-08-13 05:18:51 +03:00
len = htonl ( n ) ;
phdr [ 0 ] = len & UINT16_MAX ;
phdr [ 1 ] = ( len > > 16 ) & UINT16_MAX ;
/* ip6_nxt is only 8 bits, so fits comfortably into a uint16_t */
phdr [ 2 ] = htons ( ip6 - > ip6_nxt ) ;
sum + = uint16_checksum ( phdr , sizeof ( phdr ) ) ;
2018-06-28 13:57:08 +03:00
sum + = uint16_checksum ( data , n ) ;
sum = ( sum & 0xFFFF ) + ( sum > > 16 ) ;
sum = ( sum & 0xFFFF ) + ( sum > > 16 ) ;
sum2 = htons ( sum ) ;
sum2 = ~ sum2 ;
if ( sum2 = = 0 ) {
return 0xFFFF ;
}
return sum2 ;
}
/*
* Send gratuitous ARP request / reply or IPv6 neighbor advertisement
*/
# ifdef HAVE_PACKETSOCKET
2018-08-13 14:39:12 +03:00
/*
* Create IPv4 ARP requests / replies or IPv6 neighbour advertisement
* packets
*/
2018-08-14 04:25:02 +03:00
# define ARP_STRUCT_SIZE sizeof(struct ether_header) + \
sizeof ( struct ether_arp )
2018-08-13 14:39:12 +03:00
2018-08-14 04:25:02 +03:00
# define IP6_NA_STRUCT_SIZE sizeof(struct ether_header) + \
2018-08-13 14:39:12 +03:00
sizeof ( struct ip6_hdr ) + \
sizeof ( struct nd_neighbor_advert ) + \
sizeof ( struct nd_opt_hdr ) + \
sizeof ( struct ether_addr )
2018-08-14 04:25:02 +03:00
# define ARP_BUFFER_SIZE MAX(ARP_STRUCT_SIZE, 64)
# define IP6_NA_BUFFER_SIZE MAX(IP6_NA_STRUCT_SIZE, 64)
2018-08-13 14:39:12 +03:00
static int arp_build ( uint8_t * buffer ,
size_t buflen ,
const struct sockaddr_in * addr ,
const struct ether_addr * hwaddr ,
bool reply ,
struct ether_addr * * ether_dhost ,
size_t * len )
2018-06-28 13:57:08 +03:00
{
2018-08-13 14:39:12 +03:00
size_t l = ARP_BUFFER_SIZE ;
2018-06-28 13:57:08 +03:00
struct ether_header * eh ;
2018-08-14 05:36:25 +03:00
struct ether_arp * ea ;
2018-06-28 13:57:08 +03:00
struct arphdr * ah ;
2018-08-13 14:39:12 +03:00
if ( addr - > sin_family ! = AF_INET ) {
return EINVAL ;
}
if ( buflen < l ) {
return EMSGSIZE ;
}
memset ( buffer , 0 , l ) ;
eh = ( struct ether_header * ) buffer ;
memset ( eh - > ether_dhost , 0xff , ETH_ALEN ) ;
memcpy ( eh - > ether_shost , hwaddr , ETH_ALEN ) ;
eh - > ether_type = htons ( ETHERTYPE_ARP ) ;
2018-08-10 11:38:23 +03:00
ea = ( struct ether_arp * ) ( buffer + sizeof ( struct ether_header ) ) ;
2018-08-14 05:36:25 +03:00
ah = & ea - > ea_hdr ;
2018-08-13 14:39:12 +03:00
ah - > ar_hrd = htons ( ARPHRD_ETHER ) ;
ah - > ar_pro = htons ( ETH_P_IP ) ;
ah - > ar_hln = ETH_ALEN ;
2018-08-10 11:38:23 +03:00
ah - > ar_pln = sizeof ( ea - > arp_spa ) ;
2018-08-13 14:39:12 +03:00
if ( ! reply ) {
ah - > ar_op = htons ( ARPOP_REQUEST ) ;
2018-08-14 05:36:25 +03:00
memcpy ( ea - > arp_sha , hwaddr , ETH_ALEN ) ;
memcpy ( ea - > arp_spa , & addr - > sin_addr , sizeof ( ea - > arp_spa ) ) ;
memset ( ea - > arp_tha , 0 , ETH_ALEN ) ;
memcpy ( ea - > arp_tpa , & addr - > sin_addr , sizeof ( ea - > arp_tpa ) ) ;
2018-08-13 14:39:12 +03:00
} else {
ah - > ar_op = htons ( ARPOP_REPLY ) ;
2018-08-14 05:36:25 +03:00
memcpy ( ea - > arp_sha , hwaddr , ETH_ALEN ) ;
memcpy ( ea - > arp_spa , & addr - > sin_addr , sizeof ( ea - > arp_spa ) ) ;
memcpy ( ea - > arp_tha , hwaddr , ETH_ALEN ) ;
memcpy ( ea - > arp_tpa , & addr - > sin_addr , sizeof ( ea - > arp_tpa ) ) ;
2018-08-13 14:39:12 +03:00
}
* ether_dhost = ( struct ether_addr * ) eh - > ether_dhost ;
* len = l ;
return 0 ;
}
static int ip6_na_build ( uint8_t * buffer ,
size_t buflen ,
const struct sockaddr_in6 * addr ,
const struct ether_addr * hwaddr ,
struct ether_addr * * ether_dhost ,
size_t * len )
{
size_t l = IP6_NA_BUFFER_SIZE ;
struct ether_header * eh ;
2018-06-28 13:57:08 +03:00
struct ip6_hdr * ip6 ;
struct nd_neighbor_advert * nd_na ;
struct nd_opt_hdr * nd_oh ;
2018-08-14 06:18:01 +03:00
struct ether_addr * ea ;
2018-08-13 14:39:12 +03:00
int ret ;
if ( addr - > sin6_family ! = AF_INET6 ) {
return EINVAL ;
}
if ( buflen < l ) {
return EMSGSIZE ;
}
memset ( buffer , 0 , l ) ;
eh = ( struct ether_header * ) buffer ;
/*
* Ethernet multicast : 33 : 33 : 00 : 00 : 00 : 01 ( see RFC2464 ,
* section 7 ) - note memset 0 above !
*/
2018-08-14 06:49:25 +03:00
eh - > ether_dhost [ 0 ] = 0x33 ;
eh - > ether_dhost [ 1 ] = 0x33 ;
2018-08-13 14:39:12 +03:00
eh - > ether_dhost [ 5 ] = 0x01 ;
memcpy ( eh - > ether_shost , hwaddr , ETH_ALEN ) ;
eh - > ether_type = htons ( ETHERTYPE_IP6 ) ;
2018-08-10 11:38:23 +03:00
ip6 = ( struct ip6_hdr * ) ( buffer + sizeof ( struct ether_header ) ) ;
2018-08-29 08:48:21 +03:00
ip6 - > ip6_vfc = 6 < < 4 ;
2018-08-10 11:38:23 +03:00
ip6 - > ip6_plen = htons ( sizeof ( struct nd_neighbor_advert ) +
2018-08-13 14:39:12 +03:00
sizeof ( struct nd_opt_hdr ) +
ETH_ALEN ) ;
ip6 - > ip6_nxt = IPPROTO_ICMPV6 ;
ip6 - > ip6_hlim = 255 ;
ip6 - > ip6_src = addr - > sin6_addr ;
/* all-nodes multicast */
ret = inet_pton ( AF_INET6 , " ff02::1 " , & ip6 - > ip6_dst ) ;
if ( ret ! = 1 ) {
return EIO ;
}
2018-08-10 11:38:23 +03:00
nd_na = ( struct nd_neighbor_advert * ) ( buffer +
sizeof ( struct ether_header ) +
sizeof ( struct ip6_hdr ) ) ;
2018-08-13 14:39:12 +03:00
nd_na - > nd_na_type = ND_NEIGHBOR_ADVERT ;
nd_na - > nd_na_code = 0 ;
nd_na - > nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE ;
nd_na - > nd_na_target = addr - > sin6_addr ;
2018-08-10 11:38:23 +03:00
2018-08-13 14:39:12 +03:00
/* Option: Target link-layer address */
2018-08-10 11:38:23 +03:00
nd_oh = ( struct nd_opt_hdr * ) ( buffer +
sizeof ( struct ether_header ) +
sizeof ( struct ip6_hdr ) +
sizeof ( struct nd_neighbor_advert ) ) ;
2018-08-13 14:39:12 +03:00
nd_oh - > nd_opt_type = ND_OPT_TARGET_LINKADDR ;
2018-08-10 11:38:23 +03:00
nd_oh - > nd_opt_len = 1 ; /* multiple of 8 octets */
2018-08-13 14:39:12 +03:00
2018-08-10 11:38:23 +03:00
ea = ( struct ether_addr * ) ( buffer +
sizeof ( struct ether_header ) +
sizeof ( struct ip6_hdr ) +
sizeof ( struct nd_neighbor_advert ) +
sizeof ( struct nd_opt_hdr ) ) ;
2018-08-13 14:39:12 +03:00
memcpy ( ea , hwaddr , ETH_ALEN ) ;
nd_na - > nd_na_cksum = ip6_checksum ( ( uint16_t * ) nd_na ,
ntohs ( ip6 - > ip6_plen ) ,
ip6 ) ;
* ether_dhost = ( struct ether_addr * ) eh - > ether_dhost ;
* len = l ;
return 0 ;
}
int ctdb_sys_send_arp ( const ctdb_sock_addr * addr , const char * iface )
{
int s ;
struct sockaddr_ll sall = { 0 } ;
2018-08-10 09:14:40 +03:00
struct ifreq if_hwaddr = { { { 0 } } } ;
2018-08-13 14:39:12 +03:00
uint8_t buffer [ MAX ( ARP_BUFFER_SIZE , IP6_NA_BUFFER_SIZE ) ] ;
2018-08-10 09:14:40 +03:00
struct ifreq ifr = { { { 0 } } } ;
2018-08-13 14:39:12 +03:00
struct ether_addr * hwaddr = NULL ;
struct ether_addr * ether_dhost = NULL ;
size_t len = 0 ;
2018-08-10 10:23:56 +03:00
int ret = 0 ;
2018-06-28 13:57:08 +03:00
2018-08-10 10:04:32 +03:00
s = socket ( AF_PACKET , SOCK_RAW , 0 ) ;
if ( s = = - 1 ) {
2018-08-10 10:23:56 +03:00
ret = errno ;
2018-08-10 10:04:32 +03:00
DBG_ERR ( " Failed to open raw socket \n " ) ;
2018-08-10 10:23:56 +03:00
return ret ;
2018-08-10 10:04:32 +03:00
}
DBG_DEBUG ( " Created SOCKET FD:%d for sending arp \n " , s ) ;
2018-06-28 13:57:08 +03:00
2018-08-10 10:04:32 +03:00
/* Find interface */
strlcpy ( ifr . ifr_name , iface , sizeof ( ifr . ifr_name ) ) ;
if ( ioctl ( s , SIOCGIFINDEX , & ifr ) < 0 ) {
2018-08-10 10:23:56 +03:00
ret = errno ;
2018-08-10 10:04:32 +03:00
DBG_ERR ( " Interface '%s' not found \n " , iface ) ;
2018-08-10 10:23:56 +03:00
goto fail ;
2018-08-10 10:04:32 +03:00
}
2018-06-28 13:57:08 +03:00
2018-08-10 10:04:32 +03:00
/* Get MAC address */
strlcpy ( if_hwaddr . ifr_name , iface , sizeof ( if_hwaddr . ifr_name ) ) ;
ret = ioctl ( s , SIOCGIFHWADDR , & if_hwaddr ) ;
if ( ret < 0 ) {
2018-08-10 10:23:56 +03:00
ret = errno ;
2018-08-10 10:04:32 +03:00
DBG_ERR ( " ioctl failed \n " ) ;
2018-08-10 10:23:56 +03:00
goto fail ;
2018-08-10 10:04:32 +03:00
}
if ( ARPHRD_LOOPBACK = = if_hwaddr . ifr_hwaddr . sa_family ) {
2018-08-10 10:23:56 +03:00
ret = 0 ;
2018-08-10 10:04:32 +03:00
D_DEBUG ( " Ignoring loopback arp request \n " ) ;
2018-08-10 10:23:56 +03:00
goto fail ;
2018-08-10 10:04:32 +03:00
}
if ( if_hwaddr . ifr_hwaddr . sa_family ! = ARPHRD_ETHER ) {
2018-08-10 10:23:56 +03:00
ret = EINVAL ;
2018-08-10 10:04:32 +03:00
DBG_ERR ( " Not an ethernet address family (0x%x) \n " ,
if_hwaddr . ifr_hwaddr . sa_family ) ;
2018-08-10 10:23:56 +03:00
goto fail ; ;
2018-08-10 10:04:32 +03:00
}
2018-06-28 13:57:08 +03:00
2018-08-10 10:04:32 +03:00
/* Set up most of destination address structure */
sall . sll_family = AF_PACKET ;
sall . sll_halen = sizeof ( struct ether_addr ) ;
sall . sll_protocol = htons ( ETH_P_ALL ) ;
sall . sll_ifindex = ifr . ifr_ifindex ;
2018-06-28 13:57:08 +03:00
2018-08-13 14:39:12 +03:00
/* For clarity */
hwaddr = ( struct ether_addr * ) if_hwaddr . ifr_hwaddr . sa_data ;
2018-08-10 10:04:32 +03:00
switch ( addr - > ip . sin_family ) {
case AF_INET :
2018-08-13 14:39:12 +03:00
/* Send gratuitous ARP */
ret = arp_build ( buffer ,
sizeof ( buffer ) ,
& addr - > ip ,
hwaddr ,
false ,
& ether_dhost ,
& len ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Failed to build ARP request \n " ) ;
goto fail ;
}
2018-06-28 13:57:08 +03:00
2018-08-13 14:39:12 +03:00
memcpy ( & sall . sll_addr [ 0 ] , ether_dhost , sall . sll_halen ) ;
2018-08-10 10:04:32 +03:00
2018-08-13 14:39:12 +03:00
ret = sendto ( s ,
buffer ,
len ,
0 ,
( struct sockaddr * ) & sall ,
sizeof ( sall ) ) ;
2018-08-10 10:23:56 +03:00
if ( ret < 0 ) {
ret = errno ;
2018-06-28 13:57:08 +03:00
DBG_ERR ( " Failed sendto \n " ) ;
2018-08-10 10:23:56 +03:00
goto fail ;
2018-06-28 13:57:08 +03:00
}
2018-08-13 14:39:12 +03:00
/* Send unsolicited ARP reply */
ret = arp_build ( buffer ,
sizeof ( buffer ) ,
& addr - > ip ,
hwaddr ,
true ,
& ether_dhost ,
& len ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Failed to build ARP reply \n " ) ;
goto fail ;
}
memcpy ( & sall . sll_addr [ 0 ] , ether_dhost , sall . sll_halen ) ;
2018-06-28 13:57:08 +03:00
2018-08-13 14:39:12 +03:00
ret = sendto ( s ,
buffer ,
len ,
0 ,
( struct sockaddr * ) & sall ,
sizeof ( sall ) ) ;
2018-08-10 10:23:56 +03:00
if ( ret < 0 ) {
ret = errno ;
2018-06-28 13:57:08 +03:00
DBG_ERR ( " Failed sendto \n " ) ;
2018-08-10 10:23:56 +03:00
goto fail ;
2018-06-28 13:57:08 +03:00
}
close ( s ) ;
break ;
2018-08-13 14:39:12 +03:00
2018-06-28 13:57:08 +03:00
case AF_INET6 :
2018-08-13 14:39:12 +03:00
ret = ip6_na_build ( buffer ,
sizeof ( buffer ) ,
& addr - > ip6 ,
hwaddr ,
& ether_dhost ,
& len ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Failed to build IPv6 neighbor advertisment \n " ) ;
2018-08-10 10:23:56 +03:00
goto fail ;
2018-06-28 13:57:08 +03:00
}
2018-08-13 14:39:12 +03:00
memcpy ( & sall . sll_addr [ 0 ] , ether_dhost , sall . sll_halen ) ;
2018-08-14 06:18:01 +03:00
2018-08-13 14:39:12 +03:00
ret = sendto ( s ,
buffer ,
len ,
0 ,
( struct sockaddr * ) & sall ,
sizeof ( sall ) ) ;
2018-08-10 10:23:56 +03:00
if ( ret < 0 ) {
ret = errno ;
2018-06-28 13:57:08 +03:00
DBG_ERR ( " Failed sendto \n " ) ;
2018-08-10 10:23:56 +03:00
goto fail ;
2018-06-28 13:57:08 +03:00
}
close ( s ) ;
break ;
2018-08-13 14:39:12 +03:00
2018-06-28 13:57:08 +03:00
default :
2018-08-10 10:23:56 +03:00
ret = EINVAL ;
2018-06-28 13:57:08 +03:00
DBG_ERR ( " Not an ipv4/ipv6 address (family is %u) \n " ,
addr - > ip . sin_family ) ;
2018-08-10 10:23:56 +03:00
goto fail ;
2018-06-28 13:57:08 +03:00
}
return 0 ;
2018-08-10 10:23:56 +03:00
fail :
close ( s ) ;
return ret ;
2018-06-28 13:57:08 +03:00
}
# else /* HAVE_PACKETSOCKET */
int ctdb_sys_send_arp ( const ctdb_sock_addr * addr , const char * iface )
{
/* Not implemented */
2018-08-10 10:23:56 +03:00
return ENOSYS ;
2018-06-28 13:57:08 +03:00
}
# endif /* HAVE_PACKETSOCKET */
2018-06-28 14:06:58 +03:00
2018-08-16 04:10:40 +03:00
# define IP4_TCP_BUFFER_SIZE sizeof(struct ip) + \
sizeof ( struct tcphdr )
# define IP6_TCP_BUFFER_SIZE sizeof(struct ip6_hdr) + \
sizeof ( struct tcphdr )
static int tcp4_build ( uint8_t * buf ,
size_t buflen ,
const struct sockaddr_in * src ,
const struct sockaddr_in * dst ,
uint32_t seq ,
uint32_t ack ,
int rst ,
size_t * len )
{
size_t l = IP4_TCP_BUFFER_SIZE ;
struct {
struct ip ip ;
struct tcphdr tcp ;
} * ip4pkt ;
if ( l ! = sizeof ( * ip4pkt ) ) {
return EMSGSIZE ;
}
if ( buflen < l ) {
return EMSGSIZE ;
}
ip4pkt = ( void * ) buf ;
memset ( ip4pkt , 0 , l ) ;
ip4pkt - > ip . ip_v = 4 ;
2018-08-17 05:30:19 +03:00
ip4pkt - > ip . ip_hl = sizeof ( ip4pkt - > ip ) / sizeof ( uint32_t ) ;
2018-08-16 04:10:40 +03:00
ip4pkt - > ip . ip_len = htons ( sizeof ( ip4pkt ) ) ;
ip4pkt - > ip . ip_ttl = 255 ;
ip4pkt - > ip . ip_p = IPPROTO_TCP ;
ip4pkt - > ip . ip_src . s_addr = src - > sin_addr . s_addr ;
ip4pkt - > ip . ip_dst . s_addr = dst - > sin_addr . s_addr ;
ip4pkt - > ip . ip_sum = 0 ;
ip4pkt - > tcp . th_sport = src - > sin_port ;
ip4pkt - > tcp . th_dport = dst - > sin_port ;
ip4pkt - > tcp . th_seq = seq ;
ip4pkt - > tcp . th_ack = ack ;
ip4pkt - > tcp . th_flags = 0 ;
ip4pkt - > tcp . th_flags | = TH_ACK ;
if ( rst ) {
ip4pkt - > tcp . th_flags | = TH_RST ;
}
2018-08-17 05:30:19 +03:00
ip4pkt - > tcp . th_off = sizeof ( ip4pkt - > tcp ) / sizeof ( uint32_t ) ;
2018-08-16 04:10:40 +03:00
/* this makes it easier to spot in a sniffer */
ip4pkt - > tcp . th_win = htons ( 1234 ) ;
ip4pkt - > tcp . th_sum = ip_checksum ( ( uint16_t * ) & ip4pkt - > tcp ,
sizeof ( ip4pkt - > tcp ) ,
& ip4pkt - > ip ) ;
* len = l ;
return 0 ;
}
static int tcp6_build ( uint8_t * buf ,
size_t buflen ,
const struct sockaddr_in6 * src ,
const struct sockaddr_in6 * dst ,
uint32_t seq ,
uint32_t ack ,
int rst ,
size_t * len )
{
size_t l = IP6_TCP_BUFFER_SIZE ;
struct {
struct ip6_hdr ip6 ;
struct tcphdr tcp ;
} * ip6pkt ;
if ( l ! = sizeof ( * ip6pkt ) ) {
return EMSGSIZE ;
}
if ( buflen < l ) {
return EMSGSIZE ;
}
ip6pkt = ( void * ) buf ;
memset ( ip6pkt , 0 , l ) ;
2018-08-17 05:30:19 +03:00
ip6pkt - > ip6 . ip6_vfc = 6 < < 4 ;
ip6pkt - > ip6 . ip6_plen = htons ( sizeof ( struct tcphdr ) ) ;
2018-08-16 04:10:40 +03:00
ip6pkt - > ip6 . ip6_nxt = IPPROTO_TCP ;
ip6pkt - > ip6 . ip6_hlim = 64 ;
ip6pkt - > ip6 . ip6_src = src - > sin6_addr ;
ip6pkt - > ip6 . ip6_dst = dst - > sin6_addr ;
ip6pkt - > tcp . th_sport = src - > sin6_port ;
ip6pkt - > tcp . th_dport = dst - > sin6_port ;
ip6pkt - > tcp . th_seq = seq ;
ip6pkt - > tcp . th_ack = ack ;
ip6pkt - > tcp . th_flags = 0 ;
ip6pkt - > tcp . th_flags | = TH_ACK ;
if ( rst ) {
ip6pkt - > tcp . th_flags | = TH_RST ;
}
2018-08-17 05:30:19 +03:00
ip6pkt - > tcp . th_off = sizeof ( ip6pkt - > tcp ) / sizeof ( uint32_t ) ;
2018-08-16 04:10:40 +03:00
/* this makes it easier to spot in a sniffer */
ip6pkt - > tcp . th_win = htons ( 1234 ) ;
ip6pkt - > tcp . th_sum = ip6_checksum ( ( uint16_t * ) & ip6pkt - > tcp ,
sizeof ( ip6pkt - > tcp ) ,
& ip6pkt - > ip6 ) ;
* len = l ;
return 0 ;
}
2018-06-28 14:06:58 +03:00
/*
* Send tcp segment from the specified IP / port to the specified
* destination IP / port .
*
* This is used to trigger the receiving host into sending its own ACK ,
* which should trigger early detection of TCP reset by the client
* after IP takeover
*
* This can also be used to send RST segments ( if rst is true ) and also
* if correct seq and ack numbers are provided .
*/
int ctdb_sys_send_tcp ( const ctdb_sock_addr * dest ,
const ctdb_sock_addr * src ,
uint32_t seq ,
uint32_t ack ,
int rst )
{
2018-08-16 04:10:40 +03:00
uint8_t buf [ MAX ( IP4_TCP_BUFFER_SIZE , IP6_TCP_BUFFER_SIZE ) ] ;
size_t len = 0 ;
2018-06-28 14:06:58 +03:00
int ret ;
2018-08-16 04:10:40 +03:00
int s ;
2018-06-28 14:06:58 +03:00
uint32_t one = 1 ;
2018-08-17 06:46:50 +03:00
struct sockaddr_in6 tmpdest = { 0 } ;
2018-06-28 14:06:58 +03:00
int saved_errno ;
switch ( src - > ip . sin_family ) {
case AF_INET :
2018-08-16 04:10:40 +03:00
ret = tcp4_build ( buf ,
sizeof ( buf ) ,
& src - > ip ,
& dest - > ip ,
seq ,
ack ,
rst ,
& len ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Failed to build TCP packet (%d) \n " , ret ) ;
return ret ;
2018-06-28 14:06:58 +03:00
}
/* open a raw socket to send this segment from */
s = socket ( AF_INET , SOCK_RAW , IPPROTO_RAW ) ;
if ( s = = - 1 ) {
DBG_ERR ( " Failed to open raw socket (%s) \n " ,
strerror ( errno ) ) ;
return - 1 ;
}
ret = setsockopt ( s , IPPROTO_IP , IP_HDRINCL , & one , sizeof ( one ) ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Failed to setup IP headers (%s) \n " ,
strerror ( errno ) ) ;
close ( s ) ;
return - 1 ;
}
2018-08-16 04:10:40 +03:00
ret = sendto ( s ,
buf ,
len ,
0 ,
2018-06-28 14:06:58 +03:00
( const struct sockaddr * ) & dest - > ip ,
sizeof ( dest - > ip ) ) ;
saved_errno = errno ;
close ( s ) ;
2018-08-16 04:10:40 +03:00
if ( ret ! = len ) {
2018-06-28 14:06:58 +03:00
D_ERR ( " Failed sendto (%s) \n " , strerror ( saved_errno ) ) ;
return - 1 ;
}
break ;
2018-08-16 04:10:40 +03:00
2018-06-28 14:06:58 +03:00
case AF_INET6 :
2018-08-16 04:10:40 +03:00
ret = tcp6_build ( buf ,
sizeof ( buf ) ,
& src - > ip6 ,
& dest - > ip6 ,
seq ,
ack ,
rst ,
& len ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Failed to build TCP packet (%d) \n " , ret ) ;
return ret ;
2018-06-28 14:06:58 +03:00
}
s = socket ( AF_INET6 , SOCK_RAW , IPPROTO_RAW ) ;
if ( s = = - 1 ) {
DBG_ERR ( " Failed to open sending socket \n " ) ;
return - 1 ;
}
2018-08-17 06:46:50 +03:00
/*
* sendto ( ) on an IPv6 raw socket requires the port to
* be either 0 or a protocol value
*/
tmpdest = dest - > ip6 ;
tmpdest . sin6_port = 0 ;
2018-06-28 14:06:58 +03:00
2018-08-16 04:10:40 +03:00
ret = sendto ( s ,
buf ,
len ,
0 ,
2018-08-17 06:46:50 +03:00
( const struct sockaddr * ) & tmpdest ,
sizeof ( tmpdest ) ) ;
2018-06-28 14:06:58 +03:00
saved_errno = errno ;
close ( s ) ;
2018-08-16 04:10:40 +03:00
if ( ret ! = len ) {
2018-06-28 14:06:58 +03:00
D_ERR ( " Failed sendto (%s) \n " , strerror ( saved_errno ) ) ;
return - 1 ;
}
break ;
default :
DBG_ERR ( " Not an ipv4/v6 address \n " ) ;
return - 1 ;
}
return 0 ;
}
2018-06-28 14:12:04 +03:00
/*
* Packet capture
*
* If AF_PACKET is available then use a raw socket otherwise use pcap .
* wscript has checked to make sure that pcap is available if needed .
*/
2018-08-17 08:00:31 +03:00
static int tcp4_extract ( const uint8_t * ip_pkt ,
size_t pktlen ,
struct sockaddr_in * src ,
struct sockaddr_in * dst ,
uint32_t * ack_seq ,
uint32_t * seq ,
int * rst ,
uint16_t * window )
{
const struct ip * ip ;
const struct tcphdr * tcp ;
if ( pktlen < sizeof ( struct ip ) ) {
return EMSGSIZE ;
}
/* IP */
ip = ( const struct ip * ) ip_pkt ;
/* We only want IPv4 packets */
if ( ip - > ip_v ! = 4 ) {
return ENOMSG ;
}
/* Dont look at fragments */
if ( ( ntohs ( ip - > ip_off ) & 0x1fff ) ! = 0 ) {
return ENOMSG ;
}
/* we only want TCP */
if ( ip - > ip_p ! = IPPROTO_TCP ) {
return ENOMSG ;
}
/* make sure its not a short packet */
if ( offsetof ( struct tcphdr , th_ack ) + 4 + ( ip - > ip_hl * 4 ) > pktlen ) {
return EMSGSIZE ;
}
/* TCP */
tcp = ( const struct tcphdr * ) ( ( ip - > ip_hl * 4 ) + ( const char * ) ip ) ;
/* tell the caller which one we've found */
src - > sin_family = AF_INET ;
src - > sin_addr . s_addr = ip - > ip_src . s_addr ;
src - > sin_port = tcp - > th_sport ;
dst - > sin_family = AF_INET ;
dst - > sin_addr . s_addr = ip - > ip_dst . s_addr ;
dst - > sin_port = tcp - > th_dport ;
* ack_seq = tcp - > th_ack ;
* seq = tcp - > th_seq ;
if ( window ! = NULL ) {
* window = tcp - > th_win ;
}
if ( rst ! = NULL ) {
* rst = tcp - > th_flags & TH_RST ;
}
return 0 ;
}
static int tcp6_extract ( const uint8_t * ip_pkt ,
size_t pktlen ,
struct sockaddr_in6 * src ,
struct sockaddr_in6 * dst ,
uint32_t * ack_seq ,
uint32_t * seq ,
int * rst ,
uint16_t * window )
{
const struct ip6_hdr * ip6 ;
const struct tcphdr * tcp ;
if ( pktlen < sizeof ( struct ip6_hdr ) ) {
return EMSGSIZE ;
}
/* IP6 */
ip6 = ( const struct ip6_hdr * ) ip_pkt ;
/* we only want TCP */
if ( ip6 - > ip6_nxt ! = IPPROTO_TCP ) {
return ENOMSG ;
}
/* TCP */
tcp = ( const struct tcphdr * ) ( ip6 + 1 ) ;
/* tell the caller which one we've found */
src - > sin6_family = AF_INET6 ;
src - > sin6_port = tcp - > th_sport ;
src - > sin6_addr = ip6 - > ip6_src ;
dst - > sin6_family = AF_INET6 ;
dst - > sin6_port = tcp - > th_dport ;
dst - > sin6_addr = ip6 - > ip6_dst ;
* ack_seq = tcp - > th_ack ;
* seq = tcp - > th_seq ;
if ( window ! = NULL ) {
* window = tcp - > th_win ;
}
if ( rst ! = NULL ) {
* rst = tcp - > th_flags & TH_RST ;
}
return 0 ;
}
2018-06-28 14:12:04 +03:00
# ifdef HAVE_AF_PACKET
/*
* This function is used to open a raw socket to capture from
*/
int ctdb_sys_open_capture_socket ( const char * iface , void * * private_data )
{
int s , ret ;
/* Open a socket to capture all traffic */
s = socket ( AF_PACKET , SOCK_RAW , htons ( ETH_P_ALL ) ) ;
if ( s = = - 1 ) {
DBG_ERR ( " Failed to open raw socket \n " ) ;
return - 1 ;
}
DBG_DEBUG ( " Created RAW SOCKET FD:%d for tcp tickle \n " , s ) ;
ret = set_blocking ( s , false ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Failed to set socket non-blocking (%s) \n " ,
strerror ( errno ) ) ;
close ( s ) ;
return - 1 ;
}
set_close_on_exec ( s ) ;
return s ;
}
/*
* This function is used to do any additional cleanup required when closing
* a capture socket .
* Note that the socket itself is closed automatically in the caller .
*/
int ctdb_sys_close_capture_socket ( void * private_data )
{
return 0 ;
}
/*
* called when the raw socket becomes readable
*/
int ctdb_sys_read_tcp_packet ( int s , void * private_data ,
ctdb_sock_addr * src ,
ctdb_sock_addr * dst ,
uint32_t * ack_seq ,
uint32_t * seq ,
int * rst ,
uint16_t * window )
{
2018-08-17 07:35:07 +03:00
ssize_t nread ;
uint8_t pkt [ 100 ] ; /* Large enough for simple ACK/RST packets */
2018-06-28 14:12:04 +03:00
struct ether_header * eth ;
2018-08-17 07:35:07 +03:00
int ret ;
2018-06-28 14:12:04 +03:00
2018-08-17 07:35:07 +03:00
nread = recv ( s , pkt , sizeof ( pkt ) , MSG_TRUNC ) ;
2018-08-17 08:00:31 +03:00
if ( nread < sizeof ( * eth ) ) {
2018-08-17 07:14:12 +03:00
return EMSGSIZE ;
2018-06-28 14:12:04 +03:00
}
ZERO_STRUCTP ( src ) ;
ZERO_STRUCTP ( dst ) ;
/* Ethernet */
eth = ( struct ether_header * ) pkt ;
/* we want either IPv4 or IPv6 */
if ( ntohs ( eth - > ether_type ) = = ETHERTYPE_IP ) {
2018-08-17 08:00:31 +03:00
ret = tcp4_extract ( pkt + sizeof ( struct ether_header ) ,
( size_t ) nread - sizeof ( struct ether_header ) ,
& src - > ip ,
& dst - > ip ,
ack_seq ,
seq ,
rst ,
window ) ;
return ret ;
2018-06-28 14:12:04 +03:00
} else if ( ntohs ( eth - > ether_type ) = = ETHERTYPE_IP6 ) {
2018-08-17 08:00:31 +03:00
ret = tcp6_extract ( pkt + sizeof ( struct ether_header ) ,
( size_t ) nread - sizeof ( struct ether_header ) ,
& src - > ip6 ,
& dst - > ip6 ,
ack_seq ,
seq ,
rst ,
window ) ;
return ret ;
2018-06-28 14:12:04 +03:00
}
2018-08-17 07:14:12 +03:00
return ENOMSG ;
2018-06-28 14:12:04 +03:00
}
# else /* HAVE_AF_PACKET */
# include <pcap.h>
int ctdb_sys_open_capture_socket ( const char * iface , void * * private_data )
{
pcap_t * pt ;
pt = pcap_open_live ( iface , 100 , 0 , 0 , NULL ) ;
if ( pt = = NULL ) {
DBG_ERR ( " Failed to open capture device %s \n " , iface ) ;
return - 1 ;
}
* ( ( pcap_t * * ) private_data ) = pt ;
return pcap_fileno ( pt ) ;
}
int ctdb_sys_close_capture_socket ( void * private_data )
{
pcap_t * pt = ( pcap_t * ) private_data ;
pcap_close ( pt ) ;
return 0 ;
}
int ctdb_sys_read_tcp_packet ( int s ,
void * private_data ,
ctdb_sock_addr * src ,
ctdb_sock_addr * dst ,
uint32_t * ack_seq ,
uint32_t * seq ,
int * rst ,
uint16_t * window )
{
int ret ;
struct ether_header * eth ;
struct pcap_pkthdr pkthdr ;
const u_char * buffer ;
pcap_t * pt = ( pcap_t * ) private_data ;
buffer = pcap_next ( pt , & pkthdr ) ;
if ( buffer = = NULL ) {
2018-08-17 07:14:12 +03:00
return ENOMSG ;
2018-06-28 14:12:04 +03:00
}
ZERO_STRUCTP ( src ) ;
ZERO_STRUCTP ( dst ) ;
/* Ethernet */
eth = ( struct ether_header * ) buffer ;
/* we want either IPv4 or IPv6 */
if ( eth - > ether_type = = htons ( ETHERTYPE_IP ) ) {
2018-08-17 08:00:31 +03:00
ret = tcp4_extract ( buffer + sizeof ( struct ether_header ) ,
( size_t ) ( pkthdr . caplen -
sizeof ( struct ether_header ) ) ,
& src - > ip ,
& dst - > ip ,
ack_seq ,
seq ,
rst ,
window ) ;
return ret ;
2018-06-28 14:12:04 +03:00
} else if ( eth - > ether_type = = htons ( ETHERTYPE_IP6 ) ) {
2018-08-17 08:00:31 +03:00
ret = tcp6_extract ( buffer + sizeof ( struct ether_header ) ,
( size_t ) ( pkthdr . caplen -
sizeof ( struct ether_header ) ) ,
& src - > ip6 ,
& dst - > ip6 ,
ack_seq ,
seq ,
rst ,
window ) ;
return ret ;
2018-06-28 14:12:04 +03:00
}
2018-08-17 07:14:12 +03:00
return ENOMSG ;
2018-06-28 14:12:04 +03:00
}
# endif /* HAVE_AF_PACKET */