Merge branch 'stable/for-linus-5.15' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / net / netfilter / xt_ecn.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Xtables module for matching the value of the IPv4/IPv6 and TCP ECN bits
4  *
5  * (C) 2002 by Harald Welte <laforge@gnumonks.org>
6  * (C) 2011 Patrick McHardy <kaber@trash.net>
7  */
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9 #include <linux/in.h>
10 #include <linux/ip.h>
11 #include <net/ip.h>
12 #include <linux/module.h>
13 #include <linux/skbuff.h>
14 #include <linux/tcp.h>
15
16 #include <linux/netfilter/x_tables.h>
17 #include <linux/netfilter/xt_ecn.h>
18 #include <linux/netfilter_ipv4/ip_tables.h>
19 #include <linux/netfilter_ipv6/ip6_tables.h>
20
21 MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
22 MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match");
23 MODULE_LICENSE("GPL");
24 MODULE_ALIAS("ipt_ecn");
25 MODULE_ALIAS("ip6t_ecn");
26
27 static bool match_tcp(const struct sk_buff *skb, struct xt_action_param *par)
28 {
29         const struct xt_ecn_info *einfo = par->matchinfo;
30         struct tcphdr _tcph;
31         const struct tcphdr *th;
32
33         /* In practice, TCP match does this, so can't fail.  But let's
34          * be good citizens.
35          */
36         th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
37         if (th == NULL)
38                 return false;
39
40         if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
41                 if (einfo->invert & XT_ECN_OP_MATCH_ECE) {
42                         if (th->ece == 1)
43                                 return false;
44                 } else {
45                         if (th->ece == 0)
46                                 return false;
47                 }
48         }
49
50         if (einfo->operation & XT_ECN_OP_MATCH_CWR) {
51                 if (einfo->invert & XT_ECN_OP_MATCH_CWR) {
52                         if (th->cwr == 1)
53                                 return false;
54                 } else {
55                         if (th->cwr == 0)
56                                 return false;
57                 }
58         }
59
60         return true;
61 }
62
63 static inline bool match_ip(const struct sk_buff *skb,
64                             const struct xt_ecn_info *einfo)
65 {
66         return ((ip_hdr(skb)->tos & XT_ECN_IP_MASK) == einfo->ip_ect) ^
67                !!(einfo->invert & XT_ECN_OP_MATCH_IP);
68 }
69
70 static bool ecn_mt4(const struct sk_buff *skb, struct xt_action_param *par)
71 {
72         const struct xt_ecn_info *info = par->matchinfo;
73
74         if (info->operation & XT_ECN_OP_MATCH_IP && !match_ip(skb, info))
75                 return false;
76
77         if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
78             !match_tcp(skb, par))
79                 return false;
80
81         return true;
82 }
83
84 static int ecn_mt_check4(const struct xt_mtchk_param *par)
85 {
86         const struct xt_ecn_info *info = par->matchinfo;
87         const struct ipt_ip *ip = par->entryinfo;
88
89         if (info->operation & XT_ECN_OP_MATCH_MASK)
90                 return -EINVAL;
91
92         if (info->invert & XT_ECN_OP_MATCH_MASK)
93                 return -EINVAL;
94
95         if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
96             (ip->proto != IPPROTO_TCP || ip->invflags & IPT_INV_PROTO)) {
97                 pr_info_ratelimited("cannot match TCP bits for non-tcp packets\n");
98                 return -EINVAL;
99         }
100
101         return 0;
102 }
103
104 static inline bool match_ipv6(const struct sk_buff *skb,
105                               const struct xt_ecn_info *einfo)
106 {
107         return (((ipv6_hdr(skb)->flow_lbl[0] >> 4) & XT_ECN_IP_MASK) ==
108                 einfo->ip_ect) ^
109                !!(einfo->invert & XT_ECN_OP_MATCH_IP);
110 }
111
112 static bool ecn_mt6(const struct sk_buff *skb, struct xt_action_param *par)
113 {
114         const struct xt_ecn_info *info = par->matchinfo;
115
116         if (info->operation & XT_ECN_OP_MATCH_IP && !match_ipv6(skb, info))
117                 return false;
118
119         if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
120             !match_tcp(skb, par))
121                 return false;
122
123         return true;
124 }
125
126 static int ecn_mt_check6(const struct xt_mtchk_param *par)
127 {
128         const struct xt_ecn_info *info = par->matchinfo;
129         const struct ip6t_ip6 *ip = par->entryinfo;
130
131         if (info->operation & XT_ECN_OP_MATCH_MASK)
132                 return -EINVAL;
133
134         if (info->invert & XT_ECN_OP_MATCH_MASK)
135                 return -EINVAL;
136
137         if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
138             (ip->proto != IPPROTO_TCP || ip->invflags & IP6T_INV_PROTO)) {
139                 pr_info_ratelimited("cannot match TCP bits for non-tcp packets\n");
140                 return -EINVAL;
141         }
142
143         return 0;
144 }
145
146 static struct xt_match ecn_mt_reg[] __read_mostly = {
147         {
148                 .name           = "ecn",
149                 .family         = NFPROTO_IPV4,
150                 .match          = ecn_mt4,
151                 .matchsize      = sizeof(struct xt_ecn_info),
152                 .checkentry     = ecn_mt_check4,
153                 .me             = THIS_MODULE,
154         },
155         {
156                 .name           = "ecn",
157                 .family         = NFPROTO_IPV6,
158                 .match          = ecn_mt6,
159                 .matchsize      = sizeof(struct xt_ecn_info),
160                 .checkentry     = ecn_mt_check6,
161                 .me             = THIS_MODULE,
162         },
163 };
164
165 static int __init ecn_mt_init(void)
166 {
167         return xt_register_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
168 }
169
170 static void __exit ecn_mt_exit(void)
171 {
172         xt_unregister_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
173 }
174
175 module_init(ecn_mt_init);
176 module_exit(ecn_mt_exit);