File Index
Files and directories
Sign in
FILE

xt_cgroup.c

File preview with syntax highlighting.

linux-6.19.14/net/netfilter/xt_cgroup.c C
  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
 26#define NET_CLS_CLASSID_INVALID_MSG "xt_cgroup: classid invalid without net_cls cgroups\n"
 27
 28static int cgroup_mt_check_v0(const struct xt_mtchk_param *par)
 29{
 30	struct xt_cgroup_info_v0 *info = par->matchinfo;
 31
 32	if (info->invert & ~1)
 33		return -EINVAL;
 34
 35	if (!IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)) {
 36		pr_info(NET_CLS_CLASSID_INVALID_MSG);
 37		return -EINVAL;
 38	}
 39
 40	return 0;
 41}
 42
 43static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
 44{
 45	struct xt_cgroup_info_v1 *info = par->matchinfo;
 46	struct cgroup *cgrp;
 47
 48	if ((info->invert_path & ~1) || (info->invert_classid & ~1))
 49		return -EINVAL;
 50
 51	if (!info->has_path && !info->has_classid) {
 52		pr_info("xt_cgroup: no path or classid specified\n");
 53		return -EINVAL;
 54	}
 55
 56	if (info->has_path && info->has_classid) {
 57		pr_info_ratelimited("path and classid specified\n");
 58		return -EINVAL;
 59	}
 60
 61	if (info->has_classid && !IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)) {
 62		pr_info(NET_CLS_CLASSID_INVALID_MSG);
 63		return -EINVAL;
 64	}
 65
 66	info->priv = NULL;
 67	if (info->has_path) {
 68		if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
 69			return -ENAMETOOLONG;
 70
 71		cgrp = cgroup_get_from_path(info->path);
 72		if (IS_ERR(cgrp)) {
 73			pr_info_ratelimited("invalid path, errno=%ld\n",
 74					    PTR_ERR(cgrp));
 75			return -EINVAL;
 76		}
 77		info->priv = cgrp;
 78	}
 79
 80	return 0;
 81}
 82
 83static int cgroup_mt_check_v2(const struct xt_mtchk_param *par)
 84{
 85	struct xt_cgroup_info_v2 *info = par->matchinfo;
 86	struct cgroup *cgrp;
 87
 88	if ((info->invert_path & ~1) || (info->invert_classid & ~1))
 89		return -EINVAL;
 90
 91	if (!info->has_path && !info->has_classid) {
 92		pr_info("xt_cgroup: no path or classid specified\n");
 93		return -EINVAL;
 94	}
 95
 96	if (info->has_path && info->has_classid) {
 97		pr_info_ratelimited("path and classid specified\n");
 98		return -EINVAL;
 99	}
100
101	if (info->has_classid && !IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)) {
102		pr_info(NET_CLS_CLASSID_INVALID_MSG);
103		return -EINVAL;
104	}
105
106	info->priv = NULL;
107	if (info->has_path) {
108		if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
109			return -ENAMETOOLONG;
110
111		cgrp = cgroup_get_from_path(info->path);
112		if (IS_ERR(cgrp)) {
113			pr_info_ratelimited("invalid path, errno=%ld\n",
114					    PTR_ERR(cgrp));
115			return -EINVAL;
116		}
117		info->priv = cgrp;
118	}
119
120	return 0;
121}
122
123static bool
124cgroup_mt_v0(const struct sk_buff *skb, struct xt_action_param *par)
125{
126#ifdef CONFIG_CGROUP_NET_CLASSID
127	const struct xt_cgroup_info_v0 *info = par->matchinfo;
128	struct sock *sk = skb->sk;
129
130	if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
131		return false;
132
133	return (info->id == sock_cgroup_classid(&skb->sk->sk_cgrp_data)) ^
134		info->invert;
135#endif
136	return false;
137}
138
139static bool cgroup_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
140{
141	const struct xt_cgroup_info_v1 *info = par->matchinfo;
142	struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data;
143	struct cgroup *ancestor = info->priv;
144	struct sock *sk = skb->sk;
145
146	if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
147		return false;
148
149	if (ancestor)
150		return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^
151			info->invert_path;
152#ifdef CONFIG_CGROUP_NET_CLASSID
153	else
154		return (info->classid == sock_cgroup_classid(skcd)) ^
155			info->invert_classid;
156#endif
157	return false;
158}
159
160static bool cgroup_mt_v2(const struct sk_buff *skb, struct xt_action_param *par)
161{
162	const struct xt_cgroup_info_v2 *info = par->matchinfo;
163	struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data;
164	struct cgroup *ancestor = info->priv;
165	struct sock *sk = skb->sk;
166
167	if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
168		return false;
169
170	if (ancestor)
171		return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^
172			info->invert_path;
173#ifdef CONFIG_CGROUP_NET_CLASSID
174	else
175		return (info->classid == sock_cgroup_classid(skcd)) ^
176			info->invert_classid;
177#endif
178	return false;
179}
180
181static void cgroup_mt_destroy_v1(const struct xt_mtdtor_param *par)
182{
183	struct xt_cgroup_info_v1 *info = par->matchinfo;
184
185	if (info->priv)
186		cgroup_put(info->priv);
187}
188
189static void cgroup_mt_destroy_v2(const struct xt_mtdtor_param *par)
190{
191	struct xt_cgroup_info_v2 *info = par->matchinfo;
192
193	if (info->priv)
194		cgroup_put(info->priv);
195}
196
197static struct xt_match cgroup_mt_reg[] __read_mostly = {
198	{
199		.name		= "cgroup",
200		.revision	= 0,
201		.family		= NFPROTO_UNSPEC,
202		.checkentry	= cgroup_mt_check_v0,
203		.match		= cgroup_mt_v0,
204		.matchsize	= sizeof(struct xt_cgroup_info_v0),
205		.me		= THIS_MODULE,
206		.hooks		= (1 << NF_INET_LOCAL_OUT) |
207				  (1 << NF_INET_POST_ROUTING) |
208				  (1 << NF_INET_LOCAL_IN),
209	},
210	{
211		.name		= "cgroup",
212		.revision	= 1,
213		.family		= NFPROTO_UNSPEC,
214		.checkentry	= cgroup_mt_check_v1,
215		.match		= cgroup_mt_v1,
216		.matchsize	= sizeof(struct xt_cgroup_info_v1),
217		.usersize	= offsetof(struct xt_cgroup_info_v1, priv),
218		.destroy	= cgroup_mt_destroy_v1,
219		.me		= THIS_MODULE,
220		.hooks		= (1 << NF_INET_LOCAL_OUT) |
221				  (1 << NF_INET_POST_ROUTING) |
222				  (1 << NF_INET_LOCAL_IN),
223	},
224	{
225		.name		= "cgroup",
226		.revision	= 2,
227		.family		= NFPROTO_UNSPEC,
228		.checkentry	= cgroup_mt_check_v2,
229		.match		= cgroup_mt_v2,
230		.matchsize	= sizeof(struct xt_cgroup_info_v2),
231		.usersize	= offsetof(struct xt_cgroup_info_v2, priv),
232		.destroy	= cgroup_mt_destroy_v2,
233		.me		= THIS_MODULE,
234		.hooks		= (1 << NF_INET_LOCAL_OUT) |
235				  (1 << NF_INET_POST_ROUTING) |
236				  (1 << NF_INET_LOCAL_IN),
237	},
238};
239
240static int __init cgroup_mt_init(void)
241{
242	return xt_register_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
243}
244
245static void __exit cgroup_mt_exit(void)
246{
247	xt_unregister_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
248}
249
250module_init(cgroup_mt_init);
251module_exit(cgroup_mt_exit);
~/ Read-only file preview.