#include <config.h>
#endif
-#include <errno.h>
-#include <unistd.h>
#include <string.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
#include <net/if.h>
-#include <net/route.h>
#include <gdbus.h>
struct gateway_data {
int index;
- char *gateway;
+ char *ipv4_gateway;
struct connman_element *element;
unsigned int order;
gboolean active;
/* VPN extra data */
gboolean vpn;
char *vpn_ip;
+ int vpn_phy_index;
};
static GSList *gateway_list = NULL;
for (list = gateway_list; list; list = list->next) {
struct gateway_data *data = list->data;
- if (data->gateway == NULL)
+ if (data->ipv4_gateway == NULL)
continue;
if (data->index == index &&
- g_str_equal(data->gateway, gateway) == TRUE)
+ g_str_equal(data->ipv4_gateway, gateway)
+ == TRUE)
return data;
}
return NULL;
}
-static int add_vpn_host(struct connman_element *element,
- const char *gateway,
- const char *host)
+static int del_routes(struct gateway_data *data)
{
- struct ifreq ifr;
- struct rtentry rt;
- struct sockaddr_in addr;
- int sk, err;
-
- DBG("element %p", element);
-
- sk = socket(PF_INET, SOCK_DGRAM, 0);
- if (sk < 0)
- return -1;
-
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = element->index;
-
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- close(sk);
- return -1;
- }
- DBG("ifname %s", ifr.ifr_name);
-
- memset(&rt, 0, sizeof(rt));
- rt.rt_flags = RTF_UP | RTF_HOST | RTF_GATEWAY;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr(host);
- memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr(gateway);
- memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_NONE;
- memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
-
- rt.rt_dev = ifr.ifr_name;
-
- err = ioctl(sk, SIOCADDRT, &rt);
- if (err < 0)
- connman_error("Setting VPN host failed (%s)",
- strerror(errno));
-
- close(sk);
-
- return err;
-}
-
-static int del_vpn_host(const char *host)
-{
- struct rtentry rt;
- struct sockaddr_in addr;
- int sk, err;
-
- sk = socket(PF_INET, SOCK_DGRAM, 0);
- if (sk < 0)
- return -1;
-
- memset(&rt, 0, sizeof(rt));
- rt.rt_flags = RTF_UP | RTF_HOST;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr(host);
- memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
-
- err = ioctl(sk, SIOCDELRT, &rt);
- if (err < 0)
- connman_error("Del vpn route failed (%s)",
- strerror(errno));
-
- close(sk);
-
- return err;
-}
-
-static int set_vpn_route(struct connman_element *element, const char *gateway)
-{
- struct ifreq ifr;
- struct rtentry rt;
- struct sockaddr_in addr;
- int sk, err;
-
- DBG("set_rout1: element %p", element);
-
- sk = socket(PF_INET, SOCK_DGRAM, 0);
- if (sk < 0)
- return -1;
-
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = element->index;
-
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- close(sk);
- return -1;
- }
-
- DBG("ifname %s", ifr.ifr_name);
-
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = element->index;
-
- memset(&rt, 0, sizeof(rt));
- rt.rt_flags = RTF_UP | RTF_GATEWAY;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
- memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr(gateway);
- memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
- memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
-
- err = ioctl(sk, SIOCADDRT, &rt);
- if (err < 0)
- connman_error("Setting VPN route failed (%s)",
- strerror(errno));
-
- close(sk);
-
- return err;
-}
-
-static int del_route(struct connman_element *element, const char *gateway)
-{
- struct ifreq ifr;
- struct rtentry rt;
- struct sockaddr_in addr;
- int sk, err;
-
- DBG("element %p", element);
-
- sk = socket(PF_INET, SOCK_DGRAM, 0);
- if (sk < 0)
- return -1;
-
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = element->index;
-
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- close(sk);
- return -1;
- }
-
- DBG("ifname %s", ifr.ifr_name);
-
- memset(&rt, 0, sizeof(rt));
- rt.rt_flags = RTF_UP | RTF_GATEWAY;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
- memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr(gateway);
- memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
- memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
-
- err = ioctl(sk, SIOCDELRT, &rt);
- if (err < 0)
- connman_error("Removing default route failed (%s)",
- strerror(errno));
-
- close(sk);
-
- return err;
-}
-
-static int del_route_all(struct gateway_data *data)
-{
- int err = 0;
-
if (data->vpn) {
- del_vpn_host(data->gateway);
-
- err = del_route(data->element, data->vpn_ip);
- } else
- err = del_route(data->element, data->gateway);
-
- return err;
+ if (data->vpn_phy_index >= 0)
+ connman_inet_del_host_route(data->vpn_phy_index,
+ data->ipv4_gateway);
+ return connman_inet_clear_gateway_address(data->index,
+ data->vpn_ip);
+ } else if (g_strcmp0(data->ipv4_gateway, "0.0.0.0") == 0) {
+ return connman_inet_clear_gateway_interface(data->index);
+ } else {
+ connman_inet_del_host_route(data->index, data->ipv4_gateway);
+ return connman_inet_clear_gateway_address(data->index,
+ data->ipv4_gateway);
+ }
}
static void find_element(struct connman_element *element, gpointer user_data)
struct gateway_data *data;
struct connman_service *service;
+ if (strlen(gateway) == 0)
+ return NULL;
+
data = g_try_new0(struct gateway_data, 1);
if (data == NULL)
return NULL;
data->index = index;
- data->gateway = g_strdup(gateway);
+ data->ipv4_gateway = g_strdup(gateway);
data->active = FALSE;
data->element = NULL;
data->vpn_ip = NULL;
data->vpn = FALSE;
+ data->vpn_phy_index = -1;
__connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
find_element, data);
{
struct connman_element *element = data->element;
struct connman_service *service = NULL;
- short int ifflags;
- DBG("gateway %s", data->gateway);
+ DBG("gateway %s", data->ipv4_gateway);
if (data->vpn == TRUE) {
-
- set_vpn_route(element, data->vpn_ip);
+ connman_inet_set_gateway_address(data->index, data->vpn_ip);
+ data->active = TRUE;
/* vpn gateway going away no changes in services */
return;
}
- ifflags = connman_inet_ifflags(element->index);
- if (ifflags < 0) {
- connman_error("Fail to get network interface flags");
- return;
- }
-
- if (ifflags & IFF_POINTOPOINT) {
+ if (g_strcmp0(data->ipv4_gateway, "0.0.0.0") == 0) {
if (connman_inet_set_gateway_interface(element->index) < 0)
return;
goto done;
}
- connman_inet_add_host_route(element->index, data->gateway);
-
- if (connman_inet_set_gateway_address(element->index, data->gateway) < 0)
+ if (connman_inet_set_gateway_address(element->index,
+ data->ipv4_gateway) < 0)
return;
done:
return found;
}
-static void remove_gateway(struct gateway_data *data)
+static int remove_gateway(struct gateway_data *data)
{
- DBG("gateway %s", data->gateway);
+ int err;
+
+ DBG("gateway %s", data->ipv4_gateway);
gateway_list = g_slist_remove(gateway_list, data);
if (data->active == TRUE)
- del_route_all(data);
+ err = del_routes(data);
+ else
+ err = 0;
- g_free(data->gateway);
+ g_free(data->ipv4_gateway);
g_free(data->vpn_ip);
g_free(data);
+
+ return err;
}
static void connection_delgateway(int index, const char *gateway)
for (list = gateway_list; list; list = list->next) {
struct gateway_data *data = list->data;
+
if (data->active == TRUE)
return data;
}
if (element->parent == NULL)
return -ENODEV;
- if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
- return -ENODEV;
+ /* FIXME: Remove temporarily for the static gateway support */
+ /* if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
+ return -ENODEV; */
connman_element_get_value(element,
CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
DBG("gateway %s", gateway);
- service = __connman_element_get_service(element);
- __connman_service_indicate_state(service,
- CONNMAN_SERVICE_STATE_READY);
+ /*
+ * If gateway is NULL, it's a point to point link and the default
+ * gateway is 0.0.0.0, meaning the interface.
+ */
+ if (gateway == NULL) {
+ gateway = "0.0.0.0";
+ element->ipv4.gateway = g_strdup(gateway);
+ }
connman_element_set_enabled(element, TRUE);
- if (gateway == NULL)
- return 0;
-
active_gateway = find_active_gateway();
new_gateway = add_gateway(element->index, gateway);
+ if (new_gateway == NULL)
+ return 0;
+
+ service = __connman_element_get_service(element);
+
+ connman_inet_add_host_route(element->index,
+ new_gateway->ipv4_gateway, NULL);
+ __connman_service_nameserver_add_routes(service,
+ new_gateway->ipv4_gateway);
+
+ __connman_service_indicate_state(service, CONNMAN_SERVICE_STATE_READY);
if (service == NULL) {
new_gateway->vpn = TRUE;
new_gateway->vpn_ip = g_strdup(vpn_ip);
/* make sure vpn gateway are at higher priority */
new_gateway->order = 10;
+ if (active_gateway)
+ new_gateway->vpn_phy_index = active_gateway->index;
} else
new_gateway->vpn = FALSE;
}
if (new_gateway->vpn == TRUE) {
- add_vpn_host(active_gateway->element,
- active_gateway->gateway,
- new_gateway->gateway);
-
+ connman_inet_add_host_route(active_gateway->index,
+ new_gateway->ipv4_gateway,
+ active_gateway->ipv4_gateway);
}
if (new_gateway->order >= active_gateway->order) {
- del_route_all(active_gateway);
+ del_routes(active_gateway);
return 0;
}
const char *gateway = NULL;
struct gateway_data *data = NULL;
gboolean set_default = FALSE;
+ int err;
DBG("element %p name %s", element, element->name);
service = __connman_element_get_service(element);
+ __connman_service_nameserver_del_routes(service);
__connman_service_indicate_state(service,
CONNMAN_SERVICE_STATE_DISCONNECT);
set_default = data->vpn;
- if (data->vpn == TRUE)
- del_vpn_host(data->gateway);
-
- remove_gateway(data);
+ if (data->vpn == TRUE && data->vpn_phy_index >= 0)
+ connman_inet_del_host_route(data->vpn_phy_index,
+ data->ipv4_gateway);
+ err = remove_gateway(data);
/* with vpn this will be called after the network was deleted,
* we need to call set_default here because we will not recieve any
* gateway delete notification.
+ * We hit the same issue if remove_gateway() fails.
*/
- if (set_default) {
+ if (set_default || err < 0) {
data = find_default_gateway();
if (data != NULL)
set_default_gateway(data);
for (list = gateway_list; list; list = list->next) {
struct gateway_data *data = list->data;
- DBG("index %d gateway %s", data->index, data->gateway);
+ DBG("index %d gateway %s", data->index, data->ipv4_gateway);
- g_free(data->gateway);
+ g_free(data->ipv4_gateway);
g_free(data);
list->data = NULL;
}
default_gateway = find_default_gateway();
if (active_gateway && active_gateway != default_gateway) {
- del_route_all(active_gateway);
+ del_routes(active_gateway);
updated = TRUE;
}