summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'extensions')
-rw-r--r--extensions/libipt_sctp.c402
1 files changed, 402 insertions, 0 deletions
diff --git a/extensions/libipt_sctp.c b/extensions/libipt_sctp.c
new file mode 100644
index 00000000..d512fa63
--- /dev/null
+++ b/extensions/libipt_sctp.c
@@ -0,0 +1,402 @@
+/* Shared library add-on to iptables for SCTP matching
+ *
+ * (C) 2003 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ecn.c borrowed heavily from libipt_dscp.c
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <netdb.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_sctp.h>
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ struct ipt_sctp_info *einfo = (struct ipt_sctp_info *)m->data;
+
+ einfo->spts[1] = einfo->dpts[1] = 0xFFFF;
+}
+
+static void help(void)
+{
+ printf(
+"SCTP match v%s options\n"
+" --sctp-chunks [!] mask comp match when SCTP chunks & mask == comp\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+" match source port(s)"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n\n",
+ IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+ { .name = "source-port", .has_arg = 1, .flag = 0, .val = '1' },
+ { .name = "sport", .has_arg = 1, .flag = 0, .val = '1' },
+ { .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' },
+ { .name = "dport", .has_arg = 1, .flag = 0, .val = '2' },
+ { .name = "sctp-chunks", .has_arg = 1, .flag = 0, .val = '3' },
+ { .name = 0 }
+};
+
+static int
+service_to_port(const char *name)
+{
+ struct servent *service;
+
+ if ((service = getservbyname(name, "sctp")) != NULL)
+ return ntohs((unsigned short) service->s_port);
+
+ return -1;
+}
+
+static u_int16_t
+parse_sctp_port(const char *port)
+{
+ unsigned int portnum;
+
+ if (string_to_number(port, 0, 65535, &portnum) != -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_sctp_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_sctp_port(buffer);
+ else {
+ *cp = '\0';
+ cp++;
+
+ ports[0] = buffer[0] ? parse_sctp_port(buffer) : 0;
+ ports[1] = cp[0] ? parse_sctp_port(cp) : 0xFFFF;
+
+ if (ports[0] > ports[1])
+ exit_error(PARAMETER_PROBLEM,
+ "invalid portrange (min > max)");
+ }
+ free(buffer);
+}
+
+struct sctp_chunk_names {
+ const char *name;
+ unsigned int flag;
+};
+
+/* FIXME: */
+#define ALL_CHUNKS 0xabcdef
+static struct sctp_chunk_names sctp_chunk_names[]
+= { { .name = "DATA", .flag = (1 << 0) },
+ { .name = "INIT", .flag = (1 << 1) },
+ { .name = "INIT_ACK", .flag = (1 << 2) },
+ { .name = "SACK", .flag = (1 << 3) },
+ { .name = "HEARTBEAT", .flag = (1 << 4) },
+ { .name = "HEARTBEAT_ACK", .flag = (1 << 5) },
+ { .name = "ABORT", .flag = (1 << 6) },
+ { .name = "SHUTDOWN", .flag = (1 << 7) },
+ { .name = "SHUTDOWN_ACK", .flag = (1 << 8) },
+ { .name = "ERROR", .flag = (1 << 9) },
+ { .name = "COOKIE_ECHO", .flag = (1 << 10) },
+ { .name = "COOKIE_ACK", .flag = (1 << 11) },
+ { .name = "ECN_ECNE", .flag = (1 << 12) },
+ { .name = "ECN_CWR", .flag = (1 << 13) },
+ { .name = "SHUTDOWN_COMPLETE", .flag = (1 << 14) },
+ { .name = "ASCONF", .flag = (1 << 31) },
+ { .name = "ASCONF_ACK", .flag = (1 << 30) },
+ { .name = "ALL", .flag = ALL_CHUNKS },
+ { .name = "NONE", .flag = 0 },
+};
+
+
+static unsigned int
+parse_sctp_chunk(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;
+ int found = 0;
+ for (i = 0;
+ i < sizeof(sctp_chunk_names)/sizeof(struct sctp_chunk_names);
+ i++) {
+ if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
+ ret |= sctp_chunk_names[i].flag;
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ exit_error(PARAMETER_PROBLEM,
+ "Unknown sctp chunk `%s'", ptr);
+ }
+
+ free(buffer);
+ return ret;
+}
+
+static void
+parse_sctp_chunks(struct ipt_sctp_info *einfo,
+ const char *mask,
+ const char *cmp,
+ int invert)
+{
+ einfo->chunks = parse_sctp_chunk(mask);
+ einfo->chunk_mask = parse_sctp_chunk(cmp);
+
+ if (invert)
+ einfo->invflags |= IPT_SCTP_INV_CHUNKS;
+}
+
+#define SCTP_SRC_PORTS 0x01
+#define SCTP_DST_PORTS 0x02
+#define SCTP_CHUNKS 0x03
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct ipt_sctp_info *einfo
+ = (struct ipt_sctp_info *)(*match)->data;
+
+ switch (c) {
+ case '1':
+ if (*flags & SCTP_SRC_PORTS)
+ exit_error(PARAMETER_PROBLEM,
+ "Only one `--source-port' allowed");
+ check_inverse(optarg, &invert, &optind, 0);
+ parse_sctp_ports(argv[optind-1], einfo->spts);
+ if (invert)
+ einfo->invflags |= IPT_SCTP_INV_SRCPT;
+ *flags |= SCTP_SRC_PORTS;
+ *nfcache |= NFC_IP_SRC_PT;
+ break;
+
+ case '2':
+ if (*flags & SCTP_DST_PORTS)
+ exit_error(PARAMETER_PROBLEM,
+ "Only one `--destination-port' allowed");
+ check_inverse(optarg, &invert, &optind, 0);
+ parse_sctp_ports(argv[optind-1], einfo->dpts);
+ if (invert)
+ einfo->invflags |= IPT_SCTP_INV_DSTPT;
+ *flags |= SCTP_DST_PORTS;
+ *nfcache |= NFC_IP_DST_PT;
+ break;
+
+ case '3':
+ if (*flags & SCTP_CHUNKS)
+ exit_error(PARAMETER_PROBLEM,
+ "Only one `--sctp-chunks' allowed");
+ check_inverse(optarg, &invert, &optind, 0);
+
+ if (!argv[optind]
+ || argv[optind][0] == '-' || argv[optind][0] == '!')
+ exit_error(PARAMETER_PROBLEM,
+ "--sctp-chunks requires two args");
+
+ parse_sctp_chunks(einfo, argv[optind-1], argv[optind], invert);
+ optind++;
+ *flags |= SCTP_CHUNKS;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+ struct servent *service;
+
+ if ((service = getservbyport(htons(port), "sctp")))
+ 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_chunk(u_int32_t chunks)
+{
+ unsigned int have_flag = 0;
+
+ while (chunks) {
+ unsigned int i;
+
+ for (i = 0; (chunks & sctp_chunk_names[i].flag) == 0; i++);
+
+ if (have_flag)
+ printf(",");
+ printf("%s", sctp_chunk_names[i].name);
+ have_flag = 1;
+
+ chunks &= ~sctp_chunk_names[i].flag;
+ }
+
+ if (!have_flag)
+ printf("NONE");
+}
+
+static void
+print_chunks(u_int32_t mask, u_int32_t cmp, int invert, int numeric)
+{
+ if (mask || invert) {
+ printf("flags:%s", invert ? "!" : "");
+ if (numeric)
+ printf("0x%04X/0x%04X ", mask, cmp);
+ else {
+ print_chunk(mask);
+ printf("/");
+ print_chunk(cmp);
+ printf(" ");
+ }
+ }
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ const struct ipt_sctp_info *einfo =
+ (const struct ipt_sctp_info *)match->data;
+
+ printf("sctp ");
+
+ print_ports("spt", einfo->spts[0], einfo->spts[1],
+ einfo->invflags & IPT_SCTP_INV_SRCPT,
+ numeric);
+ print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
+ einfo->invflags & IPT_SCTP_INV_DSTPT,
+ numeric);
+
+ print_chunks(einfo->chunks, einfo->chunk_mask,
+ einfo->invflags & ~IPT_SCTP_INV_MASK,
+ numeric);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ const struct ipt_sctp_info *einfo =
+ (const struct ipt_sctp_info *)match->data;
+
+ if (einfo->spts[0] != 0
+ || einfo->spts[1] != 0xFFFF) {
+ if (einfo->invflags & IPT_SCTP_INV_SRCPT)
+ printf("! ");
+ if (einfo->spts[0] != einfo->spts[1])
+ printf("--sport %u:%u ",
+ einfo->spts[0], einfo->spts[1]);
+ else
+ printf("--sport %u ", einfo->spts[0]);
+ }
+
+ if (einfo->dpts[0] != 0
+ || einfo->dpts[1] != 0xFFFF) {
+ if (einfo->invflags & IPT_SCTP_INV_DSTPT)
+ printf("! ");
+ if (einfo->dpts[0] != einfo->dpts[1])
+ printf("--dport %u:%u ",
+ einfo->dpts[0], einfo->dpts[1]);
+ else
+ printf("--dport %u ", einfo->dpts[0]);
+ }
+
+ if (einfo->chunks
+ || (einfo->invflags & IPT_SCTP_INV_CHUNKS)) {
+ if (einfo->invflags & IPT_SCTP_INV_CHUNKS)
+ printf("! ");
+ printf("--sctp-chunks ");
+ if (einfo->chunks != ALL_CHUNKS) {
+ print_chunk(einfo->chunks);
+ }
+ printf(" ");
+ print_chunk(einfo->chunk_mask);
+ printf(" ");
+ }
+}
+
+static
+struct iptables_match sctp
+= { .name = "sctp",
+ .version = IPTABLES_VERSION,
+ .size = IPT_ALIGN(sizeof(struct ipt_sctp_info)),
+ .userspacesize = IPT_ALIGN(sizeof(struct ipt_sctp_info)),
+ .help = &help,
+ .init = &init,
+ .parse = &parse,
+ .final_check = &final_check,
+ .print = &print,
+ .save = &save,
+ .extra_opts = opts
+};
+
+void _init(void)
+{
+ register_match(&sctp);
+}