]> git.hungrycats.org Git - linux/commitdiff
[SPARC64]: Translate IPT_SO_SET_REPLACE socket option for 32-bit apps.
authorLaszlo Valko <valko@linux.karinthy.hu>
Mon, 6 Jan 2003 17:00:22 +0000 (09:00 -0800)
committerJames Morris <jmorris@intercode.com.au>
Mon, 6 Jan 2003 17:00:22 +0000 (09:00 -0800)
arch/sparc64/kernel/sys_sparc32.c

index d18415e983c9004c94dc5c34a155c5af8447d5c0..140141c99c74283085b2cf7d45b183358794c427 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/security.h>
 #include <linux/compat.h>
 #include <linux/vfs.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
 
 #include <asm/types.h>
 #include <asm/ipc.h>
@@ -2527,6 +2528,78 @@ out:
 extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
                                     char *optval, int optlen);
 
+static int do_netfilter_replace(int fd, int level, int optname,
+                               char *optval, int optlen)
+{
+       struct ipt_replace32 {
+               char name[IPT_TABLE_MAXNAMELEN];
+               __u32 valid_hooks;
+               __u32 num_entries;
+               __u32 size;
+               __u32 hook_entry[NF_IP_NUMHOOKS];
+               __u32 underflow[NF_IP_NUMHOOKS];
+               __u32 num_counters;
+               __u32 counters;
+               struct ipt_entry entries[0];
+       } *repl32 = (struct ipt_replace32 *)optval;
+       struct ipt_replace *krepl;
+       struct ipt_counters *counters32;
+       __u32 origsize;
+       unsigned int kreplsize, kcountersize;
+       mm_segment_t old_fs;
+       int ret;
+
+       if (optlen < sizeof(repl32))
+               return -EINVAL;
+
+       if (copy_from_user(&origsize,
+                       &repl32->size,
+                       sizeof(origsize)))
+               return -EFAULT;
+
+       kreplsize = sizeof(*krepl) + origsize;
+       kcountersize = krepl->num_counters * sizeof(struct ipt_counters);
+
+       /* Hack: Causes ipchains to give correct error msg --RR */
+       if (optlen != kreplsize)
+               return -ENOPROTOOPT;
+
+       krepl = (struct ipt_replace *)kmalloc(kreplsize, GFP_KERNEL);
+       if (krepl == NULL)
+               return -ENOMEM;
+
+       if (copy_from_user(krepl, optval, kreplsize)) {
+               kfree(krepl);
+               return -EFAULT;
+       }
+
+       counters32 = (struct ipt_counters *)AA(
+               ((struct ipt_replace32 *)krepl)->counters);
+
+       kcountersize = krepl->num_counters * sizeof(struct ipt_counters);
+       krepl->counters = (struct ipt_counters *)kmalloc(
+                                       kcountersize, GFP_KERNEL);
+       if (krepl->counters == NULL) {
+               kfree(krepl);
+               return -ENOMEM;
+       }
+
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+       ret = sys_setsockopt(fd, level, optname,
+                            (char *)krepl, kreplsize);
+       set_fs(old_fs);
+
+       if (ret == 0 &&
+               copy_to_user(counters32, krepl->counters, kcountersize))
+                       ret = -EFAULT;
+
+       kfree(krepl->counters);
+       kfree(krepl);
+
+       return ret;
+}
+
 static int do_set_attach_filter(int fd, int level, int optname,
                                char *optval, int optlen)
 {
@@ -2620,6 +2693,9 @@ static int do_set_sock_timeout(int fd, int level, int optname, char *optval, int
 asmlinkage int sys32_setsockopt(int fd, int level, int optname,
                                char *optval, int optlen)
 {
+       if (optname == IPT_SO_SET_REPLACE)
+               return do_netfilter_replace(fd, level, optname,
+                                           optval, optlen);
        if (optname == SO_ATTACH_FILTER)
                return do_set_attach_filter(fd, level, optname,
                                            optval, optlen);