diff options
-rw-r--r-- | src/NetworkManagerUtils.c | 12 | ||||
-rw-r--r-- | src/NetworkManagerUtils.h | 17 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-client.c | 10 | ||||
-rw-r--r-- | src/nm-ip4-config.c | 30 | ||||
-rw-r--r-- | src/nm-ip4-config.h | 2 | ||||
-rw-r--r-- | src/nm-ip6-config.c | 35 | ||||
-rw-r--r-- | src/nm-ip6-config.h | 2 | ||||
-rw-r--r-- | src/platform/nm-platform.h | 2 |
8 files changed, 99 insertions, 11 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index fb273822ea..e6814e32e6 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -104,15 +104,15 @@ nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen) * @src: source ip6 address * @plen: prefix length of network * - * Note: this function is self assignment save, to update @src inplace, set both + * Note: this function is self assignment safe, to update @src inplace, set both * @dst and @src to the same destination. */ -void +const struct in6_addr * nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen) { - g_return_if_fail (plen <= 128); - g_return_if_fail (src); - g_return_if_fail (dst); + g_return_val_if_fail (plen <= 128, NULL); + g_return_val_if_fail (src, NULL); + g_return_val_if_fail (dst, NULL); if (plen < 128) { guint nbytes = plen / 8; @@ -128,6 +128,8 @@ nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_ memset (&dst->s6_addr[nbytes], 0, 16 - nbytes); } else if (src != dst) *dst = *src; + + return dst; } diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 37e6641702..7be316e16a 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -36,7 +36,22 @@ gboolean nm_ethernet_address_is_valid (const struct ether_addr *test_addr); in_addr_t nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen); -void nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen); +const struct in6_addr *nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen); + +/** + * nm_utils_ip6_route_metric_normalize: + * @metric: the route metric + * + * For IPv6 route, kernel treats the value 0 as IP6_RT_PRIO_USER (1024). + * Thus, when comparing metric (values), we want to treat zero as NM_PLATFORM_ROUTE_METRIC_DEFAULT. + * + * Returns: @metric, if @metric is not zero, otherwise 1024. + */ +static inline guint32 +nm_utils_ip6_route_metric_normalize (guint32 metric) +{ + return metric ? metric : 1024 /*NM_PLATFORM_ROUTE_METRIC_DEFAULT*/; +} int nm_spawn_process (const char *args); diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index 1f186f3256..0a541a0ed6 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -1220,13 +1220,13 @@ ip4_options_to_config (NMDHCPClient *self) str = g_hash_table_lookup (priv->options, "new_dhcp_server_identifier"); if (str) { if (inet_pton (AF_INET, str, &tmp_addr) > 0) { - NMPlatformIP4Route route; - guint32 mask = nm_utils_ip4_prefix_to_netmask (address.plen); nm_log_info (LOGD_DHCP4, " server identifier %s", str); - if ((tmp_addr & mask) != (address.address & mask)) { - /* DHCP server not on assigned subnet, route needed */ - memset (&route, 0, sizeof (route)); + if ( nm_utils_ip4_address_clear_host_address(tmp_addr, address.plen) != nm_utils_ip4_address_clear_host_address(address.address, address.plen) + && !nm_ip4_config_get_direct_route_for_host (ip4_config, tmp_addr)) { + /* DHCP server not on assigned subnet and the no direct route was returned. Add route */ + NMPlatformIP4Route route = { 0 }; + route.network = tmp_addr; route.plen = 32; /* this will be a device route if gwaddr is 0 */ diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index caa638c7d4..7645bfd501 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -1191,6 +1191,36 @@ nm_ip4_config_get_route (const NMIP4Config *config, guint i) return &g_array_index (priv->routes, NMPlatformIP4Route, i); } +const NMPlatformIP4Route * +nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + int i; + NMPlatformIP4Route *best_route = NULL; + + g_return_val_if_fail (host, NULL); + + for (i = 0; i < priv->routes->len; i++ ) { + NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i); + + if (item->gateway != 0) + continue; + + if (best_route && best_route->plen > item->plen) + continue; + + if (nm_utils_ip4_address_clear_host_address (host, item->plen) != nm_utils_ip4_address_clear_host_address (item->network, item->plen)) + continue; + + if (best_route && best_route->metric <= item->metric) + continue; + + best_route = item; + } + + return best_route; +} + /******************************************************************/ void diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index c9096bb389..cbc8e20f9c 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -92,6 +92,8 @@ void nm_ip4_config_del_route (NMIP4Config *config, guint i); guint32 nm_ip4_config_get_num_routes (const NMIP4Config *config); const NMPlatformIP4Route *nm_ip4_config_get_route (const NMIP4Config *config, guint32 i); +const NMPlatformIP4Route *nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host); + /* Nameservers */ void nm_ip4_config_reset_nameservers (NMIP4Config *config); void nm_ip4_config_add_nameserver (NMIP4Config *config, guint32 nameserver); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 142e5a1947..e1a35eeb86 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -1194,6 +1194,41 @@ nm_ip6_config_get_route (const NMIP6Config *config, guint i) return &g_array_index (priv->routes, NMPlatformIP6Route, i); } +const NMPlatformIP6Route * +nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct in6_addr *host) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); + int i; + struct in6_addr network2, host2; + NMPlatformIP6Route *best_route = NULL; + + g_return_val_if_fail (host && !IN6_IS_ADDR_UNSPECIFIED (host), NULL); + + for (i = 0; i < priv->routes->len; i++ ) { + NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i); + + if (!IN6_IS_ADDR_UNSPECIFIED (&item->gateway)) + continue; + + if (best_route && best_route->plen > item->plen) + continue; + + nm_utils_ip6_address_clear_host_address (&host2, host, item->plen); + nm_utils_ip6_address_clear_host_address (&network2, &item->network, item->plen); + + if (!IN6_ARE_ADDR_EQUAL (&network2, &host2)) + continue; + + if (best_route && + nm_utils_ip6_route_metric_normalize (best_route->metric) <= nm_utils_ip6_route_metric_normalize (item->metric)) + continue; + + best_route = item; + } + + return best_route; +} + /******************************************************************/ void diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index b7b9d83ec7..9a7309deb4 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -93,6 +93,8 @@ void nm_ip6_config_del_route (NMIP6Config *config, guint i); guint32 nm_ip6_config_get_num_routes (const NMIP6Config *config); const NMPlatformIP6Route *nm_ip6_config_get_route (const NMIP6Config *config, guint32 i); +const NMPlatformIP6Route *nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct in6_addr *host); + /* Nameservers */ void nm_ip6_config_reset_nameservers (NMIP6Config *config); void nm_ip6_config_add_nameserver (NMIP6Config *config, const struct in6_addr *nameserver); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 348d1fa546..275557cf29 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -230,6 +230,8 @@ G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_ #undef __NMPlatformIPAddress_COMMON +/* Adding an IPv6 route with metric 0, kernel translates to IP6_RT_PRIO_USER (1024). + * Thus, the value is not choosen arbitraily, but matches kernel IPv6 default. */ #define NM_PLATFORM_ROUTE_METRIC_DEFAULT 1024 #define __NMPlatformIPRoute_COMMON \ |