Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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);