aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2005-01-04 04:13:55 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-01-04 04:13:55 -0800
commitcad3ff82ff4457261aa3bf9a6900ed0d0280896d (patch)
tree0ca7f3899e54da8f1a903f5849eb5bd80064628a /net
parent5d9ed066ad6dff86b86688ba53eb0cb22e4c3ce9 (diff)
downloadhistory-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.c84
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;