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