1#include <linux/module.h>
2#include <linux/skbuff.h>
3#include <net/ip.h>
4#include <net/ipv6.h>
5#include <linux/sctp.h>
6
7#include <linux/netfilter/x_tables.h>
8#include <linux/netfilter/xt_sctp.h>
9#include <linux/netfilter_ipv4/ip_tables.h>
10#include <linux/netfilter_ipv6/ip6_tables.h>
11
12MODULE_LICENSE("GPL");
13MODULE_AUTHOR("Kiran Kumar Immidi");
14MODULE_DESCRIPTION("Match for SCTP protocol packets");
15MODULE_ALIAS("ipt_sctp");
16
17#ifdef DEBUG_SCTP
18#define duprintf(format, args...) printk(format , ## args)
19#else
20#define duprintf(format, args...)
21#endif
22
23#define SCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
24					      || (!!((invflag) & (option)) ^ (cond)))
25
26static int
27match_flags(const struct xt_sctp_flag_info *flag_info,
28	    const int flag_count,
29	    u_int8_t chunktype,
30	    u_int8_t chunkflags)
31{
32	int i;
33
34	for (i = 0; i < flag_count; i++) {
35		if (flag_info[i].chunktype == chunktype) {
36			return (chunkflags & flag_info[i].flag_mask) == flag_info[i].flag;
37		}
38	}
39
40	return 1;
41}
42
43static inline int
44match_packet(const struct sk_buff *skb,
45	     unsigned int offset,
46	     const u_int32_t *chunkmap,
47	     int chunk_match_type,
48	     const struct xt_sctp_flag_info *flag_info,
49	     const int flag_count,
50	     int *hotdrop)
51{
52	u_int32_t chunkmapcopy[256 / sizeof (u_int32_t)];
53	sctp_chunkhdr_t _sch, *sch;
54
55#ifdef DEBUG_SCTP
56	int i = 0;
57#endif
58
59	if (chunk_match_type == SCTP_CHUNK_MATCH_ALL) {
60		SCTP_CHUNKMAP_COPY(chunkmapcopy, chunkmap);
61	}
62
63	do {
64		sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch);
65		if (sch == NULL || sch->length == 0) {
66			duprintf("Dropping invalid SCTP packet.\n");
67			*hotdrop = 1;
68			return 0;
69		}
70
71		duprintf("Chunk num: %d\toffset: %d\ttype: %d\tlength: %d\tflags: %x\n",
72				++i, offset, sch->type, htons(sch->length), sch->flags);
73
74		offset += (ntohs(sch->length) + 3) & ~3;
75
76		duprintf("skb->len: %d\toffset: %d\n", skb->len, offset);
77
78		if (SCTP_CHUNKMAP_IS_SET(chunkmap, sch->type)) {
79			switch (chunk_match_type) {
80			case SCTP_CHUNK_MATCH_ANY:
81				if (match_flags(flag_info, flag_count,
82					sch->type, sch->flags)) {
83					return 1;
84				}
85				break;
86
87			case SCTP_CHUNK_MATCH_ALL:
88				if (match_flags(flag_info, flag_count,
89					sch->type, sch->flags)) {
90					SCTP_CHUNKMAP_CLEAR(chunkmapcopy, sch->type);
91				}
92				break;
93
94			case SCTP_CHUNK_MATCH_ONLY:
95				if (!match_flags(flag_info, flag_count,
96					sch->type, sch->flags)) {
97					return 0;
98				}
99				break;
100			}
101		} else {
102			switch (chunk_match_type) {
103			case SCTP_CHUNK_MATCH_ONLY:
104				return 0;
105			}
106		}
107	} while (offset < skb->len);
108
109	switch (chunk_match_type) {
110	case SCTP_CHUNK_MATCH_ALL:
111		return SCTP_CHUNKMAP_IS_CLEAR(chunkmap);
112	case SCTP_CHUNK_MATCH_ANY:
113		return 0;
114	case SCTP_CHUNK_MATCH_ONLY:
115		return 1;
116	}
117
118	/* This will never be reached, but required to stop compiler whine */
119	return 0;
120}
121
122static int
123match(const struct sk_buff *skb,
124      const struct net_device *in,
125      const struct net_device *out,
126      const struct xt_match *match,
127      const void *matchinfo,
128      int offset,
129      unsigned int protoff,
130      int *hotdrop)
131{
132	const struct xt_sctp_info *info = matchinfo;
133	sctp_sctphdr_t _sh, *sh;
134
135	if (offset) {
136		duprintf("Dropping non-first fragment.. FIXME\n");
137		return 0;
138	}
139
140	sh = skb_header_pointer(skb, protoff, sizeof(_sh), &_sh);
141	if (sh == NULL) {
142		duprintf("Dropping evil TCP offset=0 tinygram.\n");
143		*hotdrop = 1;
144		return 0;
145	}
146	duprintf("spt: %d\tdpt: %d\n", ntohs(sh->source), ntohs(sh->dest));
147
148	return  SCCHECK(((ntohs(sh->source) >= info->spts[0])
149			&& (ntohs(sh->source) <= info->spts[1])),
150			XT_SCTP_SRC_PORTS, info->flags, info->invflags)
151		&& SCCHECK(((ntohs(sh->dest) >= info->dpts[0])
152			&& (ntohs(sh->dest) <= info->dpts[1])),
153			XT_SCTP_DEST_PORTS, info->flags, info->invflags)
154		&& SCCHECK(match_packet(skb, protoff + sizeof (sctp_sctphdr_t),
155					info->chunkmap, info->chunk_match_type,
156					info->flag_info, info->flag_count,
157					hotdrop),
158			   XT_SCTP_CHUNK_TYPES, info->flags, info->invflags);
159}
160
161static int
162checkentry(const char *tablename,
163	   const void *inf,
164	   const struct xt_match *match,
165	   void *matchinfo,
166	   unsigned int hook_mask)
167{
168	const struct xt_sctp_info *info = matchinfo;
169
170	return !(info->flags & ~XT_SCTP_VALID_FLAGS)
171		&& !(info->invflags & ~XT_SCTP_VALID_FLAGS)
172		&& !(info->invflags & ~info->flags)
173		&& ((!(info->flags & XT_SCTP_CHUNK_TYPES)) ||
174			(info->chunk_match_type &
175				(SCTP_CHUNK_MATCH_ALL
176				| SCTP_CHUNK_MATCH_ANY
177				| SCTP_CHUNK_MATCH_ONLY)));
178}
179
180static struct xt_match xt_sctp_match[] = {
181	{
182		.name		= "sctp",
183		.family		= AF_INET,
184		.checkentry	= checkentry,
185		.match		= match,
186		.matchsize	= sizeof(struct xt_sctp_info),
187		.proto		= IPPROTO_SCTP,
188		.me		= THIS_MODULE
189	},
190	{
191		.name		= "sctp",
192		.family		= AF_INET6,
193		.checkentry	= checkentry,
194		.match		= match,
195		.matchsize	= sizeof(struct xt_sctp_info),
196		.proto		= IPPROTO_SCTP,
197		.me		= THIS_MODULE
198	},
199};
200
201static int __init xt_sctp_init(void)
202{
203	return xt_register_matches(xt_sctp_match, ARRAY_SIZE(xt_sctp_match));
204}
205
206static void __exit xt_sctp_fini(void)
207{
208	xt_unregister_matches(xt_sctp_match, ARRAY_SIZE(xt_sctp_match));
209}
210
211module_init(xt_sctp_init);
212module_exit(xt_sctp_fini);
213