linux/net/netfilter/xt_cgroup.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Xtables module to match the process control group.
   4 *
   5 * Might be used to implement individual "per-application" firewall
   6 * policies in contrast to global policies based on control groups.
   7 * Matching is based upon processes tagged to net_cls' classid marker.
   8 *
   9 * (C) 2013 Daniel Borkmann <dborkman@redhat.com>
  10 */
  11
  12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13
  14#include <linux/skbuff.h>
  15#include <linux/module.h>
  16#include <linux/netfilter/x_tables.h>
  17#include <linux/netfilter/xt_cgroup.h>
  18#include <net/sock.h>
  19
  20MODULE_LICENSE("GPL");
  21MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>");
  22MODULE_DESCRIPTION("Xtables: process control group matching");
  23MODULE_ALIAS("ipt_cgroup");
  24MODULE_ALIAS("ip6t_cgroup");
  25
  26static int cgroup_mt_check_v0(const struct xt_mtchk_param *par)
  27{
  28        struct xt_cgroup_info_v0 *info = par->matchinfo;
  29
  30        if (info->invert & ~1)
  31                return -EINVAL;
  32
  33        return 0;
  34}
  35
  36static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
  37{
  38        struct xt_cgroup_info_v1 *info = par->matchinfo;
  39        struct cgroup *cgrp;
  40
  41        if ((info->invert_path & ~1) || (info->invert_classid & ~1))
  42                return -EINVAL;
  43
  44        if (!info->has_path && !info->has_classid) {
  45                pr_info("xt_cgroup: no path or classid specified\n");
  46                return -EINVAL;
  47        }
  48
  49        if (info->has_path && info->has_classid) {
  50                pr_info_ratelimited("path and classid specified\n");
  51                return -EINVAL;
  52        }
  53
  54        info->priv = NULL;
  55        if (info->has_path) {
  56                cgrp = cgroup_get_from_path(info->path);
  57                if (IS_ERR(cgrp)) {
  58                        pr_info_ratelimited("invalid path, errno=%ld\n",
  59                                            PTR_ERR(cgrp));
  60                        return -EINVAL;
  61                }
  62                info->priv = cgrp;
  63        }
  64
  65        return 0;
  66}
  67
  68static int cgroup_mt_check_v2(const struct xt_mtchk_param *par)
  69{
  70        struct xt_cgroup_info_v2 *info = par->matchinfo;
  71        struct cgroup *cgrp;
  72
  73        if ((info->invert_path & ~1) || (info->invert_classid & ~1))
  74                return -EINVAL;
  75
  76        if (!info->has_path && !info->has_classid) {
  77                pr_info("xt_cgroup: no path or classid specified\n");
  78                return -EINVAL;
  79        }
  80
  81        if (info->has_path && info->has_classid) {
  82                pr_info_ratelimited("path and classid specified\n");
  83                return -EINVAL;
  84        }
  85
  86        info->priv = NULL;
  87        if (info->has_path) {
  88                cgrp = cgroup_get_from_path(info->path);
  89                if (IS_ERR(cgrp)) {
  90                        pr_info_ratelimited("invalid path, errno=%ld\n",
  91                                            PTR_ERR(cgrp));
  92                        return -EINVAL;
  93                }
  94                info->priv = cgrp;
  95        }
  96
  97        return 0;
  98}
  99
 100static bool
 101cgroup_mt_v0(const struct sk_buff *skb, struct xt_action_param *par)
 102{
 103        const struct xt_cgroup_info_v0 *info = par->matchinfo;
 104        struct sock *sk = skb->sk;
 105
 106        if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
 107                return false;
 108
 109        return (info->id == sock_cgroup_classid(&skb->sk->sk_cgrp_data)) ^
 110                info->invert;
 111}
 112
 113static bool cgroup_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
 114{
 115        const struct xt_cgroup_info_v1 *info = par->matchinfo;
 116        struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data;
 117        struct cgroup *ancestor = info->priv;
 118        struct sock *sk = skb->sk;
 119
 120        if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
 121                return false;
 122
 123        if (ancestor)
 124                return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^
 125                        info->invert_path;
 126        else
 127                return (info->classid == sock_cgroup_classid(skcd)) ^
 128                        info->invert_classid;
 129}
 130
 131static bool cgroup_mt_v2(const struct sk_buff *skb, struct xt_action_param *par)
 132{
 133        const struct xt_cgroup_info_v2 *info = par->matchinfo;
 134        struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data;
 135        struct cgroup *ancestor = info->priv;
 136        struct sock *sk = skb->sk;
 137
 138        if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
 139                return false;
 140
 141        if (ancestor)
 142                return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^
 143                        info->invert_path;
 144        else
 145                return (info->classid == sock_cgroup_classid(skcd)) ^
 146                        info->invert_classid;
 147}
 148
 149static void cgroup_mt_destroy_v1(const struct xt_mtdtor_param *par)
 150{
 151        struct xt_cgroup_info_v1 *info = par->matchinfo;
 152
 153        if (info->priv)
 154                cgroup_put(info->priv);
 155}
 156
 157static void cgroup_mt_destroy_v2(const struct xt_mtdtor_param *par)
 158{
 159        struct xt_cgroup_info_v2 *info = par->matchinfo;
 160
 161        if (info->priv)
 162                cgroup_put(info->priv);
 163}
 164
 165static struct xt_match cgroup_mt_reg[] __read_mostly = {
 166        {
 167                .name           = "cgroup",
 168                .revision       = 0,
 169                .family         = NFPROTO_UNSPEC,
 170                .checkentry     = cgroup_mt_check_v0,
 171                .match          = cgroup_mt_v0,
 172                .matchsize      = sizeof(struct xt_cgroup_info_v0),
 173                .me             = THIS_MODULE,
 174                .hooks          = (1 << NF_INET_LOCAL_OUT) |
 175                                  (1 << NF_INET_POST_ROUTING) |
 176                                  (1 << NF_INET_LOCAL_IN),
 177        },
 178        {
 179                .name           = "cgroup",
 180                .revision       = 1,
 181                .family         = NFPROTO_UNSPEC,
 182                .checkentry     = cgroup_mt_check_v1,
 183                .match          = cgroup_mt_v1,
 184                .matchsize      = sizeof(struct xt_cgroup_info_v1),
 185                .usersize       = offsetof(struct xt_cgroup_info_v1, priv),
 186                .destroy        = cgroup_mt_destroy_v1,
 187                .me             = THIS_MODULE,
 188                .hooks          = (1 << NF_INET_LOCAL_OUT) |
 189                                  (1 << NF_INET_POST_ROUTING) |
 190                                  (1 << NF_INET_LOCAL_IN),
 191        },
 192        {
 193                .name           = "cgroup",
 194                .revision       = 2,
 195                .family         = NFPROTO_UNSPEC,
 196                .checkentry     = cgroup_mt_check_v2,
 197                .match          = cgroup_mt_v2,
 198                .matchsize      = sizeof(struct xt_cgroup_info_v2),
 199                .usersize       = offsetof(struct xt_cgroup_info_v2, priv),
 200                .destroy        = cgroup_mt_destroy_v2,
 201                .me             = THIS_MODULE,
 202                .hooks          = (1 << NF_INET_LOCAL_OUT) |
 203                                  (1 << NF_INET_POST_ROUTING) |
 204                                  (1 << NF_INET_LOCAL_IN),
 205        },
 206};
 207
 208static int __init cgroup_mt_init(void)
 209{
 210        return xt_register_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
 211}
 212
 213static void __exit cgroup_mt_exit(void)
 214{
 215        xt_unregister_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
 216}
 217
 218module_init(cgroup_mt_init);
 219module_exit(cgroup_mt_exit);
 220