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