Commit cad3ff82 authored by Rusty Russell's avatar Rusty Russell Committed by Linus Torvalds

[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: default avatarRusty Russell <rusty@rustcorp.com.au>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 5d9ed066
......@@ -158,7 +158,9 @@ struct ipt_entry
#define IPT_SO_GET_INFO (IPT_BASE_CTL)
#define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1)
#define IPT_SO_GET_MAX IPT_SO_GET_ENTRIES
#define IPT_SO_GET_REVISION_MATCH (IPT_BASE_CTL + 2)
#define IPT_SO_GET_REVISION_TARGET (IPT_BASE_CTL + 3)
#define IPT_SO_GET_MAX IPT_SO_GET_REVISION_TARGET
/* CONTINUE verdict for targets */
#define IPT_CONTINUE 0xFFFFFFFF
......@@ -288,6 +290,15 @@ struct ipt_get_entries
struct ipt_entry entrytable[0];
};
/* The argument to IPT_SO_GET_REVISION_*. Returns highest revision
* kernel supports, if >= revision. */
struct ipt_get_revision
{
char name[IPT_FUNCTION_MAXNAMELEN-1];
u_int8_t revision;
};
/* Standard return verdict, or do jump. */
#define IPT_STANDARD_TARGET ""
/* Error verdict. */
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment