static void validate (GtkWidget *dialog) { GtkBuilder *builder; GtkWidget *widget; GtkTreeModel *model; GtkTreeIter tree_iter; gboolean valid = FALSE, iter_valid = FALSE; g_return_if_fail (dialog != NULL); builder = g_object_get_data (G_OBJECT (dialog), "builder"); g_return_if_fail (builder != NULL); g_return_if_fail (GTK_IS_BUILDER (builder)); widget = GTK_WIDGET (gtk_builder_get_object (builder, "ip6_routes")); model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); iter_valid = gtk_tree_model_get_iter_first (model, &tree_iter); while (iter_valid) { struct in6_addr dest, next_hop; guint prefix = 0, metric = 0; /* Address */ if (!get_one_addr (model, &tree_iter, COL_ADDRESS, TRUE, &dest)) goto done; /* Prefix */ if (!get_one_int (model, &tree_iter, COL_PREFIX, 128, TRUE, &prefix)) goto done; /* Next hop (optional) */ if (!get_one_addr (model, &tree_iter, COL_NEXT_HOP, FALSE, &next_hop)) goto done; /* Metric (optional) */ if (!get_one_int (model, &tree_iter, COL_METRIC, G_MAXUINT32, FALSE, &metric)) goto done; iter_valid = gtk_tree_model_iter_next (model, &tree_iter); } valid = TRUE; done: widget = GTK_WIDGET (gtk_builder_get_object (builder, "ok_button")); gtk_widget_set_sensitive (widget, valid); }
static void validate (GtkWidget *dialog) { GtkBuilder *builder; GtkWidget *widget; GtkTreeModel *model; GtkTreeIter tree_iter; gboolean valid = FALSE, iter_valid = FALSE; g_return_if_fail (dialog != NULL); builder = g_object_get_data (G_OBJECT (dialog), "builder"); g_return_if_fail (builder != NULL); g_return_if_fail (GTK_IS_BUILDER (builder)); widget = GTK_WIDGET (gtk_builder_get_object (builder, "ip4_routes")); model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); iter_valid = gtk_tree_model_get_iter_first (model, &tree_iter); while (iter_valid) { guint32 addr = 0, prefix = 0, next_hop = 0, metric = 0; /* Address */ if (!get_one_addr (model, &tree_iter, COL_ADDRESS, TRUE, &addr)) goto done; /* Don't allow inserting 0.0.0.0 for now - that's not supported in libnm-util */ if (addr == 0) goto done; /* Prefix */ if (!get_one_prefix (model, &tree_iter, COL_PREFIX, TRUE, &prefix)) goto done; /* Don't allow zero prefix for now - that's not supported in libnm-util */ if (prefix == 0) goto done; /* Next hop (optional) */ if (!get_one_addr (model, &tree_iter, COL_NEXT_HOP, FALSE, &next_hop)) goto done; /* Metric (optional) */ if (!get_one_int (model, &tree_iter, COL_METRIC, G_MAXUINT32, FALSE, &metric)) goto done; iter_valid = gtk_tree_model_iter_next (model, &tree_iter); } valid = TRUE; done: widget = GTK_WIDGET (gtk_builder_get_object (builder, "ok_button")); gtk_widget_set_sensitive (widget, valid); }
static gpointer build_ip4_address_or_route (const char *key_name, const char *address_str, guint32 plen, const char *gateway_str, const char *metric_str, gboolean route) { GArray *result; guint32 addr; guint32 address = 0; guint32 gateway = 0; guint32 metric = 0; int err; g_return_val_if_fail (address_str, NULL); /* Address */ err = inet_pton (AF_INET, address_str, &addr); if (err <= 0) { g_warning ("%s: ignoring invalid IPv4 address '%s'", __func__, address_str); return NULL; } address = addr; /* Gateway */ if (gateway_str && gateway_str[0]) { err = inet_pton (AF_INET, gateway_str, &addr); if (err <= 0) { g_warning ("%s: ignoring invalid IPv4 gateway '%s'", __func__, gateway_str); return NULL; } gateway = addr; } else gateway = 0; /* parse metric, default to 0 */ if (metric_str) { if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric)) return NULL; } result = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3 + !!route); g_array_append_val (result, address); g_array_append_val (result, plen); g_array_append_val (result, gateway); if (route) g_array_append_val (result, metric); return result; }
/* The following IPv4 and IPv6 address formats are supported: * * address (DEPRECATED) * address/plen * address/gateway (DEPRECATED) * address/plen/gateway * * The following IPv4 and IPv6 route formats are supported: * * address/plen (NETWORK dev DEVICE) * address/plen/gateway (NETWORK via GATEWAY dev DEVICE) * address/plen//gateway (NETWORK dev DEVICE metric METRIC) * address/plen/gateway/metric (NETWORK via GATEWAY dev DEVICE metric METRIC) * * For backward, forward and sideward compatibility, slash (/), * semicolon (;) and comma (,) are interchangable. The use of * slash in the above examples is therefore not significant. * * Leaving out the prefix length is discouraged and DEPRECATED. The * default value of IPv6 prefix length was 64 and has not been * changed. The default for IPv4 is now 24, which is the closest * IPv4 equivalent. These defaults may just as well be changed to * match the iproute2 defaults (32 for IPv4 and 128 for IPv6). * * The returned result is GArray for IPv4 and GValueArray for IPv6. */ static gpointer read_one_ip_address_or_route (GKeyFile *file, const char *setting_name, const char *key_name, gboolean ipv6, gboolean route) { guint32 plen, metric; gpointer result; char *address_str, *plen_str, *gateway_str, *metric_str, *value, *current, *error; current = value = g_key_file_get_string (file, setting_name, key_name, NULL); if (!value) return NULL; /* get address field */ address_str = read_field (¤t, &error, IP_ADDRESS_CHARS, DELIMITERS); if (error) { g_warning ("keyfile: Unexpected character '%c' in '%s.%s' address (position %td of '%s').", *error, setting_name, key_name, error - current, current); goto error; } /* get prefix length field (skippable) */ plen_str = read_field (¤t, &error, DIGITS, DELIMITERS); /* get gateway field */ gateway_str = read_field (¤t, &error, IP_ADDRESS_CHARS, DELIMITERS); if (error) { g_warning ("keyfile: Unexpected character '%c' in '%s.%s' %s (position %td of '%s').", *error, setting_name, key_name, plen_str ? "gateway" : "gateway or prefix length", error - current, current); goto error; } /* for routes, get metric */ if (route) { metric_str = read_field (¤t, &error, DIGITS, DELIMITERS); if (error) { g_warning ("keyfile: Unexpected character '%c' in '%s.%s' prefix length (position %td of '%s').", *error, setting_name, key_name, error - current, current); goto error; } } else metric_str = NULL; if (current) { /* there is still some data */ if (*current) { /* another field follows */ g_warning ("keyfile: %s.%s: Garbage at the and of the line: %s", setting_name, key_name, current); goto error; } else { /* semicolon at the end of input */ g_message ("keyfile: %s.%s: Deprecated semicolon at the end of value.", setting_name, key_name); } } /* parse plen, fallback to defaults */ if (plen_str) g_return_val_if_fail (get_one_int (plen_str, ipv6 ? 128 : 32, key_name, &plen), NULL); else { if (route) plen = ipv6 ? 128 : 24; else plen = ipv6 ? 64 : 24; g_warning ("keyfile: Missing prefix length in '%s.%s', defaulting to %d", setting_name, key_name, plen); } /* parse metric, default to 0 */ metric = 0; if (metric_str) g_return_val_if_fail (get_one_int (metric_str, G_MAXUINT32, key_name, &metric), NULL); /* build the appropriate data structure for NetworkManager settings */ if (route) g_debug ("keyfile: %s.%s: route %s/%d gateway %s metric %d", setting_name, key_name, address_str, plen, gateway_str, metric); else g_debug ("keyfile: %s.%s: address %s/%d gateway %s", setting_name, key_name, address_str, plen, gateway_str); result = (ipv6 ? build_ip6_address_or_route : build_ip4_address_or_route) ( address_str, plen, gateway_str, metric, route); g_free (value); return result; error: g_free (value); return NULL; }
void ip6_routes_dialog_update_setting (GtkWidget *dialog, NMSettingIP6Config *s_ip6) { GtkBuilder *builder; GtkWidget *widget; GtkTreeModel *model; GtkTreeIter tree_iter; gboolean iter_valid; g_return_if_fail (dialog != NULL); g_return_if_fail (s_ip6 != NULL); builder = g_object_get_data (G_OBJECT (dialog), "builder"); g_return_if_fail (builder != NULL); g_return_if_fail (GTK_IS_BUILDER (builder)); widget = GTK_WIDGET (gtk_builder_get_object (builder, "ip6_routes")); model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); iter_valid = gtk_tree_model_get_iter_first (model, &tree_iter); nm_setting_ip6_config_clear_routes (s_ip6); while (iter_valid) { struct in6_addr dest, next_hop; guint prefix = 0, metric = 0; NMIP6Route *route; /* Address */ if (!get_one_addr (model, &tree_iter, COL_ADDRESS, TRUE, &dest)) { g_warning ("%s: IPv6 address missing or invalid!", __func__); goto next; } /* Prefix */ if (!get_one_int (model, &tree_iter, COL_PREFIX, 128, TRUE, &prefix)) { g_warning ("%s: IPv6 prefix missing or invalid!", __func__); goto next; } /* Next hop (optional) */ memset (&next_hop, 0, sizeof (struct in6_addr)); if (!get_one_addr (model, &tree_iter, COL_NEXT_HOP, FALSE, &next_hop)) { g_warning ("%s: IPv6 next hop invalid!", __func__); goto next; } /* Metric (optional) */ if (!get_one_int (model, &tree_iter, COL_METRIC, G_MAXUINT32, FALSE, &metric)) { g_warning ("%s: IPv6 metric invalid!", __func__); goto next; } route = nm_ip6_route_new (); nm_ip6_route_set_dest (route, &dest); nm_ip6_route_set_prefix (route, prefix); nm_ip6_route_set_next_hop (route, &next_hop); nm_ip6_route_set_metric (route, metric); nm_setting_ip6_config_add_route (s_ip6, route); nm_ip6_route_unref (route); next: iter_valid = gtk_tree_model_iter_next (model, &tree_iter); } widget = GTK_WIDGET (gtk_builder_get_object (builder, "ip6_ignore_auto_routes")); g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)), NULL); widget = GTK_WIDGET (gtk_builder_get_object (builder, "ip6_never_default")); g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_NEVER_DEFAULT, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)), NULL); }
static GPtrArray * read_ip4_addresses (GKeyFile *file, const char *setting_name, const char *key) { GPtrArray *addresses; int i = 0; addresses = g_ptr_array_sized_new (3); /* Look for individual addresses */ while (i++ < 1000) { gchar **tmp, **iter; char *key_name; gsize length = 0; int ret; GArray *address; guint32 empty = 0; int j; key_name = g_strdup_printf ("%s%d", key, i); tmp = g_key_file_get_string_list (file, setting_name, key_name, &length, NULL); g_free (key_name); if (!tmp || !length) break; /* all done */ if ((length < 2) || (length > 3)) { g_warning ("%s: ignoring invalid IPv4 address item '%s'", __func__, key_name); goto next; } /* convert the string array into IP addresses */ address = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3); for (iter = tmp, j = 0; *iter; iter++, j++) { struct in_addr addr; if (j == 1) { guint32 prefix = 0; /* prefix */ if (!get_one_int (*iter, 32, key_name, &prefix)) { g_array_free (address, TRUE); goto next; } g_array_append_val (address, prefix); } else { /* address and gateway */ ret = inet_pton (AF_INET, *iter, &addr); if (ret <= 0) { g_warning ("%s: ignoring invalid IPv4 %s element '%s'", __func__, key_name, *iter); g_array_free (address, TRUE); goto next; } g_array_append_val (address, addr.s_addr); } } /* fill in blank gateway if not specified */ if (address->len == 2) g_array_append_val (address, empty); g_ptr_array_add (addresses, address); next: g_strfreev (tmp); } if (addresses->len < 1) { g_ptr_array_free (addresses, TRUE); addresses = NULL; } return addresses; }
static GPtrArray * read_ip6_routes (GKeyFile *file, const char *setting_name, const char *key) { GPtrArray *routes; struct in6_addr addr; guint32 prefix, metric; int i = 0; routes = g_ptr_array_sized_new (3); /* Look for individual routes */ while (i++ < 1000) { gchar **tmp; char *key_name, *str_prefix; gsize length = 0; int ret; GValueArray *values; GByteArray *address; GValue value = { 0 }; key_name = g_strdup_printf ("%s%d", key, i); tmp = g_key_file_get_string_list (file, setting_name, key_name, &length, NULL); g_free (key_name); if (!tmp || !length) break; /* all done */ if (length != 3) { g_warning ("%s: ignoring invalid IPv6 address item '%s'", __func__, key_name); goto next; } /* convert the string array into IPv6 routes */ values = g_value_array_new (4); /* NMIP6Route has 4 items */ /* Split the route and prefix */ str_prefix = split_prefix (tmp[0]); /* destination address */ ret = inet_pton (AF_INET6, tmp[0], &addr); if (ret <= 0) { g_warning ("%s: ignoring invalid IPv6 %s element '%s'", __func__, key_name, tmp[0]); g_value_array_free (values); goto next; } address = g_byte_array_new (); g_byte_array_append (address, (guint8 *) addr.s6_addr, 16); g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY); g_value_take_boxed (&value, address); g_value_array_append (values, &value); g_value_unset (&value); /* prefix */ prefix = 0; if (str_prefix) { if (!get_one_int (str_prefix, 128, key_name, &prefix)) { g_value_array_free (values); goto next; } } else { /* default to 64 if unspecified */ prefix = 64; } g_value_init (&value, G_TYPE_UINT); g_value_set_uint (&value, prefix); g_value_array_append (values, &value); g_value_unset (&value); /* next hop address */ ret = inet_pton (AF_INET6, tmp[1], &addr); if (ret <= 0) { g_warning ("%s: ignoring invalid IPv6 %s element '%s'", __func__, key_name, tmp[1]); g_value_array_free (values); goto next; } address = g_byte_array_new (); g_byte_array_append (address, (guint8 *) addr.s6_addr, 16); g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY); g_value_take_boxed (&value, address); g_value_array_append (values, &value); g_value_unset (&value); /* metric */ metric = 0; if (!get_one_int (tmp[2], G_MAXUINT32, key_name, &metric)) { g_value_array_free (values); goto next; } g_value_init (&value, G_TYPE_UINT); g_value_set_uint (&value, metric); g_value_array_append (values, &value); g_value_unset (&value); g_ptr_array_add (routes, values); next: g_strfreev (tmp); } if (routes->len < 1) { g_ptr_array_free (routes, TRUE); routes = NULL; } return routes; }
static GPtrArray * read_ip6_addresses (GKeyFile *file, const char *setting_name, const char *key) { GPtrArray *addresses; struct in6_addr addr, gw; guint32 prefix; int i = 0; addresses = g_ptr_array_sized_new (3); /* Look for individual addresses */ while (i++ < 1000) { char *tmp, *key_name, *str_prefix, *str_gw; int ret; GValueArray *values; GByteArray *address; GByteArray *gateway; GValue value = { 0 }; key_name = g_strdup_printf ("%s%d", key, i); tmp = g_key_file_get_string (file, setting_name, key_name, NULL); g_free (key_name); if (!tmp) break; /* all done */ /* convert the string array into IPv6 addresses */ values = g_value_array_new (2); /* NMIP6Address has 2 items */ /* Split the address and prefix */ str_prefix = split_prefix (tmp); /* address */ ret = inet_pton (AF_INET6, tmp, &addr); if (ret <= 0) { g_warning ("%s: ignoring invalid IPv6 %s element '%s'", __func__, key_name, tmp); g_value_array_free (values); goto next; } address = g_byte_array_new (); g_byte_array_append (address, (guint8 *) addr.s6_addr, 16); g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY); g_value_take_boxed (&value, address); g_value_array_append (values, &value); g_value_unset (&value); /* prefix */ prefix = 0; if (str_prefix) { if (!get_one_int (str_prefix, 128, key_name, &prefix)) { g_value_array_free (values); goto next; } } else { /* Missing prefix defaults to /64 */ prefix = 64; } g_value_init (&value, G_TYPE_UINT); g_value_set_uint (&value, prefix); g_value_array_append (values, &value); g_value_unset (&value); /* Gateway (optional) */ str_gw = split_gw (str_prefix); if (str_gw) { ret = inet_pton (AF_INET6, str_gw, &gw); if (ret <= 0) { g_warning ("%s: ignoring invalid IPv6 %s gateway '%s'", __func__, key_name, tmp); g_value_array_free (values); goto next; } if (!IN6_IS_ADDR_UNSPECIFIED (&gw)) { gateway = g_byte_array_new (); g_byte_array_append (gateway, (guint8 *) gw.s6_addr, 16); g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY); g_value_take_boxed (&value, gateway); g_value_array_append (values, &value); g_value_unset (&value); } } g_ptr_array_add (addresses, values); next: g_free (tmp); } if (addresses->len < 1) { g_ptr_array_free (addresses, TRUE); addresses = NULL; } return addresses; }
static GPtrArray * read_ip4_routes (GKeyFile *file, const char *setting_name, const char *key) { GPtrArray *routes; int i = 0; routes = g_ptr_array_sized_new (3); /* Look for individual routes */ while (i++ < 1000) { gchar **tmp, **iter; char *key_name; gsize length = 0; int ret; GArray *route; int j; key_name = g_strdup_printf ("%s%d", key, i); tmp = g_key_file_get_string_list (file, setting_name, key_name, &length, NULL); g_free (key_name); if (!tmp || !length) break; /* all done */ if (length != 4) { g_warning ("%s: ignoring invalid IPv4 route item '%s'", __func__, key_name); goto next; } /* convert the string array into IP addresses */ route = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 4); for (iter = tmp, j = 0; *iter; iter++, j++) { struct in_addr addr; if (j == 1) { guint32 prefix = 0; /* prefix */ if (!get_one_int (*iter, 32, key_name, &prefix)) { g_array_free (route, TRUE); goto next; } g_array_append_val (route, prefix); } else if (j == 3) { guint32 metric = 0; /* metric */ if (!get_one_int (*iter, G_MAXUINT32, key_name, &metric)) { g_array_free (route, TRUE); goto next; } g_array_append_val (route, metric); } else { /* address and next hop */ ret = inet_pton (AF_INET, *iter, &addr); if (ret <= 0) { g_warning ("%s: ignoring invalid IPv4 %s element '%s'", __func__, key_name, *iter); g_array_free (route, TRUE); goto next; } g_array_append_val (route, addr.s_addr); } } g_ptr_array_add (routes, route); next: g_strfreev (tmp); } if (routes->len < 1) { g_ptr_array_free (routes, TRUE); routes = NULL; } return routes; }
static gpointer build_ip6_address_or_route (const char *key_name, const char *address_str, guint32 plen, const char *gateway_str, const char *metric_str, gboolean route) { GValueArray *result; struct in6_addr addr; GByteArray *address; GByteArray *gateway; guint32 metric = 0; GValue value = G_VALUE_INIT; int err; g_return_val_if_fail (address_str, NULL); result = g_value_array_new (3); /* add address */ err = inet_pton (AF_INET6, address_str, &addr); if (err <= 0) { g_warning ("%s: ignoring invalid IPv6 address '%s'", __func__, address_str); goto error_out; } address = g_byte_array_new (); g_byte_array_append (address, (guint8 *) addr.s6_addr, 16); g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY); g_value_take_boxed (&value, address); g_value_array_append (result, &value); g_value_unset (&value); /* add prefix length */ g_value_init (&value, G_TYPE_UINT); g_value_set_uint (&value, plen); g_value_array_append (result, &value); g_value_unset (&value); /* add gateway */ if (gateway_str && gateway_str[0]) { err = inet_pton (AF_INET6, gateway_str, &addr); if (err <= 0) { /* Try workaround for routes written by broken keyfile writer. * Due to bug bgo#719851, an older version of writer would have * written "a:b:c:d::/plen,metric" if the gateway was ::, instead * of "a:b:c:d::/plen,,metric" or "a:b:c:d::/plen,::,metric" * Try workaround by interepeting gateway_str as metric to accept such * invalid routes. This broken syntax should not be not officially * supported. **/ if (route && !metric_str && get_one_int (gateway_str, G_MAXUINT32, NULL, &metric)) addr = in6addr_any; else { g_warning ("%s: ignoring invalid IPv6 gateway '%s'", __func__, gateway_str); goto error_out; } } } else addr = in6addr_any; /* parse metric, default to 0 */ if (metric_str) { if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric)) goto error_out; } gateway = g_byte_array_new (); g_byte_array_append (gateway, (guint8 *) addr.s6_addr, 16); g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY); g_value_take_boxed (&value, gateway); g_value_array_append (result, &value); g_value_unset (&value); /* add metric (for routing) */ if (route) { g_value_init (&value, G_TYPE_UINT); g_value_set_uint (&value, metric); g_value_array_append (result, &value); g_value_unset (&value); } return result; error_out: g_value_array_free (result); return NULL; }