2013-10-17 05:18:36 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2013 Tom Gundersen < teg @ jklm . no >
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/>.
* * */
2014-01-06 02:01:10 +04:00
# include <resolv.h>
2013-10-17 05:18:36 +04:00
# include "path-util.h"
# include "networkd.h"
# include "libudev-private.h"
2013-12-13 06:30:42 +04:00
# include "udev-util.h"
2014-01-30 00:24:44 +04:00
# include "rtnl-util.h"
2014-01-06 02:01:10 +04:00
# include "mkdir.h"
2013-10-17 05:18:36 +04:00
2014-01-09 02:41:41 +04:00
const char * const network_dirs [ ] = {
" /etc/systemd/network " ,
" /run/systemd/network " ,
" /usr/lib/systemd/network " ,
# ifdef HAVE_SPLIT_USER
" /lib/systemd/network " ,
# endif
NULL } ;
2013-10-17 05:18:36 +04:00
int manager_new ( Manager * * ret ) {
_cleanup_manager_free_ Manager * m = NULL ;
int r ;
m = new0 ( Manager , 1 ) ;
if ( ! m )
return - ENOMEM ;
2013-11-11 22:34:13 +04:00
r = sd_event_default ( & m - > event ) ;
2013-10-17 05:18:36 +04:00
if ( r < 0 )
return r ;
2013-12-11 21:14:52 +04:00
sd_event_set_watchdog ( m - > event , true ) ;
2013-12-03 21:48:20 +04:00
r = sd_rtnl_open ( RTMGRP_LINK | RTMGRP_IPV4_IFADDR , & m - > rtnl ) ;
2013-10-17 05:18:36 +04:00
if ( r < 0 )
return r ;
2014-01-14 02:48:28 +04:00
r = sd_bus_default_system ( & m - > bus ) ;
2014-01-18 04:37:35 +04:00
if ( r < 0 & & r ! = - ENOENT ) /* TODO: drop when we can rely on kdbus */
2014-01-14 02:48:28 +04:00
return r ;
2013-10-17 05:18:36 +04:00
m - > udev = udev_new ( ) ;
if ( ! m - > udev )
return - ENOMEM ;
m - > udev_monitor = udev_monitor_new_from_netlink ( m - > udev , " udev " ) ;
if ( ! m - > udev_monitor )
return - ENOMEM ;
m - > links = hashmap_new ( uint64_hash_func , uint64_compare_func ) ;
if ( ! m - > links )
return - ENOMEM ;
2014-01-22 00:58:08 +04:00
m - > netdevs = hashmap_new ( string_hash_func , string_compare_func ) ;
if ( ! m - > netdevs )
2013-11-25 02:37:56 +04:00
return - ENOMEM ;
2013-10-17 05:18:36 +04:00
LIST_HEAD_INIT ( m - > networks ) ;
* ret = m ;
m = NULL ;
return 0 ;
}
void manager_free ( Manager * m ) {
2013-11-18 14:54:09 +04:00
Network * network ;
2014-02-07 20:03:23 +04:00
NetDev * netdev ;
2013-11-18 14:54:09 +04:00
Link * link ;
2013-10-17 05:18:36 +04:00
udev_monitor_unref ( m - > udev_monitor ) ;
udev_unref ( m - > udev ) ;
2014-01-14 02:48:28 +04:00
sd_bus_unref ( m - > bus ) ;
2013-10-17 05:18:36 +04:00
sd_event_source_unref ( m - > udev_event_source ) ;
sd_event_unref ( m - > event ) ;
2013-11-18 14:54:09 +04:00
while ( ( network = m - > networks ) )
network_free ( network ) ;
while ( ( link = hashmap_first ( m - > links ) ) )
link_free ( link ) ;
2013-10-17 05:18:36 +04:00
hashmap_free ( m - > links ) ;
2013-11-18 14:54:09 +04:00
2014-01-22 00:58:08 +04:00
while ( ( netdev = hashmap_first ( m - > netdevs ) ) )
netdev_free ( netdev ) ;
hashmap_free ( m - > netdevs ) ;
2013-11-25 02:37:56 +04:00
2013-10-17 05:18:36 +04:00
sd_rtnl_unref ( m - > rtnl ) ;
free ( m ) ;
}
2013-11-25 02:37:56 +04:00
int manager_load_config ( Manager * m ) {
int r ;
/* update timestamp */
2014-01-09 02:41:41 +04:00
paths_check_timestamp ( network_dirs , & m - > network_dirs_ts_usec , true ) ;
2013-11-25 02:37:56 +04:00
2014-01-22 00:58:08 +04:00
r = netdev_load ( m ) ;
2013-11-25 02:37:56 +04:00
if ( r < 0 )
return r ;
r = network_load ( m ) ;
if ( r < 0 )
return r ;
return 0 ;
}
bool manager_should_reload ( Manager * m ) {
2014-01-09 02:41:41 +04:00
return paths_check_timestamp ( network_dirs , & m - > network_dirs_ts_usec , false ) ;
2013-11-25 02:37:56 +04:00
}
2013-10-17 05:18:36 +04:00
static int manager_process_link ( Manager * m , struct udev_device * device ) {
Link * link ;
int r ;
if ( streq_ptr ( udev_device_get_action ( device ) , " remove " ) ) {
uint64_t ifindex ;
2014-01-02 18:30:46 +04:00
log_debug ( " %s: link removed " , udev_device_get_sysname ( device ) ) ;
2013-11-21 18:30:08 +04:00
2013-10-17 05:18:36 +04:00
ifindex = udev_device_get_ifindex ( device ) ;
link = hashmap_get ( m - > links , & ifindex ) ;
if ( ! link )
return 0 ;
link_free ( link ) ;
} else {
2014-01-03 21:20:11 +04:00
r = link_add ( m , device , & link ) ;
2013-10-17 05:18:36 +04:00
if ( r < 0 ) {
2014-01-03 19:38:18 +04:00
if ( r = = - EEXIST )
log_debug ( " %s: link already exists, ignoring " ,
2014-01-03 21:20:11 +04:00
link - > ifname ) ;
2014-01-03 19:38:18 +04:00
else
log_error ( " %s: could not handle link: %s " ,
udev_device_get_sysname ( device ) ,
strerror ( - r ) ) ;
} else
2014-01-03 21:20:11 +04:00
log_debug ( " %s: link (with ifindex % " PRIu64 " ) added " ,
link - > ifname , link - > ifindex ) ;
2013-10-17 05:18:36 +04:00
}
return 0 ;
}
int manager_udev_enumerate_links ( Manager * m ) {
2013-12-18 06:37:26 +04:00
_cleanup_udev_enumerate_unref_ struct udev_enumerate * e = NULL ;
2013-10-17 05:18:36 +04:00
struct udev_list_entry * item = NULL , * first = NULL ;
int r ;
assert ( m ) ;
e = udev_enumerate_new ( m - > udev ) ;
2013-12-18 06:37:26 +04:00
if ( ! e )
return - ENOMEM ;
2013-10-17 05:18:36 +04:00
r = udev_enumerate_add_match_subsystem ( e , " net " ) ;
if ( r < 0 )
2013-12-18 06:37:26 +04:00
return r ;
2013-10-17 05:18:36 +04:00
2013-12-18 20:12:15 +04:00
r = udev_enumerate_add_match_is_initialized ( e ) ;
if ( r < 0 )
return r ;
2013-10-17 05:18:36 +04:00
r = udev_enumerate_scan_devices ( e ) ;
if ( r < 0 )
2013-12-18 06:37:26 +04:00
return r ;
2013-10-17 05:18:36 +04:00
first = udev_enumerate_get_list_entry ( e ) ;
udev_list_entry_foreach ( item , first ) {
2013-12-18 06:37:26 +04:00
_cleanup_udev_device_unref_ struct udev_device * d = NULL ;
2013-10-17 05:18:36 +04:00
int k ;
d = udev_device_new_from_syspath ( m - > udev , udev_list_entry_get_name ( item ) ) ;
2013-12-18 06:37:26 +04:00
if ( ! d )
return - ENOMEM ;
2013-10-17 05:18:36 +04:00
2013-12-18 06:37:26 +04:00
k = manager_process_link ( m , d ) ;
2013-10-17 05:18:36 +04:00
if ( k < 0 )
r = k ;
}
return r ;
}
static int manager_dispatch_link_udev ( sd_event_source * source , int fd , uint32_t revents , void * userdata ) {
Manager * m = userdata ;
struct udev_monitor * monitor = m - > udev_monitor ;
2013-12-13 06:30:42 +04:00
_cleanup_udev_device_unref_ struct udev_device * device = NULL ;
2013-10-17 05:18:36 +04:00
device = udev_monitor_receive_device ( monitor ) ;
if ( ! device )
return - ENOMEM ;
2013-12-13 06:30:42 +04:00
manager_process_link ( m , device ) ;
2013-10-17 05:18:36 +04:00
return 0 ;
}
int manager_udev_listen ( Manager * m ) {
int r ;
r = udev_monitor_filter_add_match_subsystem_devtype ( m - > udev_monitor , " net " , NULL ) ;
if ( r < 0 ) {
log_error ( " Could not add udev monitor filter: %s " , strerror ( - r ) ) ;
return r ;
}
r = udev_monitor_enable_receiving ( m - > udev_monitor ) ;
if ( r < 0 ) {
log_error ( " Could not enable udev monitor " ) ;
return r ;
}
r = sd_event_add_io ( m - > event ,
udev_monitor_get_fd ( m - > udev_monitor ) ,
EPOLLIN , manager_dispatch_link_udev ,
m , & m - > udev_event_source ) ;
if ( r < 0 )
return r ;
return 0 ;
}
2013-11-14 19:22:51 +04:00
2013-12-03 21:48:20 +04:00
static int manager_rtnl_process_link ( sd_rtnl * rtnl , sd_rtnl_message * message , void * userdata ) {
Manager * m = userdata ;
Link * link ;
2014-01-30 00:24:44 +04:00
const char * name ;
2014-01-03 21:27:26 +04:00
uint64_t ifindex_64 ;
2013-12-03 21:48:20 +04:00
int r , ifindex ;
r = sd_rtnl_message_link_get_ifindex ( message , & ifindex ) ;
2014-01-30 00:24:44 +04:00
if ( r < 0 | | ifindex < = 0 ) {
2014-01-03 19:02:12 +04:00
log_debug ( " received RTM_NEWLINK message without valid ifindex " ) ;
2013-12-03 21:48:20 +04:00
return 0 ;
2014-01-03 19:02:12 +04:00
}
2013-12-03 21:48:20 +04:00
2014-01-30 00:24:44 +04:00
r = rtnl_message_link_get_ifname ( message , & name ) ;
if ( r < 0 )
log_debug ( " received RTM_NEWLINK message without valid IFLA_IFNAME " ) ;
else {
2014-02-07 20:03:23 +04:00
NetDev * netdev ;
2014-01-30 00:24:44 +04:00
r = netdev_get ( m , name , & netdev ) ;
if ( r > = 0 ) {
r = netdev_set_ifindex ( netdev , ifindex ) ;
if ( r < 0 )
log_debug ( " could not set ifindex of netdev '%s' to %d: %s " ,
name , ifindex , strerror ( - r ) ) ;
}
}
2014-01-03 21:27:26 +04:00
ifindex_64 = ifindex ;
link = hashmap_get ( m - > links , & ifindex_64 ) ;
2014-01-03 19:02:12 +04:00
if ( ! link ) {
2014-01-03 21:27:26 +04:00
log_debug ( " received RTM_NEWLINK message for untracked ifindex %d " , ifindex ) ;
2013-12-03 21:48:20 +04:00
return 0 ;
2014-01-03 19:02:12 +04:00
}
2013-12-03 21:48:20 +04:00
2014-01-02 18:56:10 +04:00
/* only track the status of links we want to manage */
if ( link - > network ) {
r = link_update ( link , message ) ;
if ( r < 0 )
return 0 ;
2014-01-03 19:02:12 +04:00
} else
2014-01-03 21:27:26 +04:00
log_debug ( " %s: received RTM_NEWLINK message for unmanaged link " , link - > ifname ) ;
2013-12-03 21:48:20 +04:00
return 1 ;
}
2013-11-14 19:22:51 +04:00
int manager_rtnl_listen ( Manager * m ) {
int r ;
r = sd_rtnl_attach_event ( m - > rtnl , m - > event , 0 ) ;
if ( r < 0 )
return r ;
2013-12-03 21:48:20 +04:00
r = sd_rtnl_add_match ( m - > rtnl , RTM_NEWLINK , & manager_rtnl_process_link , m ) ;
if ( r < 0 )
return r ;
2013-11-14 19:22:51 +04:00
return 0 ;
}
2014-01-06 02:01:10 +04:00
2014-01-14 02:48:28 +04:00
int manager_bus_listen ( Manager * m ) {
int r ;
2014-01-18 04:37:35 +04:00
assert ( m - > event ) ;
if ( ! m - > bus ) /* TODO: drop when we can rely on kdbus */
return 0 ;
2014-01-14 02:48:28 +04:00
r = sd_bus_attach_event ( m - > bus , m - > event , 0 ) ;
if ( r < 0 )
return r ;
return 0 ;
}
2014-01-06 02:01:10 +04:00
static void append_dns ( FILE * f , struct in_addr * dns , unsigned char family , unsigned * count ) {
char buf [ INET6_ADDRSTRLEN ] ;
const char * address ;
address = inet_ntop ( family , dns , buf , INET6_ADDRSTRLEN ) ;
if ( ! address ) {
log_warning ( " Invalid DNS address. Ignoring. " ) ;
return ;
}
if ( * count = = MAXNS )
2014-01-12 18:44:40 +04:00
fputs ( " # Too many DNS servers configured, the following entries "
" will be ignored \n " , f ) ;
2014-01-06 02:01:10 +04:00
fprintf ( f , " nameserver %s \n " , address ) ;
( * count ) + + ;
}
int manager_update_resolv_conf ( Manager * m ) {
_cleanup_free_ char * temp_path = NULL ;
_cleanup_fclose_ FILE * f = NULL ;
Link * link ;
Iterator i ;
unsigned count = 0 ;
2014-01-18 18:47:57 +04:00
const char * domainname = NULL ;
2014-01-06 02:01:10 +04:00
int r ;
assert ( m ) ;
r = mkdir_safe_label ( " /run/systemd/network " , 0755 , 0 , 0 ) ;
if ( r < 0 )
return r ;
r = fopen_temporary ( " /run/systemd/network/resolv.conf " , & f , & temp_path ) ;
if ( r < 0 )
return r ;
fchmod ( fileno ( f ) , 0644 ) ;
2014-01-17 20:09:56 +04:00
fputs ( " # This file is managed by systemd-networkd(8). Do not edit. \n # \n "
" # Third party programs must not access this file directly, but \n "
" # only through the symlink at /etc/resolv.conf. To manage \n "
" # resolv.conf(5) in a different way, replace the symlink by a \n "
" # static file or a different symlink. \n \n " , f ) ;
2014-01-06 02:01:10 +04:00
HASHMAP_FOREACH ( link , m - > links , i ) {
2014-02-05 02:13:52 +04:00
if ( link - > dhcp_lease ) {
2014-01-16 22:32:22 +04:00
struct in_addr * nameservers ;
size_t nameservers_size ;
2014-01-06 02:01:10 +04:00
2014-01-18 18:47:57 +04:00
if ( link - > network - > dhcp_dns ) {
2014-02-05 02:13:52 +04:00
r = sd_dhcp_lease_get_dns ( link - > dhcp_lease , & nameservers , & nameservers_size ) ;
2014-01-18 18:47:57 +04:00
if ( r > = 0 ) {
unsigned j ;
2014-01-06 02:01:10 +04:00
2014-01-18 18:47:57 +04:00
for ( j = 0 ; j < nameservers_size ; j + + )
append_dns ( f , & nameservers [ j ] , AF_INET , & count ) ;
}
}
if ( link - > network - > dhcp_domainname & & ! domainname ) {
2014-02-05 02:13:52 +04:00
r = sd_dhcp_lease_get_domainname ( link - > dhcp_lease , & domainname ) ;
2014-01-18 18:47:57 +04:00
if ( r > = 0 )
fprintf ( f , " domain %s \n " , domainname ) ;
2014-01-06 02:01:10 +04:00
}
}
}
HASHMAP_FOREACH ( link , m - > links , i )
if ( link - > network & & link - > network - > dns )
append_dns ( f , & link - > network - > dns - > in_addr . in ,
link - > network - > dns - > family , & count ) ;
fflush ( f ) ;
if ( ferror ( f ) | | rename ( temp_path , " /run/systemd/network/resolv.conf " ) < 0 ) {
r = - errno ;
unlink ( " /run/systemd/network/resolv.conf " ) ;
unlink ( temp_path ) ;
return r ;
}
return 0 ;
}