]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
netfilter: introduce support for reject at prerouting stage
authorLaura Garcia Liebana <nevola@gmail.com>
Sun, 31 May 2020 20:26:23 +0000 (22:26 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 30 Jun 2020 16:21:02 +0000 (18:21 +0200)
REJECT statement can be only used in INPUT, FORWARD and OUTPUT
chains. This patch adds support of REJECT, both icmp and tcp
reset, at PREROUTING stage.

The need for this patch comes from the requirement of some
forwarding devices to reject traffic before the natting and
routing decisions.

The main use case is to be able to send a graceful termination
to legitimate clients that, under any circumstances, the NATed
endpoints are not available. This option allows clients to
decide either to perform a reconnection or manage the error in
their side, instead of just dropping the connection and let
them die due to timeout.

It is supported ipv4, ipv6 and inet families for nft
infrastructure.

Signed-off-by: Laura Garcia Liebana <nevola@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/ipv4/netfilter/nf_reject_ipv4.c
net/ipv6/netfilter/nf_reject_ipv6.c
net/netfilter/nft_reject.c

index 2361fdac2c4387591c6b6ad9687af0fcf30ec001..9dcfa4e461b65aaada32b4d5e11946ae3ae919c8 100644 (file)
@@ -96,6 +96,21 @@ void nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb,
 }
 EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put);
 
+static int nf_reject_fill_skb_dst(struct sk_buff *skb_in)
+{
+       struct dst_entry *dst = NULL;
+       struct flowi fl;
+
+       memset(&fl, 0, sizeof(struct flowi));
+       fl.u.ip4.daddr = ip_hdr(skb_in)->saddr;
+       nf_ip_route(dev_net(skb_in->dev), &dst, &fl, false);
+       if (!dst)
+               return -1;
+
+       skb_dst_set(skb_in, dst);
+       return 0;
+}
+
 /* Send RST reply */
 void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook)
 {
@@ -109,6 +124,9 @@ void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook)
        if (!oth)
                return;
 
+       if (hook == NF_INET_PRE_ROUTING && nf_reject_fill_skb_dst(oldskb))
+               return;
+
        if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
                return;
 
@@ -175,6 +193,9 @@ void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
        if (iph->frag_off & htons(IP_OFFSET))
                return;
 
+       if (hook == NF_INET_PRE_ROUTING && nf_reject_fill_skb_dst(skb_in))
+               return;
+
        if (skb_csum_unnecessary(skb_in) || !nf_reject_verify_csum(proto)) {
                icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
                return;
index 5fae66f6667108f5633fa0c69b9a0aad46aa5ea1..4aef6baaa55eba1fab8ca618c03abb5e694426fc 100644 (file)
@@ -126,6 +126,21 @@ void nf_reject_ip6_tcphdr_put(struct sk_buff *nskb,
 }
 EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_put);
 
+static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in)
+{
+       struct dst_entry *dst = NULL;
+       struct flowi fl;
+
+       memset(&fl, 0, sizeof(struct flowi));
+       fl.u.ip6.daddr = ipv6_hdr(skb_in)->saddr;
+       nf_ip6_route(dev_net(skb_in->dev), &dst, &fl, false);
+       if (!dst)
+               return -1;
+
+       skb_dst_set(skb_in, dst);
+       return 0;
+}
+
 void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
 {
        struct net_device *br_indev __maybe_unused;
@@ -154,6 +169,14 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
        fl6.daddr = oip6h->saddr;
        fl6.fl6_sport = otcph->dest;
        fl6.fl6_dport = otcph->source;
+
+       if (hook == NF_INET_PRE_ROUTING) {
+               nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false);
+               if (!dst)
+                       return;
+               skb_dst_set(oldskb, dst);
+       }
+
        fl6.flowi6_oif = l3mdev_master_ifindex(skb_dst(oldskb)->dev);
        fl6.flowi6_mark = IP6_REPLY_MARK(net, oldskb->mark);
        security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6));
@@ -245,6 +268,9 @@ void nf_send_unreach6(struct net *net, struct sk_buff *skb_in,
        if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
                skb_in->dev = net->loopback_dev;
 
+       if (hooknum == NF_INET_PRE_ROUTING && nf_reject6_fill_skb_dst(skb_in))
+               return;
+
        icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0);
 }
 EXPORT_SYMBOL_GPL(nf_send_unreach6);
index 86eafbb0fdd0f60904e7dcb6e82c9c4282753e29..61fb7e8afbf0a8a43d94c4e90e096bdaf6b18724 100644 (file)
@@ -30,7 +30,8 @@ int nft_reject_validate(const struct nft_ctx *ctx,
        return nft_chain_validate_hooks(ctx->chain,
                                        (1 << NF_INET_LOCAL_IN) |
                                        (1 << NF_INET_FORWARD) |
-                                       (1 << NF_INET_LOCAL_OUT));
+                                       (1 << NF_INET_LOCAL_OUT) |
+                                       (1 << NF_INET_PRE_ROUTING));
 }
 EXPORT_SYMBOL_GPL(nft_reject_validate);