1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Kernel module to match FRAG parameters. */
3
4 /* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu>
5 */
6 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7 #include <linux/module.h>
8 #include <linux/skbuff.h>
9 #include <linux/ipv6.h>
10 #include <linux/types.h>
11 #include <net/checksum.h>
12 #include <net/ipv6.h>
13
14 #include <linux/netfilter/x_tables.h>
15 #include <linux/netfilter_ipv6/ip6_tables.h>
16 #include <linux/netfilter_ipv6/ip6t_frag.h>
17
18 MODULE_LICENSE("GPL");
19 MODULE_DESCRIPTION("Xtables: IPv6 fragment match");
20 MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
21
22 /* Returns 1 if the id is matched by the range, 0 otherwise */
23 static inline bool
id_match(u_int32_t min,u_int32_t max,u_int32_t id,bool invert)24 id_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert)
25 {
26 return (id >= min && id <= max) ^ invert;
27 }
28
29 static bool
frag_mt6(const struct sk_buff * skb,struct xt_action_param * par)30 frag_mt6(const struct sk_buff *skb, struct xt_action_param *par)
31 {
32 struct frag_hdr _frag;
33 const struct frag_hdr *fh;
34 const struct ip6t_frag *fraginfo = par->matchinfo;
35 unsigned int ptr = 0;
36 int err;
37
38 err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL, NULL);
39 if (err < 0) {
40 if (err != -ENOENT)
41 par->hotdrop = true;
42 return false;
43 }
44
45 fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag);
46 if (fh == NULL) {
47 par->hotdrop = true;
48 return false;
49 }
50
51 return id_match(fraginfo->ids[0], fraginfo->ids[1],
52 ntohl(fh->identification),
53 !!(fraginfo->invflags & IP6T_FRAG_INV_IDS)) &&
54 !((fraginfo->flags & IP6T_FRAG_RES) &&
55 (fh->reserved || (ntohs(fh->frag_off) & 0x6))) &&
56 !((fraginfo->flags & IP6T_FRAG_FST) &&
57 (ntohs(fh->frag_off) & ~0x7)) &&
58 !((fraginfo->flags & IP6T_FRAG_MF) &&
59 !(ntohs(fh->frag_off) & IP6_MF)) &&
60 !((fraginfo->flags & IP6T_FRAG_NMF) &&
61 (ntohs(fh->frag_off) & IP6_MF));
62 }
63
frag_mt6_check(const struct xt_mtchk_param * par)64 static int frag_mt6_check(const struct xt_mtchk_param *par)
65 {
66 const struct ip6t_frag *fraginfo = par->matchinfo;
67
68 if (fraginfo->invflags & ~IP6T_FRAG_INV_MASK) {
69 pr_info_ratelimited("unknown flags %X\n", fraginfo->invflags);
70 return -EINVAL;
71 }
72 return 0;
73 }
74
75 static struct xt_match frag_mt6_reg __read_mostly = {
76 .name = "frag",
77 .family = NFPROTO_IPV6,
78 .match = frag_mt6,
79 .matchsize = sizeof(struct ip6t_frag),
80 .checkentry = frag_mt6_check,
81 .me = THIS_MODULE,
82 };
83
frag_mt6_init(void)84 static int __init frag_mt6_init(void)
85 {
86 return xt_register_match(&frag_mt6_reg);
87 }
88
frag_mt6_exit(void)89 static void __exit frag_mt6_exit(void)
90 {
91 xt_unregister_match(&frag_mt6_reg);
92 }
93
94 module_init(frag_mt6_init);
95 module_exit(frag_mt6_exit);
96