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 bool 72 cgroup_mt_v0(const struct sk_buff *skb, struct xt_action_param *par) 73 { 74 const struct xt_cgroup_info_v0 *info = par->matchinfo; 75 struct sock *sk = skb->sk; 76 77 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk))) 78 return false; 79 80 return (info->id == sock_cgroup_classid(&skb->sk->sk_cgrp_data)) ^ 81 info->invert; 82 } 83 84 static bool cgroup_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) 85 { 86 const struct xt_cgroup_info_v1 *info = par->matchinfo; 87 struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data; 88 struct cgroup *ancestor = info->priv; 89 struct sock *sk = skb->sk; 90 91 if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk))) 92 return false; 93 94 if (ancestor) 95 return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^ 96 info->invert_path; 97 else 98 return (info->classid == sock_cgroup_classid(skcd)) ^ 99 info->invert_classid; 100 } 101 102 static void cgroup_mt_destroy_v1(const struct xt_mtdtor_param *par) 103 { 104 struct xt_cgroup_info_v1 *info = par->matchinfo; 105 106 if (info->priv) 107 cgroup_put(info->priv); 108 } 109 110 static struct xt_match cgroup_mt_reg[] __read_mostly = { 111 { 112 .name = "cgroup", 113 .revision = 0, 114 .family = NFPROTO_UNSPEC, 115 .checkentry = cgroup_mt_check_v0, 116 .match = cgroup_mt_v0, 117 .matchsize = sizeof(struct xt_cgroup_info_v0), 118 .me = THIS_MODULE, 119 .hooks = (1 << NF_INET_LOCAL_OUT) | 120 (1 << NF_INET_POST_ROUTING) | 121 (1 << NF_INET_LOCAL_IN), 122 }, 123 { 124 .name = "cgroup", 125 .revision = 1, 126 .family = NFPROTO_UNSPEC, 127 .checkentry = cgroup_mt_check_v1, 128 .match = cgroup_mt_v1, 129 .matchsize = sizeof(struct xt_cgroup_info_v1), 130 .usersize = offsetof(struct xt_cgroup_info_v1, priv), 131 .destroy = cgroup_mt_destroy_v1, 132 .me = THIS_MODULE, 133 .hooks = (1 << NF_INET_LOCAL_OUT) | 134 (1 << NF_INET_POST_ROUTING) | 135 (1 << NF_INET_LOCAL_IN), 136 }, 137 }; 138 139 static int __init cgroup_mt_init(void) 140 { 141 return xt_register_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg)); 142 } 143 144 static void __exit cgroup_mt_exit(void) 145 { 146 xt_unregister_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg)); 147 } 148 149 module_init(cgroup_mt_init); 150 module_exit(cgroup_mt_exit); 151