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 |
24 | id_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) |
25 | { |
26 | bool r; |
27 | pr_debug("id_match:%c 0x%x <= 0x%x <= 0x%x\n" , invert ? '!' : ' ', |
28 | min, id, max); |
29 | r = (id >= min && id <= max) ^ invert; |
30 | pr_debug(" result %s\n" , r ? "PASS" : "FAILED" ); |
31 | return r; |
32 | } |
33 | |
34 | static bool |
35 | frag_mt6(const struct sk_buff *skb, struct xt_action_param *par) |
36 | { |
37 | struct frag_hdr _frag; |
38 | const struct frag_hdr *fh; |
39 | const struct ip6t_frag *fraginfo = par->matchinfo; |
40 | unsigned int ptr = 0; |
41 | int err; |
42 | |
43 | err = ipv6_find_hdr(skb, offset: &ptr, NEXTHDR_FRAGMENT, NULL, NULL); |
44 | if (err < 0) { |
45 | if (err != -ENOENT) |
46 | par->hotdrop = true; |
47 | return false; |
48 | } |
49 | |
50 | fh = skb_header_pointer(skb, offset: ptr, len: sizeof(_frag), buffer: &_frag); |
51 | if (fh == NULL) { |
52 | par->hotdrop = true; |
53 | return false; |
54 | } |
55 | |
56 | pr_debug("INFO %04X " , fh->frag_off); |
57 | pr_debug("OFFSET %04X " , ntohs(fh->frag_off) & ~0x7); |
58 | pr_debug("RES %02X %04X" , fh->reserved, ntohs(fh->frag_off) & 0x6); |
59 | pr_debug("MF %04X " , fh->frag_off & htons(IP6_MF)); |
60 | pr_debug("ID %u %08X\n" , ntohl(fh->identification), |
61 | ntohl(fh->identification)); |
62 | |
63 | pr_debug("IPv6 FRAG id %02X " , |
64 | id_match(fraginfo->ids[0], fraginfo->ids[1], |
65 | ntohl(fh->identification), |
66 | !!(fraginfo->invflags & IP6T_FRAG_INV_IDS))); |
67 | pr_debug("res %02X %02X%04X %02X " , |
68 | fraginfo->flags & IP6T_FRAG_RES, fh->reserved, |
69 | ntohs(fh->frag_off) & 0x6, |
70 | !((fraginfo->flags & IP6T_FRAG_RES) && |
71 | (fh->reserved || (ntohs(fh->frag_off) & 0x06)))); |
72 | pr_debug("first %02X %02X %02X " , |
73 | fraginfo->flags & IP6T_FRAG_FST, |
74 | ntohs(fh->frag_off) & ~0x7, |
75 | !((fraginfo->flags & IP6T_FRAG_FST) && |
76 | (ntohs(fh->frag_off) & ~0x7))); |
77 | pr_debug("mf %02X %02X %02X " , |
78 | fraginfo->flags & IP6T_FRAG_MF, |
79 | ntohs(fh->frag_off) & IP6_MF, |
80 | !((fraginfo->flags & IP6T_FRAG_MF) && |
81 | !((ntohs(fh->frag_off) & IP6_MF)))); |
82 | pr_debug("last %02X %02X %02X\n" , |
83 | fraginfo->flags & IP6T_FRAG_NMF, |
84 | ntohs(fh->frag_off) & IP6_MF, |
85 | !((fraginfo->flags & IP6T_FRAG_NMF) && |
86 | (ntohs(fh->frag_off) & IP6_MF))); |
87 | |
88 | return id_match(min: fraginfo->ids[0], max: fraginfo->ids[1], |
89 | ntohl(fh->identification), |
90 | invert: !!(fraginfo->invflags & IP6T_FRAG_INV_IDS)) && |
91 | !((fraginfo->flags & IP6T_FRAG_RES) && |
92 | (fh->reserved || (ntohs(fh->frag_off) & 0x6))) && |
93 | !((fraginfo->flags & IP6T_FRAG_FST) && |
94 | (ntohs(fh->frag_off) & ~0x7)) && |
95 | !((fraginfo->flags & IP6T_FRAG_MF) && |
96 | !(ntohs(fh->frag_off) & IP6_MF)) && |
97 | !((fraginfo->flags & IP6T_FRAG_NMF) && |
98 | (ntohs(fh->frag_off) & IP6_MF)); |
99 | } |
100 | |
101 | static int frag_mt6_check(const struct xt_mtchk_param *par) |
102 | { |
103 | const struct ip6t_frag *fraginfo = par->matchinfo; |
104 | |
105 | if (fraginfo->invflags & ~IP6T_FRAG_INV_MASK) { |
106 | pr_debug("unknown flags %X\n" , fraginfo->invflags); |
107 | return -EINVAL; |
108 | } |
109 | return 0; |
110 | } |
111 | |
112 | static struct xt_match frag_mt6_reg __read_mostly = { |
113 | .name = "frag" , |
114 | .family = NFPROTO_IPV6, |
115 | .match = frag_mt6, |
116 | .matchsize = sizeof(struct ip6t_frag), |
117 | .checkentry = frag_mt6_check, |
118 | .me = THIS_MODULE, |
119 | }; |
120 | |
121 | static int __init frag_mt6_init(void) |
122 | { |
123 | return xt_register_match(target: &frag_mt6_reg); |
124 | } |
125 | |
126 | static void __exit frag_mt6_exit(void) |
127 | { |
128 | xt_unregister_match(target: &frag_mt6_reg); |
129 | } |
130 | |
131 | module_init(frag_mt6_init); |
132 | module_exit(frag_mt6_exit); |
133 | |