ifcfg-rh: add IPv6 addressing and routes support (rh #523288)

This commit is contained in:
Jirka Klimes 2010-01-05 19:05:01 -06:00 committed by Dan Williams
parent 3b0255f73c
commit 686425adce
19 changed files with 1553 additions and 19 deletions

View file

@ -26,6 +26,7 @@
#define IFCFG_TAG "ifcfg-"
#define KEYS_TAG "keys-"
#define ROUTE_TAG "route-"
#define ROUTE6_TAG "route6-"
#define BAK_TAG ".bak"
#define TILDE_TAG "~"

View file

@ -63,6 +63,9 @@ typedef struct {
char *routefile;
int routefile_wd;
char *route6file;
int route6file_wd;
char *udi;
char *unmanaged;
} NMIfcfgConnectionPrivate;
@ -93,7 +96,7 @@ files_changed_cb (NMInotifyHelper *ih,
NMIfcfgConnection *self = NM_IFCFG_CONNECTION (user_data);
NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
if ((evt->wd != priv->file_wd) && (evt->wd != priv->keyfile_wd) && (evt->wd != priv->routefile_wd))
if ((evt->wd != priv->file_wd) && (evt->wd != priv->keyfile_wd) && (evt->wd != priv->routefile_wd) && (evt->wd != priv->route6file_wd))
return;
/* push the event up to the plugin */
@ -111,11 +114,12 @@ nm_ifcfg_connection_new (const char *filename,
char *unmanaged = NULL;
char *keyfile = NULL;
char *routefile = NULL;
char *route6file = NULL;
NMInotifyHelper *ih;
g_return_val_if_fail (filename != NULL, NULL);
tmp = connection_from_file (filename, NULL, NULL, NULL, &unmanaged, &keyfile, &routefile, error, ignore_error);
tmp = connection_from_file (filename, NULL, NULL, NULL, &unmanaged, &keyfile, &routefile, &route6file, error, ignore_error);
if (!tmp)
return NULL;
@ -145,6 +149,9 @@ nm_ifcfg_connection_new (const char *filename,
priv->routefile = routefile;
priv->routefile_wd = nm_inotify_helper_add_watch (ih, routefile);
priv->route6file = route6file;
priv->route6file_wd = nm_inotify_helper_add_watch (ih, route6file);
return NM_IFCFG_CONNECTION (object);
}
@ -176,7 +183,6 @@ update (NMSettingsConnectionInterface *connection,
IFCFG_DIR,
priv->filename,
priv->keyfile,
priv->routefile,
&error)) {
callback (connection, error, user_data);
g_error_free (error);
@ -199,6 +205,9 @@ do_delete (NMSettingsConnectionInterface *connection,
if (priv->routefile)
g_unlink (priv->routefile);
if (priv->route6file)
g_unlink (priv->route6file);
return parent_settings_connection_iface->delete (connection, callback, user_data);
}
@ -243,6 +252,10 @@ finalize (GObject *object)
if (priv->routefile_wd >= 0)
nm_inotify_helper_remove_watch (ih, priv->routefile_wd);
g_free (priv->route6file);
if (priv->route6file_wd >= 0)
nm_inotify_helper_remove_watch (ih, priv->route6file_wd);
G_OBJECT_CLASS (nm_ifcfg_connection_parent_class)->finalize (object);
}

View file

@ -45,6 +45,7 @@
#include <NetworkManager.h>
#include <nm-setting-connection.h>
#include <nm-setting-ip4-config.h>
#include <nm-setting-ip6-config.h>
#include <nm-setting-wired.h>
#include <nm-setting-wireless.h>
#include <nm-setting-8021x.h>
@ -494,6 +495,31 @@ read_ip4_address (shvarFile *ifcfg,
return success;
}
static gboolean
parse_ip6_address (const char *value,
struct in6_addr *out_addr,
GError **error)
{
struct in6_addr ip6_addr;
gboolean success = FALSE;
g_return_val_if_fail (value != NULL, FALSE);
g_return_val_if_fail (out_addr != NULL, FALSE);
g_return_val_if_fail (error != NULL, FALSE);
g_return_val_if_fail (*error == NULL, FALSE);
*out_addr = in6addr_any;
if (inet_pton (AF_INET6, value, &ip6_addr) > 0) {
*out_addr = ip6_addr;
success = TRUE;
} else {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Invalid IP6 address '%s'", value);
}
return success;
}
static NMIP4Address *
read_full_ip4_address (shvarFile *ifcfg,
const char *network_file,
@ -851,6 +877,227 @@ error:
return success;
}
static NMIP6Address *
parse_full_ip6_address (const char *addr_str, GError **error)
{
NMIP6Address *addr;
char **list;
char *ip_tag, *prefix_tag;
struct in6_addr tmp = IN6ADDR_ANY_INIT;
gboolean success = FALSE;
g_return_val_if_fail (addr_str != NULL, NULL);
g_return_val_if_fail (error != NULL, NULL);
g_return_val_if_fail (*error == NULL, NULL);
/* Split the adddress and prefix */
list = g_strsplit_set (addr_str, "/", 2);
if (g_strv_length (list) < 1) {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Invalid IP6 address '%s'", addr_str);
goto error;
}
ip_tag = list[0];
prefix_tag = list[1];
addr = nm_ip6_address_new ();
/* IP address */
if (ip_tag) {
if (!parse_ip6_address (ip_tag, &tmp, error))
goto error;
}
nm_ip6_address_set_address (addr, &tmp);
/* Prefix */
if (prefix_tag) {
long int prefix;
errno = 0;
prefix = strtol (prefix_tag, NULL, 10);
if (errno || prefix <= 0 || prefix > 128) {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Invalid IP6 prefix '%s'", prefix_tag);
goto error;
}
nm_ip6_address_set_prefix (addr, (guint32) prefix);
} else {
/* Missing prefix is treated as prefix of 64 */
nm_ip6_address_set_prefix (addr, 64);
}
success = TRUE;
error:
if (!success) {
nm_ip6_address_unref (addr);
addr = NULL;
}
g_strfreev (list);
return addr;
}
/* IPv6 address is very complex to describe completely by a regular expression,
* so don't try to, rather use looser syntax to comprise all possibilities
* NOTE: The regexes below don't describe all variants allowed by 'ip route add',
* namely destination IP without 'to' keyword is recognized just at line start.
*/
#define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+"
static gboolean
read_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **error)
{
char *contents = NULL;
gsize len = 0;
char **lines = NULL, **iter;
GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric;
GMatchInfo *match_info;
NMIP6Route *route;
struct in6_addr ip6_addr;
char *dest = NULL, *prefix = NULL, *next_hop = NULL, *metric = NULL;
long int prefix_int, metric_int;
gboolean success = FALSE;
const char *pattern_empty = "^\\s*(\\#.*)?$";
const char *pattern_to1 = "^\\s*(" IPV6_ADDR_REGEX "|default)" /* IPv6 or 'default' keyword */
"(?:/(\\d{1,2}))?"; /* optional prefix */
const char *pattern_to2 = "to\\s+(" IPV6_ADDR_REGEX "|default)" /* IPv6 or 'default' keyword */
"(?:/(\\d{1,2}))?"; /* optional prefix */
const char *pattern_via = "via\\s+(" IPV6_ADDR_REGEX ")"; /* IPv6 of gateway */
const char *pattern_metric = "metric\\s+(\\d+)"; /* metric */
g_return_val_if_fail (filename != NULL, FALSE);
g_return_val_if_fail (s_ip6 != NULL, FALSE);
g_return_val_if_fail (error != NULL, FALSE);
g_return_val_if_fail (*error == NULL, FALSE);
/* Read the route file */
if (!g_file_get_contents (filename, &contents, &len, NULL))
return FALSE;
if (len == 0) {
g_free (contents);
return FALSE;
}
/* Create regexes for pieces to be matched */
regex_to1 = g_regex_new (pattern_to1, 0, 0, NULL);
regex_to2 = g_regex_new (pattern_to2, 0, 0, NULL);
regex_via = g_regex_new (pattern_via, 0, 0, NULL);
regex_metric = g_regex_new (pattern_metric, 0, 0, NULL);
/* New NMIP6Route structure */
route = nm_ip6_route_new ();
/* Iterate through file lines */
lines = g_strsplit_set (contents, "\n\r", -1);
for (iter = lines; iter && *iter; iter++) {
/* Skip empty lines */
if (g_regex_match_simple (pattern_empty, *iter, 0, 0))
continue;
/* Destination */
g_regex_match (regex_to1, *iter, 0, &match_info);
if (!g_match_info_matches (match_info)) {
g_match_info_free (match_info);
g_regex_match (regex_to2, *iter, 0, &match_info);
if (!g_match_info_matches (match_info)) {
g_match_info_free (match_info);
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Missing IP6 route destination address in record: '%s'", *iter);
goto error;
}
}
dest = g_match_info_fetch (match_info, 1);
g_match_info_free (match_info);
if (!strcmp (dest, "default"))
strcpy (dest, "::");
if (inet_pton (AF_INET6, dest, &ip6_addr) != 1) {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Invalid IP6 route destination address '%s'", dest);
g_free (dest);
goto error;
}
nm_ip6_route_set_dest (route, &ip6_addr);
g_free (dest);
/* Prefix - is optional; 128 if missing */
prefix = g_match_info_fetch (match_info, 2);
prefix_int = 128;
if (prefix) {
errno = 0;
prefix_int = strtol (prefix, NULL, 10);
if (errno || prefix_int < 0 || prefix_int > 128) {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Invalid IP6 route destination prefix '%s'", prefix);
g_free (prefix);
goto error;
}
}
nm_ip6_route_set_prefix (route, (guint32) prefix_int);
g_free (prefix);
/* Next hop */
g_regex_match (regex_via, *iter, 0, &match_info);
if (!g_match_info_matches (match_info)) {
g_match_info_free (match_info);
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Missing IP6 route gateway address in record: '%s'", *iter);
goto error;
}
next_hop = g_match_info_fetch (match_info, 1);
g_match_info_free (match_info);
if (inet_pton (AF_INET6, next_hop, &ip6_addr) != 1) {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Invalid IP6 route gateway address '%s'", next_hop);
g_free (next_hop);
goto error;
}
nm_ip6_route_set_next_hop (route, &ip6_addr);
g_free (next_hop);
/* Metric */
g_regex_match (regex_metric, *iter, 0, &match_info);
metric_int = 0;
if (g_match_info_matches (match_info)) {
metric = g_match_info_fetch (match_info, 1);
errno = 0;
metric_int = strtol (metric, NULL, 10);
if (errno || metric_int < 0 || metric_int > G_MAXUINT32) {
g_match_info_free (match_info);
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Invalid IP6 route metric '%s'", metric);
g_free (metric);
goto error;
}
g_free (metric);
}
nm_ip6_route_set_metric (route, (guint32) metric_int);
g_match_info_free (match_info);
if (!nm_setting_ip6_config_add_route (s_ip6, route))
PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: duplicate IP6 route");
}
success = TRUE;
error:
g_free (contents);
g_strfreev (lines);
nm_ip6_route_unref (route);
g_regex_unref (regex_to1);
g_regex_unref (regex_to2);
g_regex_unref (regex_via);
g_regex_unref (regex_metric);
return success;
}
static NMSetting *
make_ip4_setting (shvarFile *ifcfg,
@ -990,16 +1237,31 @@ make_ip4_setting (shvarFile *ifcfg,
g_free (value);
}
/* DNS servers */
/* DNS servers
* Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting())
*/
for (i = 1, tmp_success = TRUE; i <= 10 && tmp_success; i++) {
char *tag;
guint32 dns;
struct in6_addr ip6_dns;
GError *tmp_err = NULL;
tag = g_strdup_printf ("DNS%u", i);
tmp_success = read_ip4_address (ifcfg, tag, &dns, error);
if (!tmp_success) {
g_free (tag);
goto error;
/* if it's IPv6, don't exit */
dns = 0;
value = svGetValue (ifcfg, tag, FALSE);
if (value) {
tmp_success = parse_ip6_address (value, &ip6_dns, &tmp_err);
g_clear_error (&tmp_err);
g_free (value);
}
if (!tmp_success) {
g_free (tag);
goto error;
}
g_clear_error (error);
}
if (dns && !nm_setting_ip4_config_add_dns (s_ip4, dns))
@ -1091,6 +1353,202 @@ error:
return NULL;
}
static NMSetting *
make_ip6_setting (shvarFile *ifcfg,
const char *network_file,
const char *iscsiadm_path,
GError **error)
{
NMSettingIP6Config *s_ip6 = NULL;
char *value = NULL;
char *str_value;
char *route6_path = NULL;
gboolean bool_value, ipv6forwarding, ipv6_autoconf;
char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
guint32 i;
shvarFile *network_ifcfg;
gboolean never_default = FALSE, tmp_success;
s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
if (!s_ip6) {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Could not allocate IP6 setting");
return NULL;
}
/* Is IPV6 enabled? Set method to "ignored", when not enabled */
str_value = svGetValue (ifcfg, "IPV6INIT", FALSE);
bool_value = svTrueValue (ifcfg, "IPV6INIT", FALSE);
if (!str_value) {
network_ifcfg = svNewFile (network_file);
if (network_ifcfg) {
bool_value = svTrueValue (network_ifcfg, "IPV6INIT", FALSE);
svCloseFile (network_ifcfg);
}
}
g_free (str_value);
if (!bool_value) {
/* IPv6 is disabled */
g_object_set (s_ip6,
NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
NULL);
return NM_SETTING (s_ip6);
}
/* First check if IPV6_DEFROUTE is set for this device; IPV6_DEFROUTE has the
* opposite meaning from never-default. The default if IPV6_DEFROUTE is not
* specified is IPV6_DEFROUTE=yes which means that this connection can be used
* as a default route
*/
never_default = !svTrueValue (ifcfg, "IPV6_DEFROUTE", TRUE);
/* Then check if IPV6_DEFAULTGW or IPV6_DEFAULTDEV is specified;
* they are global and override IPV6_DEFROUTE
* When both are set, the device specified in IPV6_DEFAULTGW takes preference.
*/
network_ifcfg = svNewFile (network_file);
if (network_ifcfg) {
char *ipv6_defaultgw, *ipv6_defaultdev;
char *default_dev = NULL;
/* Get the connection ifcfg device name and the global default route device */
value = svGetValue (ifcfg, "DEVICE", FALSE);
ipv6_defaultgw = svGetValue (network_ifcfg, "IPV6_DEFAULTGW", FALSE);
ipv6_defaultdev = svGetValue (network_ifcfg, "IPV6_DEFAULTDEV", FALSE);
if (ipv6_defaultgw) {
default_dev = strchr (ipv6_defaultgw, '%');
if (default_dev)
default_dev++;
}
if (!default_dev)
default_dev = ipv6_defaultdev;
/* If there was a global default route device specified, then only connections
* for that device can be the default connection.
*/
if (default_dev && value)
never_default = !!strcmp (value, default_dev);
g_free (ipv6_defaultgw);
g_free (ipv6_defaultdev);
g_free (value);
svCloseFile (network_ifcfg);
}
/* Find out method property */
ipv6forwarding = svTrueValue (ifcfg, "IPV6FORWARDING", FALSE);
ipv6_autoconf = svTrueValue (ifcfg, "IPV6_AUTOCONF", !ipv6forwarding);
if (ipv6_autoconf)
method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
else {
/* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */
str_value = svGetValue (ifcfg, "IPV6ADDR", FALSE);
if (!str_value)
str_value = svGetValue (ifcfg, "IPV6ADDR_SECONDARIES", FALSE);
if (!str_value)
method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
g_free (str_value);
}
/* TODO - handle other methods */
g_object_set (s_ip6,
NM_SETTING_IP6_CONFIG_METHOD, method,
NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS, !svTrueValue (ifcfg, "IPV6_PEERDNS", TRUE),
NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES, !svTrueValue (ifcfg, "IPV6_PEERROUTES", TRUE),
NM_SETTING_IP6_CONFIG_NEVER_DEFAULT, never_default,
NULL);
if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
NMIP6Address *addr;
char *val;
char *ipv6addr, *ipv6addr_secondaries;
char **list = NULL, **iter;
ipv6addr = svGetValue (ifcfg, "IPV6ADDR", FALSE);
ipv6addr_secondaries = svGetValue (ifcfg, "IPV6ADDR_SECONDARIES", FALSE);
val = g_strjoin (ipv6addr && ipv6addr_secondaries ? " " : NULL,
ipv6addr ? ipv6addr : "",
ipv6addr_secondaries ? ipv6addr_secondaries : "",
NULL);
g_free (ipv6addr);
g_free (ipv6addr_secondaries);
list = g_strsplit_set (val, " ", 0);
g_free (val);
for (iter = list; iter && *iter; iter++, i++) {
addr = parse_full_ip6_address (*iter, error);
if (!addr) {
g_strfreev (list);
goto error;
}
if (!nm_setting_ip6_config_add_address (s_ip6, addr))
PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: duplicate IP6 address");
nm_ip6_address_unref (addr);
}
g_strfreev (list);
} else if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
/* TODO - autoconf or DHCPv6 stuff goes here */
}
/* DNS servers
* Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting())
*/
for (i = 1, tmp_success = TRUE; i <= 10 && tmp_success; i++) {
char *tag;
struct in6_addr ip6_dns;
ip6_dns = in6addr_any;
tag = g_strdup_printf ("DNS%u", i);
value = svGetValue (ifcfg, tag, FALSE);
if (value)
tmp_success = parse_ip6_address (value, &ip6_dns, error);
if (!tmp_success) {
struct in_addr ip4_addr;
if (inet_pton (AF_INET, value, &ip4_addr) != 1) {
g_free (tag);
g_free (value);
goto error;
}
/* ignore error - it is IPv4 address */
tmp_success = TRUE;
g_clear_error (error);
}
if (!IN6_IS_ADDR_UNSPECIFIED (&ip6_dns) && !nm_setting_ip6_config_add_dns (s_ip6, &ip6_dns))
PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: duplicate DNS server %s", tag);
g_free (tag);
g_free (value);
}
/* DNS searches ('DOMAIN' key) are read by make_ip4_setting() and included in NMSettingIP4Config */
/* Read static routes from route6-<interface> file */
route6_path = utils_get_route6_path (ifcfg->fileName);
if (!route6_path) {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Could not get route6 file path for '%s'", ifcfg->fileName);
goto error;
}
read_route6_file (route6_path, s_ip6, error);
g_free (route6_path);
if (error && *error)
goto error;
return NM_SETTING (s_ip6);
error:
g_object_unref (s_ip6);
return NULL;
}
static gboolean
add_one_wep_key (shvarFile *ifcfg,
const char *shvar_key,
@ -2575,13 +3033,14 @@ connection_from_file (const char *filename,
char **unmanaged,
char **keyfile,
char **routefile,
char **route6file,
GError **error,
gboolean *ignore_error)
{
NMConnection *connection = NULL;
shvarFile *parsed;
char *type, *nmc = NULL, *bootproto;
NMSetting *s_ip4;
NMSetting *s_ip4, *s_ip6;
const char *ifcfg_name = NULL;
gboolean nm_controlled = TRUE;
@ -2592,6 +3051,8 @@ connection_from_file (const char *filename,
g_return_val_if_fail (*keyfile == NULL, NULL);
g_return_val_if_fail (routefile != NULL, NULL);
g_return_val_if_fail (*routefile == NULL, NULL);
g_return_val_if_fail (route6file != NULL, NULL);
g_return_val_if_fail (*route6file == NULL, NULL);
/* Non-NULL only for unit tests; normally use /etc/sysconfig/network */
if (!network_file)
@ -2694,6 +3155,14 @@ connection_from_file (const char *filename,
nm_connection_add_setting (connection, s_ip4);
}
s_ip6 = make_ip6_setting (parsed, network_file, iscsiadm_path, error);
if (*error) {
g_object_unref (connection);
connection = NULL;
goto done;
} else if (s_ip6)
nm_connection_add_setting (connection, s_ip6);
/* iSCSI / ibft connections are read-only since their settings are
* stored in NVRAM and can only be changed in BIOS.
*/
@ -2716,6 +3185,7 @@ connection_from_file (const char *filename,
*keyfile = utils_get_keys_path (filename);
*routefile = utils_get_route_path (filename);
*route6file = utils_get_route6_path (filename);
done:
svCloseFile (parsed);

View file

@ -33,6 +33,7 @@ NMConnection *connection_from_file (const char *filename,
char **unmanaged,
char **keyfile,
char **routefile,
char **route6file,
GError **error,
gboolean *ignore_error);

View file

@ -52,7 +52,9 @@ EXTRA_DIST = \
ifcfg-test-wired-static-routes \
route-test-wired-static-routes \
ifcfg-test-wired-static-routes-legacy \
route-test-wired-static-routes-legacy
route-test-wired-static-routes-legacy \
ifcfg-test-wired-ipv6-manual \
route6-test-wired-ipv6-manual
check-local:
@for f in $(EXTRA_DIST); do \

View file

@ -7,5 +7,6 @@ BOOTPROTO=dhcp
DEFROUTE=yes
UUID=ba60d05a-7898-820d-c2db-427a88f8f2a5
ONBOOT=yes
IPV6INIT=no
PEERDNS=yes
PEERROUTES=yes

View file

@ -9,3 +9,7 @@ NM_CONTROLLED=yes
PEERDNS=yes
DEFROUTE=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=no

View file

@ -9,3 +9,7 @@ NM_CONTROLLED=yes
PEERDNS=yes
DEFROUTE=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=no

View file

@ -0,0 +1,18 @@
# Intel Corporation 82567LM Gigabit Network Connection
TYPE=Ethernet
DEVICE=eth2
HWADDR=00:11:22:33:44:ee
BOOTPROTO=dhcp
ONBOOT=yes
USERCTL=yes
NM_CONTROLLED=yes
PEERDNS=yes
DNS1=10.2.0.4
DNS2=10.2.0.5
DNS3=1:2:3:4::a
DNS4=1:2:3:4::b
DOMAIN="lorem.com ipsum.org dolor.edu"
IPV6INIT=yes
IPV6_AUTOCONF=no
IPV6ADDR="1001:abba::1234/56"
IPV6ADDR_SECONDARIES="2001:abba::2234/64 3001:abba::3234/96"

View file

@ -7,4 +7,5 @@ ONBOOT=yes
USERCTL=yes
NM_CONTROLLED=yes
PEERDNS=yes
IPV6INIT=yes
IPV6_AUTOCONF=yes

View file

@ -5,7 +5,6 @@ HWADDR=00:11:22:33:44:ee
BOOTPROTO=none
ONBOOT=yes
USERCTL=yes
IPV6INIT=no
MTU=1492
NM_CONTROLLED=yes
DNS1=4.2.2.1
@ -13,3 +12,9 @@ DNS2=4.2.2.2
IPADDR=192.168.1.5
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
IPV6INIT=yes
IPV6_AUTOCONF=no
IPV6ADDR=dead:beaf::1
IPV6ADDR_SECONDARIES="dead:beaf::2/56"
DNS3=1:2:3:4::a
DNS4=1:2:3:4::b

View file

@ -1 +1,4 @@
GATEWAYDEV=eth0
# when devices in IPV6_DEFAULTDEV and IPV6_DEFAULTGW don't match the one in IPV6_DEFAULTGW is prefered
IPV6_DEFAULTDEV=eth4
IPV6_DEFAULTGW=2001::1234%eth0

View file

@ -0,0 +1 @@
9876::1234/96 via 9876::7777 metric 2

File diff suppressed because it is too large Load diff

View file

@ -149,7 +149,8 @@ utils_should_ignore_file (const char *filename, gboolean only_ifcfg)
if (only_ifcfg == FALSE) {
if ( !strncmp (base, KEYS_TAG, strlen (KEYS_TAG))
|| !strncmp (base, ROUTE_TAG, strlen (ROUTE_TAG)))
|| !strncmp (base, ROUTE_TAG, strlen (ROUTE_TAG))
|| !strncmp (base, ROUTE6_TAG, strlen (ROUTE6_TAG)))
is_other = TRUE;
}
@ -208,6 +209,8 @@ utils_get_ifcfg_name (const char *file, gboolean only_ifcfg)
name = start + strlen (KEYS_TAG);
else if (!strncmp (start, ROUTE_TAG, strlen (ROUTE_TAG)))
name = start + strlen (ROUTE_TAG);
else if (!strncmp (start, ROUTE6_TAG, strlen (ROUTE6_TAG)))
name = start + strlen (ROUTE6_TAG);
}
return name;
@ -259,6 +262,12 @@ utils_get_route_path (const char *parent)
return utils_get_extra_path (parent, ROUTE_TAG);
}
char *
utils_get_route6_path (const char *parent)
{
return utils_get_extra_path (parent, ROUTE6_TAG);
}
shvarFile *
utils_get_extra_ifcfg (const char *parent, const char *tag, gboolean should_create)
{
@ -291,9 +300,15 @@ utils_get_route_ifcfg (const char *parent, gboolean should_create)
return utils_get_extra_ifcfg (parent, ROUTE_TAG, should_create);
}
shvarFile *
utils_get_route6_ifcfg (const char *parent, gboolean should_create)
{
return utils_get_extra_ifcfg (parent, ROUTE6_TAG, should_create);
}
/* Finds out if route file has new or older format
* Returns TRUE - new syntax (ADDRESS<n>=a.b.c.d ...), error opening file or empty
* FALSE - legacy syntax (1.2.3.0/24 via 11.22.33.44)
* FALSE - older syntax, i.e. argument to 'ip route add' (1.2.3.0/24 via 11.22.33.44)
*/
gboolean
utils_has_route_file_new_syntax (const char *filename)

View file

@ -38,10 +38,12 @@ gboolean utils_should_ignore_file (const char *filename, gboolean only_ifcfg);
char *utils_get_ifcfg_path (const char *parent);
char *utils_get_keys_path (const char *parent);
char *utils_get_route_path (const char *parent);
char *utils_get_route6_path (const char *parent);
shvarFile *utils_get_extra_ifcfg (const char *parent, const char *tag, gboolean should_create);
shvarFile *utils_get_keys_ifcfg (const char *parent, gboolean should_create);
shvarFile *utils_get_route_ifcfg (const char *parent, gboolean should_create);
shvarFile *utils_get_route6_ifcfg (const char *parent, gboolean should_create);
gboolean utils_has_route_file_new_syntax (const char *filename);

View file

@ -34,6 +34,7 @@
#include <nm-setting-wireless.h>
#include <nm-setting-8021x.h>
#include <nm-setting-ip4-config.h>
#include <nm-setting-ip6-config.h>
#include <nm-setting-pppoe.h>
#include <nm-utils.h>
@ -855,8 +856,10 @@ write_route_file_legacy (const char *filename, NMSettingIP4Config *s_ip4, GError
g_return_val_if_fail (*error == NULL, FALSE);
num = nm_setting_ip4_config_get_num_routes (s_ip4);
if (num == 0)
if (num == 0) {
unlink (filename);
return TRUE;
}
route_items = g_malloc0 (sizeof (char*) * (num + 1));
for (i = 0; i < num; i++) {
@ -1117,6 +1120,213 @@ out:
return success;
}
static gboolean
write_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **error)
{
char dest[INET6_ADDRSTRLEN];
char next_hop[INET6_ADDRSTRLEN];
char **route_items;
char *route_contents;
NMIP6Route *route;
const struct in6_addr *ip;
guint32 prefix, metric;
guint32 i, num;
gboolean success = FALSE;
g_return_val_if_fail (filename != NULL, FALSE);
g_return_val_if_fail (s_ip6 != NULL, FALSE);
g_return_val_if_fail (error != NULL, FALSE);
g_return_val_if_fail (*error == NULL, FALSE);
num = nm_setting_ip6_config_get_num_routes (s_ip6);
if (num == 0) {
unlink (filename);
return TRUE;
}
route_items = g_malloc0 (sizeof (char*) * (num + 1));
for (i = 0; i < num; i++) {
route = nm_setting_ip6_config_get_route (s_ip6, i);
memset (dest, 0, sizeof (dest));
ip = nm_ip6_route_get_dest (route);
inet_ntop (AF_INET6, (const void *) ip, &dest[0], sizeof (dest));
prefix = nm_ip6_route_get_prefix (route);
memset (next_hop, 0, sizeof (next_hop));
ip = nm_ip6_route_get_next_hop (route);
inet_ntop (AF_INET6, (const void *) ip, &next_hop[0], sizeof (next_hop));
metric = nm_ip6_route_get_metric (route);
route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n", dest, prefix, next_hop, metric);
}
route_items[num] = NULL;
route_contents = g_strjoinv (NULL, route_items);
g_strfreev (route_items);
if (!g_file_set_contents (filename, route_contents, -1, NULL)) {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Writing route6 file '%s' failed", filename);
goto error;
}
success = TRUE;
error:
g_free (route_contents);
return success;
}
static gboolean
write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
{
NMSettingIP6Config *s_ip6;
NMSettingIP4Config *s_ip4;
const char *value;
char *addr_key, *prefix;
guint32 i, num, num4;
GString *searches;
char buf[INET6_ADDRSTRLEN];
NMIP6Address *addr;
const struct in6_addr *ip;
GString *ip_str1, *ip_str2, *ip_ptr;
char *route6_path;
s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
if (!s_ip6) {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Missing '%s' setting", NM_SETTING_IP6_CONFIG_SETTING_NAME);
return FALSE;
}
value = nm_setting_ip6_config_get_method (s_ip6);
g_assert (value);
if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
svSetValue (ifcfg, "IPV6INIT", "no", FALSE);
return TRUE;
}
else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
svSetValue (ifcfg, "IPV6_AUTOCONF", "yes", FALSE);
}
else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
}
else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
svSetValue (ifcfg, "IPV6_AUTOCONF", "no", FALSE);
}
else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
svSetValue (ifcfg, "IPV6INIT", "yes", FALSE);
/* TODO */
}
if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
/* Write out IP addresses */
num = nm_setting_ip6_config_get_num_addresses (s_ip6);
ip_str1 = g_string_new (NULL);
ip_str2 = g_string_new (NULL);
for (i = 0; i < num; i++) {
if (i == 0)
ip_ptr = ip_str1;
else
ip_ptr = ip_str2;
addr = nm_setting_ip6_config_get_address (s_ip6, i);
ip = nm_ip6_address_get_address (addr);
prefix = g_strdup_printf ("%u", nm_ip6_address_get_prefix (addr));
memset (buf, 0, sizeof (buf));
inet_ntop (AF_INET6, (const void *) ip, buf, sizeof (buf));
if (i > 1)
g_string_append_c (ip_ptr, ' '); /* separate addresses in IPV6ADDR_SECONDARIES */
g_string_append (ip_ptr, buf);
g_string_append_c (ip_ptr, '/');
g_string_append (ip_ptr, prefix);
g_free (prefix);
}
svSetValue (ifcfg, "IPV6ADDR", ip_str1->str, FALSE);
svSetValue (ifcfg, "IPV6ADDR_SECONDARIES", ip_str2->str, FALSE);
g_string_free (ip_str1, TRUE);
g_string_free (ip_str2, TRUE);
}
/* Write out DNS - 'DNS' key is used both for IPv4 and IPv6 */
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
num4 = s_ip4 ? nm_setting_ip4_config_get_num_dns (s_ip4) : 0; /* from where to start with IPv6 entries */
num = nm_setting_ip6_config_get_num_dns (s_ip6);
for (i = 0; i < 254; i++) {
addr_key = g_strdup_printf ("DNS%d", i + num4 + 1);
if (i >= num)
svSetValue (ifcfg, addr_key, NULL, FALSE);
else {
ip = nm_setting_ip6_config_get_dns (s_ip6, i);
memset (buf, 0, sizeof (buf));
inet_ntop (AF_INET6, (const void *) ip, buf, sizeof (buf));
svSetValue (ifcfg, addr_key, buf, FALSE);
}
g_free (addr_key);
}
/* Write out DNS domains - 'DOMAIN' key is shared for both IPv4 and IPv6 domains */
num = nm_setting_ip6_config_get_num_dns_searches (s_ip6);
if (num > 0) {
char *ip4_domains;
ip4_domains = svGetValue (ifcfg, "DOMAIN", FALSE);
searches = g_string_new (ip4_domains);
for (i = 0; i < num; i++) {
if (searches->len > 0)
g_string_append_c (searches, ' ');
g_string_append (searches, nm_setting_ip6_config_get_dns_search (s_ip6, i));
}
svSetValue (ifcfg, "DOMAIN", searches->str, FALSE);
g_string_free (searches, TRUE);
g_free (ip4_domains);
}
/* handle IPV6_DEFROUTE */
/* IPV6_DEFROUTE has the opposite meaning from 'never-default' */
if (nm_setting_ip6_config_get_never_default(s_ip6))
svSetValue (ifcfg, "IPV6_DEFROUTE", "no", FALSE);
else
svSetValue (ifcfg, "IPV6_DEFROUTE", "yes", FALSE);
svSetValue (ifcfg, "IPV6_PEERDNS", NULL, FALSE);
svSetValue (ifcfg, "IPV6_PEERROUTES", NULL, FALSE);
if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
svSetValue (ifcfg, "IPV6_PEERDNS",
nm_setting_ip6_config_get_ignore_auto_dns (s_ip6) ? "no" : "yes",
FALSE);
svSetValue (ifcfg, "IPV6_PEERROUTES",
nm_setting_ip6_config_get_ignore_auto_routes (s_ip6) ? "no" : "yes",
FALSE);
}
/* Static routes go to route6-<dev> file */
route6_path = utils_get_route6_path (ifcfg->fileName);
if (!route6_path) {
g_set_error (error, ifcfg_plugin_error_quark (), 0,
"Could not get route6 file path for '%s'", ifcfg->fileName);
goto error;
}
write_route6_file (route6_path, s_ip6, error);
g_free (route6_path);
if (error && *error)
goto error;
return TRUE;
error:
return FALSE;
}
static char *
escape_id (const char *id)
{
@ -1142,11 +1352,11 @@ write_connection (NMConnection *connection,
const char *ifcfg_dir,
const char *filename,
const char *keyfile,
const char *routefile,
char **out_filename,
GError **error)
{
NMSettingConnection *s_con;
NMSettingIP6Config *s_ip6;
gboolean success = FALSE;
shvarFile *ifcfg = NULL;
char *ifcfg_name = NULL;
@ -1216,6 +1426,12 @@ write_connection (NMConnection *connection,
if (!write_ip4_setting (connection, ifcfg, error))
goto out;
s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
if (s_ip6) {
if (!write_ip6_setting (connection, ifcfg, error))
goto out;
}
write_connection_setting (s_con, ifcfg);
if (svWriteFile (ifcfg, 0644)) {
@ -1243,7 +1459,7 @@ writer_new_connection (NMConnection *connection,
char **out_filename,
GError **error)
{
return write_connection (connection, ifcfg_dir, NULL, NULL, NULL, out_filename, error);
return write_connection (connection, ifcfg_dir, NULL, NULL, out_filename, error);
}
gboolean
@ -1251,9 +1467,8 @@ writer_update_connection (NMConnection *connection,
const char *ifcfg_dir,
const char *filename,
const char *keyfile,
const char *routefile,
GError **error)
{
return write_connection (connection, ifcfg_dir, filename, keyfile, routefile, NULL, error);
return write_connection (connection, ifcfg_dir, filename, keyfile, NULL, error);
}

View file

@ -34,7 +34,6 @@ gboolean writer_update_connection (NMConnection *connection,
const char *ifcfg_dir,
const char *filename,
const char *keyfile,
const char *routefile,
GError **error);
#endif /* _WRITER_H_ */