linux/net/netfilter/xt_IDLETIMER.c
<<
>>
Prefs
   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 snprintf(buf, PAGE_SIZE, "%ld\n", time_diff);
  89
  90        return snprintf(buf, PAGE_SIZE, "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");
 543