Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
xt_sctp.c
Go to the documentation of this file.
1 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2 #include <linux/module.h>
3 #include <linux/skbuff.h>
4 #include <net/ip.h>
5 #include <net/ipv6.h>
6 #include <net/sctp/sctp.h>
7 #include <linux/sctp.h>
8 
9 #include <linux/netfilter/x_tables.h>
11 #include <linux/netfilter_ipv4/ip_tables.h>
12 #include <linux/netfilter_ipv6/ip6_tables.h>
13 
14 MODULE_LICENSE("GPL");
15 MODULE_AUTHOR("Kiran Kumar Immidi");
16 MODULE_DESCRIPTION("Xtables: SCTP protocol packet match");
17 MODULE_ALIAS("ipt_sctp");
18 MODULE_ALIAS("ip6t_sctp");
19 
20 #define SCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
21  || (!!((invflag) & (option)) ^ (cond)))
22 
23 static bool
24 match_flags(const struct xt_sctp_flag_info *flag_info,
25  const int flag_count,
26  u_int8_t chunktype,
27  u_int8_t chunkflags)
28 {
29  int i;
30 
31  for (i = 0; i < flag_count; i++)
32  if (flag_info[i].chunktype == chunktype)
33  return (chunkflags & flag_info[i].flag_mask) == flag_info[i].flag;
34 
35  return true;
36 }
37 
38 static inline bool
39 match_packet(const struct sk_buff *skb,
40  unsigned int offset,
41  const struct xt_sctp_info *info,
42  bool *hotdrop)
43 {
44  u_int32_t chunkmapcopy[256 / sizeof (u_int32_t)];
45  const sctp_chunkhdr_t *sch;
46  sctp_chunkhdr_t _sch;
47  int chunk_match_type = info->chunk_match_type;
48  const struct xt_sctp_flag_info *flag_info = info->flag_info;
49  int flag_count = info->flag_count;
50 
51 #ifdef DEBUG
52  int i = 0;
53 #endif
54 
55  if (chunk_match_type == SCTP_CHUNK_MATCH_ALL)
56  SCTP_CHUNKMAP_COPY(chunkmapcopy, info->chunkmap);
57 
58  do {
59  sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch);
60  if (sch == NULL || sch->length == 0) {
61  pr_debug("Dropping invalid SCTP packet.\n");
62  *hotdrop = true;
63  return false;
64  }
65 #ifdef DEBUG
66  pr_debug("Chunk num: %d\toffset: %d\ttype: %d\tlength: %d"
67  "\tflags: %x\n",
68  ++i, offset, sch->type, htons(sch->length),
69  sch->flags);
70 #endif
71  offset += WORD_ROUND(ntohs(sch->length));
72 
73  pr_debug("skb->len: %d\toffset: %d\n", skb->len, offset);
74 
75  if (SCTP_CHUNKMAP_IS_SET(info->chunkmap, sch->type)) {
76  switch (chunk_match_type) {
78  if (match_flags(flag_info, flag_count,
79  sch->type, sch->flags)) {
80  return true;
81  }
82  break;
83 
85  if (match_flags(flag_info, flag_count,
86  sch->type, sch->flags))
87  SCTP_CHUNKMAP_CLEAR(chunkmapcopy, sch->type);
88  break;
89 
91  if (!match_flags(flag_info, flag_count,
92  sch->type, sch->flags))
93  return false;
94  break;
95  }
96  } else {
97  switch (chunk_match_type) {
99  return false;
100  }
101  }
102  } while (offset < skb->len);
103 
104  switch (chunk_match_type) {
106  return SCTP_CHUNKMAP_IS_CLEAR(chunkmapcopy);
108  return false;
110  return true;
111  }
112 
113  /* This will never be reached, but required to stop compiler whine */
114  return false;
115 }
116 
117 static bool
118 sctp_mt(const struct sk_buff *skb, struct xt_action_param *par)
119 {
120  const struct xt_sctp_info *info = par->matchinfo;
121  const sctp_sctphdr_t *sh;
122  sctp_sctphdr_t _sh;
123 
124  if (par->fragoff != 0) {
125  pr_debug("Dropping non-first fragment.. FIXME\n");
126  return false;
127  }
128 
129  sh = skb_header_pointer(skb, par->thoff, sizeof(_sh), &_sh);
130  if (sh == NULL) {
131  pr_debug("Dropping evil TCP offset=0 tinygram.\n");
132  par->hotdrop = true;
133  return false;
134  }
135  pr_debug("spt: %d\tdpt: %d\n", ntohs(sh->source), ntohs(sh->dest));
136 
137  return SCCHECK(ntohs(sh->source) >= info->spts[0]
138  && ntohs(sh->source) <= info->spts[1],
139  XT_SCTP_SRC_PORTS, info->flags, info->invflags)
140  && SCCHECK(ntohs(sh->dest) >= info->dpts[0]
141  && ntohs(sh->dest) <= info->dpts[1],
142  XT_SCTP_DEST_PORTS, info->flags, info->invflags)
143  && SCCHECK(match_packet(skb, par->thoff + sizeof(sctp_sctphdr_t),
144  info, &par->hotdrop),
145  XT_SCTP_CHUNK_TYPES, info->flags, info->invflags);
146 }
147 
148 static int sctp_mt_check(const struct xt_mtchk_param *par)
149 {
150  const struct xt_sctp_info *info = par->matchinfo;
151 
152  if (info->flags & ~XT_SCTP_VALID_FLAGS)
153  return -EINVAL;
154  if (info->invflags & ~XT_SCTP_VALID_FLAGS)
155  return -EINVAL;
156  if (info->invflags & ~info->flags)
157  return -EINVAL;
158  if (!(info->flags & XT_SCTP_CHUNK_TYPES))
159  return 0;
162  return 0;
163  return -EINVAL;
164 }
165 
166 static struct xt_match sctp_mt_reg[] __read_mostly = {
167  {
168  .name = "sctp",
169  .family = NFPROTO_IPV4,
170  .checkentry = sctp_mt_check,
171  .match = sctp_mt,
172  .matchsize = sizeof(struct xt_sctp_info),
173  .proto = IPPROTO_SCTP,
174  .me = THIS_MODULE
175  },
176  {
177  .name = "sctp",
178  .family = NFPROTO_IPV6,
179  .checkentry = sctp_mt_check,
180  .match = sctp_mt,
181  .matchsize = sizeof(struct xt_sctp_info),
182  .proto = IPPROTO_SCTP,
183  .me = THIS_MODULE
184  },
185 };
186 
187 static int __init sctp_mt_init(void)
188 {
189  return xt_register_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
190 }
191 
192 static void __exit sctp_mt_exit(void)
193 {
194  xt_unregister_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
195 }
196 
197 module_init(sctp_mt_init);
198 module_exit(sctp_mt_exit);