libxt_sctp.c revision ea146a982e26c42f9954f140276f8deeb2edbe98
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, .flag = 0, .val = '1' },
73	{ .name = "sport", .has_arg = 1, .flag = 0, .val = '1' },
74	{ .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' },
75	{ .name = "dport", .has_arg = 1, .flag = 0, .val = '2' },
76	{ .name = "chunk-types", .has_arg = 1, .flag = 0, .val = '3' },
77	{ .name = 0 }
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 void
321final_check(unsigned int flags)
322{
323}
324
325static char *
326port_to_service(int port)
327{
328	struct servent *service;
329
330	if ((service = getservbyport(htons(port), "sctp")))
331		return service->s_name;
332
333	return NULL;
334}
335
336static void
337print_port(u_int16_t port, int numeric)
338{
339	char *service;
340
341	if (numeric || (service = port_to_service(port)) == NULL)
342		printf("%u", port);
343	else
344		printf("%s", service);
345}
346
347static void
348print_ports(const char *name, u_int16_t min, u_int16_t max,
349	    int invert, int numeric)
350{
351	const char *inv = invert ? "!" : "";
352
353	if (min != 0 || max != 0xFFFF || invert) {
354		printf("%s", name);
355		if (min == max) {
356			printf(":%s", inv);
357			print_port(min, numeric);
358		} else {
359			printf("s:%s", inv);
360			print_port(min, numeric);
361			printf(":");
362			print_port(max, numeric);
363		}
364		printf(" ");
365	}
366}
367
368static void
369print_chunk_flags(u_int32_t chunknum, u_int8_t chunk_flags, u_int8_t chunk_flags_mask)
370{
371	int i;
372
373	DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags,
374			chunk_flags_mask);
375
376	if (chunk_flags_mask) {
377		printf(":");
378	}
379
380	for (i = 7; i >= 0; i--) {
381		if (chunk_flags_mask & (1 << i)) {
382			if (chunk_flags & (1 << i)) {
383				printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]);
384			} else {
385				printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i]));
386			}
387		}
388	}
389}
390
391static void
392print_chunk(u_int32_t chunknum, int numeric)
393{
394	if (numeric) {
395		printf("0x%04X", chunknum);
396	}
397	else {
398		int i;
399
400		for (i = 0; i < ELEMCOUNT(sctp_chunk_names); i++) {
401			if (sctp_chunk_names[i].chunk_type == chunknum)
402				printf("%s", sctp_chunk_names[chunknum].name);
403		}
404	}
405}
406
407static void
408print_chunks(u_int32_t chunk_match_type,
409	     const u_int32_t *chunkmap,
410	     const struct xt_sctp_flag_info *flag_info,
411	     int flag_count,
412	     int numeric)
413{
414	int i, j;
415	int flag;
416
417	switch (chunk_match_type) {
418		case SCTP_CHUNK_MATCH_ANY:	printf("any "); break;
419		case SCTP_CHUNK_MATCH_ALL:	printf("all "); break;
420		case SCTP_CHUNK_MATCH_ONLY:	printf("only "); break;
421		default:	printf("Never reach herer\n"); break;
422	}
423
424	if (SCTP_CHUNKMAP_IS_CLEAR(chunkmap)) {
425		printf("NONE ");
426		goto out;
427	}
428
429	if (SCTP_CHUNKMAP_IS_ALL_SET(chunkmap)) {
430		printf("ALL ");
431		goto out;
432	}
433
434	flag = 0;
435	for (i = 0; i < 256; i++) {
436		if (SCTP_CHUNKMAP_IS_SET(chunkmap, i)) {
437			if (flag)
438				printf(",");
439			flag = 1;
440			print_chunk(i, numeric);
441			for (j = 0; j < flag_count; j++) {
442				if (flag_info[j].chunktype == i) {
443					print_chunk_flags(i, flag_info[j].flag,
444						flag_info[j].flag_mask);
445				}
446			}
447		}
448	}
449
450	if (flag)
451		printf(" ");
452out:
453	return;
454}
455
456/* Prints out the matchinfo. */
457static void
458print(const void *ip,
459      const struct xt_entry_match *match,
460      int numeric)
461{
462	const struct xt_sctp_info *einfo =
463		(const struct xt_sctp_info *)match->data;
464
465	printf("sctp ");
466
467	if (einfo->flags & XT_SCTP_SRC_PORTS) {
468		print_ports("spt", einfo->spts[0], einfo->spts[1],
469			einfo->invflags & XT_SCTP_SRC_PORTS,
470			numeric);
471	}
472
473	if (einfo->flags & XT_SCTP_DEST_PORTS) {
474		print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
475			einfo->invflags & XT_SCTP_DEST_PORTS,
476			numeric);
477	}
478
479	if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
480		/* FIXME: print_chunks() is used in save() where the printing of '!'
481		s taken care of, so we need to do that here as well */
482		if (einfo->invflags & XT_SCTP_CHUNK_TYPES) {
483			printf("! ");
484		}
485		print_chunks(einfo->chunk_match_type, einfo->chunkmap,
486			einfo->flag_info, einfo->flag_count, numeric);
487	}
488}
489
490/* Saves the union ipt_matchinfo in parsable form to stdout. */
491static void
492save(const void *ip,
493     const struct xt_entry_match *match)
494{
495	const struct xt_sctp_info *einfo =
496		(const struct xt_sctp_info *)match->data;
497
498	if (einfo->flags & XT_SCTP_SRC_PORTS) {
499		if (einfo->invflags & XT_SCTP_SRC_PORTS)
500			printf("! ");
501		if (einfo->spts[0] != einfo->spts[1])
502			printf("--sport %u:%u ",
503			       einfo->spts[0], einfo->spts[1]);
504		else
505			printf("--sport %u ", einfo->spts[0]);
506	}
507
508	if (einfo->flags & XT_SCTP_DEST_PORTS) {
509		if (einfo->invflags & XT_SCTP_DEST_PORTS)
510			printf("! ");
511		if (einfo->dpts[0] != einfo->dpts[1])
512			printf("--dport %u:%u ",
513			       einfo->dpts[0], einfo->dpts[1]);
514		else
515			printf("--dport %u ", einfo->dpts[0]);
516	}
517
518	if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
519		if (einfo->invflags & XT_SCTP_CHUNK_TYPES)
520			printf("! ");
521		printf("--chunk-types ");
522
523		print_chunks(einfo->chunk_match_type, einfo->chunkmap,
524			einfo->flag_info, einfo->flag_count, 0);
525	}
526}
527
528static struct xtables_match sctp = {
529	.name		= "sctp",
530	.family		= AF_INET,
531	.version	= IPTABLES_VERSION,
532	.size		= XT_ALIGN(sizeof(struct xt_sctp_info)),
533	.userspacesize	= XT_ALIGN(sizeof(struct xt_sctp_info)),
534	.help		= &help,
535	.init		= &init,
536	.parse		= &parse,
537	.final_check	= &final_check,
538	.print		= &print,
539	.save		= &save,
540	.extra_opts	= opts
541};
542
543static struct xtables_match sctp6 = {
544	.name		= "sctp",
545	.family		= AF_INET6,
546	.version	= IPTABLES_VERSION,
547	.size		= XT_ALIGN(sizeof(struct xt_sctp_info)),
548	.userspacesize	= XT_ALIGN(sizeof(struct xt_sctp_info)),
549	.help		= &help,
550	.init		= &init,
551	.parse		= &parse,
552	.final_check	= &final_check,
553	.print		= &print,
554	.save		= &save,
555	.extra_opts	= opts
556};
557
558void _init(void)
559{
560	xtables_register_match(&sctp);
561	xtables_register_match(&sctp6);
562}
563
564