net: add a sysctl to reflect the fwmark on replies
authorLorenzo Colitti <lorenzo@google.com>
Tue, 18 Mar 2014 11:52:27 +0000 (20:52 +0900)
committerJP Abgrall <jpa@google.com>
Fri, 16 May 2014 18:41:19 +0000 (18:41 +0000)
Kernel-originated IP packets that have no user socket associated
with them (e.g., ICMP errors and echo replies, TCP RSTs, etc.)
are emitted with a mark of zero. Add a sysctl to make them have
the same mark as the packet they are replying to.

This allows an administrator that wishes to do so to use
mark-based routing, firewalling, etc. for these replies by
marking the original packets inbound.

Tested using user-mode linux:
 - ICMP/ICMPv6 echo replies and errors.
 - TCP RST packets (IPv4 and IPv6).

Change-Id: I6873d973196797bcf32e2e91976df647c7e8b85a
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Documentation/networking/ip-sysctl.txt
include/net/ip.h
include/net/ipv6.h
include/net/netns/ipv4.h
include/net/netns/ipv6.h
net/ipv4/icmp.c
net/ipv4/ip_output.c
net/ipv4/sysctl_net_ipv4.c
net/ipv6/icmp.c
net/ipv6/sysctl_net_ipv6.c
net/ipv6/tcp_ipv6.c

index 3458d6343e01de0e66f0d3f2e09efc61bab3b886..77731bba5c674b352f6f76925e108fb3330a7f55 100644 (file)
@@ -22,6 +22,13 @@ ip_no_pmtu_disc - BOOLEAN
 min_pmtu - INTEGER
        default 552 - minimum discovered Path MTU
 
+fwmark_reflect - BOOLEAN
+       Controls the fwmark of kernel-generated IPv4 reply packets that are not
+       associated with a socket for example, TCP RSTs or ICMP echo replies).
+       If unset, these packets have a fwmark of zero. If set, they have the
+       fwmark of the packet they are replying to.
+       Default: 0
+
 route/max_size - INTEGER
        Maximum number of routes allowed in the kernel.  Increase
        this when using large numbers of interfaces and/or routes.
@@ -1087,6 +1094,13 @@ conf/all/forwarding - BOOLEAN
 proxy_ndp - BOOLEAN
        Do proxy ndp.
 
+fwmark_reflect - BOOLEAN
+       Controls the fwmark of kernel-generated IPv6 reply packets that are not
+       associated with a socket for example, TCP RSTs or ICMPv6 echo replies).
+       If unset, these packets have a fwmark of zero. If set, they have the
+       fwmark of the packet they are replying to.
+       Default: 0
+
 conf/interface/*:
        Change special settings per interface.
 
index a68f838a132c522df397b9398f8f2d64646008c0..509b88079270b21f0e4729fb36eb028ebfaf7402 100644 (file)
@@ -225,6 +225,9 @@ extern void ipfrag_init(void);
 
 extern void ip_static_sysctl_init(void);
 
+#define IP4_REPLY_MARK(net, mark) \
+       ((net)->ipv4.sysctl_fwmark_reflect ? (mark) : 0)
+
 static inline bool ip_is_fragment(const struct iphdr *iph)
 {
        return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0;
index ab47582f6c0b46fe808e9fc53a104a23de9196b1..cc344ca9d0ae2762d158ef6a87b3147df4cb7a87 100644 (file)
@@ -111,6 +111,9 @@ struct frag_hdr {
 
 #define        IP6_MF  0x0001
 
+#define IP6_REPLY_MARK(net, mark) \
+       ((net)->ipv6.sysctl.fwmark_reflect ? (mark) : 0)
+
 #include <net/sock.h>
 
 /* sysctls */
index 2ba9de89e8ec778990e8b2ff5183fd3e97eee1aa..222461a7cc5de64ae9645b74a0c5fdb6a01d624f 100644 (file)
@@ -64,6 +64,8 @@ struct netns_ipv4 {
 
        int sysctl_tcp_ecn;
 
+       int sysctl_fwmark_reflect;
+
        kgid_t sysctl_ping_group_range[2];
        long sysctl_tcp_mem[3];
 
index 005e2c2e39a9022bad13f4205343263f1821cb22..4b9f99e3a91c9cfc02f1f48d71ef30f2dec30880 100644 (file)
@@ -28,6 +28,7 @@ struct netns_sysctl_ipv6 {
        int ip6_rt_mtu_expires;
        int ip6_rt_min_advmss;
        int icmpv6_time;
+       int fwmark_reflect;
 };
 
 struct netns_ipv6 {
index 562efd91f457f324335ec6ee75ff469d28266d72..cc38f44306ed29bca916f2428332bebee6573a8e 100644 (file)
@@ -337,6 +337,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        struct sock *sk;
        struct inet_sock *inet;
        __be32 daddr, saddr;
+       u32 mark = IP4_REPLY_MARK(net, skb->mark);
 
        if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb))
                return;
@@ -349,6 +350,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        icmp_param->data.icmph.checksum = 0;
 
        inet->tos = ip_hdr(skb)->tos;
+       sk->sk_mark = mark;
        daddr = ipc.addr = ip_hdr(skb)->saddr;
        saddr = fib_compute_spec_dst(skb);
        ipc.opt = NULL;
@@ -361,6 +363,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        memset(&fl4, 0, sizeof(fl4));
        fl4.daddr = daddr;
        fl4.saddr = saddr;
+       fl4.flowi4_mark = mark;
        fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
        fl4.flowi4_proto = IPPROTO_ICMP;
        security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
