1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* x_tables module for setting the IPv4/IPv6 DSCP field, Version 1.8 |
3 | * |
4 | * (C) 2002 by Harald Welte <laforge@netfilter.org> |
5 | * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh <mgm@paktronix.com> |
6 | * |
7 | * See RFC2474 for a description of the DSCP field within the IP Header. |
8 | */ |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | #include <linux/module.h> |
11 | #include <linux/skbuff.h> |
12 | #include <linux/ip.h> |
13 | #include <linux/ipv6.h> |
14 | #include <net/dsfield.h> |
15 | |
16 | #include <linux/netfilter/x_tables.h> |
17 | #include <linux/netfilter/xt_DSCP.h> |
18 | |
19 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>" ); |
20 | MODULE_DESCRIPTION("Xtables: DSCP/TOS field modification" ); |
21 | MODULE_LICENSE("GPL" ); |
22 | MODULE_ALIAS("ipt_DSCP" ); |
23 | MODULE_ALIAS("ip6t_DSCP" ); |
24 | MODULE_ALIAS("ipt_TOS" ); |
25 | MODULE_ALIAS("ip6t_TOS" ); |
26 | |
27 | #define XT_DSCP_ECN_MASK 3u |
28 | |
29 | static unsigned int |
30 | dscp_tg(struct sk_buff *skb, const struct xt_action_param *par) |
31 | { |
32 | const struct xt_DSCP_info *dinfo = par->targinfo; |
33 | u_int8_t dscp = ipv4_get_dsfield(iph: ip_hdr(skb)) >> XT_DSCP_SHIFT; |
34 | |
35 | if (dscp != dinfo->dscp) { |
36 | if (skb_ensure_writable(skb, write_len: sizeof(struct iphdr))) |
37 | return NF_DROP; |
38 | |
39 | ipv4_change_dsfield(iph: ip_hdr(skb), XT_DSCP_ECN_MASK, |
40 | value: dinfo->dscp << XT_DSCP_SHIFT); |
41 | |
42 | } |
43 | return XT_CONTINUE; |
44 | } |
45 | |
46 | static unsigned int |
47 | dscp_tg6(struct sk_buff *skb, const struct xt_action_param *par) |
48 | { |
49 | const struct xt_DSCP_info *dinfo = par->targinfo; |
50 | u_int8_t dscp = ipv6_get_dsfield(ipv6h: ipv6_hdr(skb)) >> XT_DSCP_SHIFT; |
51 | |
52 | if (dscp != dinfo->dscp) { |
53 | if (skb_ensure_writable(skb, write_len: sizeof(struct ipv6hdr))) |
54 | return NF_DROP; |
55 | |
56 | ipv6_change_dsfield(ipv6h: ipv6_hdr(skb), XT_DSCP_ECN_MASK, |
57 | value: dinfo->dscp << XT_DSCP_SHIFT); |
58 | } |
59 | return XT_CONTINUE; |
60 | } |
61 | |
62 | static int dscp_tg_check(const struct xt_tgchk_param *par) |
63 | { |
64 | const struct xt_DSCP_info *info = par->targinfo; |
65 | |
66 | if (info->dscp > XT_DSCP_MAX) |
67 | return -EDOM; |
68 | return 0; |
69 | } |
70 | |
71 | static unsigned int |
72 | tos_tg(struct sk_buff *skb, const struct xt_action_param *par) |
73 | { |
74 | const struct xt_tos_target_info *info = par->targinfo; |
75 | struct iphdr *iph = ip_hdr(skb); |
76 | u_int8_t orig, nv; |
77 | |
78 | orig = ipv4_get_dsfield(iph); |
79 | nv = (orig & ~info->tos_mask) ^ info->tos_value; |
80 | |
81 | if (orig != nv) { |
82 | if (skb_ensure_writable(skb, write_len: sizeof(struct iphdr))) |
83 | return NF_DROP; |
84 | iph = ip_hdr(skb); |
85 | ipv4_change_dsfield(iph, mask: 0, value: nv); |
86 | } |
87 | |
88 | return XT_CONTINUE; |
89 | } |
90 | |
91 | static unsigned int |
92 | tos_tg6(struct sk_buff *skb, const struct xt_action_param *par) |
93 | { |
94 | const struct xt_tos_target_info *info = par->targinfo; |
95 | struct ipv6hdr *iph = ipv6_hdr(skb); |
96 | u_int8_t orig, nv; |
97 | |
98 | orig = ipv6_get_dsfield(ipv6h: iph); |
99 | nv = (orig & ~info->tos_mask) ^ info->tos_value; |
100 | |
101 | if (orig != nv) { |
102 | if (skb_ensure_writable(skb, write_len: sizeof(struct iphdr))) |
103 | return NF_DROP; |
104 | iph = ipv6_hdr(skb); |
105 | ipv6_change_dsfield(ipv6h: iph, mask: 0, value: nv); |
106 | } |
107 | |
108 | return XT_CONTINUE; |
109 | } |
110 | |
111 | static struct xt_target dscp_tg_reg[] __read_mostly = { |
112 | { |
113 | .name = "DSCP" , |
114 | .family = NFPROTO_IPV4, |
115 | .checkentry = dscp_tg_check, |
116 | .target = dscp_tg, |
117 | .targetsize = sizeof(struct xt_DSCP_info), |
118 | .table = "mangle" , |
119 | .me = THIS_MODULE, |
120 | }, |
121 | { |
122 | .name = "DSCP" , |
123 | .family = NFPROTO_IPV6, |
124 | .checkentry = dscp_tg_check, |
125 | .target = dscp_tg6, |
126 | .targetsize = sizeof(struct xt_DSCP_info), |
127 | .table = "mangle" , |
128 | .me = THIS_MODULE, |
129 | }, |
130 | { |
131 | .name = "TOS" , |
132 | .revision = 1, |
133 | .family = NFPROTO_IPV4, |
134 | .table = "mangle" , |
135 | .target = tos_tg, |
136 | .targetsize = sizeof(struct xt_tos_target_info), |
137 | .me = THIS_MODULE, |
138 | }, |
139 | { |
140 | .name = "TOS" , |
141 | .revision = 1, |
142 | .family = NFPROTO_IPV6, |
143 | .table = "mangle" , |
144 | .target = tos_tg6, |
145 | .targetsize = sizeof(struct xt_tos_target_info), |
146 | .me = THIS_MODULE, |
147 | }, |
148 | }; |
149 | |
150 | static int __init dscp_tg_init(void) |
151 | { |
152 | return xt_register_targets(target: dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); |
153 | } |
154 | |
155 | static void __exit dscp_tg_exit(void) |
156 | { |
157 | xt_unregister_targets(target: dscp_tg_reg, ARRAY_SIZE(dscp_tg_reg)); |
158 | } |
159 | |
160 | module_init(dscp_tg_init); |
161 | module_exit(dscp_tg_exit); |
162 | |