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