1 /*-
2 * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #ifdef _KERNEL
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: npf_ext_normalize.c,v 1.11 2021/03/08 20:01:54 christos Exp $");
30
31 #include <sys/types.h>
32 #include <sys/module.h>
33 #include <sys/kmem.h>
34
35 #include <net/if.h>
36 #include <netinet/in_systm.h>
37 #include <netinet/in.h>
38 #include <netinet/in_var.h>
39 #endif
40
41 #include "npf.h"
42 #include "npf_impl.h"
43
44 /*
45 * NPF extension module definition and the identifier.
46 */
47 NPF_EXT_MODULE(npf_ext_normalize, "");
48
49 #define NPFEXT_NORMALIZE_VER 1
50
51 static void * npf_ext_normalize_id;
52
53 /*
54 * Normalisation parameters.
55 */
56 typedef struct {
57 unsigned n_minttl;
58 unsigned n_maxmss;
59 bool n_random_id;
60 bool n_no_df;
61 } npf_normalize_t;
62
63 /*
64 * npf_normalize_ctor: a constructor for the normalisation rule procedure
65 * with the given parameters.
66 */
67 static int
npf_normalize_ctor(npf_rproc_t * rp,const nvlist_t * params)68 npf_normalize_ctor(npf_rproc_t *rp, const nvlist_t *params)
69 {
70 npf_normalize_t *np;
71
72 /* Create a structure for normalisation parameters. */
73 np = kmem_zalloc(sizeof(npf_normalize_t), KM_SLEEP);
74
75 /* IP ID randomisation and IP_DF flag cleansing. */
76 np->n_random_id = dnvlist_get_bool(params, "random-id", false);
77 np->n_no_df = dnvlist_get_bool(params, "no-df", false);
78
79 /* Minimum IP TTL and maximum TCP MSS. */
80 np->n_minttl = dnvlist_get_number(params, "min-ttl", 0);
81 np->n_maxmss = dnvlist_get_number(params, "max-mss", 0);
82
83 /* Assign the parameters for this rule procedure. */
84 npf_rproc_assign(rp, np);
85 return 0;
86 }
87
88 /*
89 * npf_normalize_dtor: a destructor for a normalisation rule procedure.
90 */
91 static void
npf_normalize_dtor(npf_rproc_t * rp,void * params)92 npf_normalize_dtor(npf_rproc_t *rp, void *params)
93 {
94 /* Free our meta-data, associated with the procedure. */
95 kmem_free(params, sizeof(npf_normalize_t));
96 }
97
98 /*
99 * npf_normalize_ip4: routine to normalize IPv4 header (randomize ID,
100 * clear "don't fragment" and/or enforce minimum TTL).
101 */
102 static inline void
npf_normalize_ip4(npf_cache_t * npc,npf_normalize_t * np)103 npf_normalize_ip4(npf_cache_t *npc, npf_normalize_t *np)
104 {
105 struct ip *ip = npc->npc_ip.v4;
106 uint16_t cksum = ip->ip_sum;
107 uint16_t ip_off = ip->ip_off;
108 uint8_t ttl = ip->ip_ttl;
109 unsigned minttl = np->n_minttl;
110
111 KASSERT(np->n_random_id || np->n_no_df || minttl);
112
113 /* Randomize IPv4 ID. */
114 if (np->n_random_id) {
115 uint16_t oid = ip->ip_id, nid;
116
117 nid = ip_randomid();
118 cksum = npf_fixup16_cksum(cksum, oid, nid);
119 ip->ip_id = nid;
120 }
121
122 /* IP_DF flag cleansing. */
123 if (np->n_no_df && (ip_off & htons(IP_DF)) != 0) {
124 uint16_t nip_off = ip_off & ~htons(IP_DF);
125
126 cksum = npf_fixup16_cksum(cksum, ip_off, nip_off);
127 ip->ip_off = nip_off;
128 }
129
130 /* Enforce minimum TTL. */
131 if (minttl && ttl < minttl) {
132 cksum = npf_fixup16_cksum(cksum, ttl, minttl);
133 ip->ip_ttl = minttl;
134 }
135
136 /* Update IPv4 checksum. */
137 ip->ip_sum = cksum;
138 }
139
140 /*
141 * npf_normalize: the main routine to normalize IPv4 and/or TCP headers.
142 */
143 static bool
npf_normalize(npf_cache_t * npc,void * params,const npf_match_info_t * mi,int * decision)144 npf_normalize(npf_cache_t *npc, void *params, const npf_match_info_t *mi,
145 int *decision)
146 {
147 npf_normalize_t *np = params;
148 uint16_t cksum, mss, maxmss = np->n_maxmss;
149 uint16_t old[2], new[2];
150 struct tcphdr *th;
151 int wscale;
152 bool mid;
153
154 /* Skip, if already blocking. */
155 if (*decision == NPF_DECISION_BLOCK) {
156 return true;
157 }
158
159 /* Normalize IPv4. Nothing to do for IPv6. */
160 if (npf_iscached(npc, NPC_IP4) && (np->n_random_id || np->n_minttl)) {
161 npf_normalize_ip4(npc, np);
162 }
163 th = npc->npc_l4.tcp;
164
165 /*
166 * TCP Maximum Segment Size (MSS) "clamping". Only if SYN packet.
167 * Fetch MSS and check whether rewrite to lower is needed.
168 */
169 if (maxmss == 0 || !npf_iscached(npc, NPC_TCP) ||
170 (th->th_flags & TH_SYN) == 0) {
171 /* Not required; done. */
172 return true;
173 }
174 mss = 0;
175 if (!npf_fetch_tcpopts(npc, &mss, &wscale)) {
176 return true;
177 }
178 if (ntohs(mss) <= maxmss) {
179 /* Nothing else to do. */
180 return true;
181 }
182 maxmss = htons(maxmss);
183
184 /*
185 * Store new MSS, calculate TCP checksum and update it. The MSS may
186 * not be aligned and fall in the middle of two uint16_t's, so we
187 * need to take care of that when calculating the checksum.
188 *
189 * WARNING: must re-fetch the TCP header after the modification.
190 */
191 if (npf_set_mss(npc, maxmss, old, new, &mid) &&
192 !nbuf_cksum_barrier(npc->npc_nbuf, mi->mi_di)) {
193 th = npc->npc_l4.tcp;
194 if (mid) {
195 cksum = th->th_sum;
196 cksum = npf_fixup16_cksum(cksum, old[0], new[0]);
197 cksum = npf_fixup16_cksum(cksum, old[1], new[1]);
198 } else {
199 cksum = npf_fixup16_cksum(th->th_sum, mss, maxmss);
200 }
201 th->th_sum = cksum;
202 }
203
204 return true;
205 }
206
207 __dso_public int
npf_ext_normalize_init(npf_t * npf)208 npf_ext_normalize_init(npf_t *npf)
209 {
210 static const npf_ext_ops_t npf_normalize_ops = {
211 .version = NPFEXT_NORMALIZE_VER,
212 .ctx = NULL,
213 .ctor = npf_normalize_ctor,
214 .dtor = npf_normalize_dtor,
215 .proc = npf_normalize
216 };
217 npf_ext_normalize_id = npf_ext_register(npf,
218 "normalize", &npf_normalize_ops);
219 return npf_ext_normalize_id ? 0 : EEXIST;
220 }
221
222 __dso_public int
npf_ext_normalize_fini(npf_t * npf)223 npf_ext_normalize_fini(npf_t *npf)
224 {
225 return npf_ext_unregister(npf, npf_ext_normalize_id);
226 }
227
228 #ifdef _KERNEL
229 static int
npf_ext_normalize_modcmd(modcmd_t cmd,void * arg)230 npf_ext_normalize_modcmd(modcmd_t cmd, void *arg)
231 {
232 npf_t *npf = npf_getkernctx();
233
234 switch (cmd) {
235 case MODULE_CMD_INIT:
236 return npf_ext_normalize_init(npf);
237 case MODULE_CMD_FINI:
238 return npf_ext_unregister(npf, npf_ext_normalize_id);
239 case MODULE_CMD_AUTOUNLOAD:
240 return npf_autounload_p() ? 0 : EBUSY;
241 default:
242 return ENOTTY;
243 }
244 return 0;
245 }
246 #endif
247