2011-05-09 16:57:15 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
2013-01-05 21:42:40 +04:00
This file is part of systemd .
2011-05-09 16:57:15 +04:00
Copyright 2008 - 2011 Lennart Poettering
2014-04-14 14:46:09 +04:00
Copyright 2014 Tom Gundersen
2011-05-09 16:57:15 +04:00
2013-01-05 21:42:40 +04:00
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 .
2011-05-09 16:57:15 +04:00
2013-01-05 21:42:40 +04:00
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
2011-05-09 16:57:15 +04:00
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
2013-01-05 21:42:40 +04:00
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
2011-05-09 16:57:15 +04:00
* * */
2014-04-14 14:46:09 +04:00
# include "sd-rtnl.h"
# include "rtnl-util.h"
# include "macro.h"
2014-07-10 23:01:25 +04:00
# include "local-addresses.h"
2011-05-09 16:57:15 +04:00
2014-07-10 21:55:53 +04:00
static int address_compare ( const void * _a , const void * _b ) {
2014-07-10 23:01:25 +04:00
const struct local_address * a = _a , * b = _b ;
2014-07-10 21:55:53 +04:00
/* Order lowest scope first, IPv4 before IPv6, lowest interface index first */
2014-12-03 23:42:58 +03:00
if ( a - > family = = AF_INET & & b - > family = = AF_INET6 )
return - 1 ;
if ( a - > family = = AF_INET6 & & b - > family = = AF_INET )
return 1 ;
2014-07-10 21:55:53 +04:00
if ( a - > scope < b - > scope )
return - 1 ;
if ( a - > scope > b - > scope )
return 1 ;
2014-12-03 23:42:58 +03:00
if ( a - > metric < b - > metric )
2014-07-10 21:55:53 +04:00
return - 1 ;
2014-12-03 23:42:58 +03:00
if ( a - > metric > b - > metric )
2014-07-10 21:55:53 +04:00
return 1 ;
if ( a - > ifindex < b - > ifindex )
return - 1 ;
if ( a - > ifindex > b - > ifindex )
return 1 ;
2014-12-04 06:16:29 +03:00
return memcmp ( & a - > address , & b - > address , FAMILY_ADDRESS_SIZE ( a - > family ) ) ;
2014-07-10 21:55:53 +04:00
}
2014-12-04 03:41:12 +03:00
int local_addresses ( sd_rtnl * context , int ifindex , int af , struct local_address * * ret ) {
2014-04-14 14:46:09 +04:00
_cleanup_rtnl_message_unref_ sd_rtnl_message * req = NULL , * reply = NULL ;
2014-07-10 21:55:53 +04:00
_cleanup_rtnl_unref_ sd_rtnl * rtnl = NULL ;
2014-07-10 23:01:25 +04:00
_cleanup_free_ struct local_address * list = NULL ;
2014-07-10 21:55:53 +04:00
size_t n_list = 0 , n_allocated = 0 ;
sd_rtnl_message * m ;
2014-04-14 14:46:09 +04:00
int r ;
2013-03-30 18:46:04 +04:00
2014-07-10 23:01:25 +04:00
assert ( ret ) ;
2014-08-12 03:41:42 +04:00
if ( context )
rtnl = sd_rtnl_ref ( context ) ;
else {
r = sd_rtnl_open ( & rtnl , 0 ) ;
if ( r < 0 )
return r ;
}
2011-05-09 16:57:15 +04:00
2014-12-04 03:41:12 +03:00
r = sd_rtnl_message_new_addr ( rtnl , & req , RTM_GETADDR , 0 , af ) ;
2014-04-14 14:46:09 +04:00
if ( r < 0 )
return r ;
2011-05-09 16:57:15 +04:00
2014-04-14 14:46:09 +04:00
r = sd_rtnl_call ( rtnl , req , 0 , & reply ) ;
if ( r < 0 )
return r ;
2014-07-10 21:55:53 +04:00
for ( m = reply ; m ; m = sd_rtnl_message_next ( m ) ) {
2014-07-10 23:01:25 +04:00
struct local_address * a ;
2014-04-14 14:46:09 +04:00
unsigned char flags ;
2014-07-10 21:55:53 +04:00
uint16_t type ;
2014-12-04 03:41:12 +03:00
int ifi , family ;
2014-04-14 14:46:09 +04:00
r = sd_rtnl_message_get_errno ( m ) ;
if ( r < 0 )
return r ;
r = sd_rtnl_message_get_type ( m , & type ) ;
if ( r < 0 )
return r ;
if ( type ! = RTM_NEWADDR )
2011-05-09 16:57:15 +04:00
continue ;
2014-08-12 03:41:42 +04:00
r = sd_rtnl_message_addr_get_ifindex ( m , & ifi ) ;
if ( r < 0 )
return r ;
2014-12-04 03:41:12 +03:00
if ( ifindex > 0 & & ifi ! = ifindex )
continue ;
2014-08-12 03:41:42 +04:00
2014-12-04 03:41:12 +03:00
r = sd_rtnl_message_addr_get_family ( m , & family ) ;
if ( r < 0 )
return r ;
if ( af ! = AF_UNSPEC & & af ! = family )
2014-08-12 03:41:42 +04:00
continue ;
2014-07-10 21:55:53 +04:00
r = sd_rtnl_message_addr_get_flags ( m , & flags ) ;
2014-04-14 14:46:09 +04:00
if ( r < 0 )
return r ;
2014-07-10 21:55:53 +04:00
if ( flags & IFA_F_DEPRECATED )
2013-03-30 18:46:04 +04:00
continue ;
2011-05-09 16:57:15 +04:00
2014-12-03 23:42:58 +03:00
if ( ! GREEDY_REALLOC0 ( list , n_allocated , n_list + 1 ) )
2014-07-10 21:55:53 +04:00
return - ENOMEM ;
a = list + n_list ;
r = sd_rtnl_message_addr_get_scope ( m , & a - > scope ) ;
2014-04-14 14:46:09 +04:00
if ( r < 0 )
return r ;
2011-05-09 16:57:15 +04:00
2014-08-12 03:41:42 +04:00
if ( ifindex = = 0 & & ( a - > scope = = RT_SCOPE_HOST | | a - > scope = = RT_SCOPE_NOWHERE ) )
2013-03-30 18:46:04 +04:00
continue ;
2011-05-09 16:57:15 +04:00
2014-12-04 03:41:12 +03:00
switch ( family ) {
2014-07-10 21:55:53 +04:00
2014-04-14 14:46:09 +04:00
case AF_INET :
2014-07-10 21:55:53 +04:00
r = sd_rtnl_message_read_in_addr ( m , IFA_LOCAL , & a - > address . in ) ;
2014-04-14 14:46:09 +04:00
if ( r < 0 ) {
2014-07-10 21:55:53 +04:00
r = sd_rtnl_message_read_in_addr ( m , IFA_ADDRESS , & a - > address . in ) ;
2014-04-14 14:46:09 +04:00
if ( r < 0 )
continue ;
}
break ;
2014-07-10 21:55:53 +04:00
2014-04-14 14:46:09 +04:00
case AF_INET6 :
2014-07-10 21:55:53 +04:00
r = sd_rtnl_message_read_in6_addr ( m , IFA_LOCAL , & a - > address . in6 ) ;
2014-04-14 14:46:09 +04:00
if ( r < 0 ) {
2014-07-10 21:55:53 +04:00
r = sd_rtnl_message_read_in6_addr ( m , IFA_ADDRESS , & a - > address . in6 ) ;
2014-04-14 14:46:09 +04:00
if ( r < 0 )
continue ;
}
break ;
2014-07-10 21:55:53 +04:00
2014-04-14 14:46:09 +04:00
default :
2013-03-30 18:46:04 +04:00
continue ;
}
2011-05-09 16:57:15 +04:00
2014-08-12 03:41:42 +04:00
a - > ifindex = ifi ;
2014-12-04 03:41:12 +03:00
a - > family = family ;
2011-05-09 16:57:15 +04:00
2014-04-14 14:46:09 +04:00
n_list + + ;
2014-07-10 21:55:53 +04:00
} ;
2011-05-09 16:57:15 +04:00
2014-12-03 23:42:58 +03:00
if ( n_list > 0 )
qsort ( list , n_list , sizeof ( struct local_address ) , address_compare ) ;
* ret = list ;
list = NULL ;
return ( int ) n_list ;
}
2014-12-04 03:41:12 +03:00
int local_gateways ( sd_rtnl * context , int ifindex , int af , struct local_address * * ret ) {
2014-12-03 23:42:58 +03:00
_cleanup_rtnl_message_unref_ sd_rtnl_message * req = NULL , * reply = NULL ;
_cleanup_rtnl_unref_ sd_rtnl * rtnl = NULL ;
_cleanup_free_ struct local_address * list = NULL ;
sd_rtnl_message * m = NULL ;
size_t n_list = 0 , n_allocated = 0 ;
int r ;
assert ( ret ) ;
if ( context )
rtnl = sd_rtnl_ref ( context ) ;
else {
r = sd_rtnl_open ( & rtnl , 0 ) ;
if ( r < 0 )
return r ;
}
2014-12-04 03:41:12 +03:00
r = sd_rtnl_message_new_route ( rtnl , & req , RTM_GETROUTE , af , RTPROT_UNSPEC ) ;
2014-12-03 23:42:58 +03:00
if ( r < 0 )
return r ;
r = sd_rtnl_message_request_dump ( req , true ) ;
if ( r < 0 )
return r ;
r = sd_rtnl_call ( rtnl , req , 0 , & reply ) ;
if ( r < 0 )
return r ;
for ( m = reply ; m ; m = sd_rtnl_message_next ( m ) ) {
struct local_address * a ;
uint16_t type ;
2014-12-04 00:23:06 +03:00
unsigned char dst_len , src_len ;
2014-12-03 23:42:58 +03:00
uint32_t ifi ;
2014-12-04 03:41:12 +03:00
int family ;
2014-12-03 23:42:58 +03:00
r = sd_rtnl_message_get_errno ( m ) ;
if ( r < 0 )
return r ;
r = sd_rtnl_message_get_type ( m , & type ) ;
if ( r < 0 )
return r ;
if ( type ! = RTM_NEWROUTE )
continue ;
2014-12-04 00:23:06 +03:00
/* We only care for default routes */
2014-12-04 18:14:06 +03:00
r = sd_rtnl_message_route_get_dst_prefixlen ( m , & dst_len ) ;
2014-12-03 23:42:58 +03:00
if ( r < 0 )
return r ;
if ( dst_len ! = 0 )
continue ;
2014-12-04 18:14:06 +03:00
r = sd_rtnl_message_route_get_src_prefixlen ( m , & src_len ) ;
2014-12-04 00:23:06 +03:00
if ( r < 0 )
return r ;
if ( src_len ! = 0 )
continue ;
2014-12-03 23:42:58 +03:00
r = sd_rtnl_message_read_u32 ( m , RTA_OIF , & ifi ) ;
if ( r < 0 )
return r ;
if ( ifindex > 0 & & ( int ) ifi ! = ifindex )
continue ;
2014-12-04 03:41:12 +03:00
r = sd_rtnl_message_route_get_family ( m , & family ) ;
if ( r < 0 )
return r ;
if ( af ! = AF_UNSPEC & & af ! = family )
continue ;
2014-12-03 23:42:58 +03:00
if ( ! GREEDY_REALLOC0 ( list , n_allocated , n_list + 1 ) )
return - ENOMEM ;
a = list + n_list ;
2014-12-04 03:41:12 +03:00
switch ( family ) {
2014-12-03 23:42:58 +03:00
case AF_INET :
r = sd_rtnl_message_read_in_addr ( m , RTA_GATEWAY , & a - > address . in ) ;
if ( r < 0 )
continue ;
break ;
case AF_INET6 :
r = sd_rtnl_message_read_in6_addr ( m , RTA_GATEWAY , & a - > address . in6 ) ;
if ( r < 0 )
continue ;
break ;
default :
continue ;
}
sd_rtnl_message_read_u32 ( m , RTA_PRIORITY , & a - > metric ) ;
a - > ifindex = ifi ;
2014-12-04 03:41:12 +03:00
a - > family = family ;
2014-12-03 23:42:58 +03:00
2014-12-04 03:41:12 +03:00
n_list + + ;
2014-12-03 23:42:58 +03:00
}
if ( n_list > 0 )
2014-07-10 23:01:25 +04:00
qsort ( list , n_list , sizeof ( struct local_address ) , address_compare ) ;
2013-03-30 18:46:04 +04:00
2014-07-10 23:01:25 +04:00
* ret = list ;
2014-04-14 14:46:09 +04:00
list = NULL ;
2013-03-30 18:46:04 +04:00
2014-07-10 23:01:25 +04:00
return ( int ) n_list ;
2011-05-09 16:57:15 +04:00
}