2013-10-28 23:59:56 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright ( C ) 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/>.
* * */
# include <netinet/ether.h>
2013-11-25 04:33:04 +04:00
# include <linux/if.h>
2013-11-10 01:19:42 +04:00
# include <arpa/inet.h>
2014-02-21 01:55:42 +04:00
# include <fnmatch.h>
2013-10-28 23:59:56 +04:00
2014-03-21 22:23:35 +04:00
# include "strv.h"
# include "siphash24.h"
# include "libudev-private.h"
2014-03-22 00:38:14 +04:00
# include "network-internal.h"
2013-11-02 05:13:48 +04:00
# include "log.h"
2013-10-28 23:59:56 +04:00
# include "utf8.h"
2013-10-29 18:59:45 +04:00
# include "util.h"
2013-10-28 23:59:56 +04:00
# include "conf-parser.h"
2014-02-20 22:39:49 +04:00
# include "condition.h"
2013-10-28 23:59:56 +04:00
2014-03-21 22:23:35 +04:00
# define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
int net_get_unique_predictable_data ( struct udev_device * device , uint8_t result [ 8 ] ) {
size_t l , sz = 0 ;
2014-03-27 23:13:11 +04:00
const char * name = NULL , * field = NULL ;
2014-03-21 22:23:35 +04:00
int r ;
uint8_t * v ;
/* fetch some persistent data unique (on this machine) to this device */
FOREACH_STRING ( field , " ID_NET_NAME_ONBOARD " , " ID_NET_NAME_SLOT " , " ID_NET_NAME_PATH " , " ID_NET_NAME_MAC " ) {
name = udev_device_get_property_value ( device , field ) ;
if ( name )
break ;
}
if ( ! name )
return - ENOENT ;
l = strlen ( name ) ;
sz = sizeof ( sd_id128_t ) + l ;
v = alloca ( sz ) ;
/* fetch some persistent data unique to this machine */
r = sd_id128_get_machine ( ( sd_id128_t * ) v ) ;
if ( r < 0 )
return r ;
memcpy ( v + sizeof ( sd_id128_t ) , name , l ) ;
/* Let's hash the machine ID plus the device name. We
* use a fixed , but originally randomly created hash
* key here . */
siphash24 ( result , v , sz , HASH_KEY . bytes ) ;
return 0 ;
}
2013-11-02 05:13:48 +04:00
bool net_match_config ( const struct ether_addr * match_mac ,
const char * match_path ,
const char * match_driver ,
const char * match_type ,
const char * match_name ,
2014-02-20 22:39:49 +04:00
Condition * match_host ,
Condition * match_virt ,
Condition * match_kernel ,
2014-02-21 17:51:19 +04:00
Condition * match_arch ,
2014-04-15 16:21:44 +04:00
const struct ether_addr * dev_mac ,
2013-11-05 04:35:26 +04:00
const char * dev_path ,
2014-02-22 01:29:25 +04:00
const char * dev_parent_driver ,
2013-11-05 04:35:26 +04:00
const char * dev_driver ,
const char * dev_type ,
const char * dev_name ) {
2013-11-02 05:13:48 +04:00
2014-02-20 22:39:49 +04:00
if ( match_host & & ! condition_test_host ( match_host ) )
return 0 ;
if ( match_virt & & ! condition_test_virtualization ( match_virt ) )
return 0 ;
if ( match_kernel & & ! condition_test_kernel_command_line ( match_kernel ) )
return 0 ;
2014-02-21 17:51:19 +04:00
if ( match_arch & & ! condition_test_architecture ( match_arch ) )
return 0 ;
2014-04-15 16:21:44 +04:00
if ( match_mac & & ( ! dev_mac | | memcmp ( match_mac , dev_mac , ETH_ALEN ) ) )
2014-01-02 18:30:46 +04:00
return 0 ;
2013-11-02 05:13:48 +04:00
2014-02-21 01:55:42 +04:00
if ( match_path & & ( ! dev_path | | fnmatch ( match_path , dev_path , 0 ) ) )
2014-01-02 18:30:46 +04:00
return 0 ;
2013-11-02 05:13:48 +04:00
2014-02-22 01:29:25 +04:00
if ( match_driver ) {
if ( dev_parent_driver & & ! streq ( match_driver , dev_parent_driver ) )
return 0 ;
else if ( ! streq_ptr ( match_driver , dev_driver ) )
return 0 ;
}
2013-10-28 23:59:56 +04:00
2014-01-04 07:22:06 +04:00
if ( match_type & & ! streq_ptr ( match_type , dev_type ) )
2014-01-02 18:30:46 +04:00
return 0 ;
2013-10-28 23:59:56 +04:00
2014-02-22 01:29:25 +04:00
if ( match_name & & ( ! dev_name | | fnmatch ( match_name , dev_name , 0 ) ) )
2014-01-02 18:30:46 +04:00
return 0 ;
2013-10-28 23:59:56 +04:00
2013-11-02 05:13:48 +04:00
return 1 ;
}
2013-10-28 23:59:56 +04:00
2014-01-29 02:23:31 +04:00
unsigned net_netmask_to_prefixlen ( const struct in_addr * addr ) {
assert ( addr ) ;
2014-01-30 20:23:34 +04:00
return 32 - u32ctz ( be32toh ( addr - > s_addr ) ) ;
2014-01-29 02:23:31 +04:00
}
2014-02-20 22:39:49 +04:00
int config_parse_net_condition ( const char * unit ,
const char * filename ,
unsigned line ,
const char * section ,
unsigned section_line ,
const char * lvalue ,
int ltype ,
const char * rvalue ,
void * data ,
void * userdata ) {
ConditionType cond = ltype ;
Condition * * ret = data ;
bool negate ;
Condition * c ;
_cleanup_free_ char * s = NULL ;
assert ( filename ) ;
assert ( lvalue ) ;
assert ( rvalue ) ;
assert ( data ) ;
negate = rvalue [ 0 ] = = ' ! ' ;
if ( negate )
rvalue + + ;
s = strdup ( rvalue ) ;
if ( ! s )
return log_oom ( ) ;
c = condition_new ( cond , s , false , negate ) ;
if ( ! c )
return log_oom ( ) ;
if ( * ret )
condition_free ( * ret ) ;
* ret = c ;
return 0 ;
}
2013-10-28 23:59:56 +04:00
int config_parse_ifname ( const char * unit ,
const char * filename ,
unsigned line ,
const char * section ,
2013-11-19 19:17:55 +04:00
unsigned section_line ,
2013-10-28 23:59:56 +04:00
const char * lvalue ,
int ltype ,
const char * rvalue ,
void * data ,
void * userdata ) {
char * * s = data ;
char * n ;
assert ( filename ) ;
assert ( lvalue ) ;
assert ( rvalue ) ;
assert ( data ) ;
n = strdup ( rvalue ) ;
if ( ! n )
return log_oom ( ) ;
if ( ! ascii_is_valid ( n ) | | strlen ( n ) > = IFNAMSIZ ) {
log_syntax ( unit , LOG_ERR , filename , line , EINVAL ,
" Interface name is not ASCII clean or is too long, ignoring assignment: %s " , rvalue ) ;
free ( n ) ;
return 0 ;
}
free ( * s ) ;
if ( * n )
* s = n ;
else {
free ( n ) ;
* s = NULL ;
}
return 0 ;
}
2013-11-25 04:33:04 +04:00
int config_parse_ifalias ( const char * unit ,
const char * filename ,
unsigned line ,
const char * section ,
2013-11-19 19:17:55 +04:00
unsigned section_line ,
2013-11-25 04:33:04 +04:00
const char * lvalue ,
int ltype ,
const char * rvalue ,
void * data ,
void * userdata ) {
char * * s = data ;
char * n ;
assert ( filename ) ;
assert ( lvalue ) ;
assert ( rvalue ) ;
assert ( data ) ;
n = strdup ( rvalue ) ;
if ( ! n )
return log_oom ( ) ;
if ( ! ascii_is_valid ( n ) | | strlen ( n ) > = IFALIASZ ) {
log_syntax ( unit , LOG_ERR , filename , line , EINVAL ,
" Interface alias is not ASCII clean or is too long, ignoring assignment: %s " , rvalue ) ;
free ( n ) ;
return 0 ;
}
free ( * s ) ;
if ( * n )
* s = n ;
else {
free ( n ) ;
* s = NULL ;
}
return 0 ;
}
2013-10-28 23:59:56 +04:00
int config_parse_hwaddr ( const char * unit ,
const char * filename ,
unsigned line ,
const char * section ,
2013-11-19 19:17:55 +04:00
unsigned section_line ,
2013-10-28 23:59:56 +04:00
const char * lvalue ,
int ltype ,
const char * rvalue ,
void * data ,
void * userdata ) {
struct ether_addr * * hwaddr = data ;
struct ether_addr * n ;
int r ;
assert ( filename ) ;
assert ( lvalue ) ;
assert ( rvalue ) ;
assert ( data ) ;
2013-10-29 18:59:45 +04:00
n = new0 ( struct ether_addr , 1 ) ;
2013-10-28 23:59:56 +04:00
if ( ! n )
return log_oom ( ) ;
r = sscanf ( rvalue , " %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx " ,
& n - > ether_addr_octet [ 0 ] ,
& n - > ether_addr_octet [ 1 ] ,
& n - > ether_addr_octet [ 2 ] ,
& n - > ether_addr_octet [ 3 ] ,
& n - > ether_addr_octet [ 4 ] ,
& n - > ether_addr_octet [ 5 ] ) ;
if ( r ! = 6 ) {
log_syntax ( unit , LOG_ERR , filename , line , EINVAL ,
" Not a valid MAC address, ignoring assignment: %s " , rvalue ) ;
free ( n ) ;
return 0 ;
}
free ( * hwaddr ) ;
* hwaddr = n ;
return 0 ;
}
2013-11-10 01:19:42 +04:00
int net_parse_inaddr ( const char * address , unsigned char * family , void * dst ) {
int r ;
assert ( address ) ;
assert ( family ) ;
assert ( dst ) ;
/* IPv4 */
r = inet_pton ( AF_INET , address , dst ) ;
2014-01-28 23:00:47 +04:00
if ( r > 0 ) {
/* succsefully parsed IPv4 address */
if ( * family = = AF_UNSPEC )
* family = AF_INET ;
else if ( * family ! = AF_INET )
return - EINVAL ;
} else if ( r < 0 )
2013-11-10 01:19:42 +04:00
return - errno ;
else {
/* not an IPv4 address, so let's try IPv6 */
r = inet_pton ( AF_INET6 , address , dst ) ;
2014-01-28 23:00:47 +04:00
if ( r > 0 ) {
/* successfully parsed IPv6 address */
if ( * family = = AF_UNSPEC )
* family = AF_INET6 ;
else if ( * family ! = AF_INET6 )
return - EINVAL ;
} else if ( r < 0 )
2013-11-10 01:19:42 +04:00
return - errno ;
else
return - EINVAL ;
}
return 0 ;
}