]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
Extension revision number support (if kernel supports the getsockopts).
authorRusty Russell <rusty@rustcorp.com.au>
Mon, 3 Jan 2005 03:48:40 +0000 (03:48 +0000)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 3 Jan 2005 03:48:40 +0000 (03:48 +0000)
Enhance MARK match with second revision.
Committed in anticipation of the kernel patch being applied.

extensions/libipt_MARK.c
include/iptables.h
include/linux/netfilter_ipv4/ipt_MARK.h
iptables.c
libiptc/libiptc.c

index bc3dd42ef9b62a97ce59601188f86191c5e3b259..457f6ad393886bc5aed7f1afc4957ec95b88a863 100644 (file)
@@ -16,12 +16,16 @@ help(void)
        printf(
 "MARK target v%s options:\n"
 "  --set-mark value                   Set nfmark value\n"
+"  --and-mark value                   Binary AND the nfmark with value\n"
+"  --or-mark  value                   Binary OR  the nfmark with value\n"
 "\n",
 IPTABLES_VERSION);
 }
 
 static struct option opts[] = {
        { "set-mark", 1, 0, '1' },
+       { "and-mark", 1, 0, '2' },
+       { "or-mark", 1, 0, '3' },
        { 0 }
 };
 
@@ -34,9 +38,9 @@ init(struct ipt_entry_target *t, unsigned int *nfcache)
 /* Function which parses command options; returns true if it
    ate an option */
 static int
-parse(int c, char **argv, int invert, unsigned int *flags,
-      const struct ipt_entry *entry,
-      struct ipt_entry_target **target)
+parse_v0(int c, char **argv, int invert, unsigned int *flags,
+        const struct ipt_entry *entry,
+        struct ipt_entry_target **target)
 {
        struct ipt_mark_target_info *markinfo
                = (struct ipt_mark_target_info *)(*target)->data;
@@ -56,7 +60,12 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                                   "MARK target: Can't specify --set-mark twice");
                *flags = 1;
                break;
-
+       case '2':
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: kernel too old for --and-mark");
+       case '3':
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: kernel too old for --or-mark");
        default:
                return 0;
        }
@@ -69,7 +78,47 @@ final_check(unsigned int flags)
 {
        if (!flags)
                exit_error(PARAMETER_PROBLEM,
-                          "MARK target: Parameter --set-mark is required");
+                          "MARK target: Parameter --set/and/or-mark"
+                          " is required");
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse_v1(int c, char **argv, int invert, unsigned int *flags,
+        const struct ipt_entry *entry,
+        struct ipt_entry_target **target)
+{
+       struct ipt_mark_target_info_v1 *markinfo
+               = (struct ipt_mark_target_info_v1 *)(*target)->data;
+
+       switch (c) {
+       case '1':
+               markinfo->mode = IPT_MARK_SET;
+               break;
+       case '2':
+               markinfo->mode = IPT_MARK_AND;
+               break;
+       case '3':
+               markinfo->mode = IPT_MARK_OR;
+               break;
+       default:
+               return 0;
+       }
+
+#ifdef KERNEL_64_USERSPACE_32
+       if (string_to_number_ll(optarg, 0, 0,  &markinfo->mark))
+#else
+       if (string_to_number_l(optarg, 0, 0, &markinfo->mark))
+#endif
+               exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+
+       if (*flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "MARK target: Can't specify --set-mark twice");
+
+       *flags = 1;
+       return 1;
 }
 
 #ifdef KERNEL_64_USERSPACE_32
@@ -88,9 +137,9 @@ print_mark(unsigned long mark)
 
 /* Prints out the targinfo. */
 static void
