/* * net/sched/ipt.c iptables target interface * *TODO: Add other tables. For now we only support the ipv4 table targets * * 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. * * Copyright: Jamal Hadi Salim (2002-4) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* use generic hash table */ #define MY_TAB_SIZE 16 #define MY_TAB_MASK 15 static u32 idx_gen; static struct tcf_ipt *tcf_ipt_ht[MY_TAB_SIZE]; /* ipt hash table lock */ static rwlock_t ipt_lock = RW_LOCK_UNLOCKED; /* ovewrride the defaults */ #define tcf_st tcf_ipt #define tcf_t_lock ipt_lock #define tcf_ht tcf_ipt_ht #include static inline int init_targ(struct tcf_ipt *p) { struct ipt_target *target; int ret = 0; struct ipt_entry_target *t = p->t; target = __ipt_find_target_lock(t->u.user.name, &ret); if (!target) { printk("init_targ: Failed to find %s\n", t->u.user.name); return -1; } DPRINTK("init_targ: found %s\n", target->name); /* we really need proper ref counting seems to be only needed for modules?? Talk to laforge */ /* if (target->me) __MOD_INC_USE_COUNT(target->me); */ t->u.kernel.target = target; __ipt_mutex_up(); if (t->u.kernel.target->checkentry && !t->u.kernel.target->checkentry(p->tname, NULL, t->data, t->u.target_size - sizeof (*t), p->hook)) { /* if (t->u.kernel.target->me) __MOD_DEC_USE_COUNT(t->u.kernel.target->me); */ DPRINTK("ip_tables: check failed for `%s'.\n", t->u.kernel.target->name); ret = -EINVAL; } return ret; } static int tcf_ipt_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a, int ovr, int bind) { struct ipt_entry_target *t; unsigned h; struct rtattr *tb[TCA_IPT_MAX]; struct tcf_ipt *p; int ret = 0; u32 index = 0; u32 hook = 0; if (NULL == a || NULL == rta || (rtattr_parse(tb, TCA_IPT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)) < 0)) { return -1; } if (tb[TCA_IPT_INDEX - 1]) { index = *(u32 *) RTA_DATA(tb[TCA_IPT_INDEX - 1]); DPRINTK("ipt index %d\n", index); } if (index && (p = tcf_hash_lookup(index)) != NULL) { a->priv = (void *) p; spin_lock(&p->lock); if (bind) { p->bindcnt += 1; p->refcnt += 1; } if (ovr) { goto override; } spin_unlock(&p->lock); return ret; } if (NULL == tb[TCA_IPT_TARG - 1] || NULL == tb[TCA_IPT_HOOK - 1]) { return -1; } p = kmalloc(sizeof (*p), GFP_KERNEL); if (p == NULL) return -1; memset(p, 0, sizeof (*p)); p->refcnt = 1; ret = 1; spin_lock_init(&p->lock); p->stats_lock = &p->lock; if (bind) p->bindcnt = 1; override: hook = *(u32 *) RTA_DATA(tb[TCA_IPT_HOOK - 1]); t = (struct ipt_entry_target *) RTA_DATA(tb[TCA_IPT_TARG - 1]); p->t = kmalloc(t->u.target_size, GFP_KERNEL); if (p->t == NULL) { if (ovr) { printk("ipt policy messed up \n"); spin_unlock(&p->lock); return -1; } kfree(p); return -1; } memcpy(p->t, RTA_DATA(tb[TCA_IPT_TARG - 1]), t->u.target_size); DPRINTK(" target NAME %s size %d data[0] %x data[1] %x\n", t->u.user.name, t->u.target_size, t->data[0], t->data[1]); p->tname = kmalloc(IFNAMSIZ, GFP_KERNEL); if (p->tname == NULL) { if (ovr) { printk("ipt policy messed up 2 \n"); spin_unlock(&p->lock); return -1; } kfree(p->t); kfree(p); return -1; } else { int csize = IFNAMSIZ - 1; memset(p->tname, 0, IFNAMSIZ); if (tb[TCA_IPT_TABLE - 1]) { if (strlen((char *) RTA_DATA(tb[TCA_IPT_TABLE - 1])) < csize) csize = strlen(RTA_DATA(tb[TCA_IPT_TABLE - 1])); strncpy(p->tname, RTA_DATA(tb[TCA_IPT_TABLE - 1]), csize); DPRINTK("table name %s\n", p->tname); } else { strncpy(p->tname, "mangle", 1 + strlen("mangle")); } } if (0 > init_targ(p)) { if (ovr) { printk("ipt policy messed up 2 \n"); spin_unlock(&p->lock); return -1; } kfree(p->tname); kfree(p->t); kfree(p); return -1; } if (ovr) { spin_unlock(&p->lock); return -1; } p->index = index ? : tcf_hash_new_index(); p->tm.lastuse = jiffies; /* p->tm.expires = jiffies; */ p->tm.install = jiffies; #ifdef CONFIG_NET_ESTIMATOR if (est) gen_new_estimator(&p->bstats, &p->rate_est, p->stats_lock, est); #endif h = tcf_hash(p->index); write_lock_bh(&ipt_lock); p->next = tcf_ipt_ht[h]; tcf_ipt_ht[h] = p; write_unlock_bh(&ipt_lock); a->priv = (void *) p; return ret; } static int tcf_ipt_cleanup(struct tc_action *a, int bind) { struct tcf_ipt *p; p = PRIV(a,ipt); if (NULL != p) return tcf_hash_release(p, bind); return 0; } static int tcf_ipt(struct sk_buff **pskb, struct tc_action *a) { int ret = 0, result = 0; struct tcf_ipt *p; struct sk_buff *skb = *pskb; p = PRIV(a,ipt); if (NULL == p || NULL == skb) { return -1; } spin_lock(&p->lock); p->tm.lastuse = jiffies; p->bstats.bytes += skb->len; p->bstats.packets++; if (skb_cloned(skb) ) { if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { return -1; } } /* yes, we have to worry about both in and out dev worry later - danger - this API seems to have changed from earlier kernels */ ret = p->t->u.kernel.target->target(&skb, skb->dev, NULL, p->hook, p->t->data, (void *)NULL); switch (ret) { case NF_ACCEPT: result = TC_ACT_OK; break; case NF_DROP: result = TC_ACT_SHOT; p->qstats.drops++; break; case IPT_CONTINUE: result = TC_ACT_PIPE; break; default: if (net_ratelimit()) printk("Bogus netfilter code %d assume ACCEPT\n", ret); result = TC_POLICE_OK; break; } spin_unlock(&p->lock); return result; } static int tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { struct ipt_entry_target *t; struct tcf_t tm; struct tc_cnt c; unsigned char *b = skb->tail; struct tcf_ipt *p; p = PRIV(a,ipt); if (NULL == p) { printk("BUG: tcf_ipt_dump called with NULL params\n"); goto rtattr_failure; } /* for simple targets kernel size == user size ** user name = target name ** for foolproof you need to not assume this */ t = kmalloc(p->t->u.user.target_size, GFP_ATOMIC); if (NULL == t) goto rtattr_failure; c.bindcnt = p->bindcnt - bind; c.refcnt = p->refcnt - ref; memcpy(t, p->t, p->t->u.user.target_size); strcpy(t->u.user.name, p->t->u.kernel.target->name); DPRINTK("\ttcf_ipt_dump tablename %s length %d\n", p->tname, strlen(p->tname)); DPRINTK ("\tdump target name %s size %d size user %d data[0] %x data[1] %x\n", p->t->u.kernel.target->name, p->t->u.target_size, p->t->u.user.target_size, p->t->data[0], p->t->data[1]); RTA_PUT(skb, TCA_IPT_TARG, p->t->u.user.target_size, t); RTA_PUT(skb, TCA_IPT_INDEX, 4, &p->index); RTA_PUT(skb, TCA_IPT_HOOK, 4, &p->hook); RTA_PUT(skb, TCA_IPT_CNT, sizeof(struct tc_cnt), &c); RTA_PUT(skb, TCA_IPT_TABLE, IFNAMSIZ, p->tname); tm.install = jiffies_to_clock_t(jiffies - p->tm.install); tm.lastuse = jiffies_to_clock_t(jiffies - p->tm.lastuse); tm.expires = jiffies_to_clock_t(p->tm.expires); RTA_PUT(skb, TCA_IPT_TM, sizeof (tm), &tm); return skb->len; rtattr_failure: skb_trim(skb, b - skb->data); return -1; } static struct tc_action_ops act_ipt_ops = { .next = NULL, .kind = "ipt", .type = TCA_ACT_IPT, .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_ipt, .dump = tcf_ipt_dump, .cleanup = tcf_ipt_cleanup, .lookup = tcf_hash_search, .init = tcf_ipt_init, .walk = tcf_generic_walker }; MODULE_AUTHOR("Jamal Hadi Salim(2002-4)"); MODULE_DESCRIPTION("Iptables target actions"); MODULE_LICENSE("GPL"); static int __init ipt_init_module(void) { return tcf_register_action(&act_ipt_ops); } static void __exit ipt_cleanup_module(void) { tcf_unregister_action(&act_ipt_ops); } module_init(ipt_init_module); module_exit(ipt_cleanup_module);