1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22e4e6a17SHarald Welte /*
32e4e6a17SHarald Welte * iptables module for DCCP protocol header matching
42e4e6a17SHarald Welte *
52e4e6a17SHarald Welte * (C) 2005 by Harald Welte <laforge@netfilter.org>
62e4e6a17SHarald Welte */
72e4e6a17SHarald Welte
82e4e6a17SHarald Welte #include <linux/module.h>
92e4e6a17SHarald Welte #include <linux/skbuff.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
112e4e6a17SHarald Welte #include <linux/spinlock.h>
122e4e6a17SHarald Welte #include <net/ip.h>
132e4e6a17SHarald Welte #include <linux/dccp.h>
142e4e6a17SHarald Welte
152e4e6a17SHarald Welte #include <linux/netfilter/x_tables.h>
162e4e6a17SHarald Welte #include <linux/netfilter/xt_dccp.h>
172e4e6a17SHarald Welte
182e4e6a17SHarald Welte #include <linux/netfilter_ipv4/ip_tables.h>
192e4e6a17SHarald Welte #include <linux/netfilter_ipv6/ip6_tables.h>
202e4e6a17SHarald Welte
212e4e6a17SHarald Welte MODULE_LICENSE("GPL");
222e4e6a17SHarald Welte MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
232ae15b64SJan Engelhardt MODULE_DESCRIPTION("Xtables: DCCP protocol packet match");
242e4e6a17SHarald Welte MODULE_ALIAS("ipt_dccp");
2573aaf935SJan Engelhardt MODULE_ALIAS("ip6t_dccp");
262e4e6a17SHarald Welte
272e4e6a17SHarald Welte #define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
282e4e6a17SHarald Welte || (!!((invflag) & (option)) ^ (cond)))
292e4e6a17SHarald Welte
302e4e6a17SHarald Welte static unsigned char *dccp_optbuf;
312e4e6a17SHarald Welte static DEFINE_SPINLOCK(dccp_buflock);
322e4e6a17SHarald Welte
331d93a9cbSJan Engelhardt static inline bool
dccp_find_option(u_int8_t option,const struct sk_buff * skb,unsigned int protoff,const struct dccp_hdr * dh,bool * hotdrop)342e4e6a17SHarald Welte dccp_find_option(u_int8_t option,
352e4e6a17SHarald Welte const struct sk_buff *skb,
362e4e6a17SHarald Welte unsigned int protoff,
372e4e6a17SHarald Welte const struct dccp_hdr *dh,
38cff533acSJan Engelhardt bool *hotdrop)
392e4e6a17SHarald Welte {
402e4e6a17SHarald Welte /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
41a47362a2SJan Engelhardt const unsigned char *op;
422e4e6a17SHarald Welte unsigned int optoff = __dccp_hdr_len(dh);
432e4e6a17SHarald Welte unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
442e4e6a17SHarald Welte unsigned int i;
452e4e6a17SHarald Welte
4679f55f11SIlpo Järvinen if (dh->dccph_doff * 4 < __dccp_hdr_len(dh))
4779f55f11SIlpo Järvinen goto invalid;
482e4e6a17SHarald Welte
492e4e6a17SHarald Welte if (!optlen)
501d93a9cbSJan Engelhardt return false;
512e4e6a17SHarald Welte
522e4e6a17SHarald Welte spin_lock_bh(&dccp_buflock);
532e4e6a17SHarald Welte op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
542e4e6a17SHarald Welte if (op == NULL) {
552e4e6a17SHarald Welte /* If we don't have the whole header, drop packet. */
5679f55f11SIlpo Järvinen goto partial;
572e4e6a17SHarald Welte }
582e4e6a17SHarald Welte
592e4e6a17SHarald Welte for (i = 0; i < optlen; ) {
602e4e6a17SHarald Welte if (op[i] == option) {
612e4e6a17SHarald Welte spin_unlock_bh(&dccp_buflock);
621d93a9cbSJan Engelhardt return true;
632e4e6a17SHarald Welte }
642e4e6a17SHarald Welte
652e4e6a17SHarald Welte if (op[i] < 2)
662e4e6a17SHarald Welte i++;
672e4e6a17SHarald Welte else
682e4e6a17SHarald Welte i += op[i+1]?:1;
692e4e6a17SHarald Welte }
702e4e6a17SHarald Welte
712e4e6a17SHarald Welte spin_unlock_bh(&dccp_buflock);
721d93a9cbSJan Engelhardt return false;
7379f55f11SIlpo Järvinen
7479f55f11SIlpo Järvinen partial:
7579f55f11SIlpo Järvinen spin_unlock_bh(&dccp_buflock);
7679f55f11SIlpo Järvinen invalid:
7779f55f11SIlpo Järvinen *hotdrop = true;
7879f55f11SIlpo Järvinen return false;
792e4e6a17SHarald Welte }
802e4e6a17SHarald Welte
812e4e6a17SHarald Welte
821d93a9cbSJan Engelhardt static inline bool
match_types(const struct dccp_hdr * dh,u_int16_t typemask)832e4e6a17SHarald Welte match_types(const struct dccp_hdr *dh, u_int16_t typemask)
842e4e6a17SHarald Welte {
857c4e36bcSJan Engelhardt return typemask & (1 << dh->dccph_type);
862e4e6a17SHarald Welte }
872e4e6a17SHarald Welte
881d93a9cbSJan Engelhardt static inline bool
match_option(u_int8_t option,const struct sk_buff * skb,unsigned int protoff,const struct dccp_hdr * dh,bool * hotdrop)892e4e6a17SHarald Welte match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff,
90cff533acSJan Engelhardt const struct dccp_hdr *dh, bool *hotdrop)
912e4e6a17SHarald Welte {
922e4e6a17SHarald Welte return dccp_find_option(option, skb, protoff, dh, hotdrop);
932e4e6a17SHarald Welte }
942e4e6a17SHarald Welte
951d93a9cbSJan Engelhardt static bool
dccp_mt(const struct sk_buff * skb,struct xt_action_param * par)9662fc8051SJan Engelhardt dccp_mt(const struct sk_buff *skb, struct xt_action_param *par)
972e4e6a17SHarald Welte {
98f7108a20SJan Engelhardt const struct xt_dccp_info *info = par->matchinfo;
993cf93c96SJan Engelhardt const struct dccp_hdr *dh;
1003cf93c96SJan Engelhardt struct dccp_hdr _dh;
1012e4e6a17SHarald Welte
102f7108a20SJan Engelhardt if (par->fragoff != 0)
1031d93a9cbSJan Engelhardt return false;
1042e4e6a17SHarald Welte
105f7108a20SJan Engelhardt dh = skb_header_pointer(skb, par->thoff, sizeof(_dh), &_dh);
1062e4e6a17SHarald Welte if (dh == NULL) {
107b4ba2611SJan Engelhardt par->hotdrop = true;
1081d93a9cbSJan Engelhardt return false;
1092e4e6a17SHarald Welte }
1102e4e6a17SHarald Welte
1117c4e36bcSJan Engelhardt return DCCHECK(ntohs(dh->dccph_sport) >= info->spts[0]
1127c4e36bcSJan Engelhardt && ntohs(dh->dccph_sport) <= info->spts[1],
1132e4e6a17SHarald Welte XT_DCCP_SRC_PORTS, info->flags, info->invflags)
1147c4e36bcSJan Engelhardt && DCCHECK(ntohs(dh->dccph_dport) >= info->dpts[0]
1157c4e36bcSJan Engelhardt && ntohs(dh->dccph_dport) <= info->dpts[1],
1162e4e6a17SHarald Welte XT_DCCP_DEST_PORTS, info->flags, info->invflags)
1172e4e6a17SHarald Welte && DCCHECK(match_types(dh, info->typemask),
1182e4e6a17SHarald Welte XT_DCCP_TYPE, info->flags, info->invflags)
119f7108a20SJan Engelhardt && DCCHECK(match_option(info->option, skb, par->thoff, dh,
120b4ba2611SJan Engelhardt &par->hotdrop),
1212e4e6a17SHarald Welte XT_DCCP_OPTION, info->flags, info->invflags);
1222e4e6a17SHarald Welte }
1232e4e6a17SHarald Welte
dccp_mt_check(const struct xt_mtchk_param * par)124b0f38452SJan Engelhardt static int dccp_mt_check(const struct xt_mtchk_param *par)
1252e4e6a17SHarald Welte {
1269b4fce7aSJan Engelhardt const struct xt_dccp_info *info = par->matchinfo;
1272e4e6a17SHarald Welte
1289f567317SJan Engelhardt if (info->flags & ~XT_DCCP_VALID_FLAGS)
129bd414ee6SJan Engelhardt return -EINVAL;
1309f567317SJan Engelhardt if (info->invflags & ~XT_DCCP_VALID_FLAGS)
131bd414ee6SJan Engelhardt return -EINVAL;
1329f567317SJan Engelhardt if (info->invflags & ~info->flags)
133bd414ee6SJan Engelhardt return -EINVAL;
134bd414ee6SJan Engelhardt return 0;
1352e4e6a17SHarald Welte }
1362e4e6a17SHarald Welte
137d3c5ee6dSJan Engelhardt static struct xt_match dccp_mt_reg[] __read_mostly = {
1382e4e6a17SHarald Welte {
1392e4e6a17SHarald Welte .name = "dccp",
140ee999d8bSJan Engelhardt .family = NFPROTO_IPV4,
141d3c5ee6dSJan Engelhardt .checkentry = dccp_mt_check,
142d3c5ee6dSJan Engelhardt .match = dccp_mt,
1435d04bff0SPatrick McHardy .matchsize = sizeof(struct xt_dccp_info),
1445d04bff0SPatrick McHardy .proto = IPPROTO_DCCP,
1452e4e6a17SHarald Welte .me = THIS_MODULE,
1464470bbc7SPatrick McHardy },
1474470bbc7SPatrick McHardy {
1484470bbc7SPatrick McHardy .name = "dccp",
149ee999d8bSJan Engelhardt .family = NFPROTO_IPV6,
150d3c5ee6dSJan Engelhardt .checkentry = dccp_mt_check,
151d3c5ee6dSJan Engelhardt .match = dccp_mt,
1524470bbc7SPatrick McHardy .matchsize = sizeof(struct xt_dccp_info),
1534470bbc7SPatrick McHardy .proto = IPPROTO_DCCP,
1544470bbc7SPatrick McHardy .me = THIS_MODULE,
1554470bbc7SPatrick McHardy },
1562e4e6a17SHarald Welte };
1572e4e6a17SHarald Welte
dccp_mt_init(void)158d3c5ee6dSJan Engelhardt static int __init dccp_mt_init(void)
1592e4e6a17SHarald Welte {
1602e4e6a17SHarald Welte int ret;
1612e4e6a17SHarald Welte
1622e4e6a17SHarald Welte /* doff is 8 bits, so the maximum option size is (4*256). Don't put
1632e4e6a17SHarald Welte * this in BSS since DaveM is worried about locked TLB's for kernel
1642e4e6a17SHarald Welte * BSS. */
1652e4e6a17SHarald Welte dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
1662e4e6a17SHarald Welte if (!dccp_optbuf)
1672e4e6a17SHarald Welte return -ENOMEM;
168d3c5ee6dSJan Engelhardt ret = xt_register_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
1692e4e6a17SHarald Welte if (ret)
1702e4e6a17SHarald Welte goto out_kfree;
1712e4e6a17SHarald Welte return ret;
1722e4e6a17SHarald Welte
1732e4e6a17SHarald Welte out_kfree:
1742e4e6a17SHarald Welte kfree(dccp_optbuf);
1752e4e6a17SHarald Welte return ret;
1762e4e6a17SHarald Welte }
1772e4e6a17SHarald Welte
dccp_mt_exit(void)178d3c5ee6dSJan Engelhardt static void __exit dccp_mt_exit(void)
1792e4e6a17SHarald Welte {
180d3c5ee6dSJan Engelhardt xt_unregister_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
1812e4e6a17SHarald Welte kfree(dccp_optbuf);
1822e4e6a17SHarald Welte }
1832e4e6a17SHarald Welte
184d3c5ee6dSJan Engelhardt module_init(dccp_mt_init);
185d3c5ee6dSJan Engelhardt module_exit(dccp_mt_exit);
186