xt_cgroup.c (5583B)
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);