1 /* 2 * Xtables module to match the process control group. 3 * 4 * Might be used to implement individual "per-application" firewall 5 * policies in contrast to global policies based on control groups. 6 * Matching is based upon processes tagged to net_cls' classid marker. 7 * 8 * (C) 2013 Daniel Borkmann <dborkman@redhat.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14 15 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 16 17 #include <linux/skbuff.h> 18 #include <linux/module.h> 19 #include <linux/netfilter/x_tables.h> 20 #include <linux/netfilter/xt_cgroup.h> 21 #include <net/sock.h> 22 23 MODULE_LICENSE("GPL"); 24 MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>"); 25 MODULE_DESCRIPTION("Xtables: process control group matching"); 26 MODULE_ALIAS("ipt_cgroup"); 27 MODULE_ALIAS("ip6t_cgroup"); 28 29 static int cgroup_mt_check_v0(const struct xt_mtchk_param *par) 30 { 31 struct xt_cgroup_info_v0 *info = par->matchinfo; 32 33 if (info->invert & ~1) 34 return -EINVAL; 35 36 return 0; 37 } 38 39 static int cgroup_mt_check_v1(const struct xt_mtchk_param *par) 40 { 41 struct xt_cgroup_info_v1 *info = par->matchinfo; 42 struct cgroup *cgrp; 43 44 if ((info->invert_path & ~1) || (info->invert_classid & ~1)) 45 return -EINVAL; 46 47 if (!info->has_path && !info->has_classid) { 48 pr_info("xt_cgroup: no path or classid specified\n"); 49 return -EINVAL; 50 } 51 52 if (info->has_path && info->has_classid) { 53 pr_info_ratelimited("path and classid specified\n"); 54 return -EINVAL; 55 } 56 57 info->priv = NULL; 58 if (info->has_path) { 59 cgrp = cgroup_get_from_path(info->path); 60 if (IS_ERR(cgrp)) { 61 pr_info_ratelimited("invalid path, errno=%ld\n", 62 PTR_ERR(cgrp)); 63 return -EINVAL; 64 } 65 info->priv = cgrp; 66 } 67 68 return 0; 69 } 70 71 static int cgroup_mt_check_v2(const struct xt_mtchk_param *par) 72 { 73 struct xt_cgroup_info_v2 *info = par->matchinfo; 74 struct cgroup *cgrp; 75 76 if ((info->invert_path & ~1) || (info->invert_classid & ~1)) 77 return -EINVAL; 78 79 if (!info->has_path && !info->has_classid) { 80 pr_info("xt_cgroup: no path or classid specified\n"); 81 return -EINVAL; 82 } 83 84 if (info->has_path && info->has_classid) { 85 pr_info_ratelimited("path and classid specified\n"); 86 return -EINVAL; 87 } 88 89 info->priv = NULL; 90 if (info->has_path) { 91 cgrp = cgroup_get_from_path(info->path); 92 if (IS_ERR(cgrp)) { 93 pr_info_ratelimited("invalid path, errno=%ld\n", 94 PTR_ERR(cgrp)); 95 return -EINVAL; 96 } 97 info->priv = cgrp; 98 } 99 100 return 0; 101 } 102 103 static bool 104 cgroup_mt_v0(const struct sk_buff *skb, struct xt_action_param *par) 105 { 106 const struct xt_cgroup_info_v0 *info = par->matchinfo; 107 struct sock *sk = skb->sk; 108 109 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk))) 110 return false; 111 112 return (info->id == sock_cgroup_classid(&skb->sk->sk_cgrp_data)) ^ 113 info->invert; 114 } 115 116 static bool cgroup_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) 117 { 118 const struct xt_cgroup_info_v1 *info = par->matchinfo; 119 struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data; 120 struct cgroup *ancestor = info->priv; 121 struct sock *sk = skb->sk; 122 123 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk))) 124 return false; 125 126 if (ancestor) 127 return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^ 128 info->invert_path; 129 else 130 return (info->classid == sock_cgroup_classid(skcd)) ^ 131 info->invert_classid; 132 } 133 134 static bool cgroup_mt_v2(const struct sk_buff *skb, struct xt_action_param *par) 135 { 136 const struct xt_cgroup_info_v2 *info = par->matchinfo; 137 struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data; 138 struct cgroup *ancestor = info->priv; 139 struct sock *sk = skb->sk; 140 141 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk))) 142 return false; 143 144 if (ancestor) 145 return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^ 146 info->invert_path; 147 else 148 return (info->classid == sock_cgroup_classid(skcd)) ^ 149 info->invert_classid; 150 } 151 152 static void cgroup_mt_destroy_v1(const struct xt_mtdtor_param *par) 153 { 154 struct xt_cgroup_info_v1 *info = par->matchinfo; 155 156 if (info->priv) 157 cgroup_put(info->priv); 158 } 159 160 static void cgroup_mt_destroy_v2(const struct xt_mtdtor_param *par) 161 { 162 struct xt_cgroup_info_v2 *info = par->matchinfo; 163 164 if (info->priv) 165 cgroup_put(info->priv); 166 } 167 168 static struct xt_match cgroup_mt_reg[] __read_mostly = { 169 { 170 .name = "cgroup", 171 .revision = 0, 172 .family = NFPROTO_UNSPEC, 173 .checkentry = cgroup_mt_check_v0, 174 .match = cgroup_mt_v0, 175 .matchsize = sizeof(struct xt_cgroup_info_v0), 176 .me = THIS_MODULE, 177 .hooks = (1 << NF_INET_LOCAL_OUT) | 178 (1 << NF_INET_POST_ROUTING) | 179 (1 << NF_INET_LOCAL_IN), 180 }, 181 { 182 .name = "cgroup", 183 .revision = 1, 184 .family = NFPROTO_UNSPEC, 185 .checkentry = cgroup_mt_check_v1, 186 .match = cgroup_mt_v1, 187 .matchsize = sizeof(struct xt_cgroup_info_v1), 188 .usersize = offsetof(struct xt_cgroup_info_v1, priv), 189 .destroy = cgroup_mt_destroy_v1, 190 .me = THIS_MODULE, 191 .hooks = (1 << NF_INET_LOCAL_OUT) | 192 (1 << NF_INET_POST_ROUTING) | 193 (1 << NF_INET_LOCAL_IN), 194 }, 195 { 196 .name = "cgroup", 197 .revision = 2, 198 .family = NFPROTO_UNSPEC, 199 .checkentry = cgroup_mt_check_v2, 200 .match = cgroup_mt_v2, 201 .matchsize = sizeof(struct xt_cgroup_info_v2), 202 .usersize = offsetof(struct xt_cgroup_info_v2, priv), 203 .destroy = cgroup_mt_destroy_v2, 204 .me = THIS_MODULE, 205 .hooks = (1 << NF_INET_LOCAL_OUT) | 206 (1 << NF_INET_POST_ROUTING) | 207 (1 << NF_INET_LOCAL_IN), 208 }, 209 }; 210 211 static int __init cgroup_mt_init(void) 212 { 213 return xt_register_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg)); 214 } 215 216 static void __exit cgroup_mt_exit(void) 217 { 218 xt_unregister_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg)); 219 } 220 221 module_init(cgroup_mt_init); 222 module_exit(cgroup_mt_exit); 223