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 20 MODULE_LICENSE("GPL"); 21 MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>"); 22 MODULE_DESCRIPTION("Xtables: process control group matching"); 23 MODULE_ALIAS("ipt_cgroup"); 24 MODULE_ALIAS("ip6t_cgroup"); 25 26 #define NET_CLS_CLASSID_INVALID_MSG "xt_cgroup: classid invalid without net_cls cgroups\n" 27 28 static 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 43 static 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 cgrp = cgroup_get_from_path(info->path); 69 if (IS_ERR(cgrp)) { 70 pr_info_ratelimited("invalid path, errno=%ld\n", 71 PTR_ERR(cgrp)); 72 return -EINVAL; 73 } 74 info->priv = cgrp; 75 } 76 77 return 0; 78 } 79 80 static int cgroup_mt_check_v2(const struct xt_mtchk_param *par) 81 { 82 struct xt_cgroup_info_v2 *info = par->matchinfo; 83 struct cgroup *cgrp; 84 85 if ((info->invert_path & ~1) || (info->invert_classid & ~1)) 86 return -EINVAL; 87 88 if (!info->has_path && !info->has_classid) { 89 pr_info("xt_cgroup: no path or classid specified\n"); 90 return -EINVAL; 91 } 92 93 if (info->has_path && info->has_classid) { 94 pr_info_ratelimited("path and classid specified\n"); 95 return -EINVAL; 96 } 97 98 if (info->has_classid && !IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)) { 99 pr_info(NET_CLS_CLASSID_INVALID_MSG); 100 return -EINVAL; 101 } 102 103 info->priv = NULL; 104 if (info->has_path) { 105 cgrp = cgroup_get_from_path(info->path); 106 if (IS_ERR(cgrp)) { 107 pr_info_ratelimited("invalid path, errno=%ld\n", 108 PTR_ERR(cgrp)); 109 return -EINVAL; 110 } 111 info->priv = cgrp; 112 } 113 114 return 0; 115 } 116 117 static bool 118 cgroup_mt_v0(const struct sk_buff *skb, struct xt_action_param *par) 119 { 120 #ifdef CONFIG_CGROUP_NET_CLASSID 121 const struct xt_cgroup_info_v0 *info = par->matchinfo; 122 struct sock *sk = skb->sk; 123 124 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk))) 125 return false; 126 127 return (info->id == sock_cgroup_classid(&skb->sk->sk_cgrp_data)) ^ 128 info->invert; 129 #endif 130 return false; 131 } 132 133 static bool cgroup_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) 134 { 135 const struct xt_cgroup_info_v1 *info = par->matchinfo; 136 struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data; 137 struct cgroup *ancestor = info->priv; 138 struct sock *sk = skb->sk; 139 140 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk))) 141 return false; 142 143 if (ancestor) 144 return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^ 145 info->invert_path; 146 #ifdef CONFIG_CGROUP_NET_CLASSID 147 else 148 return (info->classid == sock_cgroup_classid(skcd)) ^ 149 info->invert_classid; 150 #endif 151 return false; 152 } 153 154 static bool cgroup_mt_v2(const struct sk_buff *skb, struct xt_action_param *par) 155 { 156 const struct xt_cgroup_info_v2 *info = par->matchinfo; 157 struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data; 158 struct cgroup *ancestor = info->priv; 159 struct sock *sk = skb->sk; 160 161 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk))) 162 return false; 163 164 if (ancestor) 165 return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^ 166 info->invert_path; 167 #ifdef CONFIG_CGROUP_NET_CLASSID 168 else 169 return (info->classid == sock_cgroup_classid(skcd)) ^ 170 info->invert_classid; 171 #endif 172 return false; 173 } 174 175 static void cgroup_mt_destroy_v1(const struct xt_mtdtor_param *par) 176 { 177 struct xt_cgroup_info_v1 *info = par->matchinfo; 178 179 if (info->priv) 180 cgroup_put(info->priv); 181 } 182 183 static void cgroup_mt_destroy_v2(const struct xt_mtdtor_param *par) 184 { 185 struct xt_cgroup_info_v2 *info = par->matchinfo; 186 187 if (info->priv) 188 cgroup_put(info->priv); 189 } 190 191 static struct xt_match cgroup_mt_reg[] __read_mostly = { 192 { 193 .name = "cgroup", 194 .revision = 0, 195 .family = NFPROTO_UNSPEC, 196 .checkentry = cgroup_mt_check_v0, 197 .match = cgroup_mt_v0, 198 .matchsize = sizeof(struct xt_cgroup_info_v0), 199 .me = THIS_MODULE, 200 .hooks = (1 << NF_INET_LOCAL_OUT) | 201 (1 << NF_INET_POST_ROUTING) | 202 (1 << NF_INET_LOCAL_IN), 203 }, 204 { 205 .name = "cgroup", 206 .revision = 1, 207 .family = NFPROTO_UNSPEC, 208 .checkentry = cgroup_mt_check_v1, 209 .match = cgroup_mt_v1, 210 .matchsize = sizeof(struct xt_cgroup_info_v1), 211 .usersize = offsetof(struct xt_cgroup_info_v1, priv), 212 .destroy = cgroup_mt_destroy_v1, 213 .me = THIS_MODULE, 214 .hooks = (1 << NF_INET_LOCAL_OUT) | 215 (1 << NF_INET_POST_ROUTING) | 216 (1 << NF_INET_LOCAL_IN), 217 }, 218 { 219 .name = "cgroup", 220 .revision = 2, 221 .family = NFPROTO_UNSPEC, 222 .checkentry = cgroup_mt_check_v2, 223 .match = cgroup_mt_v2, 224 .matchsize = sizeof(struct xt_cgroup_info_v2), 225 .usersize = offsetof(struct xt_cgroup_info_v2, priv), 226 .destroy = cgroup_mt_destroy_v2, 227 .me = THIS_MODULE, 228 .hooks = (1 << NF_INET_LOCAL_OUT) | 229 (1 << NF_INET_POST_ROUTING) | 230 (1 << NF_INET_LOCAL_IN), 231 }, 232 }; 233 234 static int __init cgroup_mt_init(void) 235 { 236 return xt_register_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg)); 237 } 238 239 static void __exit cgroup_mt_exit(void) 240 { 241 xt_unregister_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg)); 242 } 243 244 module_init(cgroup_mt_init); 245 module_exit(cgroup_mt_exit); 246