diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2005-01-04 04:13:55 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-01-04 04:13:55 -0800 |
commit | cad3ff82ff4457261aa3bf9a6900ed0d0280896d (patch) | |
tree | 0ca7f3899e54da8f1a903f5849eb5bd80064628a /net | |
parent | 5d9ed066ad6dff86b86688ba53eb0cb22e4c3ce9 (diff) | |
download | history-cad3ff82ff4457261aa3bf9a6900ed0d0280896d.tar.gz |
[PATCH] iptables revision getsockopt
This adds a new getsockopt to iptables, which allows userspace to
query the revision number of extensions. iptables 1.3.0 (to be
released soon) already has support for this.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 187bac7f91b74a..7eebab02e08cee 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -488,6 +488,65 @@ static inline struct ipt_target *find_target_lock(const char *name, u8 revision) return ERR_PTR(-EPROTOTYPE); } +static int match_revfn(const char *name, u8 revision, int *bestp) +{ + struct ipt_match *m; + int have_rev = 0; + + list_for_each_entry(m, &ipt_match, list) { + if (strcmp(m->name, name) == 0) { + if (m->revision > *bestp) + *bestp = m->revision; + if (m->revision == revision) + have_rev = 1; + } + } + return have_rev; +} + +static int target_revfn(const char *name, u8 revision, int *bestp) +{ + struct ipt_target *t; + int have_rev = 0; + + list_for_each_entry(t, &ipt_target, list) { + if (strcmp(t->name, name) == 0) { + if (t->revision > *bestp) + *bestp = t->revision; + if (t->revision == revision) + have_rev = 1; + } + } + return have_rev; +} + +/* Returns true or false (if no such extension at all) */ +static inline int find_revision(const char *name, u8 revision, + int (*revfn)(const char *, u8, int *), + int *err) +{ + int have_rev, best = -1; + + if (down_interruptible(&ipt_mutex) != 0) { + *err = -EINTR; + return 1; + } + have_rev = revfn(name, revision, &best); + up(&ipt_mutex); + + /* Nothing at all? Return 0 to try loading module. */ + if (best == -1) { + *err = -ENOENT; + return 0; + } + + *err = best; + if (!have_rev) + *err = -EPROTONOSUPPORT; + return 1; +} + + /* All zeroes == unconditional rule. */ static inline int unconditional(const struct ipt_ip *ip) @@ -1316,6 +1375,31 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) break; } + case IPT_SO_GET_REVISION_MATCH: + case IPT_SO_GET_REVISION_TARGET: { + struct ipt_get_revision rev; + int (*revfn)(const char *, u8, int *); + + if (*len != sizeof(rev)) { + ret = -EINVAL; + break; + } + if (copy_from_user(&rev, user, sizeof(rev)) != 0) { + ret = -EFAULT; + break; + } + + if (cmd == IPT_SO_GET_REVISION_TARGET) + revfn = target_revfn; + else + revfn = match_revfn; + + try_then_request_module(find_revision(rev.name, rev.revision, + revfn, &ret), + "ipt_%s", rev.name); + break; + } + default: duprintf("do_ipt_get_ctl: unknown request %i\n", cmd); ret = -EINVAL; |