projects/linux·blob master · refs

183 lines · 4.6 KiB

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
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 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
104static bool
105sctp_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
131static 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
151static 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
172static int __init sctp_mt_init(void)
173{
174 return xt_register_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
175}
176
177static void __exit sctp_mt_exit(void)
178{
179 xt_unregister_matches(sctp_mt_reg, ARRAY_SIZE(sctp_mt_reg));
180}
181
182module_init(sctp_mt_init);
183module_exit(sctp_mt_exit);