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 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 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 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 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 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 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 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