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
match_flags(const struct xt_sctp_flag_info * flag_info,const int flag_count,u_int8_t chunktype,u_int8_t chunkflags)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
match_packet(const struct sk_buff * skb,unsigned int offset,const struct xt_sctp_info * info,bool * hotdrop)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
sctp_mt(const struct sk_buff * skb,struct xt_action_param * par)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
sctp_mt_check(const struct xt_mtchk_param * par)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
sctp_mt_init(void)172 static int __init sctp_mt_init(void)
173 {
174 return xt_register_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
175 }
176
sctp_mt_exit(void)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