The policy routing doesn't work because:

(local process) -> (routing decision) -> [raw:output] ->
  contrack -> [mangle:output] -> (reroute check) ->
  [nat:output] -> [filter:output] -> ...

When the local process generates a packet, the network stack
takes the routing decision really early on. In our case earlier
then we are able to mark them with iptables. The marking happens
in the mangle table and therefore the policy routing table is
not considered at this point.

My naive assumption was that the reroute check would be done
unconditionally. Unfortunately, this is not true.

The reroute check is only done if the packet was modified in
the mangle output chain. So for example for the ICMP protocol:

net/ipv4/ping.c:
  ping_v4_sendmsg()
    ip_route_output_flow()

And the rerouting happens here:

net/ipv4/netfilter/iptable_mangle.c
  ipt_mangle_out()
    ip_route_me_harder()
      ip_route_output_key()
        ip_route_output_flow()

So let's reroute the packets via

iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 192.168.1.100

The policy routing table is not completely useless. For example the
process could mark its packet on its own. Another possible way is to
have a cgroup controller setting the SO_MARK or we teach the policy
routing table to match on sk_classid (cls cgroup controller).

Obviously, we don't need both the policy routing table and the
NAT rules. But I am reluctant to rip out the policy routing code at this
point. Maybe someone is using it already.

Reported by: Gianfranco Casanova <gianfranco.casan...@gmail.com>
---
 src/session.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/src/session.c b/src/session.c
index 08facc1..388aae7 100644
--- a/src/session.c
+++ b/src/session.c
@@ -69,6 +69,7 @@ struct connman_session {
 
        enum connman_session_id_type id_type;
        struct firewall_context *fw;
+       int snat_id;
        uint32_t mark;
        int index;
        char *gateway;
@@ -365,6 +366,63 @@ static void add_default_route(struct connman_session 
*session)
                DBG("session %p %s", session, strerror(-err));
 }
 
+static void del_nat_rules(struct connman_session *session)
+{
+       int err;
+
+       if (!session->fw || session->snat_id == 0)
+               return;
+
+       err = __connman_firewall_disable_rule(session->fw, session->snat_id);
+       if (err < 0) {
+               DBG("could not disable SNAT rule");
+               return;
+       }
+
+       err = __connman_firewall_remove_rule(session->fw, session->snat_id);
+       if (err < 0)
+               DBG("could not remove SNAT rule");
+
+
+       session->snat_id = 0;
+}
+
+static void add_nat_rules(struct connman_session *session)
+{
+       struct connman_ipconfig *ipconfig;
+       const char *addr;
+       char *ifname;
+       int index, id, err;
+
+       if (!session->fw)
+               return;
+
+       DBG("");
+
+       ipconfig = __connman_service_get_ip4config(session->service);
+       index = __connman_ipconfig_get_index(ipconfig);
+       ifname = connman_inet_ifname(index);
+       addr = __connman_ipconfig_get_local(ipconfig);
+
+       id = __connman_firewall_add_rule(session->fw, "nat", "POSTROUTING",
+                               "-o %s -j SNAT --to-source %s",
+                               ifname, addr);
+       g_free(ifname);
+       if (id < 0) {
+               DBG("failed to add SNAT rule");
+               return;
+       }
+
+       err = __connman_firewall_enable_rule(session->fw, id);
+       if (err < 0) {
+               DBG("could not enable SNAT rule");
+               __connman_firewall_remove_rule(session->fw, id);
+               return;
+       }
+
+       session->snat_id = id;
+}
+
 static void cleanup_routing_table(struct connman_session *session)
 {
        DBG("");
@@ -387,6 +445,12 @@ static void update_routing_table(struct connman_session 
*session)
        add_default_route(session);
 }
 
+static void update_nat_rules(struct connman_session *session)
+{
+       del_nat_rules(session);
+       add_nat_rules(session);
+}
+
 static void destroy_policy_config(struct connman_session *session)
 {
        if (!policy) {
@@ -1482,6 +1546,7 @@ static void update_session_state(struct connman_session 
*session)
        DBG("session %p state %s", session, state2string(state));
 
        update_routing_table(session);
+       update_nat_rules(session);
        session_notify(session);
 }
 
-- 
2.4.3

_______________________________________________
connman mailing list
connman@connman.net
https://lists.connman.net/mailman/listinfo/connman

Reply via email to