2010-08-14 21:59:25 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-05-09 20:02:38 +04:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
2012-04-12 02:20:58 +04:00
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
2010-05-09 20:02:38 +04:00
( 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
2012-04-12 02:20:58 +04:00
Lesser General Public License for more details .
2010-05-09 20:02:38 +04:00
2012-04-12 02:20:58 +04:00
You should have received a copy of the GNU Lesser General Public License
2010-05-09 20:02:38 +04:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <errno.h>
# include <sys/socket.h>
# include <net/if.h>
# include <asm/types.h>
# include <netinet/in.h>
# include <string.h>
# include <stdlib.h>
# include <unistd.h>
# include "util.h"
# include "macro.h"
# include "loopback-setup.h"
2010-09-20 23:33:14 +04:00
# include "socket-util.h"
2013-10-30 02:50:07 +04:00
# include "sd-rtnl.h"
# include "rtnl-util.h"
2010-05-09 20:02:38 +04:00
2013-10-30 02:50:07 +04:00
static int pipe_handler ( sd_rtnl * rtnl , sd_rtnl_message * m , void * userdata ) {
int * counter = userdata ;
int r ;
2010-05-09 20:02:38 +04:00
2013-10-30 02:50:07 +04:00
( * counter ) - - ;
2010-05-09 20:02:38 +04:00
2013-10-30 02:50:07 +04:00
r = sd_rtnl_message_get_errno ( m ) ;
2010-05-09 20:02:38 +04:00
2013-10-30 02:50:07 +04:00
return r = = - EEXIST ? 0 : r ;
2010-05-09 20:02:38 +04:00
}
2013-12-15 17:00:20 +04:00
static int add_addresses ( sd_rtnl * rtnl , int if_loopback , struct in_addr * ipv4_address , int * counter ) {
2014-02-13 06:44:14 +04:00
_cleanup_rtnl_message_unref_ sd_rtnl_message * ipv4 = NULL , * ipv6 = NULL ;
2010-05-09 20:02:38 +04:00
int r ;
2014-02-18 03:10:08 +04:00
r = sd_rtnl_message_new_addr ( rtnl , RTM_NEWADDR , if_loopback , AF_INET , & ipv4 ) ;
2014-01-21 18:55:57 +04:00
if ( r < 0 )
return r ;
r = sd_rtnl_message_addr_set_prefixlen ( ipv4 , 8 ) ;
if ( r < 0 )
return r ;
r = sd_rtnl_message_addr_set_flags ( ipv4 , IFA_F_PERMANENT ) ;
if ( r < 0 )
return r ;
r = sd_rtnl_message_addr_set_scope ( ipv4 , RT_SCOPE_HOST ) ;
2013-10-30 02:50:07 +04:00
if ( r < 0 )
return r ;
2010-05-09 20:02:38 +04:00
2013-12-15 17:00:20 +04:00
r = sd_rtnl_message_append_in_addr ( ipv4 , IFA_LOCAL , ipv4_address ) ;
2012-04-13 19:10:21 +04:00
if ( r < 0 )
2010-05-09 20:02:38 +04:00
return r ;
2013-10-30 02:50:07 +04:00
r = sd_rtnl_call_async ( rtnl , ipv4 , & pipe_handler , counter , 0 , NULL ) ;
if ( r < 0 )
return r ;
( * counter ) + + ;
2010-09-20 23:33:14 +04:00
if ( ! socket_ipv6_is_supported ( ) )
return 0 ;
2010-05-09 20:02:38 +04:00
2014-02-18 03:10:08 +04:00
r = sd_rtnl_message_new_addr ( rtnl , RTM_NEWADDR , if_loopback , AF_INET6 , & ipv6 ) ;
2014-01-21 18:55:57 +04:00
if ( r < 0 )
return r ;
r = sd_rtnl_message_addr_set_prefixlen ( ipv6 , 128 ) ;
if ( r < 0 )
return r ;
r = sd_rtnl_message_addr_set_flags ( ipv6 , IFA_F_PERMANENT ) ;
if ( r < 0 )
return r ;
r = sd_rtnl_message_addr_set_scope ( ipv6 , RT_SCOPE_HOST ) ;
2013-10-30 02:50:07 +04:00
if ( r < 0 )
return r ;
2010-05-09 20:02:38 +04:00
2013-12-15 17:00:20 +04:00
r = sd_rtnl_message_append_in6_addr ( ipv6 , IFA_LOCAL , & in6addr_loopback ) ;
2013-10-30 02:50:07 +04:00
if ( r < 0 )
return r ;
2010-05-09 20:02:38 +04:00
2013-10-30 02:50:07 +04:00
r = sd_rtnl_call_async ( rtnl , ipv6 , & pipe_handler , counter , 0 , NULL ) ;
2012-04-13 19:10:21 +04:00
if ( r < 0 )
2010-05-09 20:02:38 +04:00
return r ;
2013-10-30 02:50:07 +04:00
( * counter ) + + ;
2010-05-09 20:02:38 +04:00
return 0 ;
}
2013-12-15 17:00:20 +04:00
static int start_interface ( sd_rtnl * rtnl , int if_loopback , struct in_addr * ipv4_address , int * counter ) {
2014-02-13 06:44:14 +04:00
_cleanup_rtnl_message_unref_ sd_rtnl_message * req = NULL ;
2013-10-30 02:50:07 +04:00
int r ;
2010-05-09 20:02:38 +04:00
2014-02-18 03:10:08 +04:00
r = sd_rtnl_message_new_link ( rtnl , RTM_SETLINK , if_loopback , & req ) ;
2013-12-06 21:16:16 +04:00
if ( r < 0 )
return r ;
2014-01-21 18:20:42 +04:00
r = sd_rtnl_message_link_set_flags ( req , IFF_UP , IFF_UP ) ;
2013-10-30 02:50:07 +04:00
if ( r < 0 )
return r ;
2010-05-09 20:02:38 +04:00
2013-10-30 02:50:07 +04:00
r = sd_rtnl_call_async ( rtnl , req , & pipe_handler , counter , 0 , NULL ) ;
if ( r < 0 )
return r ;
2010-05-09 20:02:38 +04:00
2013-10-30 02:50:07 +04:00
( * counter ) + + ;
2010-09-20 23:33:14 +04:00
2010-05-09 20:02:38 +04:00
return 0 ;
}
2012-04-13 19:10:21 +04:00
static int check_loopback ( void ) {
2013-03-25 03:45:16 +04:00
int r ;
2013-10-30 02:50:07 +04:00
_cleanup_close_ int fd = - 1 ;
2012-04-13 19:10:21 +04:00
union {
struct sockaddr sa ;
struct sockaddr_in in ;
2013-03-25 03:59:00 +04:00
} sa = {
. in . sin_family = AF_INET ,
. in . sin_addr . s_addr = INADDR_LOOPBACK ,
} ;
2012-04-13 19:10:21 +04:00
/* If we failed to set up the loop back device, check whether
* it might already be set up */
fd = socket ( AF_INET , SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC , 0 ) ;
if ( fd < 0 )
return - errno ;
if ( bind ( fd , & sa . sa , sizeof ( sa . in ) ) > = 0 )
r = 1 ;
else
r = errno = = EADDRNOTAVAIL ? 0 : - errno ;
return r ;
}
2010-05-09 20:02:38 +04:00
int loopback_setup ( void ) {
2014-02-13 06:44:14 +04:00
_cleanup_rtnl_unref_ sd_rtnl * rtnl = NULL ;
2013-10-30 02:50:07 +04:00
int r , if_loopback , counter = 0 ;
2012-04-13 19:10:21 +04:00
bool eperm = false ;
2013-12-15 17:00:20 +04:00
struct in_addr ipv4_address ;
2010-05-09 20:02:38 +04:00
errno = 0 ;
2012-04-13 19:10:21 +04:00
if_loopback = ( int ) if_nametoindex ( " lo " ) ;
if ( if_loopback < = 0 )
2010-05-09 20:02:38 +04:00
return errno ? - errno : - ENODEV ;
2013-12-15 17:00:20 +04:00
ipv4_address . s_addr = htonl ( INADDR_LOOPBACK ) ;
2013-10-30 02:50:07 +04:00
r = sd_rtnl_open ( 0 , & rtnl ) ;
2012-04-13 19:10:21 +04:00
if ( r < 0 )
2013-10-30 02:50:07 +04:00
return r ;
2010-05-09 20:02:38 +04:00
2013-12-15 17:00:20 +04:00
r = add_addresses ( rtnl , if_loopback , & ipv4_address , & counter ) ;
2012-04-13 19:10:21 +04:00
if ( r < 0 )
2013-10-30 02:50:07 +04:00
return r ;
2010-05-09 20:02:38 +04:00
2013-12-15 17:00:20 +04:00
r = start_interface ( rtnl , if_loopback , & ipv4_address , & counter ) ;
2013-10-30 02:50:07 +04:00
if ( r < 0 )
return r ;
2012-04-13 19:10:21 +04:00
2013-10-30 02:50:07 +04:00
while ( counter > 0 ) {
r = sd_rtnl_wait ( rtnl , 0 ) ;
if ( r < 0 )
return r ;
r = sd_rtnl_process ( rtnl , 0 ) ;
if ( r < 0 ) {
if ( r = = - EPERM )
eperm = true ;
else {
log_warning ( " Failed to configure loopback device: %s " , strerror ( - r ) ) ;
return r ;
}
}
2010-09-20 23:33:14 +04:00
}
2010-05-09 20:02:38 +04:00
2012-04-13 19:10:21 +04:00
if ( eperm & & check_loopback ( ) < 0 ) {
2013-10-30 02:50:07 +04:00
log_warning ( " Failed to configure loopback device: %s " , strerror ( EPERM ) ) ;
return - EPERM ;
2012-04-13 19:10:21 +04:00
}
2013-03-25 03:45:16 +04:00
return 0 ;
2010-05-09 20:02:38 +04:00
}