1 // SPDX-License-Identifier: GPL-2.0-only 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 /* This will never be reached, but required to stop compiler whine */ 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); 184