cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

xt_IDLETIMER.c (13331B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * linux/net/netfilter/xt_IDLETIMER.c
      4 *
      5 * Netfilter module to trigger a timer when packet matches.
      6 * After timer expires a kevent will be sent.
      7 *
      8 * Copyright (C) 2004, 2010 Nokia Corporation
      9 * Written by Timo Teras <ext-timo.teras@nokia.com>
     10 *
     11 * Converted to x_tables and reworked for upstream inclusion
     12 * by Luciano Coelho <luciano.coelho@nokia.com>
     13 *
     14 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
     15 */
     16
     17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     18
     19#include <linux/module.h>
     20#include <linux/timer.h>
     21#include <linux/alarmtimer.h>
     22#include <linux/list.h>
     23#include <linux/mutex.h>
     24#include <linux/netfilter.h>
     25#include <linux/netfilter/x_tables.h>
     26#include <linux/netfilter/xt_IDLETIMER.h>
     27#include <linux/kdev_t.h>
     28#include <linux/kobject.h>
     29#include <linux/workqueue.h>
     30#include <linux/sysfs.h>
     31
     32struct idletimer_tg {
     33	struct list_head entry;
     34	struct alarm alarm;
     35	struct timer_list timer;
     36	struct work_struct work;
     37
     38	struct kobject *kobj;
     39	struct device_attribute attr;
     40
     41	unsigned int refcnt;
     42	u8 timer_type;
     43};
     44
     45static LIST_HEAD(idletimer_tg_list);
     46static DEFINE_MUTEX(list_mutex);
     47
     48static struct kobject *idletimer_tg_kobj;
     49
     50static
     51struct idletimer_tg *__idletimer_tg_find_by_label(const char *label)
     52{
     53	struct idletimer_tg *entry;
     54
     55	list_for_each_entry(entry, &idletimer_tg_list, entry) {
     56		if (!strcmp(label, entry->attr.attr.name))
     57			return entry;
     58	}
     59
     60	return NULL;
     61}
     62
     63static ssize_t idletimer_tg_show(struct device *dev,
     64				 struct device_attribute *attr, char *buf)
     65{
     66	struct idletimer_tg *timer;
     67	unsigned long expires = 0;
     68	struct timespec64 ktimespec = {};
     69	long time_diff = 0;
     70
     71	mutex_lock(&list_mutex);
     72
     73	timer =	__idletimer_tg_find_by_label(attr->attr.name);
     74	if (timer) {
     75		if (timer->timer_type & XT_IDLETIMER_ALARM) {
     76			ktime_t expires_alarm = alarm_expires_remaining(&timer->alarm);
     77			ktimespec = ktime_to_timespec64(expires_alarm);
     78			time_diff = ktimespec.tv_sec;
     79		} else {
     80			expires = timer->timer.expires;
     81			time_diff = jiffies_to_msecs(expires - jiffies) / 1000;
     82		}
     83	}
     84
     85	mutex_unlock(&list_mutex);
     86
     87	if (time_after(expires, jiffies) || ktimespec.tv_sec > 0)
     88		return sysfs_emit(buf, "%ld\n", time_diff);
     89
     90	return sysfs_emit(buf, "0\n");
     91}
     92
     93static void idletimer_tg_work(struct work_struct *work)
     94{
     95	struct idletimer_tg *timer = container_of(work, struct idletimer_tg,
     96						  work);
     97
     98	sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name);
     99}
    100
    101static void idletimer_tg_expired(struct timer_list *t)
    102{
    103	struct idletimer_tg *timer = from_timer(timer, t, timer);
    104
    105	pr_debug("timer %s expired\n", timer->attr.attr.name);
    106
    107	schedule_work(&timer->work);
    108}
    109
    110static enum alarmtimer_restart idletimer_tg_alarmproc(struct alarm *alarm,
    111							  ktime_t now)
    112{
    113	struct idletimer_tg *timer = alarm->data;
    114
    115	pr_debug("alarm %s expired\n", timer->attr.attr.name);
    116	schedule_work(&timer->work);
    117	return ALARMTIMER_NORESTART;
    118}
    119
    120static int idletimer_check_sysfs_name(const char *name, unsigned int size)
    121{
    122	int ret;
    123
    124	ret = xt_check_proc_name(name, size);
    125	if (ret < 0)
    126		return ret;
    127
    128	if (!strcmp(name, "power") ||
    129	    !strcmp(name, "subsystem") ||
    130	    !strcmp(name, "uevent"))
    131		return -EINVAL;
    132
    133	return 0;
    134}
    135
    136static int idletimer_tg_create(struct idletimer_tg_info *info)
    137{
    138	int ret;
    139
    140	info->timer = kzalloc(sizeof(*info->timer), GFP_KERNEL);
    141	if (!info->timer) {
    142		ret = -ENOMEM;
    143		goto out;
    144	}
    145
    146	ret = idletimer_check_sysfs_name(info->label, sizeof(info->label));
    147	if (ret < 0)
    148		goto out_free_timer;
    149
    150	sysfs_attr_init(&info->timer->attr.attr);
    151	info->timer->attr.attr.name = kstrdup(info->label, GFP_KERNEL);
    152	if (!info->timer->attr.attr.name) {
    153		ret = -ENOMEM;
    154		goto out_free_timer;
    155	}
    156	info->timer->attr.attr.mode = 0444;
    157	info->timer->attr.show = idletimer_tg_show;
    158
    159	ret = sysfs_create_file(idletimer_tg_kobj, &info->timer->attr.attr);
    160	if (ret < 0) {
    161		pr_debug("couldn't add file to sysfs");
    162		goto out_free_attr;
    163	}
    164
    165	list_add(&info->timer->entry, &idletimer_tg_list);
    166
    167	timer_setup(&info->timer->timer, idletimer_tg_expired, 0);
    168	info->timer->refcnt = 1;
    169
    170	INIT_WORK(&info->timer->work, idletimer_tg_work);
    171
    172	mod_timer(&info->timer->timer,
    173		  msecs_to_jiffies(info->timeout * 1000) + jiffies);
    174
    175	return 0;
    176
    177out_free_attr:
    178	kfree(info->timer->attr.attr.name);
    179out_free_timer:
    180	kfree(info->timer);
    181out:
    182	return ret;
    183}
    184
    185static int idletimer_tg_create_v1(struct idletimer_tg_info_v1 *info)
    186{
    187	int ret;
    188
    189	info->timer = kmalloc(sizeof(*info->timer), GFP_KERNEL);
    190	if (!info->timer) {
    191		ret = -ENOMEM;
    192		goto out;
    193	}
    194
    195	ret = idletimer_check_sysfs_name(info->label, sizeof(info->label));
    196	if (ret < 0)
    197		goto out_free_timer;
    198
    199	sysfs_attr_init(&info->timer->attr.attr);
    200	info->timer->attr.attr.name = kstrdup(info->label, GFP_KERNEL);
    201	if (!info->timer->attr.attr.name) {
    202		ret = -ENOMEM;
    203		goto out_free_timer;
    204	}
    205	info->timer->attr.attr.mode = 0444;
    206	info->timer->attr.show = idletimer_tg_show;
    207
    208	ret = sysfs_create_file(idletimer_tg_kobj, &info->timer->attr.attr);
    209	if (ret < 0) {
    210		pr_debug("couldn't add file to sysfs");
    211		goto out_free_attr;
    212	}
    213
    214	/*  notify userspace  */
    215	kobject_uevent(idletimer_tg_kobj,KOBJ_ADD);
    216
    217	list_add(&info->timer->entry, &idletimer_tg_list);
    218	pr_debug("timer type value is %u", info->timer_type);
    219	info->timer->timer_type = info->timer_type;
    220	info->timer->refcnt = 1;
    221
    222	INIT_WORK(&info->timer->work, idletimer_tg_work);
    223
    224	if (info->timer->timer_type & XT_IDLETIMER_ALARM) {
    225		ktime_t tout;
    226		alarm_init(&info->timer->alarm, ALARM_BOOTTIME,
    227			   idletimer_tg_alarmproc);
    228		info->timer->alarm.data = info->timer;
    229		tout = ktime_set(info->timeout, 0);
    230		alarm_start_relative(&info->timer->alarm, tout);
    231	} else {
    232		timer_setup(&info->timer->timer, idletimer_tg_expired, 0);
    233		mod_timer(&info->timer->timer,
    234				msecs_to_jiffies(info->timeout * 1000) + jiffies);
    235	}
    236
    237	return 0;
    238
    239out_free_attr:
    240	kfree(info->timer->attr.attr.name);
    241out_free_timer:
    242	kfree(info->timer);
    243out:
    244	return ret;
    245}
    246
    247/*
    248 * The actual xt_tables plugin.
    249 */
    250static unsigned int idletimer_tg_target(struct sk_buff *skb,
    251					 const struct xt_action_param *par)
    252{
    253	const struct idletimer_tg_info *info = par->targinfo;
    254
    255	pr_debug("resetting timer %s, timeout period %u\n",
    256		 info->label, info->timeout);
    257
    258	mod_timer(&info->timer->timer,
    259		  msecs_to_jiffies(info->timeout * 1000) + jiffies);
    260
    261	return XT_CONTINUE;
    262}
    263
    264/*
    265 * The actual xt_tables plugin.
    266 */
    267static unsigned int idletimer_tg_target_v1(struct sk_buff *skb,
    268					 const struct xt_action_param *par)
    269{
    270	const struct idletimer_tg_info_v1 *info = par->targinfo;
    271
    272	pr_debug("resetting timer %s, timeout period %u\n",
    273		 info->label, info->timeout);
    274
    275	if (info->timer->timer_type & XT_IDLETIMER_ALARM) {
    276		ktime_t tout = ktime_set(info->timeout, 0);
    277		alarm_start_relative(&info->timer->alarm, tout);
    278	} else {
    279		mod_timer(&info->timer->timer,
    280				msecs_to_jiffies(info->timeout * 1000) + jiffies);
    281	}
    282
    283	return XT_CONTINUE;
    284}
    285
    286static int idletimer_tg_helper(struct idletimer_tg_info *info)
    287{
    288	if (info->timeout == 0) {
    289		pr_debug("timeout value is zero\n");
    290		return -EINVAL;
    291	}
    292	if (info->timeout >= INT_MAX / 1000) {
    293		pr_debug("timeout value is too big\n");
    294		return -EINVAL;
    295	}
    296	if (info->label[0] == '\0' ||
    297	    strnlen(info->label,
    298		    MAX_IDLETIMER_LABEL_SIZE) == MAX_IDLETIMER_LABEL_SIZE) {
    299		pr_debug("label is empty or not nul-terminated\n");
    300		return -EINVAL;
    301	}
    302	return 0;
    303}
    304
    305
    306static int idletimer_tg_checkentry(const struct xt_tgchk_param *par)
    307{
    308	struct idletimer_tg_info *info = par->targinfo;
    309	int ret;
    310
    311	pr_debug("checkentry targinfo%s\n", info->label);
    312
    313	ret = idletimer_tg_helper(info);
    314	if(ret < 0)
    315	{
    316		pr_debug("checkentry helper return invalid\n");
    317		return -EINVAL;
    318	}
    319	mutex_lock(&list_mutex);
    320
    321	info->timer = __idletimer_tg_find_by_label(info->label);
    322	if (info->timer) {
    323		info->timer->refcnt++;
    324		mod_timer(&info->timer->timer,
    325			  msecs_to_jiffies(info->timeout * 1000) + jiffies);
    326
    327		pr_debug("increased refcnt of timer %s to %u\n",
    328			 info->label, info->timer->refcnt);
    329	} else {
    330		ret = idletimer_tg_create(info);
    331		if (ret < 0) {
    332			pr_debug("failed to create timer\n");
    333			mutex_unlock(&list_mutex);
    334			return ret;
    335		}
    336	}
    337
    338	mutex_unlock(&list_mutex);
    339	return 0;
    340}
    341
    342static int idletimer_tg_checkentry_v1(const struct xt_tgchk_param *par)
    343{
    344	struct idletimer_tg_info_v1 *info = par->targinfo;
    345	int ret;
    346
    347	pr_debug("checkentry targinfo%s\n", info->label);
    348
    349	if (info->send_nl_msg)
    350		return -EOPNOTSUPP;
    351
    352	ret = idletimer_tg_helper((struct idletimer_tg_info *)info);
    353	if(ret < 0)
    354	{
    355		pr_debug("checkentry helper return invalid\n");
    356		return -EINVAL;
    357	}
    358
    359	if (info->timer_type > XT_IDLETIMER_ALARM) {
    360		pr_debug("invalid value for timer type\n");
    361		return -EINVAL;
    362	}
    363
    364	mutex_lock(&list_mutex);
    365
    366	info->timer = __idletimer_tg_find_by_label(info->label);
    367	if (info->timer) {
    368		if (info->timer->timer_type != info->timer_type) {
    369			pr_debug("Adding/Replacing rule with same label and different timer type is not allowed\n");
    370			mutex_unlock(&list_mutex);
    371			return -EINVAL;
    372		}
    373
    374		info->timer->refcnt++;
    375		if (info->timer_type & XT_IDLETIMER_ALARM) {
    376			/* calculate remaining expiry time */
    377			ktime_t tout = alarm_expires_remaining(&info->timer->alarm);
    378			struct timespec64 ktimespec = ktime_to_timespec64(tout);
    379
    380			if (ktimespec.tv_sec > 0) {
    381				pr_debug("time_expiry_remaining %lld\n",
    382					 ktimespec.tv_sec);
    383				alarm_start_relative(&info->timer->alarm, tout);
    384			}
    385		} else {
    386				mod_timer(&info->timer->timer,
    387					msecs_to_jiffies(info->timeout * 1000) + jiffies);
    388		}
    389		pr_debug("increased refcnt of timer %s to %u\n",
    390			 info->label, info->timer->refcnt);
    391	} else {
    392		ret = idletimer_tg_create_v1(info);
    393		if (ret < 0) {
    394			pr_debug("failed to create timer\n");
    395			mutex_unlock(&list_mutex);
    396			return ret;
    397		}
    398	}
    399
    400	mutex_unlock(&list_mutex);
    401	return 0;
    402}
    403
    404static void idletimer_tg_destroy(const struct xt_tgdtor_param *par)
    405{
    406	const struct idletimer_tg_info *info = par->targinfo;
    407
    408	pr_debug("destroy targinfo %s\n", info->label);
    409
    410	mutex_lock(&list_mutex);
    411
    412	if (--info->timer->refcnt == 0) {
    413		pr_debug("deleting timer %s\n", info->label);
    414
    415		list_del(&info->timer->entry);
    416		del_timer_sync(&info->timer->timer);
    417		cancel_work_sync(&info->timer->work);
    418		sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr);
    419		kfree(info->timer->attr.attr.name);
    420		kfree(info->timer);
    421	} else {
    422		pr_debug("decreased refcnt of timer %s to %u\n",
    423			 info->label, info->timer->refcnt);
    424	}
    425
    426	mutex_unlock(&list_mutex);
    427}
    428
    429static void idletimer_tg_destroy_v1(const struct xt_tgdtor_param *par)
    430{
    431	const struct idletimer_tg_info_v1 *info = par->targinfo;
    432
    433	pr_debug("destroy targinfo %s\n", info->label);
    434
    435	mutex_lock(&list_mutex);
    436
    437	if (--info->timer->refcnt == 0) {
    438		pr_debug("deleting timer %s\n", info->label);
    439
    440		list_del(&info->timer->entry);
    441		if (info->timer->timer_type & XT_IDLETIMER_ALARM) {
    442			alarm_cancel(&info->timer->alarm);
    443		} else {
    444			del_timer_sync(&info->timer->timer);
    445		}
    446		cancel_work_sync(&info->timer->work);
    447		sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr);
    448		kfree(info->timer->attr.attr.name);
    449		kfree(info->timer);
    450	} else {
    451		pr_debug("decreased refcnt of timer %s to %u\n",
    452			 info->label, info->timer->refcnt);
    453	}
    454
    455	mutex_unlock(&list_mutex);
    456}
    457
    458
    459static struct xt_target idletimer_tg[] __read_mostly = {
    460	{
    461	.name		= "IDLETIMER",
    462	.family		= NFPROTO_UNSPEC,
    463	.target		= idletimer_tg_target,
    464	.targetsize     = sizeof(struct idletimer_tg_info),
    465	.usersize	= offsetof(struct idletimer_tg_info, timer),
    466	.checkentry	= idletimer_tg_checkentry,
    467	.destroy        = idletimer_tg_destroy,
    468	.me		= THIS_MODULE,
    469	},
    470	{
    471	.name		= "IDLETIMER",
    472	.family		= NFPROTO_UNSPEC,
    473	.revision	= 1,
    474	.target		= idletimer_tg_target_v1,
    475	.targetsize     = sizeof(struct idletimer_tg_info_v1),
    476	.usersize	= offsetof(struct idletimer_tg_info_v1, timer),
    477	.checkentry	= idletimer_tg_checkentry_v1,
    478	.destroy        = idletimer_tg_destroy_v1,
    479	.me		= THIS_MODULE,
    480	},
    481
    482
    483};
    484
    485static struct class *idletimer_tg_class;
    486
    487static struct device *idletimer_tg_device;
    488
    489static int __init idletimer_tg_init(void)
    490{
    491	int err;
    492
    493	idletimer_tg_class = class_create(THIS_MODULE, "xt_idletimer");
    494	err = PTR_ERR(idletimer_tg_class);
    495	if (IS_ERR(idletimer_tg_class)) {
    496		pr_debug("couldn't register device class\n");
    497		goto out;
    498	}
    499
    500	idletimer_tg_device = device_create(idletimer_tg_class, NULL,
    501					    MKDEV(0, 0), NULL, "timers");
    502	err = PTR_ERR(idletimer_tg_device);
    503	if (IS_ERR(idletimer_tg_device)) {
    504		pr_debug("couldn't register system device\n");
    505		goto out_class;
    506	}
    507
    508	idletimer_tg_kobj = &idletimer_tg_device->kobj;
    509
    510	err = xt_register_targets(idletimer_tg, ARRAY_SIZE(idletimer_tg));
    511
    512	if (err < 0) {
    513		pr_debug("couldn't register xt target\n");
    514		goto out_dev;
    515	}
    516
    517	return 0;
    518out_dev:
    519	device_destroy(idletimer_tg_class, MKDEV(0, 0));
    520out_class:
    521	class_destroy(idletimer_tg_class);
    522out:
    523	return err;
    524}
    525
    526static void __exit idletimer_tg_exit(void)
    527{
    528	xt_unregister_targets(idletimer_tg, ARRAY_SIZE(idletimer_tg));
    529
    530	device_destroy(idletimer_tg_class, MKDEV(0, 0));
    531	class_destroy(idletimer_tg_class);
    532}
    533
    534module_init(idletimer_tg_init);
    535module_exit(idletimer_tg_exit);
    536
    537MODULE_AUTHOR("Timo Teras <ext-timo.teras@nokia.com>");
    538MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
    539MODULE_DESCRIPTION("Xtables: idle time monitor");
    540MODULE_LICENSE("GPL v2");
    541MODULE_ALIAS("ipt_IDLETIMER");
    542MODULE_ALIAS("ip6t_IDLETIMER");