Blame view
net/netfilter/xt_dccp.c
4.3 KB
d2912cb15
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
1d3de414e
|
2 3 4 5 |
/* * iptables module for DCCP protocol header matching * * (C) 2005 by Harald Welte <laforge@netfilter.org> |
1d3de414e
|
6 7 8 9 |
*/ #include <linux/module.h> #include <linux/skbuff.h> |
5a0e3ad6a
|
10 |
#include <linux/slab.h> |
1d3de414e
|
11 12 13 |
#include <linux/spinlock.h> #include <net/ip.h> #include <linux/dccp.h> |
2e4e6a17a
|
14 15 |
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/xt_dccp.h> |
1d3de414e
|
16 |
#include <linux/netfilter_ipv4/ip_tables.h> |
2e4e6a17a
|
17 18 19 20 |
#include <linux/netfilter_ipv6/ip6_tables.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); |
2ae15b64e
|
21 |
MODULE_DESCRIPTION("Xtables: DCCP protocol packet match"); |
2e4e6a17a
|
22 |
MODULE_ALIAS("ipt_dccp"); |
73aaf9355
|
23 |
MODULE_ALIAS("ip6t_dccp"); |
1d3de414e
|
24 25 |
#define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \ |
601e68e10
|
26 |
|| (!!((invflag) & (option)) ^ (cond))) |
1d3de414e
|
27 28 29 |
static unsigned char *dccp_optbuf; static DEFINE_SPINLOCK(dccp_buflock); |
1d93a9cba
|
30 |
static inline bool |
1d3de414e
|
31 32 |
dccp_find_option(u_int8_t option, const struct sk_buff *skb, |
2e4e6a17a
|
33 |
unsigned int protoff, |
1d3de414e
|
34 |
const struct dccp_hdr *dh, |
cff533ac1
|
35 |
bool *hotdrop) |
1d3de414e
|
36 37 |
{ /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ |
a47362a22
|
38 |
const unsigned char *op; |
1d3de414e
|
39 40 41 |
unsigned int optoff = __dccp_hdr_len(dh); unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh); unsigned int i; |
79f55f11a
|
42 43 |
if (dh->dccph_doff * 4 < __dccp_hdr_len(dh)) goto invalid; |
1d3de414e
|
44 45 |
if (!optlen) |
1d93a9cba
|
46 |
return false; |
1d3de414e
|
47 48 |
spin_lock_bh(&dccp_buflock); |
2e4e6a17a
|
49 |
op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf); |
1d3de414e
|
50 51 |
if (op == NULL) { /* If we don't have the whole header, drop packet. */ |
79f55f11a
|
52 |
goto partial; |
1d3de414e
|
53 54 55 56 57 |
} for (i = 0; i < optlen; ) { if (op[i] == option) { spin_unlock_bh(&dccp_buflock); |
1d93a9cba
|
58 |
return true; |
1d3de414e
|
59 |
} |
601e68e10
|
60 |
if (op[i] < 2) |
1d3de414e
|
61 |
i++; |
601e68e10
|
62 |
else |
1d3de414e
|
63 64 65 66 |
i += op[i+1]?:1; } spin_unlock_bh(&dccp_buflock); |
1d93a9cba
|
67 |
return false; |
79f55f11a
|
68 69 70 71 72 73 |
partial: spin_unlock_bh(&dccp_buflock); invalid: *hotdrop = true; return false; |
1d3de414e
|
74 |
} |
1d93a9cba
|
75 |
static inline bool |
1d3de414e
|
76 77 |
match_types(const struct dccp_hdr *dh, u_int16_t typemask) { |
7c4e36bc1
|
78 |
return typemask & (1 << dh->dccph_type); |
1d3de414e
|
79 |
} |
1d93a9cba
|
80 |
static inline bool |
2e4e6a17a
|
81 |
match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff, |
cff533ac1
|
82 |
const struct dccp_hdr *dh, bool *hotdrop) |
1d3de414e
|
83 |
{ |
2e4e6a17a
|
84 |
return dccp_find_option(option, skb, protoff, dh, hotdrop); |
1d3de414e
|
85 |
} |
1d93a9cba
|
86 |
static bool |
62fc80510
|
87 |
dccp_mt(const struct sk_buff *skb, struct xt_action_param *par) |
1d3de414e
|
88 |
{ |
f7108a20d
|
89 |
const struct xt_dccp_info *info = par->matchinfo; |
3cf93c96a
|
90 91 |
const struct dccp_hdr *dh; struct dccp_hdr _dh; |
1d3de414e
|
92 |
|
f7108a20d
|
93 |
if (par->fragoff != 0) |
1d93a9cba
|
94 |
return false; |
601e68e10
|
95 |
|
f7108a20d
|
96 |
dh = skb_header_pointer(skb, par->thoff, sizeof(_dh), &_dh); |
1d3de414e
|
97 |
if (dh == NULL) { |
b4ba26119
|
98 |
par->hotdrop = true; |
1d93a9cba
|
99 |
return false; |
601e68e10
|
100 |
} |
1d3de414e
|
101 |
|
7c4e36bc1
|
102 103 |
return DCCHECK(ntohs(dh->dccph_sport) >= info->spts[0] && ntohs(dh->dccph_sport) <= info->spts[1], |
601e68e10
|
104 |
XT_DCCP_SRC_PORTS, info->flags, info->invflags) |
7c4e36bc1
|
105 106 |
&& DCCHECK(ntohs(dh->dccph_dport) >= info->dpts[0] && ntohs(dh->dccph_dport) <= info->dpts[1], |
2e4e6a17a
|
107 |
XT_DCCP_DEST_PORTS, info->flags, info->invflags) |
1d3de414e
|
108 |
&& DCCHECK(match_types(dh, info->typemask), |
2e4e6a17a
|
109 |
XT_DCCP_TYPE, info->flags, info->invflags) |
f7108a20d
|
110 |
&& DCCHECK(match_option(info->option, skb, par->thoff, dh, |
b4ba26119
|
111 |
&par->hotdrop), |
2e4e6a17a
|
112 |
XT_DCCP_OPTION, info->flags, info->invflags); |
1d3de414e
|
113 |
} |
b0f38452f
|
114 |
static int dccp_mt_check(const struct xt_mtchk_param *par) |
1d3de414e
|
115 |
{ |
9b4fce7a3
|
116 |
const struct xt_dccp_info *info = par->matchinfo; |
1d3de414e
|
117 |
|
9f5673174
|
118 |
if (info->flags & ~XT_DCCP_VALID_FLAGS) |
bd414ee60
|
119 |
return -EINVAL; |
9f5673174
|
120 |
if (info->invflags & ~XT_DCCP_VALID_FLAGS) |
bd414ee60
|
121 |
return -EINVAL; |
9f5673174
|
122 |
if (info->invflags & ~info->flags) |
bd414ee60
|
123 124 |
return -EINVAL; return 0; |
2e4e6a17a
|
125 |
} |
d3c5ee6d5
|
126 |
static struct xt_match dccp_mt_reg[] __read_mostly = { |
4470bbc74
|
127 128 |
{ .name = "dccp", |
ee999d8b9
|
129 |
.family = NFPROTO_IPV4, |
d3c5ee6d5
|
130 131 |
.checkentry = dccp_mt_check, .match = dccp_mt, |
4470bbc74
|
132 133 134 135 136 137 |
.matchsize = sizeof(struct xt_dccp_info), .proto = IPPROTO_DCCP, .me = THIS_MODULE, }, { .name = "dccp", |
ee999d8b9
|
138 |
.family = NFPROTO_IPV6, |
d3c5ee6d5
|
139 140 |
.checkentry = dccp_mt_check, .match = dccp_mt, |
4470bbc74
|
141 142 143 144 |
.matchsize = sizeof(struct xt_dccp_info), .proto = IPPROTO_DCCP, .me = THIS_MODULE, }, |
1d3de414e
|
145 |
}; |
d3c5ee6d5
|
146 |
static int __init dccp_mt_init(void) |
1d3de414e
|
147 148 149 150 151 152 153 154 155 |
{ int ret; /* doff is 8 bits, so the maximum option size is (4*256). Don't put * this in BSS since DaveM is worried about locked TLB's for kernel * BSS. */ dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL); if (!dccp_optbuf) return -ENOMEM; |
d3c5ee6d5
|
156 |
ret = xt_register_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg)); |
1d3de414e
|
157 |
if (ret) |
2e4e6a17a
|
158 |
goto out_kfree; |
2e4e6a17a
|
159 |
return ret; |
2e4e6a17a
|
160 161 |
out_kfree: kfree(dccp_optbuf); |
1d3de414e
|
162 163 |
return ret; } |
d3c5ee6d5
|
164 |
static void __exit dccp_mt_exit(void) |
1d3de414e
|
165 |
{ |
d3c5ee6d5
|
166 |
xt_unregister_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg)); |
1d3de414e
|
167 168 |
kfree(dccp_optbuf); } |
d3c5ee6d5
|
169 170 |
module_init(dccp_mt_init); module_exit(dccp_mt_exit); |