If more than one service is connected at the same time,
then activate loose mode routing by setting the
/proc/sys/net/ipv4/conf/all/rp_filter to value 2
If the loose mode routing is not activated, then packets
are not routed properly if services are connected to same
subnet.

The original value of rp_filter is restored when the other
services are disconnected and only one service is connected.

For details of rp_filter setting, see Linux kernel file
Documentation/networking/ip-sysctl.txt

Fixes BMC#23606
---
 src/service.c |   86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 86 insertions(+), 0 deletions(-)

diff --git a/src/service.c b/src/service.c
index 1b95995..7cfeed5 100644
--- a/src/service.c
+++ b/src/service.c
@@ -5606,12 +5606,96 @@ void __connman_service_downgrade_state(struct 
connman_service *service)
                                                CONNMAN_IPCONFIG_TYPE_IPV6);
 }
 
+/*
+ * How many networks are connected at the same time. If more than 1,
+ * then set the rp_filter setting properly (loose routing) so that network
+ * connectivity works ok. This is only done for IPv4 networks as IPv6
+ * does not have rp_filter knob.
+ */
+static int connected_networks_count;
+static int original_rp_filter;
+
+static void service_state_changed(struct connman_service *service,
+                               enum connman_service_state state)
+{
+       enum connman_ipconfig_method method;
+
+       if (service->ipconfig_ipv4 == NULL)
+               return;
+
+       method = __connman_ipconfig_get_method(service->ipconfig_ipv4);
+
+       switch (method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               break;
+       }
+
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               return;
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_READY:
+               break;
+       }
+
+       if (state == CONNMAN_SERVICE_STATE_READY) {
+               /* We are connected */
+               if (connected_networks_count == 1) {
+                       int filter_value;
+                       filter_value = __connman_ipconfig_set_rp_filter(NULL);
+                       if (filter_value < 0)
+                               return;
+
+                       original_rp_filter = filter_value;
+               }
+               connected_networks_count++;
+
+       } else {
+               /* We are disconnected */
+               if (connected_networks_count == 2)
+                       __connman_ipconfig_unset_rp_filter(NULL,
+                                                       original_rp_filter);
+
+               connected_networks_count--;
+               if (connected_networks_count < 0)
+                       connected_networks_count = 0;
+       }
+
+       DBG("service %s ipconfig %p method %d state %d count %d filter %d",
+               service->identifier, service->ipconfig_ipv4, method,
+               service->state, connected_networks_count, original_rp_filter);
+}
+
+static struct connman_notifier service_notifier = {
+       .name                   = "service state changed",
+       .service_state_changed  = service_state_changed,
+};
+
 int __connman_service_init(void)
 {
+       int err;
+
        DBG("");
 
        connection = connman_dbus_get_connection();
 
+       err = connman_notifier_register(&service_notifier);
+       if (err < 0) {
+               dbus_connection_unref(connection);
+               return err;
+       }
+
        service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                                NULL, NULL);
 
@@ -5626,6 +5710,8 @@ void __connman_service_cleanup(void)
 
        DBG("");
 
+       connman_notifier_unregister(&service_notifier);
+
        list = service_list;
        service_list = NULL;
        g_sequence_free(list);
-- 
1.7.1

_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman

Reply via email to