libxt_sctp.c revision 830132ac9c0d270bf9dcfe85c2464e3fe8c73fb9
1/* Shared library add-on to iptables for SCTP matching
2 *
3 * (C) 2003 by Harald Welte <laforge@gnumonks.org>
4 *
5 * This program is distributed under the terms of GNU GPL v2, 1991
6 *
7 * libipt_ecn.c borrowed heavily from libipt_dscp.c
8 *
9 */
10#include <stdio.h>
11#include <string.h>
12#include <stdlib.h>
13#include <getopt.h>
14#include <netdb.h>
15#include <ctype.h>
16
17#include <xtables.h>
18
19#ifndef ARRAY_SIZE
20#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
21#endif
22
23#include <linux/netfilter/xt_sctp.h>
24
25/* Some ZS!#@:$%*#$! has replaced the ELEMCOUNT macro in ipt_sctp.h with
26 * ARRAY_SIZE without noticing that this file is used from userserspace,
27 * and userspace doesn't have ARRAY_SIZE */
28
29#ifndef ELEMCOUNT
30#define ELEMCOUNT ARRAY_SIZE
31#endif
32
33#if 0
34#define DEBUGP(format, first...) printf(format, ##first)
35#define static
36#else
37#define DEBUGP(format, fist...)
38#endif
39
40static void
41print_chunk(u_int32_t chunknum, int numeric);
42
43/* Initialize the match. */
44static void
45init(struct xt_entry_match *m)
46{
47	int i;
48	struct xt_sctp_info *einfo = (struct xt_sctp_info *)m->data;
49
50	memset(einfo, 0, sizeof(struct xt_sctp_info));
51
52	for (i = 0; i < XT_NUM_SCTP_FLAGS; i++) {
53		einfo->flag_info[i].chunktype = -1;
54	}
55}
56
57static void help(void)
58{
59	printf(
60"SCTP match v%s options\n"
61" --source-port [!] port[:port]                          match source port(s)\n"
62" --sport ...\n"
63" --destination-port [!] port[:port]                     match destination port(s)\n"
64" --dport ...\n"
65" --chunk-types [!] (all|any|none) (chunktype[:flags])+	match if all, any or none of\n"
66"						        chunktypes are present\n"
67"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK ALL NONE\n",
68	IPTABLES_VERSION);
69}
70
71static const struct option opts[] = {
72	{ .name = "source-port", .has_arg = 1, .val = '1' },
73	{ .name = "sport", .has_arg = 1, .val = '1' },
74	{ .name = "destination-port", .has_arg = 1, .val = '2' },
75	{ .name = "dport", .has_arg = 1, .val = '2' },
76	{ .name = "chunk-types", .has_arg = 1, .val = '3' },
77	{ }
78};
79
80static void
81parse_sctp_ports(const char *portstring,
82		 u_int16_t *ports)
83{
84	char *buffer;
85	char *cp;
86
87	buffer = strdup(portstring);
88	DEBUGP("%s\n", portstring);
89	if ((cp = strchr(buffer, ':')) == NULL) {
90		ports[0] = ports[1] = parse_port(buffer, "sctp");
91	}
92	else {
93		*cp = '\0';
94		cp++;
95
96		ports[0] = buffer[0] ? parse_port(buffer, "sctp") : 0;
97		ports[1] = cp[0] ? parse_port(cp, "sctp") : 0xFFFF;
98
99		if (ports[0] > ports[1])
100			exit_error(PARAMETER_PROBLEM,
101				   "invalid portrange (min > max)");
102	}
103	free(buffer);
104}
105
106struct sctp_chunk_names {
107	const char *name;
108	unsigned int chunk_type;
109	const char *valid_flags;
110};
111
112/*'ALL' and 'NONE' will be treated specially. */
113static struct sctp_chunk_names sctp_chunk_names[]
114= { { .name = "DATA", 		.chunk_type = 0,   .valid_flags = "-----UBE"},
115    { .name = "INIT", 		.chunk_type = 1,   .valid_flags = "--------"},
116    { .name = "INIT_ACK", 	.chunk_type = 2,   .valid_flags = "--------"},
117    { .name = "SACK",		.chunk_type = 3,   .valid_flags = "--------"},
118    { .name = "HEARTBEAT",	.chunk_type = 4,   .valid_flags = "--------"},
119    { .name = "HEARTBEAT_ACK",	.chunk_type = 5,   .valid_flags = "--------"},
120    { .name = "ABORT",		.chunk_type = 6,   .valid_flags = "-------T"},
121    { .name = "SHUTDOWN",	.chunk_type = 7,   .valid_flags = "--------"},
122    { .name = "SHUTDOWN_ACK",	.chunk_type = 8,   .valid_flags = "--------"},
123    { .name = "ERROR",		.chunk_type = 9,   .valid_flags = "--------"},
124    { .name = "COOKIE_ECHO",	.chunk_type = 10,  .valid_flags = "--------"},
125    { .name = "COOKIE_ACK",	.chunk_type = 11,  .valid_flags = "--------"},
126    { .name = "ECN_ECNE",	.chunk_type = 12,  .valid_flags = "--------"},
127    { .name = "ECN_CWR",	.chunk_type = 13,  .valid_flags = "--------"},
128    { .name = "SHUTDOWN_COMPLETE", .chunk_type = 14,  .valid_flags = "-------T"},
129    { .name = "ASCONF",		.chunk_type = 31,  .valid_flags = "--------"},
130    { .name = "ASCONF_ACK",	.chunk_type = 30,  .valid_flags = "--------"},
131};
132
133static void
134save_chunk_flag_info(struct xt_sctp_flag_info *flag_info,
135		     int *flag_count,
136		     int chunktype,
137		     int bit,
138		     int set)
139{
140	int i;
141
142	for (i = 0; i < *flag_count; i++) {
143		if (flag_info[i].chunktype == chunktype) {
144			DEBUGP("Previous match found\n");
145			flag_info[i].chunktype = chunktype;
146			flag_info[i].flag_mask |= (1 << bit);
147			if (set) {
148				flag_info[i].flag |= (1 << bit);
149			}
150
151			return;
152		}
153	}
154
155	if (*flag_count == XT_NUM_SCTP_FLAGS) {
156		exit_error (PARAMETER_PROBLEM,
157			"Number of chunk types with flags exceeds currently allowed limit."
158			"Increasing this limit involves changing IPT_NUM_SCTP_FLAGS and"
159			"recompiling both the kernel space and user space modules\n");
160	}
161
162	flag_info[*flag_count].chunktype = chunktype;
163	flag_info[*flag_count].flag_mask |= (1 << bit);
164	if (set) {
165		flag_info[*flag_count].flag |= (1 << bit);
166	}
167	(*flag_count)++;
168}
169
170static void
171parse_sctp_chunk(struct xt_sctp_info *einfo,
172		 const char *chunks)
173{
174	char *ptr;
175	char *buffer;
176	unsigned int i, j;
177	int found = 0;
178	char *chunk_flags;
179
180	buffer = strdup(chunks);
181	DEBUGP("Buffer: %s\n", buffer);
182
183	SCTP_CHUNKMAP_RESET(einfo->chunkmap);
184
185	if (!strcasecmp(buffer, "ALL")) {
186		SCTP_CHUNKMAP_SET_ALL(einfo->chunkmap);
187		goto out;
188	}
189
190	if (!strcasecmp(buffer, "NONE")) {
191		SCTP_CHUNKMAP_RESET(einfo->chunkmap);
192		goto out;
193	}
194
195	for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
196		found = 0;
197		DEBUGP("Next Chunk type %s\n", ptr);
198
199		if ((chunk_flags = strchr(ptr, ':')) != NULL) {
200			*chunk_flags++ = 0;
201		}
202
203		for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
204			if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
205				DEBUGP("Chunk num %d\n", sctp_chunk_names[i].chunk_type);
206				SCTP_CHUNKMAP_SET(einfo->chunkmap,
207					sctp_chunk_names[i].chunk_type);
208				found = 1;
209				break;
210			}
211		}
212		if (!found)
213			exit_error(PARAMETER_PROBLEM,
214				   "Unknown sctp chunk `%s'", ptr);
215
216		if (chunk_flags) {
217			DEBUGP("Chunk flags %s\n", chunk_flags);
218			for (j = 0; j < strlen(chunk_flags); j++) {
219				char *p;
220				int bit;
221
222				if ((p = strchr(sctp_chunk_names[i].valid_flags,
223						toupper(chunk_flags[j]))) != NULL) {
224					bit = p - sctp_chunk_names[i].valid_flags;
225					bit = 7 - bit;
226
227					save_chunk_flag_info(einfo->flag_info,
228						&(einfo->flag_count), i, bit,
229						isupper(chunk_flags[j]));
230				} else {
231					exit_error(PARAMETER_PROBLEM,
232						"Invalid flags for chunk type %d\n", i);
233				}
234			}
235		}
236	}
237out:
238	free(buffer);
239}
240
241static void
242parse_sctp_chunks(struct xt_sctp_info *einfo,
243		  const char *match_type,
244		  const char *chunks)
245{
246	DEBUGP("Match type: %s Chunks: %s\n", match_type, chunks);
247	if (!strcasecmp(match_type, "ANY")) {
248		einfo->chunk_match_type = SCTP_CHUNK_MATCH_ANY;
249	} else 	if (!strcasecmp(match_type, "ALL")) {
250		einfo->chunk_match_type = SCTP_CHUNK_MATCH_ALL;
251	} else 	if (!strcasecmp(match_type, "ONLY")) {
252		einfo->chunk_match_type = SCTP_CHUNK_MATCH_ONLY;
253	} else {
254		exit_error (PARAMETER_PROBLEM,
255			"Match type has to be one of \"ALL\", \"ANY\" or \"ONLY\"");
256	}
257
258	SCTP_CHUNKMAP_RESET(einfo->chunkmap);
259	parse_sctp_chunk(einfo, chunks);
260}
261
262static int
263parse(int c, char **argv, int invert, unsigned int *flags,
264      const void *entry,
265      struct xt_entry_match **match)
266{
267	struct xt_sctp_info *einfo
268		= (struct xt_sctp_info *)(*match)->data;
269
270	switch (c) {
271	case '1':
272		if (*flags & XT_SCTP_SRC_PORTS)
273			exit_error(PARAMETER_PROBLEM,
274			           "Only one `--source-port' allowed");
275		einfo->flags |= XT_SCTP_SRC_PORTS;
276		check_inverse(optarg, &invert, &optind, 0);
277		parse_sctp_ports(argv[optind-1], einfo->spts);
278		if (invert)
279			einfo->invflags |= XT_SCTP_SRC_PORTS;
280		*flags |= XT_SCTP_SRC_PORTS;
281		break;
282
283	case '2':
284		if (*flags & XT_SCTP_DEST_PORTS)
285			exit_error(PARAMETER_PROBLEM,
286				   "Only one `--destination-port' allowed");
287		einfo->flags |= XT_SCTP_DEST_PORTS;
288		check_inverse(optarg, &invert, &optind, 0);
289		parse_sctp_ports(argv[optind-1], einfo->dpts);
290		if (invert)
291			einfo->invflags |= XT_SCTP_DEST_PORTS;
292		*flags |= XT_SCTP_DEST_PORTS;
293		break;
294
295	case '3':
296		if (*flags & XT_SCTP_CHUNK_TYPES)
297			exit_error(PARAMETER_PROBLEM,
298				   "Only one `--chunk-types' allowed");
299		check_inverse(optarg, &invert, &optind, 0);
300
301		if (!argv[optind]
302		    || argv[optind][0] == '-' || argv[optind][0] == '!')
303			exit_error(PARAMETER_PROBLEM,
304				   "--chunk-types requires two args");
305
306		einfo->flags |= XT_SCTP_CHUNK_TYPES;
307		parse_sctp_chunks(einfo, argv[optind-1], argv[optind]);
308		if (invert)
309			einfo->invflags |= XT_SCTP_CHUNK_TYPES;
310		optind++;
311		*flags |= XT_SCTP_CHUNK_TYPES;
312		break;
313
314	default:
315		return 0;
316	}
317	return 1;
318}
319
320static char *
321port_to_service(int port)
322{
323	struct servent *service;
324
325	if ((service = getservbyport(htons(port), "sctp")))
326		return service->s_name;
327
328	return NULL;
329}
330
331static void
332print_port(u_int16_t port, int numeric)
333{
334	char *service;
335
336	if (numeric || (service = port_to_service(port)) == NULL)
337		printf("%u", port);
338	else
339		printf("%s", service);
340}
341
342static void
343print_ports(const char *name, u_int16_t min, u_int16_t max,
344	    int invert, int numeric)
345{
346	const char *inv = invert ? "!" : "";
347
348	if (min != 0 || max != 0xFFFF || invert) {
349		printf("%s", name);
350		if (min == max) {
351			printf(":%s", inv);
352			print_port(min, numeric);
353		} else {
354			printf("s:%s", inv);
355			print_port(min, numeric);
356			printf(":");
357			print_port(max, numeric);
358		}
359		printf(" ");
360	}
361}
362
363static void
364print_chunk_flags(u_int32_t chunknum, u_int8_t chunk_flags, u_int8_t chunk_flags_mask)
365{
366	int i;
367
368	DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags,
369			chunk_flags_mask);
370
371	if (chunk_flags_mask) {
372		printf(":");
373	}
374
375	for (i = 7; i >= 0; i--) {
376		if (chunk_flags_mask & (1 << i)) {
377			if (chunk_flags & (1 << i)) {
378				printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]);
379			} else {
380				printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i]));
381			}
382		}
383	}
384}
385
386static void
387print_chunk(u_int32_t chunknum, int numeric)
388{
389	if (numeric) {
390		printf("0x%04X", chunknum);
391	}
392	else {
393		int i;
394
395		for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
396			if (sctp_chunk_names[i].chunk_type == chunknum)
397				printf("%s", sctp_chunk_names[chunknum].name);
398		}
399	}
400}
401
402static void
403print_chunks(u_int32_t chunk_match_type,
404	     const u_int32_t *chunkmap,
405	     const struct xt_sctp_flag_info *flag_info,
406	     int flag_count,
407	     int numeric)
408{
409	int i, j;
410	int flag;
411
412	switch (chunk_match_type) {
413		case SCTP_CHUNK_MATCH_ANY:	printf("any "); break;
414		case SCTP_CHUNK_MATCH_ALL:	printf("all "); break;
415		case SCTP_CHUNK_MATCH_ONLY:	printf("only "); break;
416		default:	printf("Never reach herer\n"); break;
417	}
418
419	if (SCTP_CHUNKMAP_IS_CLEAR(chunkmap)) {
420		printf("NONE ");
421		goto out;
422	}
423
424	if (SCTP_CHUNKMAP_IS_ALL_SET(chunkmap)) {
425		printf("ALL ");
426		goto out;
427	}
428
429	flag = 0;
430	for (i = 0; i < 256; i++) {
431		if (SCTP_CHUNKMAP_IS_SET(chunkmap, i)) {
432			if (flag)
433				printf(",");
434			flag = 1;
435			print_chunk(i, numeric);
436			for (j = 0; j < flag_count; j++) {
437				if (flag_info[j].chunktype == i) {
438					print_chunk_flags(i, flag_info[j].flag,
439						flag_info[j].flag_mask);
440				}
441			}
442		}
443	}
444
445	if (flag)
446		printf(" ");
447out:
448	return;
449}
450
451/* Prints out the matchinfo. */
452static void
453print(const void *ip,
454      const struct xt_entry_match *match,
455      int numeric)
456{
457	const struct xt_sctp_info *einfo =
458		(const struct xt_sctp_info *)match->data;
459
460	printf("sctp ");
461
462	if (einfo->flags & XT_SCTP_SRC_PORTS) {
463		print_ports("spt", einfo->spts[0], einfo->spts[1],
464			einfo->invflags & XT_SCTP_SRC_PORTS,
465			numeric);
466	}
467
468	if (einfo->flags & XT_SCTP_DEST_PORTS) {
469		print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
470			einfo->invflags & XT_SCTP_DEST_PORTS,
471			numeric);
472	}
473
474	if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
475		/* FIXME: print_chunks() is used in save() where the printing of '!'
476		s taken care of, so we need to do that here as well */
477		if (einfo->invflags & XT_SCTP_CHUNK_TYPES) {
478			printf("! ");
479		}
480		print_chunks(einfo->chunk_match_type, einfo->chunkmap,
481			einfo->flag_info, einfo->flag_count, numeric);
482	}
483}
484
485/* Saves the union ipt_matchinfo in parsable form to stdout. */
486static void
487save(const void *ip,
488     const struct xt_entry_match *match)
489{
490	const struct xt_sctp_info *einfo =
491		(const struct xt_sctp_info *)match->data;
492
493	if (einfo->flags & XT_SCTP_SRC_PORTS) {
494		if (einfo->invflags & XT_SCTP_SRC_PORTS)
495			printf("! ");
496		if (einfo->spts[0] != einfo->spts[1])
497			printf("--sport %u:%u ",
498			       einfo->spts[0], einfo->spts[1]);
499		else
500			printf("--sport %u ", einfo->spts[0]);
501	}
502
503	if (einfo->flags & XT_SCTP_DEST_PORTS) {
504		if (einfo->invflags & XT_SCTP_DEST_PORTS)
505			printf("! ");
506		if (einfo->dpts[0] != einfo->dpts[1])
507			printf("--dport %u:%u ",
508			       einfo->dpts[0], einfo->dpts[1]);
509		else
510			printf("--dport %u ", einfo->dpts[0]);
511	}
512
513	if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
514		if (einfo->invflags & XT_SCTP_CHUNK_TYPES)
515			printf("! ");
516		printf("--chunk-types ");
517
518		print_chunks(einfo->chunk_match_type, einfo->chunkmap,
519			einfo->flag_info, einfo->flag_count, 0);
520	}
521}
522
523static struct xtables_match sctp = {
524	.name		= "sctp",
525	.family		= AF_INET,
526	.version	= IPTABLES_VERSION,
527	.size		= XT_ALIGN(sizeof(struct xt_sctp_info)),
528	.userspacesize	= XT_ALIGN(sizeof(struct xt_sctp_info)),
529	.help		= &help,
530	.init		= &init,
531	.parse		= &parse,
532	.print		= &print,
533	.save		= &save,
534	.extra_opts	= opts
535};
536
537static struct xtables_match sctp6 = {
538	.name		= "sctp",
539	.family		= AF_INET6,
540	.version	= IPTABLES_VERSION,
541	.size		= XT_ALIGN(sizeof(struct xt_sctp_info)),
542	.userspacesize	= XT_ALIGN(sizeof(struct xt_sctp_info)),
543	.help		= &help,
544	.init		= &init,
545	.parse		= &parse,
546	.print		= &print,
547	.save		= &save,
548	.extra_opts	= opts
549};
550
551void _init(void)
552{
553	xtables_register_match(&sctp);
554	xtables_register_match(&sctp6);
555}
556
557