From patchwork Thu Mar 19 18:41:22 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Mack X-Patchwork-Id: 452141 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 5CE3814007F for ; Fri, 20 Mar 2015 05:41:46 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751093AbbCSSlc (ORCPT ); Thu, 19 Mar 2015 14:41:32 -0400 Received: from svenfoo.org ([82.94.215.22]:38706 "EHLO mail.zonque.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750997AbbCSSl0 (ORCPT ); Thu, 19 Mar 2015 14:41:26 -0400 Received: from localhost (localhost [127.0.0.1]) by mail.zonque.de (Postfix) with ESMTP id 51CE1C02AC; Thu, 19 Mar 2015 19:41:22 +0100 (CET) Received: from mail.zonque.de ([127.0.0.1]) by localhost (rambrand.bugwerft.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id h0Km5-bmWq35; Thu, 19 Mar 2015 19:41:22 +0100 (CET) Received: from [192.168.178.71] (p5DDC4C06.dip0.t-ipconnect.de [93.220.76.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.zonque.de (Postfix) with ESMTPSA id CA9CBC012A; Thu, 19 Mar 2015 19:41:21 +0100 (CET) Message-ID: <550B1852.2020209@zonque.org> Date: Thu, 19 Mar 2015 19:41:22 +0100 From: Daniel Mack User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.5.0 MIME-Version: 1.0 To: Daniel Borkmann , Alexey Perevalov CC: Pablo Neira Ayuso , netdev Subject: cgroup matches in INPUT chain Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Hi, I'm currently looking into the netclass CGroup controller and its netfilter module in order to build a per-application firewall with it. I'm having trouble understanding the commit log of a00e76349f35 ("netfilter: x_tables: allow to use cgroup match for LOCAL_IN nf hooks"), especially the following paragraph: > It's possible to get classified sk_buff after PREROUTING, due to > socket lookup being done in early_demux (tcp_v4_early_demux). Also > it works for udp as well. What is "after PREROUTING" supposed to mean exactly? After all, the examples in the commit log put the rules into the "INPUT" chain. In my tests, however, NF_INET_LOCAL_IN is iterated before early_demux() is called, and for skbs that do not have a socket assigned, the cgroup match code bails out early, making the rules ineffective. Hence, NF_INET_LOCAL_IN can't work reliably for these matches IMO, as the cgroup rules don't apply to at least every first packet in a TCP stream. Am I missing something? It would also possible to do something similar to what the "socket" module does, and look up a listening socket directly from cgroup_mt() in case skb->sk == NULL. I've attached a patch that implements that and which works for me, but I'm not sure if that's a sane way to go. Thanks, Daniel From 7562d97b6090a36395f0c95b54f5d5ecd6bfc01f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 13 Mar 2015 15:48:41 +0100 Subject: [PATCH RFC] netfilter: x_tables: implement cgroup matching for skb->sk == NULL For skbs which do not have a socket assigned (which is at least the case for every first packet in a stream), the cgroup matching code currently bails out early, which makes cgroup rules ineffective. Only subsequently received packets of the stream (if any) are caught. In order to use this type of matches for a per-application firewall, we need to make sure to catch all packets, including the first one. This patch adds code to look up listening TCP and UDP sockets in case the skb does not have a socket assigned yet. If one is found, the cgroup match is done against the class ID of the found socket. As this makes the implementation specific to tcp/udp, the module now has to implement match functions for both IPv4 and IPv6. Signed-off-by: Daniel Mack --- net/netfilter/xt_cgroup.c | 141 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 124 insertions(+), 17 deletions(-) diff --git a/net/netfilter/xt_cgroup.c b/net/netfilter/xt_cgroup.c index 7198d66..766732f 100644 --- a/net/netfilter/xt_cgroup.c +++ b/net/netfilter/xt_cgroup.c @@ -16,7 +16,10 @@ #include #include #include +#include #include +#include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Daniel Borkmann "); @@ -35,37 +38,141 @@ static int cgroup_mt_check(const struct xt_mtchk_param *par) } static bool -cgroup_mt(const struct sk_buff *skb, struct xt_action_param *par) +cgroup_mt_ipv4(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_cgroup_info *info = par->matchinfo; + struct sock *sk = skb->sk; + bool ret; - if (skb->sk == NULL) - return false; + if (!sk) { + const struct iphdr *iph = ip_hdr(skb); + struct udphdr hdr, *hp; - return (info->id == skb->sk->sk_classid) ^ info->invert; + hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(hdr), &hdr); + if (!hp) + return false; + + switch (iph->protocol) { + case IPPROTO_UDP: + sk = udp4_lib_lookup(dev_net(skb->dev), + iph->saddr, hp->source, + iph->daddr, hp->dest, + par->in->ifindex); + break; + + case IPPROTO_TCP: + sk = __inet_lookup(dev_net(skb->dev), &tcp_hashinfo, + iph->saddr, hp->source, + iph->daddr, hp->dest, + par->in->ifindex); + break; + + default: + break; + } + + if (!sk) + return false; + } + + ret = (info->id == sk->sk_classid) ^ info->invert; + + if (sk != skb->sk) + sock_gen_put(sk); + + return ret; +} + +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) +static bool +cgroup_mt_ipv6(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_cgroup_info *info = par->matchinfo; + struct sock *sk = skb->sk; + bool ret; + + if (!sk) { + const struct ipv6hdr *iph = ipv6_hdr(skb); + struct udphdr hdr, *hp; + int tproto, thoff = 0; + + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); + if (tproto < 0) + return false; + + hp = skb_header_pointer(skb, thoff, sizeof(hdr), &hdr); + if (!hp) + return false; + + switch (tproto) { + case IPPROTO_UDP: + sk = udp6_lib_lookup(dev_net(skb->dev), + &iph->saddr, hp->source, + &iph->daddr, hp->dest, + par->in->ifindex); + break; + + case IPPROTO_TCP: + sk = inet6_lookup(dev_net(skb->dev), &tcp_hashinfo, + &iph->saddr, hp->source, + &iph->daddr, hp->dest, + par->in->ifindex); + break; + + default: + break; + } + + if (!sk) + return false; + } + + ret = (info->id == sk->sk_classid) ^ info->invert; + + if (sk != skb->sk) + sock_gen_put(sk); + + return ret; } +#endif -static struct xt_match cgroup_mt_reg __read_mostly = { - .name = "cgroup", - .revision = 0, - .family = NFPROTO_UNSPEC, - .checkentry = cgroup_mt_check, - .match = cgroup_mt, - .matchsize = sizeof(struct xt_cgroup_info), - .me = THIS_MODULE, - .hooks = (1 << NF_INET_LOCAL_OUT) | - (1 << NF_INET_POST_ROUTING) | - (1 << NF_INET_LOCAL_IN), +static struct xt_match cgroup_mt_reg[] __read_mostly = { + { + .name = "cgroup", + .revision = 0, + .family = NFPROTO_IPV4, + .checkentry = cgroup_mt_check, + .match = cgroup_mt_ipv4, + .matchsize = sizeof(struct xt_cgroup_info), + .me = THIS_MODULE, + .hooks = (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_IN), + }, +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) + { + .name = "cgroup", + .revision = 0, + .family = NFPROTO_IPV6, + .checkentry = cgroup_mt_check, + .match = cgroup_mt_ipv6, + .matchsize = sizeof(struct xt_cgroup_info), + .me = THIS_MODULE, + .hooks = (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_IN), + }, +#endif }; static int __init cgroup_mt_init(void) { - return xt_register_match(&cgroup_mt_reg); + return xt_register_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg)); } static void __exit cgroup_mt_exit(void) { - xt_unregister_match(&cgroup_mt_reg); + xt_unregister_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg)); } module_init(cgroup_mt_init); -- 2.3.2