@@ -379,7 +382,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
                                        struct flowi4 *fl4,
                                        struct sk_buff *skb_in,
                                        const struct iphdr *iph,
-                                       __be32 saddr, u8 tos,
+                                       __be32 saddr, u8 tos, u32 mark,
                                        int type, int code,
                                        struct icmp_bxm *param)
 {
@@ -391,6 +394,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
        fl4->daddr = (param->replyopts.opt.opt.srr ?
                      param->replyopts.opt.opt.faddr : iph->saddr);
        fl4->saddr = saddr;
+       fl4->flowi4_mark = mark;
        fl4->flowi4_tos = RT_TOS(tos);
        fl4->flowi4_proto = IPPROTO_ICMP;
        fl4->fl4_icmp_type = type;
@@ -488,6 +492,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        struct flowi4 fl4;
        __be32 saddr;
        u8  tos;
+       u32 mark;
        struct net *net;
        struct sock *sk;
 
@@ -584,6 +589,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) |
                                           IPTOS_PREC_INTERNETCONTROL) :
                                          iph->tos;
+       mark = IP4_REPLY_MARK(net, skb_in->mark);
 
        if (ip_options_echo(&icmp_param.replyopts.opt.opt, skb_in))
                goto out_unlock;
@@ -600,11 +606,12 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        icmp_param.skb    = skb_in;
        icmp_param.offset = skb_network_offset(skb_in);
        inet_sk(sk)->tos = tos;
+       sk->sk_mark = mark;
        ipc.addr = iph->saddr;
        ipc.opt = &icmp_param.replyopts.opt;
        ipc.tx_flags = 0;
 
-       rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos,
+       rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, mark,
                               type, code, &icmp_param);
        if (IS_ERR(rt))
                goto out_unlock;
index 4bcabf3ab4cad3bdc43f5b9ed33eba9c1357557d..c2ee385cecfbdf68c16b4c5987614a54f0169407 100644 (file)
@@ -1497,7 +1497,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
                        daddr = replyopts.opt.opt.faddr;
        }
 
-       flowi4_init_output(&fl4, arg->bound_dev_if, 0,
+       flowi4_init_output(&fl4, arg->bound_dev_if,
+                          IP4_REPLY_MARK(net, skb->mark),
                           RT_TOS(arg->tos),
                           RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol,
                           ip_reply_arg_flowi_flags(arg),
index f9bb5d7488e0d3d8599efecb5db1b9386cbb25f9..e6cdcb32b3395a1d7b3ced052f97f2a3e0eee247 100644 (file)
@@ -859,6 +859,13 @@ static struct ctl_table ipv4_net_table[] = {
                .mode           = 0644,
                .proc_handler   = ipv4_tcp_mem,
        },
+       {
+               .procname       = "fwmark_reflect",
+               .data           = &init_net.ipv4.sysctl_fwmark_reflect,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
        { }
 };
 
index 1d2902e617866eee6553ff1197ee464d745886ef..28da4003e842a297ae175d4564b66839b969fb20 100644 (file)
@@ -397,6 +397,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        int len;
        int hlimit;
        int err = 0;
+       u32 mark = IP6_REPLY_MARK(net, skb->mark);
 
        if ((u8 *)hdr < skb->head ||
            (skb->network_header + sizeof(*hdr)) > skb->tail)
@@ -462,6 +463,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        fl6.daddr = hdr->saddr;
        if (saddr)
                fl6.saddr = *saddr;
+       fl6.flowi6_mark = mark;
        fl6.flowi6_oif = iif;
        fl6.fl6_icmp_type = type;
        fl6.fl6_icmp_code = code;
@@ -470,6 +472,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        sk = icmpv6_xmit_lock(net);
        if (sk == NULL)
                return;
+       sk->sk_mark = mark;
        np = inet6_sk(sk);
 
        if (!icmpv6_xrlim_allow(sk, type, &fl6))
@@ -551,6 +554,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
        struct dst_entry *dst;
        int err = 0;
        int hlimit;
+       u32 mark = IP6_REPLY_MARK(net, skb->mark);
 
        saddr = &ipv6_hdr(skb)->daddr;
 
@@ -567,11 +571,13 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
                fl6.saddr = *saddr;
        fl6.flowi6_oif = skb->dev->ifindex;
        fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
+       fl6.flowi6_mark = mark;
        security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
 
        sk = icmpv6_xmit_lock(net);
        if (sk == NULL)
                return;
+       sk->sk_mark = mark;
        np = inet6_sk(sk);
 
        if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
index e85c48bd404f4036b0c4e7db1e29bd6a92192f75..53a9f5a64536fd7b55199b17b323a76d80841049 100644 (file)
@@ -24,6 +24,13 @@ static ctl_table ipv6_table_template[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
+       {
+               .procname       = "fwmark_reflect",
+               .data           = &init_net.ipv6.sysctl.fwmark_reflect,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
        { }
 };
 
index 0a17ed9eaf390c4b84a6264c03697f60af9281a1..71545cb17ab6552d410aa0e04e3bfb8c41d56140 100644 (file)
@@ -791,6 +791,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
        fl6.flowi6_proto = IPPROTO_TCP;
        if (ipv6_addr_type(&fl6.daddr) & IPV6_ADDR_LINKLOCAL)
                fl6.flowi6_oif = inet6_iif(skb);
+       fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);
        fl6.fl6_dport = t1->dest;
        fl6.fl6_sport = t1->source;
        security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));