linux/net/netfilter/xt_sctp.c
<<
>>
Prefs
   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>
  10#include <linux/netfilter/xt_sctp.h>
  11#include <linux/netfilter_ipv4/ip_tables.h>
  12#include <linux/netfilter_ipv6/ip6_tables.h>
  13
  14MODULE_LICENSE("GPL");
  15MODULE_AUTHOR("Kiran Kumar Immidi");
  16MODULE_DESCRIPTION("Xtables: SCTP protocol packet match");
  17MODULE_ALIAS("ipt_sctp");
  18MODULE_ALIAS("ip6t_sctp");
  19
  20#define SCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
  21                                              || (!!((invflag) & (option)) ^ (cond)))
  22
  23static bool
  24match_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
  38static inline bool
  39match_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) {
  77                        case SCTP_CHUNK_MATCH_ANY:
  78                                if (match_flags(flag_info, flag_count,
  79                                        sch->type, sch->flags)) {
  80                                        return true;
  81                                }
  82                                break;
  83
  84                        case SCTP_CHUNK_MATCH_ALL:
  85                                if (match_flags(flag_info, flag_count,
  86                                    sch->type, sch->flags))
  87                                        SCTP_CHUNKMAP_CLEAR(chunkmapcopy, sch->type);
  88                                break;
  89
  90                        case SCTP_CHUNK_MATCH_ONLY:
  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) {
  98                        case SCTP_CHUNK_MATCH_ONLY:
  99                                return false;
 100                        }
 101                }
 102        } while (offset < skb->len);
 103
 104        switch (chunk_match_type) {
 105        case SCTP_CHUNK_MATCH_ALL:
 106                return SCTP_CHUNKMAP_IS_CLEAR(chunkmapcopy);
 107        case SCTP_CHUNK_MATCH_ANY:
 108                return false;
 109        case SCTP_CHUNK_MATCH_ONLY:
 110                return true;
 111        }
 112
 113        /* This will never be reached, but required to stop compiler whine */
 114        return false;
 115}
 116
 117static bool
 118sctp_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
 148static 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;
 160        if (info->chunk_match_type & (SCTP_CHUNK_MATCH_ALL |
 161            SCTP_CHUNK_MATCH_ANY | SCTP_CHUNK_MATCH_ONLY))
 162                return 0;
 163        return -EINVAL;
 164}
 165
 166static 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
 187static int __init sctp_mt_init(void)
 188{
 189        return xt_register_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
 190}
 191
 192static void __exit sctp_mt_exit(void)
 193{
 194        xt_unregister_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
 195}
 196
 197module_init(sctp_mt_init);
 198module_exit(sctp_mt_exit);
 199