-print(const struct ipt_ip *ip,
-      const struct ipt_entry_target *target,
-      int numeric)
+print_v0(const struct ipt_ip *ip,
+        const struct ipt_entry_target *target,
+        int numeric)
 {
        const struct ipt_mark_target_info *markinfo =
                (const struct ipt_mark_target_info *)target->data;
@@ -100,7 +149,7 @@ print(const struct ipt_ip *ip,
 
 /* Saves the union ipt_targinfo in parsable form to stdout. */
 static void
-save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+save_v0(const struct ipt_ip *ip, const struct ipt_entry_target *target)
 {
        const struct ipt_mark_target_info *markinfo =
                (const struct ipt_mark_target_info *)target->data;
@@ -109,23 +158,86 @@ save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
        print_mark(markinfo->mark);
 }
 
+/* Prints out the targinfo. */
+static void
+print_v1(const struct ipt_ip *ip,
+        const struct ipt_entry_target *target,
+        int numeric)
+{
+       const struct ipt_mark_target_info_v1 *markinfo =
+               (const struct ipt_mark_target_info_v1 *)target->data;
+
+       switch (markinfo->mode) {
+       case IPT_MARK_SET:
+               printf("MARK set ");
+               break;
+       case IPT_MARK_AND:
+               printf("MARK and ");
+               break;
+       case IPT_MARK_OR: 
+               printf("MARK or ");
+               break;
+       }
+       print_mark(markinfo->mark);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save_v1(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_mark_target_info_v1 *markinfo =
+               (const struct ipt_mark_target_info_v1 *)target->data;
+
+       switch (markinfo->mode) {
+       case IPT_MARK_SET:
+               printf("--set-mark ");
+               break;
+       case IPT_MARK_AND:
+               printf("--and-mark ");
+               break;
+       case IPT_MARK_OR: 
+               printf("--or-mark ");
+               break;
+       }
+       print_mark(markinfo->mark);
+}
+
 static
-struct iptables_target mark = {
+struct iptables_target mark_v0 = {
        .next           = NULL,
        .name           = "MARK",
        .version        = IPTABLES_VERSION,
+       .revision       = 0,
        .size           = IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
        .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
        .help           = &help,
        .init           = &init,
-       .parse          = &parse,
+       .parse          = &parse_v0,
+       .final_check    = &final_check,
+       .print          = &print_v0,
+       .save           = &save_v0,
+       .extra_opts     = opts
+};
+
+static
+struct iptables_target mark_v1 = {
+       .next           = NULL,
+       .name           = "MARK",
+       .version        = IPTABLES_VERSION,
+       .revision       = 1,
+       .size           = IPT_ALIGN(sizeof(struct ipt_mark_target_info_v1)),
+       .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mark_target_info_v1)),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse_v1,
        .final_check    = &final_check,
-       .print          = &print,
-       .save           = &save,
+       .print          = &print_v1,
+       .save           = &save_v1,
        .extra_opts     = opts
 };
 
 void _init(void)
 {
-       register_target(&mark);
+       register_target(&mark_v0);
+       register_target(&mark_v1);
 }
index 6d997f7ce4a59dda3aa7d058745d642a84df610b..25f36aeb14925e69ef0e3d8cc2a9533daf26b302 100644 (file)
 #define IPPROTO_SCTP 132
 #endif
 
+#ifndef IPT_SO_GET_REVISION_MATCH /* Old kernel source. */
+#define IPT_SO_GET_REVISION_MATCH      (IPT_BASE_CTL + 2)
+#define IPT_SO_GET_REVISION_TARGET     (IPT_BASE_CTL + 3)
+
+struct ipt_get_revision
+{
+       char name[IPT_FUNCTION_MAXNAMELEN-1];
+
+       u_int8_t revision;
+};
+#endif /* IPT_SO_GET_REVISION_MATCH   Old kernel source */
+
 struct iptables_rule_match
 {
        struct iptables_rule_match *next;
@@ -26,6 +38,9 @@ struct iptables_match
 
        ipt_chainlabel name;
 
+       /* Revision of match (0 by default). */
+       u_int8_t revision;
+
        const char *version;
 
        /* Size of match data. */
@@ -76,6 +91,9 @@ struct iptables_target
 
        ipt_chainlabel name;
 
+       /* Revision of target (0 by default). */
+       u_int8_t revision;
+
        const char *version;
 
        /* Size of target data. */
index 6febfe649f080a17184d506e9722b215dc08c57c..3694e488c477a5dd06264969f7a0ec26558a0bec 100644 (file)
@@ -9,4 +9,19 @@ struct ipt_mark_target_info {
 #endif
 };
 
+enum {
+       IPT_MARK_SET=0,
+       IPT_MARK_AND,
+       IPT_MARK_OR
+};
+
+struct ipt_mark_target_info_v1 {
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned long long mark;
+#else
+       unsigned long mark;
+#endif
+       u_int8_t mode;
+};
+
 #endif /*_IPT_MARK_H_target*/
index ca0a66faddcfdb89badfb393c1e8e7f2bc07c875..3cedaccf72d5d6fd5e82b0b47608a0fd4772ef64 100644 (file)
@@ -1033,10 +1033,56 @@ merge_options(struct option *oldopts, const struct option *newopts,
        return merge;
 }
 
+static int compatible_revision(const char *name, u_int8_t revision, int opt)
+{
+       struct ipt_get_revision rev;
+       socklen_t s = sizeof(rev);
+       int max_rev, sockfd;
+
+       sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0) {
+               fprintf(stderr, "Could not open socket to kernel: %s\n",
+                       strerror(errno));
+               exit(1);
+       }
+
+       strcpy(rev.name, name);
+       rev.revision = revision;
+
+       max_rev = getsockopt(sockfd, IPPROTO_IP, opt, &rev, &s);
+       if (max_rev < 0) {
+               /* Definitely don't support this? */
+               if (errno == EPROTONOSUPPORT) {
+                       close(sockfd);
+                       return 0;
+               } else if (errno == ENOPROTOOPT) {
+                       close(sockfd);
+                       /* Assume only revision 0 support (old kernel) */
+                       return (revision == 0);
+               } else {
+                       fprintf(stderr, "getsockopt failed strangely: %s\n",
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       close(sockfd);
+       return 1;
+}
+
+static int compatible_match_revision(const char *name, u_int8_t revision)
+{
+       return compatible_revision(name, revision, IPT_SO_GET_REVISION_MATCH);
+}
+
+static int compatible_target_revision(const char *name, u_int8_t revision)
+{
+       return compatible_revision(name, revision, IPT_SO_GET_REVISION_TARGET);
+}
+
 void
 register_match(struct iptables_match *me)
 {
-       struct iptables_match **i;
+       struct iptables_match **i, *old;
 
        if (strcmp(me->version, program_version) != 0) {
                fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
@@ -1044,12 +1090,36 @@ register_match(struct iptables_match *me)
                exit(1);
        }
 
-       if (find_match(me->name, DONT_LOAD, NULL)) {
-               fprintf(stderr, "%s: match `%s' already registered.\n",
+       /* Revision field stole a char: check for 30 char names. */
+       if (!memchr(me->name, 0, IPT_FUNCTION_MAXNAMELEN-1)) {
+               fprintf(stderr, "%s: target `%s' has invalid name\n",
                        program_name, me->name);
                exit(1);
        }
 
+       old = find_match(me->name, DONT_LOAD, NULL);
+       if (old) {
+               if (old->revision == me->revision) {
+                       fprintf(stderr,
+                               "%s: match `%s' already registered.\n",
+                               program_name, me->name);
+                       exit(1);
+               }
+
+               /* Now we have two (or more) options, check compatibility. */
+               if (compatible_match_revision(old->name, old->revision)
+                   && old->revision > me->revision)
+                       return;
+
+               /* Replace if compatible. */
+               if (!compatible_match_revision(me->name, me->revision))
+                       return;
+
+               /* Delete old one. */
+               for (i = &iptables_matches; *i!=old; i = &(*i)->next);
+               *i = old->next;
+       }
+
        if (me->size != IPT_ALIGN(me->size)) {
                fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
                        program_name, me->name, (unsigned int)me->size);
@@ -1068,18 +1138,49 @@ register_match(struct iptables_match *me)
 void
 register_target(struct iptables_target *me)
 {
+       struct iptables_target *old;
+
        if (strcmp(me->version, program_version) != 0) {
                fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
                        program_name, me->name, me->version, program_version);
                exit(1);
        }
 
-       if (find_target(me->name, DONT_LOAD)) {
-               fprintf(stderr, "%s: target `%s' already registered.\n",
+       /* Revision field stole a char: check for 30 char names. */
+       if (!memchr(me->name, 0, IPT_FUNCTION_MAXNAMELEN)) {
+               fprintf(stderr, "%s: target `%s' has invalid name\n",
                        program_name, me->name);
                exit(1);
        }
 
+       old = find_target(me->name, DONT_LOAD);
+       if (old) {
+               struct iptables_target **i;
+
+               if (old->revision == me->revision) {
+                       fprintf(stderr,
+                               "%s: target `%s' already registered.\n",
+                               program_name, me->name);
+                       exit(1);
+               }
+
+               fprintf(stderr, "%s v%i vs v%i\n",
+                       me->name, me->revision, old->revision);
+
+               /* Now we have two (or more) options, check compatibility. */
+               if (compatible_target_revision(old->name, old->revision)
+                   && old->revision > me->revision)
+                       return;
+
+               /* Replace if compatible. */
+               if (!compatible_target_revision(me->name, me->revision))
+                       return;
+
+               /* Delete old one. */
+               for (i = &iptables_targets; *i!=old; i = &(*i)->next);
+               *i = old->next;
+       }
+
        if (me->size != IPT_ALIGN(me->size)) {
                fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
                        program_name, me->name, (unsigned int)me->size);
@@ -1684,6 +1785,14 @@ void clear_rule_matches(struct iptables_rule_match **matches)
        *matches = NULL;
 }
 
+static void set_revision(char *name, u_int8_t revision)
+{
+       /* Old kernel sources don't have ".revision" field,
+          but we stole a byte from name. */
+       name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
+       name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
+}
+
 int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
 {
        struct ipt_entry fw, *e = NULL;
@@ -1916,6 +2025,8 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                                target->t = fw_calloc(1, size);
                                target->t->u.target_size = size;
                                strcpy(target->t->u.user.name, jumpto);
+                               set_revision(target->t->u.user.name,
+                                            target->revision);
                                target->init(target->t, &fw.nfcache);
                                opts = merge_options(opts, target->extra_opts, &target->option_offset);
                        }
@@ -1969,6 +2080,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                        m->m = fw_calloc(1, size);
                        m->m->u.match_size = size;
                        strcpy(m->m->u.user.name, m->name);
+                       set_revision(m->m->u.user.name, m->revision);
                        m->init(m->m, &fw.nfcache);
                        opts = merge_options(opts, m->extra_opts, &m->option_offset);
                }
@@ -2110,6 +2222,8 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                                        m->m = fw_calloc(1, size);
                                        m->m->u.match_size = size;
                                        strcpy(m->m->u.user.name, m->name);
+                                       set_revision(m->m->u.user.name,
+                                                    m->revision);
                                        m->init(m->m, &fw.nfcache);
 
                                        opts = merge_options(opts,
@@ -2237,6 +2351,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
                        target->t = fw_calloc(1, size);
                        target->t->u.target_size = size;
                        strcpy(target->t->u.user.name, jumpto);
+                       set_revision(target->t->u.user.name, target->revision);
                        target->init(target->t, &fw.nfcache);
                }
 
index 22b80a6b416633bd1404b71caf531004b951eaa0..ad0b57c494757ec23358bb5f951fd00c9636dc30 100644 (file)
@@ -1234,10 +1234,10 @@ iptcc_map_target(const TC_HANDLE_T handle,
        }
 
        /* Must be a module?  If not, kernel will reject... */
-       /* memset to all 0 for your memcmp convenience. */
+       /* memset to all 0 for your memcmp convenience: don't clear version */
        memset(t->u.user.name + strlen(t->u.user.name),
               0,
-              FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
+              FUNCTION_MAXNAMELEN - 1 - strlen(t->u.user.name));
        r->type = IPTCC_R_MODULE;
        set_changed(handle);
        return 1;