]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
Phil Blundell's new ipv6 extensions.
authorPhilip Blundell <Philip.Blundell@pobox.com>
Sun, 4 Jun 2000 17:38:47 +0000 (17:38 +0000)
committerRusty Russell <rusty@rustcorp.com.au>
Sun, 4 Jun 2000 17:38:47 +0000 (17:38 +0000)
extensions/libip6t_icmp.c [new file with mode: 0644]
extensions/libip6t_tcp.c [new file with mode: 0644]
extensions/libip6t_udp.c [new file with mode: 0644]

diff --git a/extensions/libip6t_icmp.c b/extensions/libip6t_icmp.c
new file mode 100644 (file)
index 0000000..d864112
--- /dev/null
@@ -0,0 +1,281 @@
+/* Shared library add-on to iptables to add ICMP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+struct icmp_names {
+       const char *name;
+       u_int8_t type;
+       u_int8_t code_min, code_max;
+};
+
+static const struct icmp_names icmp_codes[] = {
+       { "destination-unreachable", 1, 0, 0xFF },
+       {   "no-route", 1, 0, 0 },
+       {   "communication-prohibited", 1, 1, 1 },
+       {   "address-unreachable", 1, 3, 3 },
+       {   "port-unreachable", 1, 4, 4 },
+
+       { "packet-too-big", 2, 0, 0xFF },
+
+       { "time-exceeded", 3, 0, 0xFF },
+       /* Alias */ { "ttl-exceeded", 3, 0, 0xFF },
+       {   "ttl-zero-during-transit", 3, 0, 0 },
+       {   "ttl-zero-during-reassembly", 3, 1, 1 },
+
+       { "parameter-problem", 4, 0, 0xFF },
+       {   "bad-header", 4, 0, 0 },
+       {   "unknown-header-type", 4, 1, 1 },
+       {   "unknown-option", 4, 2, 2 },
+
+       { "echo-request", 128, 0, 0xFF },
+       /* Alias */ { "ping", 128, 0, 0xFF },
+
+       { "echo-reply", 129, 0, 0xFF },
+       /* Alias */ { "pong", 129, 0, 0xFF },
+
+       { "router-solicitation", 133, 0, 0xFF },
+
+       { "router-advertisement", 134, 0, 0xFF },
+
+       { "neighbour-solicitation", 135, 0, 0xFF },
+       /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF },
+
+       { "neighbour-advertisement", 136, 0, 0xFF },
+       /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF },
+
+       { "redirect", 137, 0, 0xFF },
+
+};
+
+static void
+print_icmptypes()
+{
+       unsigned int i;
+       printf("Valid ICMPv6 Types:");
+
+       for (i = 0; i < sizeof(icmp_codes)/sizeof(struct icmp_names); i++) {
+               if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
+                       if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
+                           && (icmp_codes[i].code_max
+                               == icmp_codes[i-1].code_max))
+                               printf(" (%s)", icmp_codes[i].name);
+                       else
+                               printf("\n   %s", icmp_codes[i].name);
+               }
+               else
+                       printf("\n%s", icmp_codes[i].name);
+       }
+       printf("\n");
+}
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ICMPv6 v%s options:\n"
+" --icmp-type [!] typename     match icmp type\n"
+"                              (or numeric type or type/code)\n"
+"\n", NETFILTER_VERSION);
+       print_icmptypes();
+}
+
+static struct option opts[] = {
+       { "icmp-type", 1, 0, '1' },
+       {0}
+};
+
+static unsigned int
+parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[])
+{
+       unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names);
+       unsigned int match = limit;
+       unsigned int i;
+
+       for (i = 0; i < limit; i++) {
+               if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))
+                   == 0) {
+                       if (match != limit)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Ambiguous ICMPv6 type `%s':"
+                                          " `%s' or `%s'?",
+                                          icmptype,
+                                          icmp_codes[match].name,
+                                          icmp_codes[i].name);
+                       match = i;
+               }
+       }
+
+       if (match != limit) {
+               *type = icmp_codes[match].type;
+               code[0] = icmp_codes[match].code_min;
+               code[1] = icmp_codes[match].code_max;
+       } else {
+               char *slash;
+               char buffer[strlen(icmptype) + 1];
+               int number;
+
+               strcpy(buffer, icmptype);
+               slash = strchr(buffer, '/');
+
+               if (slash)
+                       *slash = '\0';
+
+               number = string_to_number(buffer, 0, 255);
+               if (number == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid ICMPv6 type `%s'\n", buffer);
+               *type = number;
+               if (slash) {
+                       number = string_to_number(slash+1, 0, 255);
+                       if (number == -1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Invalid ICMPv6 code `%s'\n",
+                                          slash+1);
+                       code[0] = code[1] = number;
+               } else {
+                       code[0] = 0;
+                       code[1] = 0xFF;
+               }
+       }
+
+       if (code[0] == 0 && code[1] == 0xFF)
+               return NFC_IP6_SRC_PT;
+       else return NFC_IP6_SRC_PT | NFC_IP6_DST_PT;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_icmp *icmpinfo = (struct ip6t_icmp *)m->data;
+
+       icmpinfo->code[1] = 0xFF;
+}
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_icmp *icmpinfo = (struct ip6t_icmp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (check_inverse(optarg, &invert))
+                       optind++;
+               *nfcache |= parse_icmp(argv[optind-1],
+                                      &icmpinfo->type,
+                                      icmpinfo->code);
+               if (invert)
+                       icmpinfo->invflags |= IP6T_ICMP_INV;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void print_icmptype(u_int8_t type,
+                          u_int8_t code_min, u_int8_t code_max,
+                          int invert,
+                          int numeric)
+{
+       if (!numeric) {
+               unsigned int i;
+
+               for (i = 0;
+                    i < sizeof(icmp_codes)/sizeof(struct icmp_names);
+                    i++) {
+                       if (icmp_codes[i].type == type
+                           && icmp_codes[i].code_min == code_min
+                           && icmp_codes[i].code_max == code_max)
+                               break;
+               }
+
+               if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) {
+                       printf("%s%s ",
+                              invert ? "!" : "",
+                              icmp_codes[i].name);
+                       return;
+               }
+       }
+
+       if (invert)
+               printf("!");
+
+       printf("type %u", type);
+       if (code_min == 0 && code_max == 0xFF)
+               printf(" ");
+       else if (code_min == code_max)
+               printf(" code %u ", code_min);
+       else
+               printf(" codes %u-%u ", code_min, code_max);
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct ip6t_icmp *icmp = (struct ip6t_icmp *)match->data;
+
+       printf("icmp ");
+       print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
+                      icmp->invflags & IP6T_ICMP_INV,
+                      numeric);
+
+       if (icmp->invflags & ~IP6T_ICMP_INV)
+               printf("Unknown invflags: 0x%X ",
+                      icmp->invflags & ~IP6T_ICMP_INV);
+}
+
+/* Saves the match in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_icmp *icmp = (struct ip6t_icmp *)match->data;
+
+       if (icmp->invflags & IP6T_ICMP_INV)
+               printf("! ");
+
+       printf("--icmp-type %u", icmp->type);
+       if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
+               printf("/%u", icmp->code[0]);
+       printf(" ");
+}
+
+/* Final check; we don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+struct ip6tables_match icmp
+= { NULL,
+    "icmp",
+    NETFILTER_VERSION,
+    sizeof(struct ip6t_icmp),
+    sizeof(struct ip6t_icmp),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&icmp);
+}
diff --git a/extensions/libip6t_tcp.c b/extensions/libip6t_tcp.c
new file mode 100644 (file)
index 0000000..1cbba9a
--- /dev/null
@@ -0,0 +1,442 @@
+/* Shared library add-on to iptables to add TCP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"TCP v%s options:\n"
+" --tcp-flags [!] mask comp    match when TCP flags & mask == comp\n"
+"                              (Flags: SYN ACK FIN RST URG PSH ALL NONE)\n"
+"[!] --syn                     match when only SYN flag set\n"
+"                              (equivalent to --tcp-flags SYN,RST,ACK SYN)\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+"                              match source port(s)\n"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n"
+"                              match destination port(s)\n"
+" --tcp-option [!] number       match if TCP option set\n\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+       { "source-port", 1, 0, '1' },
+       { "sport", 1, 0, '1' }, /* synonym */
+       { "destination-port", 1, 0, '2' },
+       { "dport", 1, 0, '2' }, /* synonym */
+       { "syn", 0, 0, '3' },
+       { "tcp-flags", 1, 0, '4' },
+       { "tcp-option", 1, 0, '5' },
+       {0}
+};
+
+static int
+service_to_port(const char *name)
+{
+       struct servent *service;
+
+       if ((service = getservbyname(name, "tcp")) != NULL)
+               return ntohs((unsigned short) service->s_port);
+
+       return -1;
+}
+
+static u_int16_t
+parse_tcp_port(const char *port)
+{
+       int portnum;
+
+       if ((portnum = string_to_number(port, 0, 65535)) != -1 ||
+           (portnum = service_to_port(port)) != -1)
+               return (u_int16_t)portnum;
+
+       exit_error(PARAMETER_PROBLEM,
+                  "invalid TCP port/service `%s' specified", port);
+}
+
+static void
+parse_tcp_ports(const char *portstring, u_int16_t *ports)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(portstring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               ports[0] = ports[1] = parse_tcp_port(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               ports[0] = buffer[0] ? parse_tcp_port(buffer) : 0;
+               ports[1] = cp[0] ? parse_tcp_port(cp) : 0xFFFF;
+       }
+       free(buffer);
+}
+
+struct tcp_flag_names {
+       const char *name;
+       unsigned int flag;
+};
+
+static struct tcp_flag_names tcp_flag_names[]
+= { { "FIN", 0x01 },
+    { "SYN", 0x02 },
+    { "RST", 0x04 },
+    { "PSH", 0x08 },
+    { "ACK", 0x10 },
+    { "URG", 0x20 },
+    { "ALL", 0x3F },
+    { "NONE", 0 },
+};
+
+static unsigned int
+parse_tcp_flag(const char *flags)
+{
+       unsigned int ret = 0;
+       char *ptr;
+       char *buffer;
+
+       buffer = strdup(flags);
+
+       for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
+               unsigned int i;
+               for (i = 0;
+                    i < sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names);
+                    i++) {
+                       if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) {
+                               ret |= tcp_flag_names[i].flag;
+                               break;
+                       }
+               }
+               if (i == sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown TCP flag `%s'", buffer);
+               }
+
+       free(buffer);
+       return ret;
+}
+
+static void
+parse_tcp_flags(struct ip6t_tcp *tcpinfo,
+               const char *mask,
+               const char *cmp,
+               int invert)
+{
+       tcpinfo->flg_mask = parse_tcp_flag(mask);
+       tcpinfo->flg_cmp = parse_tcp_flag(cmp);
+
+       if (invert)
+               tcpinfo->invflags |= IP6T_TCP_INV_FLAGS;
+}
+
+static void
+parse_tcp_option(const char *option, u_int8_t *result)
+{
+       int ret;
+
+       ret = string_to_number(option, 1, 255);
+       if (ret == -1)
+               exit_error(PARAMETER_PROBLEM, "Bad TCP option `%s'", option);
+
+       *result = (u_int8_t)ret;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)m->data;
+
+       tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF;
+}
+
+#define TCP_SRC_PORTS 0x01
+#define TCP_DST_PORTS 0x02
+#define TCP_FLAGS 0x04
+#define TCP_OPTION     0x08
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & TCP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               if (check_inverse(optarg, &invert))
+                       optind++;
+               parse_tcp_ports(argv[optind-1], tcpinfo->spts);
+               if (invert)
+                       tcpinfo->invflags |= IP6T_TCP_INV_SRCPT;
+               *flags |= TCP_SRC_PORTS;
+               *nfcache |= NFC_IP6_SRC_PT;
+               break;
+
+       case '2':
+               if (*flags & TCP_DST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               if (check_inverse(optarg, &invert))
+                       optind++;
+               parse_tcp_ports(argv[optind-1], tcpinfo->dpts);
+               if (invert)
+                       tcpinfo->invflags |= IP6T_TCP_INV_DSTPT;
+               *flags |= TCP_DST_PORTS;
+               *nfcache |= NFC_IP6_DST_PT;
+               break;
+
+       case '3':
+               if (*flags & TCP_FLAGS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one of `--syn' or `--tcp-flags' "
+                                  " allowed");
+               parse_tcp_flags(tcpinfo, "SYN,RST,ACK", "SYN", invert);
+               *flags |= TCP_FLAGS;
+               *nfcache |= NFC_IP6_TCPFLAGS;
+               break;
+
+       case '4':
+               if (*flags & TCP_FLAGS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one of `--syn' or `--tcp-flags' "
+                                  " allowed");
+               if (check_inverse(optarg, &invert))
+                       optind++;
+
+               if (!argv[optind]
+                   || argv[optind][0] == '-' || argv[optind][0] == '!')
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--tcp-flags requires two args.");
+
+               parse_tcp_flags(tcpinfo, optarg, argv[optind++], invert);
+               *flags |= TCP_FLAGS;
+               *nfcache |= NFC_IP6_TCPFLAGS;
+               break;
+
+       case '5':
+               if (*flags & TCP_OPTION)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--tcp-option' allowed");
+               if (check_inverse(optarg, &invert))
+                       optind++;
+               parse_tcp_option(argv[optind-1], &tcpinfo->option);
+               if (invert)
+                       tcpinfo->invflags |= IP6T_TCP_INV_OPTION;
+               *flags |= TCP_OPTION;
+               *nfcache |= NFC_IP6_PROTO_UNKNOWN;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), "tcp")))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+           int invert, int numeric)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       print_port(min, numeric);
+               } else {
+                       printf("s:%s", inv);
+                       print_port(min, numeric);
+                       printf(":");
+                       print_port(max, numeric);
+               }
+               printf(" ");
+       }
+}
+
+static void
+print_option(u_int8_t option, int invert, int numeric)
+{
+       if (option || invert)
+               printf("option=%s%u ", invert ? "!" : "", option);
+}
+
+static void
+print_tcpf(u_int8_t flags)
+{
+       int have_flag = 0;
+
+       while (flags) {
+               unsigned int i;
+
+               for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++);
+
+               if (have_flag)
+                       printf(",");
+               printf("%s", tcp_flag_names[i].name);
+               have_flag = 1;
+
+               flags &= ~tcp_flag_names[i].flag;
+       }
+
+       if (!have_flag)
+               printf("NONE");
+}
+
+static void
+print_flags(u_int8_t mask, u_int8_t cmp, int invert, int numeric)
+{
+       if (mask || invert) {
+               printf("flags:%s", invert ? "!" : "");
+               if (numeric)
+                       printf("0x02%X/0x02%X ", mask, cmp);
+               else {
+                       print_tcpf(cmp);
+                       printf("/");
+                       print_tcpf(mask);
+                       printf(" ");
+               }
+       }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_tcp *tcp = (struct ip6t_tcp *)match->data;
+
+       printf("tcp ");
+       print_ports("spt", tcp->spts[0], tcp->spts[1],
+                   tcp->invflags & IP6T_TCP_INV_SRCPT,
+                   numeric);
+       print_ports("dpt", tcp->dpts[0], tcp->dpts[1],
+                   tcp->invflags & IP6T_TCP_INV_DSTPT,
+                   numeric);
+       print_option(tcp->option,
+                    tcp->invflags & IP6T_TCP_INV_OPTION,
+                    numeric);
+       print_flags(tcp->flg_mask, tcp->flg_cmp,
+                   tcp->invflags & IP6T_TCP_INV_FLAGS,
+                   numeric);
+       if (tcp->invflags & ~IP6T_TCP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      tcp->invflags & ~IP6T_TCP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)match->data;
+
+       if (tcpinfo->spts[0] != 0
+           && tcpinfo->spts[1] != 0xFFFF) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_SRCPT)
+                       printf("! ");
+               if (tcpinfo->spts[0]
+                   != tcpinfo->spts[1])
+                       printf("--sport %u:%u ",
+                              tcpinfo->spts[0],
+                              tcpinfo->spts[1]);
+               else
+                       printf("--sport %u ",
+                              tcpinfo->spts[0]);
+       }
+
+       if (tcpinfo->dpts[0] != 0
+           && tcpinfo->dpts[1] != 0xFFFF) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_DSTPT)
+                       printf("! ");
+               if (tcpinfo->dpts[0]
+                   != tcpinfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              tcpinfo->dpts[0],
+                              tcpinfo->dpts[1]);
+               else
+                       printf("--dport %u ",
+                              tcpinfo->dpts[0]);
+       }
+
+       if (tcpinfo->option
+           || (tcpinfo->invflags & IP6T_TCP_INV_OPTION)) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_OPTION)
+                       printf("! ");
+               printf("--tcp-option %u ", tcpinfo->option);
+       }
+
+       if (tcpinfo->flg_mask
+           || (tcpinfo->invflags & IP6T_TCP_INV_FLAGS)) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_FLAGS)
+                       printf("! ");
+
+               print_tcpf(tcpinfo->flg_cmp);
+               if (tcpinfo->flg_mask != 0xFF) {
+                       printf("/");
+                       print_tcpf(tcpinfo->flg_mask);
+               }
+       }
+}
+
+struct ip6tables_match tcp
+= { NULL,
+    "tcp",
+    NETFILTER_VERSION,
+    sizeof(struct ip6t_tcp),
+    sizeof(struct ip6t_tcp),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts };
+
+void
+_init(void)
+{
+       register_match6(&tcp);
+}
diff --git a/extensions/libip6t_udp.c b/extensions/libip6t_udp.c
new file mode 100644 (file)
index 0000000..f2c0b6a
--- /dev/null
@@ -0,0 +1,253 @@
+/* Shared library add-on to iptables to add UDP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"UDP v%s options:\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+"                              match source port(s)\n"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n"
+"                              match destination port(s)\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+       { "source-port", 1, 0, '1' },
+       { "sport", 1, 0, '1' }, /* synonym */
+       { "destination-port", 1, 0, '2' },
+       { "dport", 1, 0, '2' }, /* synonym */
+       {0}
+};
+
+static int
+service_to_port(const char *name)
+{
+       struct servent *service;
+
+       if ((service = getservbyname(name, "udp")) != NULL)
+               return ntohs((unsigned short) service->s_port);
+
+               return -1;
+}
+
+static u_int16_t
+parse_udp_port(const char *port)
+{
+       int portnum;
+
+       if ((portnum = string_to_number(port, 0, 65535)) != -1 ||
+           (portnum = service_to_port(port)) != -1)
+               return (u_int16_t)portnum;
+
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid UDP port/service `%s' specified", port);
+       }
+
+static void
+parse_udp_ports(const char *portstring, u_int16_t *ports)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(portstring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               ports[0] = ports[1] = parse_udp_port(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               ports[0] = buffer[0] ? parse_udp_port(buffer) : 0;
+               ports[1] = cp[0] ? parse_udp_port(cp) : 0xFFFF;
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_udp *udpinfo = (struct ip6t_udp *)m->data;
+
+       udpinfo->spts[1] = udpinfo->dpts[1] = 0xFFFF;
+}
+
+#define UDP_SRC_PORTS 0x01
+#define UDP_DST_PORTS 0x02
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_udp *udpinfo = (struct ip6t_udp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & UDP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               if (check_inverse(optarg, &invert))
+                       optind++;
+               parse_udp_ports(argv[optind-1], udpinfo->spts);
+               if (invert)
+                       udpinfo->invflags |= IP6T_UDP_INV_SRCPT;
+               *flags |= UDP_SRC_PORTS;
+               *nfcache |= NFC_IP6_SRC_PT;
+               break;
+
+       case '2':
+               if (*flags & UDP_DST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               if (check_inverse(optarg, &invert))
+                       optind++;
+               parse_udp_ports(argv[optind-1], udpinfo->dpts);
+               if (invert)
+                       udpinfo->invflags |= IP6T_UDP_INV_DSTPT;
+               *flags |= UDP_DST_PORTS;
+               *nfcache |= NFC_IP6_DST_PT;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), "udp")))
+               return service->s_name;
+
+       return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+       char *service;
+
+       if (numeric || (service = port_to_service(port)) == NULL)
+               printf("%u", port);
+       else
+               printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+           int invert, int numeric)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       print_port(min, numeric);
+               } else {
+                       printf("s:%s", inv);
+                       print_port(min, numeric);
+                       printf(":");
+                       print_port(max, numeric);
+               }
+               printf(" ");
+       }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_udp *udp = (struct ip6t_udp *)match->data;
+
+       printf("udp ");
+       print_ports("spt", udp->spts[0], udp->spts[1],
+                   udp->invflags & IP6T_UDP_INV_SRCPT,
+                   numeric);
+       print_ports("dpt", udp->dpts[0], udp->dpts[1],
+                   udp->invflags & IP6T_UDP_INV_DSTPT,
+                   numeric);
+       if (udp->invflags & ~IP6T_UDP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      udp->invflags & ~IP6T_UDP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_udp *udpinfo = (struct ip6t_udp *)match->data;
+
+       if (udpinfo->spts[0] != 0
+           && udpinfo->spts[1] != 0xFFFF) {
+               if (udpinfo->invflags & IP6T_UDP_INV_SRCPT)
+                       printf("! ");
+               if (udpinfo->spts[0]
+                   != udpinfo->spts[1])
+                       printf("--sport %u:%u ",
+                              udpinfo->spts[0],
+                              udpinfo->spts[1]);
+               else
+                       printf("--sport %u ",
+                              udpinfo->spts[0]);
+       }
+
+       if (udpinfo->dpts[0] != 0
+           && udpinfo->dpts[1] != 0xFFFF) {
+               if (udpinfo->invflags & IP6T_UDP_INV_DSTPT)
+                       printf("! ");
+               if (udpinfo->dpts[0]
+                   != udpinfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              udpinfo->dpts[0],
+                              udpinfo->dpts[1]);
+               else
+                       printf("--dport %u ",
+                              udpinfo->dpts[0]);
+       }
+}
+
+struct ip6tables_match udp
+= { NULL,
+    "udp",
+    NETFILTER_VERSION,
+    sizeof(struct ip6t_udp),
+    sizeof(struct ip6t_udp),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void
+_init(void)
+{
+       register_match6(&udp);
+}