[parisc-linux-cvs] iptables support diff
Grant Grundler
grundler@puffin.external.hp.com
Mon, 30 Apr 2001 00:22:41 -0600
Grant Grundler wrote:
> I'm reworking the syscall wrapper.
> Touching ip_tables.h will result in breaking binary compatibility
> on other arches. That's not ok.
It turns out the alignment of "counters" pointer in struct ipt_replace
is 64-bit "compatible" and another 32-bits of implicit padding are
behind it. Not very clean but don't need to change iptables.h.
Diff is appended. I've left a "cleaner" version I was working on in
the #else part of #if 1/#else/#endif just in case we need it. If someone
is sure it's not needed, I'll drop it.
grant
Grant Grundler
parisc-linux {PCI|IOMMU|SMP} hacker
+1.408.447.7253
Index: arch/parisc/kernel/sys_parisc32.c
===================================================================
RCS file: /home/cvs/parisc/linux/arch/parisc/kernel/sys_parisc32.c,v
retrieving revision 1.9
diff -u -p -r1.9 sys_parisc32.c
--- sys_parisc32.c 2001/04/27 17:39:47 1.9
+++ sys_parisc32.c 2001/04/30 05:57:07
@@ -42,7 +42,11 @@
#include <linux/poll.h>
#include <linux/personality.h>
#include <linux/stat.h>
-#include <linux/filter.h>
+#include <linux/filter.h> /* for setsockopt() */
+#include <linux/icmpv6.h> /* for setsockopt() */
+#include <linux/netfilter_ipv4/ip_queue.h> /* for setsockopt() */
+#include <linux/netfilter_ipv4/ip_tables.h> /* for setsockopt() */
+#include <linux/netfilter_ipv6/ip6_tables.h> /* for setsockopt() */
#include <linux/highmem.h>
#include <linux/highuid.h>
#include <linux/mman.h>
@@ -2223,8 +2227,7 @@ out:
return len;
}
-/* didn't think we needed these translators but could be wrong -PB */
-#if 0
+
extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
char *optval, int optlen);
@@ -2298,20 +2301,137 @@ static int do_set_icmpv6_filter(int fd,
return ret;
}
+
+static int do_ipv4_set_replace(int fd, int level, int optname,
+ char *optval, int optlen)
+#if 1
+/* Fields happen to be padded such that this works.
+** Don't need to change iptables.h:struct ipt_replace
+*/
+{
+ struct ipt_replace *repl = (struct ipt_replace *) optval;
+ unsigned long ptr64;
+ unsigned int ptr32;
+ int ret;
+
+ if (copy_from_user(&ptr32, &repl->counters, sizeof(ptr32)))
+ return -EFAULT;
+ ptr64 = (unsigned long) ptr32;
+ if (copy_to_user(&repl->counters, &ptr64, sizeof(ptr64)))
+ return -EFAULT;
+
+ ret = sys_setsockopt(fd, level, optname, (char *) optval, optlen);
+
+ /* Restore 32-bit ptr */
+ if (copy_to_user(&repl->counters, &ptr32, sizeof(ptr32)))
+ return -EFAULT;
+
+ return ret;
+}
+#else
+/* This version tries to "do it right". ie allocate kernel buffers for
+** everything and copy data in/out. Way too complicated.
+** NOT TESTED for correctness!
+*/
+{
+ struct ipt_replace *kern_repl;
+ struct ipt_counters *kern_counters;
+ unsigned int user_counters;
+ mm_segment_t old_fs;
+ int ret = 0;
+
+ kern_repl = (struct ipt_replace *) kmalloc(optlen+8, GFP_KERNEL);
+ if (!kern_repl)
+ return -ENOMEM;
+
+ if (copy_from_user(kern_repl, optval, optlen)) {
+ ret = -EFAULT;
+ goto err02;
+ }
+
+ /* 32-bit ptr is in the MSB's */
+ user_counters = (unsigned int) (((unsigned long) kern_repl->counters) >> 32);
+ /*
+ ** We are going to set_fs() to kernel space - and thus need
+ ** "relocate" the counters buffer to the kernel space.
+ */
+ kern_counters = (struct ipt_counters *) kmalloc(kern_repl->num_counters * sizeof(struct ipt_counters), GFP_KERNEL);
+ if (!user_counters) {
+ ret = -ENOMEM;
+ goto err02;
+ }
+
+ if (copy_from_user(kern_counters, (char *) user_counters, optlen)) {
+ ret = -EFAULT;
+ goto err01;
+ }
+
+ /* We can update the kernel ptr now that we have the data. */
+ kern_repl->counters = kern_counters;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ ret = sys_setsockopt(fd, level, optname, (char *) optval, optlen);
+
+ set_fs(old_fs);
+
+ /* Copy counters back out to user space */
+ if (copy_to_user((char *) user_counters, kern_counters,
+ kern_repl->num_counters * sizeof(struct ipt_counters)))
+ {
+ ret = -EFAULT;
+ goto err01;
+ }
+
+ /* restore counters so userspace can consume it */
+ kern_repl->counters = NULL;
+ (unsigned int) kern_repl->counters = user_counters;
+
+ /* Copy repl back out to user space */
+ if (copy_to_user(optval, kern_repl, optlen))
+ {
+ ret = -EFAULT;
+ }
+
+err01:
+ kfree(kern_counters);
+err02:
+ kfree(kern_repl);
+ return ret;
+}
+#endif
+
+
asmlinkage int sys32_setsockopt(int fd, int level, int optname,
char *optval, int optlen)
{
if (optname == SO_ATTACH_FILTER)
- return do_set_attach_filter(fd, level, optname,
- optval, optlen);
- if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER)
- return do_set_icmpv6_filter(fd, level, optname,
- optval, optlen);
+ return do_set_attach_filter(fd, level, optname, optval, optlen);
+
+ if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER)
+ return do_set_icmpv6_filter(fd, level, optname, optval, optlen);
+
+ /*
+ ** Beware: IPT_SO_SET_REPLACE == IP6T_SO_SET_REPLACE
+ */
+ if (level == IPPROTO_IP && optname == IPT_SO_SET_REPLACE)
+ return do_ipv4_set_replace(fd, level, optname, optval, optlen);
+ if (level == IPPROTO_IPV6 && optname == IP6T_SO_SET_REPLACE)
+#if 0
+ /* FIXME: I don't (yet) use IPV6. -ggg */
+ return do_ipv6_set_replace(fd, level, optname, optval, optlen);
+#else
+ {
+ BUG();
+ return -ENXIO;
+ }
+#endif
+
return sys_setsockopt(fd, level, optname, optval, optlen);
}
-#endif
/*** copied from mips64 ***/
/*
Index: arch/parisc/kernel/syscall.S
===================================================================
RCS file: /home/cvs/parisc/linux/arch/parisc/kernel/syscall.S,v
retrieving revision 1.63
diff -u -p -r1.63 syscall.S
--- syscall.S 2001/04/17 21:37:27 1.63
+++ syscall.S 2001/04/30 05:57:07
@@ -550,8 +550,8 @@ sys_call_table:
ENTRY_UHOH(rt_sigqueueinfo)
ENTRY_SAME(rt_sigsuspend_wrapper) /* not really SAME -- see the code */
ENTRY_SAME(chown) /* 180 */
- /* *sockopt() might work... */
- ENTRY_SAME(setsockopt)
+ /* setsockopt() used by iptables: SO_SET_REPLACE/SO_SET_ADD_COUNTERS */
+ ENTRY_DIFF(setsockopt)
ENTRY_SAME(getsockopt)
ENTRY_DIFF(sendmsg)
ENTRY_DIFF(recvmsg)