0001
0002 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0003 #include <linux/module.h>
0004 #include <linux/skbuff.h>
0005 #include <net/ip.h>
0006 #include <net/ipv6.h>
0007 #include <net/sctp/sctp.h>
0008 #include <linux/sctp.h>
0009
0010 #include <linux/netfilter/x_tables.h>
0011 #include <linux/netfilter/xt_sctp.h>
0012 #include <linux/netfilter_ipv4/ip_tables.h>
0013 #include <linux/netfilter_ipv6/ip6_tables.h>
0014
0015 MODULE_LICENSE("GPL");
0016 MODULE_AUTHOR("Kiran Kumar Immidi");
0017 MODULE_DESCRIPTION("Xtables: SCTP protocol packet match");
0018 MODULE_ALIAS("ipt_sctp");
0019 MODULE_ALIAS("ip6t_sctp");
0020
0021 #define SCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
0022 || (!!((invflag) & (option)) ^ (cond)))
0023
0024 static bool
0025 match_flags(const struct xt_sctp_flag_info *flag_info,
0026 const int flag_count,
0027 u_int8_t chunktype,
0028 u_int8_t chunkflags)
0029 {
0030 int i;
0031
0032 for (i = 0; i < flag_count; i++)
0033 if (flag_info[i].chunktype == chunktype)
0034 return (chunkflags & flag_info[i].flag_mask) == flag_info[i].flag;
0035
0036 return true;
0037 }
0038
0039 static inline bool
0040 match_packet(const struct sk_buff *skb,
0041 unsigned int offset,
0042 const struct xt_sctp_info *info,
0043 bool *hotdrop)
0044 {
0045 u_int32_t chunkmapcopy[256 / sizeof (u_int32_t)];
0046 const struct sctp_chunkhdr *sch;
0047 struct sctp_chunkhdr _sch;
0048 int chunk_match_type = info->chunk_match_type;
0049 const struct xt_sctp_flag_info *flag_info = info->flag_info;
0050 int flag_count = info->flag_count;
0051
0052 #ifdef DEBUG
0053 int i = 0;
0054 #endif
0055
0056 if (chunk_match_type == SCTP_CHUNK_MATCH_ALL)
0057 SCTP_CHUNKMAP_COPY(chunkmapcopy, info->chunkmap);
0058
0059 do {
0060 sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch);
0061 if (sch == NULL || sch->length == 0) {
0062 pr_debug("Dropping invalid SCTP packet.\n");
0063 *hotdrop = true;
0064 return false;
0065 }
0066 #ifdef DEBUG
0067 pr_debug("Chunk num: %d\toffset: %d\ttype: %d\tlength: %d"
0068 "\tflags: %x\n",
0069 ++i, offset, sch->type, htons(sch->length),
0070 sch->flags);
0071 #endif
0072 offset += SCTP_PAD4(ntohs(sch->length));
0073
0074 pr_debug("skb->len: %d\toffset: %d\n", skb->len, offset);
0075
0076 if (SCTP_CHUNKMAP_IS_SET(info->chunkmap, sch->type)) {
0077 switch (chunk_match_type) {
0078 case SCTP_CHUNK_MATCH_ANY:
0079 if (match_flags(flag_info, flag_count,
0080 sch->type, sch->flags)) {
0081 return true;
0082 }
0083 break;
0084
0085 case SCTP_CHUNK_MATCH_ALL:
0086 if (match_flags(flag_info, flag_count,
0087 sch->type, sch->flags))
0088 SCTP_CHUNKMAP_CLEAR(chunkmapcopy, sch->type);
0089 break;
0090
0091 case SCTP_CHUNK_MATCH_ONLY:
0092 if (!match_flags(flag_info, flag_count,
0093 sch->type, sch->flags))
0094 return false;
0095 break;
0096 }
0097 } else {
0098 switch (chunk_match_type) {
0099 case SCTP_CHUNK_MATCH_ONLY:
0100 return false;
0101 }
0102 }
0103 } while (offset < skb->len);
0104
0105 switch (chunk_match_type) {
0106 case SCTP_CHUNK_MATCH_ALL:
0107 return SCTP_CHUNKMAP_IS_CLEAR(chunkmapcopy);
0108 case SCTP_CHUNK_MATCH_ANY:
0109 return false;
0110 case SCTP_CHUNK_MATCH_ONLY:
0111 return true;
0112 }
0113
0114
0115 return false;
0116 }
0117
0118 static bool
0119 sctp_mt(const struct sk_buff *skb, struct xt_action_param *par)
0120 {
0121 const struct xt_sctp_info *info = par->matchinfo;
0122 const struct sctphdr *sh;
0123 struct sctphdr _sh;
0124
0125 if (par->fragoff != 0) {
0126 pr_debug("Dropping non-first fragment.. FIXME\n");
0127 return false;
0128 }
0129
0130 sh = skb_header_pointer(skb, par->thoff, sizeof(_sh), &_sh);
0131 if (sh == NULL) {
0132 pr_debug("Dropping evil TCP offset=0 tinygram.\n");
0133 par->hotdrop = true;
0134 return false;
0135 }
0136 pr_debug("spt: %d\tdpt: %d\n", ntohs(sh->source), ntohs(sh->dest));
0137
0138 return SCCHECK(ntohs(sh->source) >= info->spts[0]
0139 && ntohs(sh->source) <= info->spts[1],
0140 XT_SCTP_SRC_PORTS, info->flags, info->invflags) &&
0141 SCCHECK(ntohs(sh->dest) >= info->dpts[0]
0142 && ntohs(sh->dest) <= info->dpts[1],
0143 XT_SCTP_DEST_PORTS, info->flags, info->invflags) &&
0144 SCCHECK(match_packet(skb, par->thoff + sizeof(_sh),
0145 info, &par->hotdrop),
0146 XT_SCTP_CHUNK_TYPES, info->flags, info->invflags);
0147 }
0148
0149 static int sctp_mt_check(const struct xt_mtchk_param *par)
0150 {
0151 const struct xt_sctp_info *info = par->matchinfo;
0152
0153 if (info->flags & ~XT_SCTP_VALID_FLAGS)
0154 return -EINVAL;
0155 if (info->invflags & ~XT_SCTP_VALID_FLAGS)
0156 return -EINVAL;
0157 if (info->invflags & ~info->flags)
0158 return -EINVAL;
0159 if (!(info->flags & XT_SCTP_CHUNK_TYPES))
0160 return 0;
0161 if (info->chunk_match_type & (SCTP_CHUNK_MATCH_ALL |
0162 SCTP_CHUNK_MATCH_ANY | SCTP_CHUNK_MATCH_ONLY))
0163 return 0;
0164 return -EINVAL;
0165 }
0166
0167 static struct xt_match sctp_mt_reg[] __read_mostly = {
0168 {
0169 .name = "sctp",
0170 .family = NFPROTO_IPV4,
0171 .checkentry = sctp_mt_check,
0172 .match = sctp_mt,
0173 .matchsize = sizeof(struct xt_sctp_info),
0174 .proto = IPPROTO_SCTP,
0175 .me = THIS_MODULE
0176 },
0177 {
0178 .name = "sctp",
0179 .family = NFPROTO_IPV6,
0180 .checkentry = sctp_mt_check,
0181 .match = sctp_mt,
0182 .matchsize = sizeof(struct xt_sctp_info),
0183 .proto = IPPROTO_SCTP,
0184 .me = THIS_MODULE
0185 },
0186 };
0187
0188 static int __init sctp_mt_init(void)
0189 {
0190 return xt_register_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
0191 }
0192
0193 static void __exit sctp_mt_exit(void)
0194 {
0195 xt_unregister_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
0196 }
0197
0198 module_init(sctp_mt_init);
0199 module_exit(sctp_mt_exit);