| 1 | |
| 2 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
| 3 | #include <linux/module.h>
|
| 4 | #include <linux/skbuff.h>
|
| 5 | #include <net/ip.h>
|
| 6 | #include <net/ipv6.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 |
|
| 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 struct sctp_chunkhdr *sch;
|
| 46 | struct sctp_chunkhdr _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 | if (chunk_match_type == SCTP_CHUNK_MATCH_ALL)
|
| 52 | SCTP_CHUNKMAP_COPY(chunkmapcopy, info->chunkmap);
|
| 53 |
|
| 54 | do {
|
| 55 | sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch);
|
| 56 | if (sch == NULL || sch->length == 0) {
|
| 57 | *hotdrop = true;
|
| 58 | return false;
|
| 59 | }
|
| 60 | offset += SCTP_PAD4(ntohs(sch->length));
|
| 61 |
|
| 62 | if (SCTP_CHUNKMAP_IS_SET(info->chunkmap, sch->type)) {
|
| 63 | switch (chunk_match_type) {
|
| 64 | case SCTP_CHUNK_MATCH_ANY:
|
| 65 | if (match_flags(flag_info, flag_count,
|
| 66 | sch->type, sch->flags)) {
|
| 67 | return true;
|
| 68 | }
|
| 69 | break;
|
| 70 |
|
| 71 | case SCTP_CHUNK_MATCH_ALL:
|
| 72 | if (match_flags(flag_info, flag_count,
|
| 73 | sch->type, sch->flags))
|
| 74 | SCTP_CHUNKMAP_CLEAR(chunkmapcopy, sch->type);
|
| 75 | break;
|
| 76 |
|
| 77 | case SCTP_CHUNK_MATCH_ONLY:
|
| 78 | if (!match_flags(flag_info, flag_count,
|
| 79 | sch->type, sch->flags))
|
| 80 | return false;
|
| 81 | break;
|
| 82 | }
|
| 83 | } else {
|
| 84 | switch (chunk_match_type) {
|
| 85 | case SCTP_CHUNK_MATCH_ONLY:
|
| 86 | return false;
|
| 87 | }
|
| 88 | }
|
| 89 | } while (offset < skb->len);
|
| 90 |
|
| 91 | switch (chunk_match_type) {
|
| 92 | case SCTP_CHUNK_MATCH_ALL:
|
| 93 | return SCTP_CHUNKMAP_IS_CLEAR(chunkmapcopy);
|
| 94 | case SCTP_CHUNK_MATCH_ANY:
|
| 95 | return false;
|
| 96 | case SCTP_CHUNK_MATCH_ONLY:
|
| 97 | return true;
|
| 98 | }
|
| 99 |
|
| 100 |
|
| 101 | return false;
|
| 102 | }
|
| 103 |
|
| 104 | static bool
|
| 105 | sctp_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
| 106 | {
|
| 107 | const struct xt_sctp_info *info = par->matchinfo;
|
| 108 | const struct sctphdr *sh;
|
| 109 | struct sctphdr _sh;
|
| 110 |
|
| 111 | if (par->fragoff != 0)
|
| 112 | return false;
|
| 113 |
|
| 114 | sh = skb_header_pointer(skb, par->thoff, sizeof(_sh), &_sh);
|
| 115 | if (sh == NULL) {
|
| 116 | par->hotdrop = true;
|
| 117 | return false;
|
| 118 | }
|
| 119 |
|
| 120 | return SCCHECK(ntohs(sh->source) >= info->spts[0]
|
| 121 | && ntohs(sh->source) <= info->spts[1],
|
| 122 | XT_SCTP_SRC_PORTS, info->flags, info->invflags) &&
|
| 123 | SCCHECK(ntohs(sh->dest) >= info->dpts[0]
|
| 124 | && ntohs(sh->dest) <= info->dpts[1],
|
| 125 | XT_SCTP_DEST_PORTS, info->flags, info->invflags) &&
|
| 126 | SCCHECK(match_packet(skb, par->thoff + sizeof(_sh),
|
| 127 | info, &par->hotdrop),
|
| 128 | XT_SCTP_CHUNK_TYPES, info->flags, info->invflags);
|
| 129 | }
|
| 130 |
|
| 131 | static int sctp_mt_check(const struct xt_mtchk_param *par)
|
| 132 | {
|
| 133 | const struct xt_sctp_info *info = par->matchinfo;
|
| 134 |
|
| 135 | if (info->flag_count > ARRAY_SIZE(info->flag_info))
|
| 136 | return -EINVAL;
|
| 137 | if (info->flags & ~XT_SCTP_VALID_FLAGS)
|
| 138 | return -EINVAL;
|
| 139 | if (info->invflags & ~XT_SCTP_VALID_FLAGS)
|
| 140 | return -EINVAL;
|
| 141 | if (info->invflags & ~info->flags)
|
| 142 | return -EINVAL;
|
| 143 | if (!(info->flags & XT_SCTP_CHUNK_TYPES))
|
| 144 | return 0;
|
| 145 | if (info->chunk_match_type & (SCTP_CHUNK_MATCH_ALL |
|
| 146 | SCTP_CHUNK_MATCH_ANY | SCTP_CHUNK_MATCH_ONLY))
|
| 147 | return 0;
|
| 148 | return -EINVAL;
|
| 149 | }
|
| 150 |
|
| 151 | static struct xt_match sctp_mt_reg[] __read_mostly = {
|
| 152 | {
|
| 153 | .name = "sctp",
|
| 154 | .family = NFPROTO_IPV4,
|
| 155 | .checkentry = sctp_mt_check,
|
| 156 | .match = sctp_mt,
|
| 157 | .matchsize = sizeof(struct xt_sctp_info),
|
| 158 | .proto = IPPROTO_SCTP,
|
| 159 | .me = THIS_MODULE
|
| 160 | },
|
| 161 | {
|
| 162 | .name = "sctp",
|
| 163 | .family = NFPROTO_IPV6,
|
| 164 | .checkentry = sctp_mt_check,
|
| 165 | .match = sctp_mt,
|
| 166 | .matchsize = sizeof(struct xt_sctp_info),
|
| 167 | .proto = IPPROTO_SCTP,
|
| 168 | .me = THIS_MODULE
|
| 169 | },
|
| 170 | };
|
| 171 |
|
| 172 | static int __init sctp_mt_init(void)
|
| 173 | {
|
| 174 | return xt_register_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
|
| 175 | }
|
| 176 |
|
| 177 | static void __exit sctp_mt_exit(void)
|
| 178 | {
|
| 179 | xt_unregister_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
|
| 180 | }
|
| 181 |
|
| 182 | module_init(sctp_mt_init);
|
| 183 | module_exit(sctp_mt_exit);
|