0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0013
0014 #include <linux/skbuff.h>
0015 #include <linux/module.h>
0016 #include <linux/netfilter/x_tables.h>
0017 #include <linux/netfilter/xt_cgroup.h>
0018 #include <net/sock.h>
0019
0020 MODULE_LICENSE("GPL");
0021 MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>");
0022 MODULE_DESCRIPTION("Xtables: process control group matching");
0023 MODULE_ALIAS("ipt_cgroup");
0024 MODULE_ALIAS("ip6t_cgroup");
0025
0026 static int cgroup_mt_check_v0(const struct xt_mtchk_param *par)
0027 {
0028 struct xt_cgroup_info_v0 *info = par->matchinfo;
0029
0030 if (info->invert & ~1)
0031 return -EINVAL;
0032
0033 return 0;
0034 }
0035
0036 static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
0037 {
0038 struct xt_cgroup_info_v1 *info = par->matchinfo;
0039 struct cgroup *cgrp;
0040
0041 if ((info->invert_path & ~1) || (info->invert_classid & ~1))
0042 return -EINVAL;
0043
0044 if (!info->has_path && !info->has_classid) {
0045 pr_info("xt_cgroup: no path or classid specified\n");
0046 return -EINVAL;
0047 }
0048
0049 if (info->has_path && info->has_classid) {
0050 pr_info_ratelimited("path and classid specified\n");
0051 return -EINVAL;
0052 }
0053
0054 info->priv = NULL;
0055 if (info->has_path) {
0056 cgrp = cgroup_get_from_path(info->path);
0057 if (IS_ERR(cgrp)) {
0058 pr_info_ratelimited("invalid path, errno=%ld\n",
0059 PTR_ERR(cgrp));
0060 return -EINVAL;
0061 }
0062 info->priv = cgrp;
0063 }
0064
0065 return 0;
0066 }
0067
0068 static int cgroup_mt_check_v2(const struct xt_mtchk_param *par)
0069 {
0070 struct xt_cgroup_info_v2 *info = par->matchinfo;
0071 struct cgroup *cgrp;
0072
0073 if ((info->invert_path & ~1) || (info->invert_classid & ~1))
0074 return -EINVAL;
0075
0076 if (!info->has_path && !info->has_classid) {
0077 pr_info("xt_cgroup: no path or classid specified\n");
0078 return -EINVAL;
0079 }
0080
0081 if (info->has_path && info->has_classid) {
0082 pr_info_ratelimited("path and classid specified\n");
0083 return -EINVAL;
0084 }
0085
0086 info->priv = NULL;
0087 if (info->has_path) {
0088 cgrp = cgroup_get_from_path(info->path);
0089 if (IS_ERR(cgrp)) {
0090 pr_info_ratelimited("invalid path, errno=%ld\n",
0091 PTR_ERR(cgrp));
0092 return -EINVAL;
0093 }
0094 info->priv = cgrp;
0095 }
0096
0097 return 0;
0098 }
0099
0100 static bool
0101 cgroup_mt_v0(const struct sk_buff *skb, struct xt_action_param *par)
0102 {
0103 const struct xt_cgroup_info_v0 *info = par->matchinfo;
0104 struct sock *sk = skb->sk;
0105
0106 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
0107 return false;
0108
0109 return (info->id == sock_cgroup_classid(&skb->sk->sk_cgrp_data)) ^
0110 info->invert;
0111 }
0112
0113 static bool cgroup_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
0114 {
0115 const struct xt_cgroup_info_v1 *info = par->matchinfo;
0116 struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data;
0117 struct cgroup *ancestor = info->priv;
0118 struct sock *sk = skb->sk;
0119
0120 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
0121 return false;
0122
0123 if (ancestor)
0124 return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^
0125 info->invert_path;
0126 else
0127 return (info->classid == sock_cgroup_classid(skcd)) ^
0128 info->invert_classid;
0129 }
0130
0131 static bool cgroup_mt_v2(const struct sk_buff *skb, struct xt_action_param *par)
0132 {
0133 const struct xt_cgroup_info_v2 *info = par->matchinfo;
0134 struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data;
0135 struct cgroup *ancestor = info->priv;
0136 struct sock *sk = skb->sk;
0137
0138 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
0139 return false;
0140
0141 if (ancestor)
0142 return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^
0143 info->invert_path;
0144 else
0145 return (info->classid == sock_cgroup_classid(skcd)) ^
0146 info->invert_classid;
0147 }
0148
0149 static void cgroup_mt_destroy_v1(const struct xt_mtdtor_param *par)
0150 {
0151 struct xt_cgroup_info_v1 *info = par->matchinfo;
0152
0153 if (info->priv)
0154 cgroup_put(info->priv);
0155 }
0156
0157 static void cgroup_mt_destroy_v2(const struct xt_mtdtor_param *par)
0158 {
0159 struct xt_cgroup_info_v2 *info = par->matchinfo;
0160
0161 if (info->priv)
0162 cgroup_put(info->priv);
0163 }
0164
0165 static struct xt_match cgroup_mt_reg[] __read_mostly = {
0166 {
0167 .name = "cgroup",
0168 .revision = 0,
0169 .family = NFPROTO_UNSPEC,
0170 .checkentry = cgroup_mt_check_v0,
0171 .match = cgroup_mt_v0,
0172 .matchsize = sizeof(struct xt_cgroup_info_v0),
0173 .me = THIS_MODULE,
0174 .hooks = (1 << NF_INET_LOCAL_OUT) |
0175 (1 << NF_INET_POST_ROUTING) |
0176 (1 << NF_INET_LOCAL_IN),
0177 },
0178 {
0179 .name = "cgroup",
0180 .revision = 1,
0181 .family = NFPROTO_UNSPEC,
0182 .checkentry = cgroup_mt_check_v1,
0183 .match = cgroup_mt_v1,
0184 .matchsize = sizeof(struct xt_cgroup_info_v1),
0185 .usersize = offsetof(struct xt_cgroup_info_v1, priv),
0186 .destroy = cgroup_mt_destroy_v1,
0187 .me = THIS_MODULE,
0188 .hooks = (1 << NF_INET_LOCAL_OUT) |
0189 (1 << NF_INET_POST_ROUTING) |
0190 (1 << NF_INET_LOCAL_IN),
0191 },
0192 {
0193 .name = "cgroup",
0194 .revision = 2,
0195 .family = NFPROTO_UNSPEC,
0196 .checkentry = cgroup_mt_check_v2,
0197 .match = cgroup_mt_v2,
0198 .matchsize = sizeof(struct xt_cgroup_info_v2),
0199 .usersize = offsetof(struct xt_cgroup_info_v2, priv),
0200 .destroy = cgroup_mt_destroy_v2,
0201 .me = THIS_MODULE,
0202 .hooks = (1 << NF_INET_LOCAL_OUT) |
0203 (1 << NF_INET_POST_ROUTING) |
0204 (1 << NF_INET_LOCAL_IN),
0205 },
0206 };
0207
0208 static int __init cgroup_mt_init(void)
0209 {
0210 return xt_register_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
0211 }
0212
0213 static void __exit cgroup_mt_exit(void)
0214 {
0215 xt_unregister_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
0216 }
0217
0218 module_init(cgroup_mt_init);
0219 module_exit(cgroup_mt_exit);