Messages in this thread | | | Subject | [RFC/PATCH] CKRM rule-based classification engine (RBCE) | From | Shailabh Nagar <> | Date | 29 Aug 2003 15:54:34 -0400 |
| |
Module implementing a Rule-based classification Engine (RBCE) for use with Class-based Kernel Resource Management.
Requires ckcore-A01-2.6.0-test2 patch posted earlier. To define classification rules, classes and policies, you also need userspace libraries and a control program which can be downloaded from
http://ckrm.sourceforge.net/downloads/rbce-A01.tar.gz
For more details, please refer to http://ckrm.sf.net
and earlier postings on lkml.
-- Shailabh
----------------------------------------------------------------------------------- Makefile ----------------------------------------------------------------------------------- DIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) # KDIR is the CKRM enabled Linux kernel src tree KDIR := /usr/src/linux
obj-m := rbcemod.o
rbcemem-objs := rbcemod.o
default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean: rm -f *.o *.ko *~ core
----------------------------------------------------------------------------------- rbcemod.mod.c -----------------------------------------------------------------------------------
#include <linux/module.h> #include <linux/vermagic.h> #include <linux/compiler.h>
MODULE_INFO(vermagic, VERMAGIC_STRING);
static const char __module_depends[] __attribute_used__ __attribute__((section(".modinfo"))) = "depends=";
----------------------------------------------------------------------------------- rbcemod.c ----------------------------------------------------------------------------------- /* Rule-based Classification Engine (RBCE) module * * Copyright (C) Hubertus Franke, IBM Corp. 2003 * (C) Chandra Seetharaman, IBM Corp. 2003 * * Module for loading of classification policies and providing * a user API for Class-based Kernel Resource Management (CKRM) * * Latest version, more details at http://ckrm.sf.net * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * */
/* Changes * * 28 Aug 2003 * Created. First cut with much scope for cleanup ! */
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <asm/io.h> #include <linux/ckrm.h> #include <asm/uaccess.h> #include <linux/proc_fs.h>
#include "../include/rbce.h"
MODULE_AUTHOR("Hubertus Franke, Chandra Seetharaman (IBM)"); MODULE_DESCRIPTION("Rule Based Classification Engine Module for CKRM"); MODULE_LICENSE("GPL");
static char modname[] = "rbce"; static int rbce_major = 179; // driver's assigned major number
struct named_obj_hdr { struct list_head link; rbce_id_t id; char name[RBCE_MAX_NAME_LEN]; };
struct rbce_class { struct named_obj_hdr obj;
struct ckrm_cpu_class *cpu_class; struct ckrm_mem_class *mem_class; struct ckrm_net_class *net_class; struct ckrm_io_class *io_class; };
struct rbce_rule_term { rbce_rule_op_t op; union { char cmd[RBCE_MAX_COMMAND_LEN]; char tag[RBCE_MAX_TAG_LEN]; long id; struct rbce_rule *deprule; }; };
struct rbce_rule { struct named_obj_hdr obj; struct rbce_class *target_class; int num_terms; struct rbce_rule_term *terms; };
#define RBCE_RULE_NAME 1 #define RBCE_RULE_ID 2
struct rbce_policy { struct named_obj_hdr obj; rbce_policy_type_t type; struct rbce_class *def_class; rwlock_t rwlock; struct list_head classes; struct list_head rules; };
void rbce_classify(struct task_struct *);
extern int errno;
int rbcedebug = 1; #define cprintk(lvl,x) if ((lvl) <= rbcedebug) printk x
// /proc stuff for debugging
static ssize_t my_write(struct file *file, const char *buf, size_t count, loff_t *p) { if (count) { char c;
if (get_user(c, buf)) { return -EFAULT; } if (c < '0' || c > '9') { return -EINVAL; } rbcedebug = c - '0'; } return count; }
static int my_read(struct file *file, char *ubuf, size_t size, loff_t *ppos) { int len; char kbuf[10];
if (size < 5) { return -ENOMEM; } len = sprintf(kbuf, "%d\n", rbcedebug); if (copy_to_user(ubuf, kbuf, len+1)) { return -EFAULT; } *ppos += len; return len; }
static struct file_operations my_proc_operations = { .read = my_read, .write = my_write, };
// Forward declarations
static void reclassify_tasks(struct rbce_class *); static void rbce_classify_l(struct task_struct *);
/*-----------------------------------------------------------------------*/ rwlock_t global_rwlock = RW_LOCK_UNLOCKED; /* This lock protects "policies" list and cur_policy. * Anybody that needs to access any policy in the "policies" list * or cur_policy have to get this lock in read mode and then get the * (reader/writer depending on the operation) lock on that specific * policy. After getting the policy's lock this global lock can be * dropped. * * Insertions of policy to the "policies" list and changing cur_policy * must be done with this lock held in write mode. * * Both the locks must be held(read or write) while deferencing the * class pointer in the task data structure. */ LIST_HEAD(policies);
static struct rbce_policy default_policy = { .obj = { .link = LIST_HEAD_INIT(default_policy.obj.link), .id = (rbce_id_t)&default_policy, .name = "default policy" }, .type = RBCE_POLICY_MONITOR, .rwlock = RW_LOCK_UNLOCKED, .classes = LIST_HEAD_INIT(default_policy.classes), .rules = LIST_HEAD_INIT(default_policy.rules) };
static struct rbce_class default_class = { .obj = { .link = LIST_HEAD_INIT(default_policy.obj.link), .id = (rbce_id_t)&default_class, .name = "default class" }, };
static struct rbce_policy *cur_policy = &default_policy;
inline static rbce_id_t getid(void *obj) { return (rbce_id_t) obj; }
static struct named_obj_hdr* find_named_obj(struct list_head *lst, char *name) { struct named_obj_hdr *pos;
list_for_each_entry(pos, lst, link) { if (!strcmp(pos->name, name)) return pos; } return NULL; }
static struct named_obj_hdr* find_id_obj(struct list_head *lst, rbce_id_t id) { struct named_obj_hdr *pos;
list_for_each_entry(pos, lst, link) { if (pos->id == id) return pos; } return NULL; }
static inline struct rbce_policy* find_policy_name(char *name) { return (struct rbce_policy*) find_named_obj(&policies, name); }
static inline struct rbce_policy* find_policy_id(rbce_id_t id) { return (struct rbce_policy*) find_id_obj(&policies, id); }
static inline struct rbce_class* find_class_name(struct rbce_policy *policy, char *name) { return (struct rbce_class*) find_named_obj(&policy->classes, name); }
static inline struct rbce_class* find_class_id(struct rbce_policy *policy, rbce_id_t id) { return (struct rbce_class*) find_id_obj(&policy->classes, id); }
static inline struct rbce_rule* find_rule_name(struct rbce_policy *policy, char *name) { return (struct rbce_rule*) find_named_obj(&policy->rules, name); }
static inline struct rbce_rule* find_rule_id(struct rbce_policy *policy, rbce_id_t id) { return (struct rbce_rule*) find_id_obj(&policy->rules, id); }
static rbce_id_t create_policy(struct rbce_policy_descr *udescr) { rbce_id_t polid = 0, classid; struct rbce_policy_descr kdescr; struct rbce_policy *policy; struct rbce_class *newcls; static int firsttime = 1;
if (firsttime) { struct proc_dir_entry *entry;
firsttime = 0; entry = create_proc_entry("rbce_debug", S_IWUGO|S_IRUGO, NULL); if (entry) { entry->proc_fops = &my_proc_operations; } }
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_policy_descr))) { return (rbce_id_t)-EFAULT; }
if (!strlen(kdescr.name) || ((kdescr.type != RBCE_POLICY_MONITOR) && (kdescr.type != RBCE_POLICY_MANAGE))) { return (rbce_id_t)-EINVAL; }
write_lock(&global_rwlock); policy = find_policy_name(kdescr.name); if (policy) { polid = (rbce_id_t)-EINVAL; goto out; } policy = kmalloc(sizeof(struct rbce_policy), GFP_KERNEL); if (!policy) { polid = (rbce_id_t)-ENOMEM; goto out; } polid = getid(policy); policy->obj.id = polid; strcpy(policy->obj.name, kdescr.name); policy->type = kdescr.type; rwlock_init(&policy->rwlock); INIT_LIST_HEAD(&policy->classes); INIT_LIST_HEAD(&policy->rules); list_add_tail(&policy->obj.link, &policies);
// Create the default class. newcls = kmalloc(sizeof(struct rbce_class), GFP_KERNEL); if (!newcls) { polid = (rbce_id_t)(-ENOMEM); goto out_free; } classid = getid(newcls); policy->def_class = newcls; newcls->obj.id = classid; strcpy(newcls->obj.name, "policy_default_class"); newcls->cpu_class = ckrm_dflt_cpu_class(&default_class); newcls->mem_class = ckrm_dflt_mem_class(&default_class); newcls->net_class = ckrm_dflt_net_class(&default_class); newcls->io_class = ckrm_dflt_io_class(&default_class); list_add_tail(&newcls->obj.link, &policy->classes); goto out;
out_free: kfree(policy);
out: write_unlock(&global_rwlock); cprintk(3,("create_policy polid %ld\n", polid)); return polid; }
static int commit_policy(struct rbce_policy_descr *udescr) { struct rbce_policy_descr kdescr; struct rbce_policy *policy, *old_policy; struct rbce_class *cls; int rc = 0;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_policy_descr))) { return -EFAULT; }
if (kdescr.policy_id <= 0) { return (rbce_id_t)-EINVAL; }
write_lock(&global_rwlock); policy = find_policy_id(kdescr.policy_id); if (!policy) { rc = -EINVAL; goto out; }
if (policy == cur_policy) { rc = -EBUSY; goto out; }
// Get the cur_policy's write lock to protect the current readers. write_lock(&cur_policy->rwlock);
// Reclassify all tasks in all classes in the old policy. // Strictly speaking, there should be no tasks in the class.... we // should ASSERT this state instead of reclassifying the tasks. list_for_each_entry(cls, &cur_policy->classes, obj.link) { reclassify_tasks(cls); }
// XXX Need to clear the resource information in the classes of "policy" // before dropping the policy's rwlock. write_unlock(&cur_policy->rwlock);
old_policy = cur_policy; cur_policy = policy;
out: write_unlock(&global_rwlock); return rc; }
static int decommit_policy(struct rbce_policy_descr *udescr) { struct rbce_policy_descr kdescr; struct rbce_policy *policy; struct rbce_class *cls; int rc = 0;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_policy_descr))) { return -EFAULT; }
if (kdescr.policy_id <= 0) { return (rbce_id_t)-EINVAL; }
write_lock(&global_rwlock); policy = find_policy_id(kdescr.policy_id); if (!policy || policy != cur_policy) { rc = -EINVAL; goto out; } if (policy == &default_policy) {// paranoia goto out; }
// Get the cur_policy's write lock to protect the current readers. write_lock(&cur_policy->rwlock); cur_policy = &default_policy;
// Reclassify all tasks in all classes in the decommited policy. list_for_each_entry(cls, &policy->classes, obj.link) { reclassify_tasks(cls); } write_unlock(&cur_policy->rwlock);
out: write_unlock(&global_rwlock); return rc; }
static int delete_policy(struct rbce_policy_descr *udescr) { struct rbce_policy_descr kdescr; struct rbce_policy *policy; struct list_head *lh1, *lh2; struct rbce_rule *rule;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_policy_descr))) { return -EFAULT; }
if (kdescr.policy_id <= 0) { return (rbce_id_t)-EINVAL; }
write_lock(&global_rwlock); policy = find_policy_id(kdescr.policy_id); if (!policy || policy == cur_policy) { write_unlock(&global_rwlock); return (rbce_id_t)-EINVAL; }
// Get the policy's write lock to protect the current readers, if any write_lock(&policy->rwlock);
list_del(&policy->obj.link); write_unlock(&policy->rwlock); write_unlock(&global_rwlock);
// delete the rules list_for_each_safe(lh1, lh2, &policy->rules) { list_del(lh1); rule = (struct rbce_rule *) lh1; kfree(rule->terms); kfree(rule); }
// delete the classses list_for_each_safe(lh1, lh2, &policy->classes) { list_del(lh1); kfree(lh1); }
// delete the policy kfree(policy);
return 0; }
static int set_policy_type(struct rbce_policy_descr *udescr) { struct rbce_policy_descr kdescr; struct rbce_policy *policy; int rc = -EINVAL;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_policy_descr))) { return -EFAULT; }
if (kdescr.policy_id <= 0) { return (rbce_id_t)-EINVAL; }
if (kdescr.type != RBCE_POLICY_MONITOR && kdescr.type != RBCE_POLICY_MANAGE) { return rc; }
read_lock(&global_rwlock); policy = find_policy_id(kdescr.policy_id); if (policy && policy != &default_policy) { // can't change type of default policy. write_lock(&policy->rwlock); read_unlock(&global_rwlock); policy->type = kdescr.type; write_unlock(&policy->rwlock); rc = 0; } else { read_unlock(&global_rwlock); }
return rc; }
static int get_policy_type(struct rbce_policy_descr *udescr) { struct rbce_policy_descr kdescr; struct rbce_policy *policy; int rc = -EINVAL;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_policy_descr))) { return -EFAULT; }
if (kdescr.policy_id <= 0) { return rc; }
read_lock(&global_rwlock); policy = find_policy_id(kdescr.policy_id); if (policy) { read_lock(&policy->rwlock); rc = policy->type; read_unlock(&policy->rwlock); } read_unlock(&global_rwlock);
return rc; }
static rbce_id_t get_policy_byname(struct rbce_policy_descr *udescr) { struct rbce_policy_descr kdescr; struct rbce_policy *policy; int rc = -EINVAL;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_policy_descr))) { return -EFAULT; } read_lock(&global_rwlock); policy = find_policy_name(kdescr.name); if (policy) { read_lock(&policy->rwlock); rc = getid(policy); read_unlock(&policy->rwlock); } read_unlock(&global_rwlock); return rc; }
static rbce_id_t create_class(struct rbce_class_descr *udescr) { struct rbce_class_descr kdescr; struct rbce_policy *policy; struct rbce_class *newcls = NULL; rbce_id_t classid;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_class_descr))) { return -EFAULT; }
read_lock(&global_rwlock); policy = find_policy_id(kdescr.policy_id); if (policy == NULL) { read_unlock(&global_rwlock); classid = (rbce_id_t)(-EINVAL); goto out; } write_lock(&policy->rwlock); read_unlock(&global_rwlock);
if (find_class_name(policy, kdescr.name)) { classid = (rbce_id_t)(-EINVAL); goto out_unlock; }
newcls = kmalloc(sizeof(struct rbce_class), GFP_KERNEL); if (!newcls) { classid = (rbce_id_t)(-ENOMEM); goto out_unlock; } classid = getid(newcls); newcls->obj.id = classid; strncpy(newcls->obj.name, kdescr.name,RBCE_MAX_NAME_LEN); list_add_tail(&newcls->obj.link, &policy->classes);
newcls->cpu_class = ckrm_alloc_cpu_class(newcls); newcls->mem_class = ckrm_alloc_mem_class(newcls); newcls->net_class = ckrm_alloc_net_class(newcls); newcls->io_class = ckrm_alloc_io_class(newcls);
out_unlock: write_unlock(&policy->rwlock); out: cprintk(3,("create_class classid %ld\n", classid)); return classid; }
static int delete_class(struct rbce_delete_class_descr *udescr) { struct rbce_delete_class_descr kdescr; struct rbce_class *cls; struct rbce_rule *rule; struct rbce_policy *policy; int rc = 0;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_delete_class_descr))) { return -EFAULT; }
read_lock(&global_rwlock); policy = find_policy_id(kdescr.policy_id); if (policy == NULL) { read_unlock(&global_rwlock); return -EINVAL; } write_lock(&policy->rwlock);
if ((cls = find_class_id(policy, kdescr.class_id)) != NULL) { // Make sure that there are no rules ending in this class. list_for_each_entry(rule, &policy->rules, obj.link) { if (cls == rule->target_class) { rc = -EINVAL; goto out_unlock; } } list_del(&cls->obj.link); reclassify_tasks(cls); ckrm_free_cpu_class(cls->cpu_class); ckrm_free_mem_class(cls->mem_class); ckrm_free_net_class(cls->net_class); ckrm_free_io_class(cls->io_class); } else { rc = -EINVAL; }
out_unlock: write_unlock(&policy->rwlock); read_unlock(&global_rwlock); return rc; }
static rbce_id_t get_class_byname(struct rbce_class_descr *udescr) { struct rbce_class_descr kdescr; struct rbce_policy *policy; struct rbce_class *cls; rbce_id_t classid = -EINVAL;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_class_descr))) { return (rbce_id_t)-EFAULT; }
read_lock(&global_rwlock); policy = find_policy_id(kdescr.policy_id); if (policy == NULL) { read_unlock(&global_rwlock); return classid; } read_lock(&policy->rwlock); read_unlock(&global_rwlock);
if ((cls = find_class_name(policy, kdescr.name)) != NULL) { classid = getid(cls); } read_unlock(&policy->rwlock); return classid; }
static rbce_id_t create_rule(struct rbce_rule_descr *udescr) { struct rbce_rule_term_descr *kterms = NULL; struct rbce_rule_descr kdescr; struct rbce_rule *newrule = NULL; struct rbce_rule_term *terms = NULL; struct rbce_class *targetcls; struct rbce_policy *policy; int i; rbce_id_t ruleid; struct list_head *after = NULL;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_rule_descr))) { return (rbce_id_t)-EFAULT; }
kterms = kmalloc(kdescr.num_terms * sizeof(struct rbce_rule_term_descr), GFP_KERNEL); if (!kterms) { return (rbce_id_t)-ENOMEM; }
if (copy_from_user(kterms, kdescr.terms, sizeof(struct rbce_rule_term_descr) * kdescr.num_terms)) { kfree(kterms); return (rbce_id_t)-EFAULT; }
terms = kmalloc(kdescr.num_terms * sizeof(struct rbce_rule_term), GFP_KERNEL); if (!terms) { kfree(kterms); return (rbce_id_t)-ENOMEM; }
read_lock(&global_rwlock); ruleid = (rbce_id_t)(-EINVAL);
/* find policy and target class and verify all rules */ if ((policy = find_policy_id(kdescr.policy_id)) == NULL || (targetcls = (struct rbce_class*) find_id_obj(&policy->classes, kdescr.class_id)) == NULL) { read_unlock(&global_rwlock); goto out_free; } write_lock(&policy->rwlock); read_unlock(&global_rwlock);
for (i = 0; i < kdescr.num_terms; i++) { switch(kterms[i].op) { case RBCE_RULE_CMD_PATH: case RBCE_RULE_CMD: strcpy(terms[i].cmd, kterms[i].ruledata.cmd); break; case RBCE_RULE_REAL_UID: case RBCE_RULE_REAL_GID: case RBCE_RULE_EFFECTIVE_UID: case RBCE_RULE_EFFECTIVE_GID: terms[i].id = kterms[i].ruledata.id; break; case RBCE_RULE_APP_TAG: strcpy(terms[i].tag, kterms[i].ruledata.tag); break; case RBCE_RULE_DEP_RULE: terms[i].deprule = find_rule_id(policy, kterms[i].ruledata.dep_rule); if (i != 0 || terms[i].deprule == NULL) { //dependent rule must be the first term and must exist goto out_unlock; } break; default: goto out_unlock; } terms[i].op = kterms[i].op; }
newrule = kmalloc(sizeof(struct rbce_rule), GFP_KERNEL); if (!newrule) { ruleid = (rbce_id_t)-ENOMEM; goto out_unlock; }
ruleid = getid(newrule); newrule->obj.id = ruleid; strcpy(newrule->obj.name, kdescr.name); newrule->target_class = targetcls; newrule->num_terms = kdescr.num_terms; newrule->terms = terms; switch(kdescr.insert_after) { case 0: // Add at the head. list_add(&newrule->obj.link, &policy->rules); break; default: // Add after the specified rule. after = (struct list_head *) find_rule_id(policy, kdescr.insert_after); if (!after) { goto out_unlock; } list_add(&newrule->obj.link, after); break; case -1: // Add at the tail. list_add_tail(&newrule->obj.link, &policy->rules);
} write_unlock(&policy->rwlock); kfree(kterms); goto out;
out_unlock: write_unlock(&policy->rwlock); out_free: if (newrule) kfree(newrule); if (kterms) kfree(kterms); if (terms) kfree(terms); out: cprintk(3,("create_rule ruleid %ld\n", ruleid)); return ruleid; }
static rbce_id_t change_rule(struct rbce_rule_descr *udescr) { struct rbce_rule_term_descr *kterms = NULL; struct rbce_rule_descr kdescr; struct rbce_rule *rule; struct rbce_rule_term *terms = NULL; struct rbce_class *targetcls; struct rbce_policy *policy; int i; rbce_id_t ruleid; struct list_head *after = NULL;
kterms = kmalloc(kdescr.num_terms * sizeof(struct rbce_rule_term_descr), GFP_KERNEL); if (!kterms) { return (rbce_id_t)-ENOMEM; }
if (copy_from_user(&kterms, kdescr.terms, sizeof(struct rbce_rule_term_descr) * kdescr.num_terms)) { kfree(kterms); return (rbce_id_t)-EFAULT; }
terms = kmalloc(kdescr.num_terms * sizeof(struct rbce_rule_term), GFP_KERNEL); if (!terms) { kfree(kterms); return (rbce_id_t)-ENOMEM; }
ruleid = (rbce_id_t)(-EINVAL);
/* find policy and target class and verify all rules */ read_lock(&global_rwlock); if ((policy = find_policy_id(kdescr.policy_id)) == NULL || (targetcls = (struct rbce_class*) find_id_obj(&policy->classes, kdescr.class_id)) == NULL || (rule = find_rule_id(policy, kdescr.rule_id)) == NULL) { read_unlock(&global_rwlock); goto out_free; } write_lock(&policy->rwlock); read_unlock(&global_rwlock);
for (i = 0; i < kdescr.num_terms; i++) { switch(kterms[i].op) { case RBCE_RULE_CMD_PATH: case RBCE_RULE_CMD: strcpy(terms[i].cmd, kterms[i].ruledata.cmd); break; case RBCE_RULE_REAL_UID: case RBCE_RULE_REAL_GID: case RBCE_RULE_EFFECTIVE_UID: case RBCE_RULE_EFFECTIVE_GID: terms[i].id = kterms[i].ruledata.id; break; case RBCE_RULE_APP_TAG: strcpy(terms[i].tag, kterms[i].ruledata.tag); break; case RBCE_RULE_DEP_RULE: terms[i].deprule = find_rule_id(policy, kterms[i].ruledata.dep_rule); if (i != 0 || terms[i].deprule == NULL) { // dependent rule must be the first term // that rule must exist goto out_unlock; } break; default: goto out_unlock; } terms[i].op = kterms[i].op; }
// Free the old terms. list_del(&rule->obj.link); kfree(rule->terms);
switch(kdescr.insert_after) { case 0: // Add at the head. list_add(&rule->obj.link, &policy->rules); break; default: // Add after the specified rule. after = (struct list_head *) find_rule_id(policy, kdescr.insert_after); if (!after) { // XXX need to put back the original rule in this case. goto out_unlock; } list_add(&rule->obj.link, after); break; case -1: // Add at the tail. list_add_tail(&rule->obj.link, &policy->rules);
} ruleid = getid(rule); rule->obj.id = ruleid; rule->target_class = targetcls; rule->num_terms = kdescr.num_terms; rule->terms = terms; write_unlock(&policy->rwlock); kfree(kterms); goto out;
out_unlock: write_unlock(&policy->rwlock); out_free: kfree(kterms); kfree(terms); out: return ruleid; }
static rbce_id_t delete_rule(struct rbce_delete_rule_descr *udescr) { struct rbce_delete_rule_descr kdescr; struct rbce_policy *policy; struct rbce_rule *rule;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_delete_rule_descr))) { return (rbce_id_t)-EFAULT; }
read_lock(&global_rwlock); if ((policy = find_policy_id(kdescr.policy_id)) == NULL || (rule = find_rule_id(policy, kdescr.rule_id)) == NULL) { read_unlock(&global_rwlock); return (rbce_id_t)-EINVAL; } write_lock(&policy->rwlock); read_unlock(&global_rwlock); list_del(&rule->obj.link); write_unlock(&policy->rwlock); kfree(rule->terms); kfree(rule); return 0; }
static rbce_id_t get_rule_bytype(struct rbce_rule_descr *udescr, int type_info) { struct rbce_rule_descr kdescr; struct rbce_rule *rule = NULL; struct rbce_rule_term *terms = NULL; struct rbce_policy *policy; int num_terms; rbce_id_t ruleid;
if (copy_from_user(&kdescr, udescr, sizeof(struct rbce_rule_descr))) { return (rbce_id_t)-EFAULT; }
if (kdescr.policy_id <= 0) { return (rbce_id_t)-EINVAL; }
read_lock(&global_rwlock); ruleid = (rbce_id_t)(-EINVAL);
if ((policy = find_policy_id(kdescr.policy_id)) == NULL) { read_unlock(&global_rwlock); return ruleid; }
switch (type_info) { case RBCE_RULE_NAME: rule = find_rule_name(policy, kdescr.name); break; case RBCE_RULE_ID: rule = find_rule_id(policy, kdescr.rule_id); break; default: read_unlock(&global_rwlock); return ruleid; }
read_lock(&policy->rwlock); read_unlock(&global_rwlock);
kdescr.rule_id = ruleid = getid(rule); kdescr.class_id = getid(rule->target_class); num_terms = rule->num_terms; if (kdescr.num_terms >= num_terms) { terms = kmalloc(num_terms * sizeof(struct rbce_rule_term), GFP_KERNEL); if (terms == NULL) { ruleid = -ENOMEM; read_unlock(&policy->rwlock); } else { memcpy(terms, rule->terms, num_terms * sizeof(struct rbce_rule_term)); read_unlock(&policy->rwlock); if (copy_to_user(kdescr.terms, terms, num_terms * sizeof(struct rbce_rule_term))) { ruleid = -EFAULT; kdescr.terms = NULL; } kfree(terms); } } else { read_unlock(&policy->rwlock); } kdescr.num_terms = num_terms; kdescr.insert_after = 0; return ruleid; }
static void reclassify_all_tasks(struct rbce_class *cls) { struct task_struct *proc, *thread;
// Could call rbce_classify() without holding the following locks, but // grabbing the locks at this level will reduce the number of // lock/unlocks of global_rwlock and cur_policy->rwlock. read_lock(&global_rwlock); read_lock(&cur_policy->rwlock);
read_lock(&tasklist_lock); do_each_thread(proc, thread) { /* todo: need to descriminate based on <cls> */ rbce_classify_l(thread); } while_each_thread(proc, thread); read_unlock(&tasklist_lock);
read_unlock(&cur_policy->rwlock); read_unlock(&global_rwlock); return; }
static int get_my_class(struct rbce_my_info_descr * udescr) { #if 0 char c_name[RBCE_MAX_NAME_LEN]; char p_name[RBCE_MAX_NAME_LEN];
// To avoid copying to user land while holding the locks, copy // the contents to local variables first. read_lock(&global_rwlock); read_lock(&cur_policy->rwlock); strcpy(c_name, current->class->obj.name); strcpy(p_name, cur_policy->obj.name); read_unlock(&cur_policy->rwlock); read_unlock(&global_rwlock);
if (copy_to_user(udescr->class_name, c_name, RBCE_MAX_NAME_LEN-1)) { return -EFAULT; } if (copy_to_user(udescr->policy_name, p_name, RBCE_MAX_NAME_LEN-1)) { return -EFAULT; } #endif return 0; }
rbce_id_t rbce_ctl(rbce_op_t op, void *data) { char utag[RBCE_MAX_TAG_LEN];
switch (op) { case RBCE_CREATE_POLICY: return create_policy((struct rbce_policy_descr*) data);
case RBCE_COMMIT_POLICY: return commit_policy((struct rbce_policy_descr*) data);
case RBCE_DECOMMIT_POLICY: return decommit_policy((struct rbce_policy_descr*) data);
case RBCE_DELETE_POLICY: return delete_policy((struct rbce_policy_descr *) data);
case RBCE_SET_POLICY_TYPE: return set_policy_type((struct rbce_policy_descr *) data);
case RBCE_GET_POLICY_TYPE: return get_policy_type((struct rbce_policy_descr *) data);
case RBCE_GET_POLICY_BYNAME: return get_policy_byname((struct rbce_policy_descr *) data);
case RBCE_CREATE_CLASS: return create_class((struct rbce_class_descr*) data);
case RBCE_DELETE_CLASS: return delete_class((struct rbce_delete_class_descr*) data);
case RBCE_GET_CLASS_BYNAME: return get_class_byname((struct rbce_class_descr*) data);
case RBCE_SET_CLASS_SHARES: case RBCE_GET_CLASS_SHARES: case RBCE_GET_RESOURCE_USAGE: case RBCE_GET_CUM_RESOURCE_USAGE: return -ENOSYS;
case RBCE_CREATE_RULE: return create_rule((struct rbce_rule_descr*) data);
case RBCE_DELETE_RULE: return delete_rule((struct rbce_delete_rule_descr*) data);
case RBCE_CHANGE_RULE: return change_rule((struct rbce_rule_descr*) data);
case RBCE_GET_RULE_BYNAME: return get_rule_bytype((struct rbce_rule_descr*) data, RBCE_RULE_NAME);
case RBCE_GET_RULE_BYID: return get_rule_bytype((struct rbce_rule_descr*) data, RBCE_RULE_ID);
case RBCE_RECLASSIFY_ALL_TASKS: reclassify_all_tasks(NULL); return 0;
case RBCE_GET_MY_INFO: return get_my_class((struct rbce_my_info_descr *) data);
case RBCE_SET_TAG: if (copy_from_user(current->ckrm_client_data, utag, RBCE_MAX_TAG_LEN-1)) return -EFAULT; rbce_classify(current); return 0;
case RBCE_GET_TAG: if (copy_from_user(utag, current->ckrm_client_data, RBCE_MAX_TAG_LEN-1)) return -EFAULT; return 0;
default: return -EINVAL; } }
static void rbce_set_task_classes(struct task_struct *tsk, struct rbce_class *cls) { cprintk(1,("__classify %d<%s> -> <%s>\n", tsk->pid, tsk->comm, cls->obj.name));
/* Don't set directly tsk->cpu_class = cls->cpu_class; tsk->mem_class = cls->cpu_class; tsk->net_class = cls->net_class; tsk->io_class = cls->io_class;
*/
ckrm_cpu_change_class(tsk, cls->cpu_class); ckrm_mem_change_class(tsk, cls->mem_class); ckrm_io_change_class(tsk, cls->io_class); ckrm_net_change_class(tsk, cls->net_class);
}
/*====================== Start Classification Engine =======================*/
static int match_cmd(char *tsk_comm, char *cmd_exp, int fullpath) { char *c, *t, *last_ast, next_c; char tsk_command[128];
/* first we need to retrieve the full name of the tsk */ if (fullpath) { strncpy(tsk_command, tsk_comm, 16); tsk_command[16] = '\0'; } else { // Make sure there is no '/' in the command expression. c = cmd_exp; while(*c && *c != '/') c++; if (*c) { return 0; }
// Get only the command name from tsk_comm. c = strrchr (tsk_comm, '/'); if (c != NULL) { strcpy(tsk_command, c+1); } else { strcpy(tsk_command, tsk_comm); } } cprintk(4,("tsk_command |%s| tsk_comm |%s| cmd_exp |%s|\n", tsk_command, tsk_comm, cmd_exp));
/* now faithfully assume the entire pathname of the file is in tsk_command */
/* we now have to effectively implement a regular expression * for now assume * '?' any single character * '*' one or more '?' * rest must match */
c = cmd_exp; t = tsk_command; last_ast = NULL; next_c = '\0';
while (*c && *t) { switch (*c) { case '?': if (*t == '/') { return 0; } c++; t++; continue; case '*': if (*t == '/') { return 0; } // eat up all '*' in c while (*(c+1) == '*') c++; next_c = '\0'; last_ast = c; //t++; // Add this for matching '*' with "one" or more chars. while (*t && (*t != *(c+1)) && *t != '/') t++; if (*t == *(c+1)) { c++; if (*c != '/') { if (*c == '?') { if (*t == '/') { return 0; } t++; c++; } next_c = *c; if (*c) { if (*t == '/') { return 0; } t++; c++; if (!*c && *t) c = last_ast; } } else { last_ast = NULL; } continue; } return 0; case '/': next_c = '\0'; /*FALLTHRU*/ default: if (*t == *c && next_c != *t) { c++, t++; continue; } else { /* reset to last asterix and continue from there */ if (last_ast) { c = last_ast; } else { return 0; } } } }
/* check for trailing "*" */ while (*c == '*') c++;
return (!*c && !*t); }
static int evaluate_rule(struct task_struct *tsk,struct rbce_rule *rule) { struct rbce_rule_term *term; char *task_cmd; int i; int rc = 1;
/* we count down to anticipate deprules to listed first */
// XXX Full command name is between mm->arg_start & mm->arg_end. // for now use tsk->comm task_cmd = tsk->comm;
for (i = rule->num_terms-1, term = &rule->terms[i]; (rc == 1) && (i >= 0); i--, term-- ) {
switch(term->op) {
case RBCE_RULE_CMD_PATH: rc &= match_cmd(task_cmd, term->cmd, 1); break; case RBCE_RULE_CMD: rc &= match_cmd(task_cmd, term->cmd, 0); break; case RBCE_RULE_REAL_UID: rc &= (tsk->uid == term->id); break; case RBCE_RULE_REAL_GID: rc &= (tsk->gid == term->id); break; case RBCE_RULE_EFFECTIVE_UID: rc &= (tsk->euid == term->id); break; case RBCE_RULE_EFFECTIVE_GID: rc &= (tsk->egid == term->id); break; case RBCE_RULE_APP_TAG: rc &= !strcmp(tsk->ckrm_client_data, term->tag); break; case RBCE_RULE_DEP_RULE: rc &= evaluate_rule(tsk, term->deprule); break; } } return rc; }
/* * global lock(in read or write mode) and cur_policy's lock(write mode) * must be held while calling this function. */ static void rbce_classify_l(struct task_struct *tsk) { struct rbce_class *cls = NULL;
struct rbce_rule *rule;
list_for_each_entry(rule, &cur_policy->rules, obj.link) { if (evaluate_rule(tsk, rule) && rule->target_class) { cls = rule->target_class; break; } } if (cls == NULL) cls = cur_policy->def_class; rbce_set_task_classes(tsk,cls); }
/* * Must be called with both global_lock and policy_lock(of the policy to * which the class belongs) held(in read or write mode). */ static void reclassify_tasks(struct rbce_class *cls) { reclassify_all_tasks(cls); }
/* * Called from the kernel routines from the following system calls: * execve() * setuid(), setgid() and family * set_my_tag() */ void rbce_classify(struct task_struct *tsk) { cprintk(3,("enter rbce_classify\n")); read_lock(&global_rwlock); read_lock(&cur_policy->rwlock);
rbce_classify_l(tsk);
read_unlock(&cur_policy->rwlock); read_unlock(&global_rwlock); cprintk(3,("exit rbce_classify\n")); return; }
/* * Called from the kernel routines from the following system calls: * fork(), after copy_process() */ void rbce_fork_task(struct task_struct *tsk) { cprintk(2,("fork task %d\n",tsk->pid));
// memset(current->tag, 0, RBCE_MAX_TAG_LEN); // tag not inherited. rbce_classify(tsk); return; }
/* * To be called from exit() */ void rbce_exit_task(struct task_struct *tsk) { cprintk(2,("Exit task %d\n",tsk->pid));
#if 0 read_lock(&global_rwlock); read_lock(&cur_policy->rwlock);
spin_lock(&tsk->class->lock); list_del(&tsk->rbce_link); spin_unlock(&tsk->class->lock);
read_unlock(&cur_policy->rwlock); read_unlock(&global_rwlock); #endif return; }
void rbce_exec_task(struct task_struct *tsk, char *filename) {
cprintk(2,("Exec task %d<%s><%s>\n",tsk->pid,tsk->comm,filename));
// memset(current->tag, 0, RBCE_MAX_TAG_LEN); // tag not inherited. rbce_classify(tsk); return; }
void rbce_id_change(void) { struct task_struct *tsk = current; cprintk(2,("ID change pid=%d\n",tsk->pid));
rbce_classify(current); return; }
/*================ calback testing only ==================*/ void rbce_useradd(struct user_struct *up) { cprintk(2,("useradd\n")); }
void rbce_userdel(struct user_struct *up) { cprintk(2,("userdel\n")); }
/*====================== end classification engine =======================*/
struct ckrm_callbacks call_table = { .fork = rbce_fork_task, .exec = rbce_exec_task, .exit = rbce_exit_task, .id = rbce_id_change, .useradd = rbce_useradd, .userdel = rbce_useradd, };
/* ===================== initialization ==========================*/
static int rbce_open(struct inode *inode, struct file *filp) { if (! try_module_get(THIS_MODULE)) { cprintk(1,("rbce_open failed\n")); return -EFAULT; }
cprintk(1,("rbce_open() by <%s>\n",current->comm)); return 0; }
static int rbce_release(struct inode *inode, struct file *filp) { cprintk(1,("rbce_close() by <%s>\n",current->comm)); module_put(THIS_MODULE); return 0; }
static int rbce_ioctl( struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) { cprintk(1,("rbc_ioctl %d\n",ioctl_num)); return rbce_ctl ( ioctl_num, (void*)ioctl_param ); }
static struct file_operations rbce_fops = { owner: THIS_MODULE, ioctl: rbce_ioctl, open: rbce_open, release: rbce_release, };
/* ===================== initialization ==========================*/
int init_rbce ( void ) { int rc;
printk( "<1>\nInstalling \'%s\' module\n", modname ); if ((rc = register_chrdev( rbce_major, modname, &rbce_fops )) != 0) { printk("can't register device\n"); return rc; } default_policy.def_class = &default_class;
default_class.cpu_class = ckrm_dflt_cpu_class(&default_class); default_class.mem_class = ckrm_dflt_mem_class(&default_class); default_class.net_class = ckrm_dflt_net_class(&default_class); default_class.io_class = ckrm_dflt_io_class(&default_class); ckrm_register_engine(&call_table); return 0; }
void exit_rbce ( void ) { printk( "<1>Removing \'%s\' module\n", modname ); ckrm_unregister_engine(); unregister_chrdev( rbce_major, modname ); }
/* ===================== System call interfaces ==========================*/
module_init(init_rbce); module_exit(exit_rbce);
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |