Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* Kernel module to match FRAG parameters. */ |
3 | ||
4 | /* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu> | |
1da177e4 | 5 | */ |
ff67e4e4 | 6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
1da177e4 LT |
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 | ||
6709dbbb | 14 | #include <linux/netfilter/x_tables.h> |
1da177e4 LT |
15 | #include <linux/netfilter_ipv6/ip6_tables.h> |
16 | #include <linux/netfilter_ipv6/ip6t_frag.h> | |
17 | ||
18 | MODULE_LICENSE("GPL"); | |
2ae15b64 | 19 | MODULE_DESCRIPTION("Xtables: IPv6 fragment match"); |
1da177e4 LT |
20 | MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); |
21 | ||
1da177e4 | 22 | /* Returns 1 if the id is matched by the range, 0 otherwise */ |
1d93a9cb JE |
23 | static inline bool |
24 | id_match(u_int32_t min, u_int32_t max, u_int32_t id, bool invert) | |
1da177e4 | 25 | { |
1d93a9cb | 26 | bool r; |
ff67e4e4 | 27 | pr_debug("id_match:%c 0x%x <= 0x%x <= 0x%x\n", invert ? '!' : ' ', |
0d53778e | 28 | min, id, max); |
f0daaa65 | 29 | r = (id >= min && id <= max) ^ invert; |
0d53778e | 30 | pr_debug(" result %s\n", r ? "PASS" : "FAILED"); |
f0daaa65 | 31 | return r; |
1da177e4 LT |
32 | } |
33 | ||
1d93a9cb | 34 | static bool |
62fc8051 | 35 | frag_mt6(const struct sk_buff *skb, struct xt_action_param *par) |
1da177e4 | 36 | { |
a47362a2 JE |
37 | struct frag_hdr _frag; |
38 | const struct frag_hdr *fh; | |
f7108a20 | 39 | const struct ip6t_frag *fraginfo = par->matchinfo; |
84018f55 | 40 | unsigned int ptr = 0; |
6d381634 | 41 | int err; |
1da177e4 | 42 | |
84018f55 | 43 | err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL, NULL); |
6d381634 PM |
44 | if (err < 0) { |
45 | if (err != -ENOENT) | |
b4ba2611 | 46 | par->hotdrop = true; |
1d93a9cb | 47 | return false; |
6d381634 | 48 | } |
1da177e4 | 49 | |
e674d0f3 | 50 | fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag); |
f0daaa65 | 51 | if (fh == NULL) { |
b4ba2611 | 52 | par->hotdrop = true; |
1d93a9cb | 53 | return false; |
e674d0f3 | 54 | } |
1da177e4 | 55 | |
0d53778e PM |
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, | |
3666ed1c JP |
70 | !((fraginfo->flags & IP6T_FRAG_RES) && |
71 | (fh->reserved || (ntohs(fh->frag_off) & 0x06)))); | |
0d53778e PM |
72 | pr_debug("first %02X %02X %02X ", |
73 | fraginfo->flags & IP6T_FRAG_FST, | |
74 | ntohs(fh->frag_off) & ~0x7, | |
3666ed1c JP |
75 | !((fraginfo->flags & IP6T_FRAG_FST) && |
76 | (ntohs(fh->frag_off) & ~0x7))); | |
0d53778e PM |
77 | pr_debug("mf %02X %02X %02X ", |
78 | fraginfo->flags & IP6T_FRAG_MF, | |
79 | ntohs(fh->frag_off) & IP6_MF, | |
3666ed1c JP |
80 | !((fraginfo->flags & IP6T_FRAG_MF) && |
81 | !((ntohs(fh->frag_off) & IP6_MF)))); | |
0d53778e PM |
82 | pr_debug("last %02X %02X %02X\n", |
83 | fraginfo->flags & IP6T_FRAG_NMF, | |
84 | ntohs(fh->frag_off) & IP6_MF, | |
3666ed1c JP |
85 | !((fraginfo->flags & IP6T_FRAG_NMF) && |
86 | (ntohs(fh->frag_off) & IP6_MF))); | |
87 | ||
88 | return (fh != NULL) && | |
89 | id_match(fraginfo->ids[0], fraginfo->ids[1], | |
90 | ntohl(fh->identification), | |
91 | !!(fraginfo->invflags & IP6T_FRAG_INV_IDS)) && | |
92 | !((fraginfo->flags & IP6T_FRAG_RES) && | |
93 | (fh->reserved || (ntohs(fh->frag_off) & 0x6))) && | |
94 | !((fraginfo->flags & IP6T_FRAG_FST) && | |
95 | (ntohs(fh->frag_off) & ~0x7)) && | |
96 | !((fraginfo->flags & IP6T_FRAG_MF) && | |
97 | !(ntohs(fh->frag_off) & IP6_MF)) && | |
98 | !((fraginfo->flags & IP6T_FRAG_NMF) && | |
99 | (ntohs(fh->frag_off) & IP6_MF)); | |
1da177e4 LT |
100 | } |
101 | ||
b0f38452 | 102 | static int frag_mt6_check(const struct xt_mtchk_param *par) |
1da177e4 | 103 | { |
9b4fce7a | 104 | const struct ip6t_frag *fraginfo = par->matchinfo; |
f0daaa65 | 105 | |
f0daaa65 | 106 | if (fraginfo->invflags & ~IP6T_FRAG_INV_MASK) { |
ff67e4e4 | 107 | pr_debug("unknown flags %X\n", fraginfo->invflags); |
bd414ee6 | 108 | return -EINVAL; |
f0daaa65 | 109 | } |
bd414ee6 | 110 | return 0; |
1da177e4 LT |
111 | } |
112 | ||
d3c5ee6d | 113 | static struct xt_match frag_mt6_reg __read_mostly = { |
1da177e4 | 114 | .name = "frag", |
ee999d8b | 115 | .family = NFPROTO_IPV6, |
d3c5ee6d | 116 | .match = frag_mt6, |
7f939713 | 117 | .matchsize = sizeof(struct ip6t_frag), |
d3c5ee6d | 118 | .checkentry = frag_mt6_check, |
1da177e4 LT |
119 | .me = THIS_MODULE, |
120 | }; | |
121 | ||
d3c5ee6d | 122 | static int __init frag_mt6_init(void) |
1da177e4 | 123 | { |
d3c5ee6d | 124 | return xt_register_match(&frag_mt6_reg); |
1da177e4 LT |
125 | } |
126 | ||
d3c5ee6d | 127 | static void __exit frag_mt6_exit(void) |
1da177e4 | 128 | { |
d3c5ee6d | 129 | xt_unregister_match(&frag_mt6_reg); |
1da177e4 LT |
130 | } |
131 | ||
d3c5ee6d JE |
132 | module_init(frag_mt6_init); |
133 | module_exit(frag_mt6_exit); |