1 /* $NetBSD: class.c,v 1.2 2018/04/07 22:37:30 christos Exp $ */ 2 3 /* class.c 4 5 Handling for client classes. */ 6 7 /* 8 * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") 9 * Copyright (c) 1998-2003 by Internet Software Consortium 10 * 11 * This Source Code Form is subject to the terms of the Mozilla Public 12 * License, v. 2.0. If a copy of the MPL was not distributed with this 13 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 * 23 * Internet Systems Consortium, Inc. 24 * 950 Charter Street 25 * Redwood City, CA 94063 26 * <info@isc.org> 27 * https://www.isc.org/ 28 * 29 */ 30 31 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: class.c,v 1.2 2018/04/07 22:37:30 christos Exp $"); 33 34 #include "dhcpd.h" 35 36 struct executable_statement *default_classification_rules; 37 38 int have_billing_classes; 39 40 /* Build the default classification rule tree. */ 41 42 void classification_setup () 43 { 44 /* eval ... */ 45 default_classification_rules = (struct executable_statement *)0; 46 if (!executable_statement_allocate (&default_classification_rules, 47 MDL)) 48 log_fatal ("Can't allocate check of default collection"); 49 default_classification_rules -> op = eval_statement; 50 51 /* check-collection "default" */ 52 if (!expression_allocate (&default_classification_rules -> data.eval, 53 MDL)) 54 log_fatal ("Can't allocate default check expression"); 55 default_classification_rules -> data.eval -> op = expr_check; 56 default_classification_rules -> data.eval -> data.check = 57 &default_collection; 58 } 59 60 void classify_client (packet) 61 struct packet *packet; 62 { 63 execute_statements (NULL, packet, NULL, NULL, packet->options, NULL, 64 &global_scope, default_classification_rules, NULL); 65 } 66 67 int check_collection (packet, lease, collection) 68 struct packet *packet; 69 struct lease *lease; 70 struct collection *collection; 71 { 72 struct class *class, *nc; 73 struct data_string data; 74 int matched = 0; 75 int status; 76 int ignorep; 77 int classfound; 78 79 for (class = collection -> classes; class; class = class -> nic) { 80 #if defined (DEBUG_CLASS_MATCHING) 81 log_info ("checking against class %s...", class -> name); 82 #endif 83 memset (&data, 0, sizeof data); 84 85 /* If there is a "match if" expression, check it. If 86 we get a match, and there's no subclass expression, 87 it's a match. If we get a match and there is a subclass 88 expression, then we check the submatch. If it's not a 89 match, that's final - we don't check the submatch. */ 90 91 if (class -> expr) { 92 status = (evaluate_boolean_expression_result 93 (&ignorep, packet, lease, 94 (struct client_state *)0, 95 packet -> options, (struct option_state *)0, 96 lease ? &lease -> scope : &global_scope, 97 class -> expr)); 98 if (status) { 99 if (!class -> submatch) { 100 matched = 1; 101 #if defined (DEBUG_CLASS_MATCHING) 102 log_info ("matches class."); 103 #endif 104 classify (packet, class); 105 continue; 106 } 107 } else 108 continue; 109 } 110 111 /* Check to see if the client matches an existing subclass. 112 If it doesn't, and this is a spawning class, spawn a new 113 subclass and put the client in it. */ 114 if (class -> submatch) { 115 status = (evaluate_data_expression 116 (&data, packet, lease, 117 (struct client_state *)0, 118 packet -> options, (struct option_state *)0, 119 lease ? &lease -> scope : &global_scope, 120 class -> submatch, MDL)); 121 if (status && data.len) { 122 nc = (struct class *)0; 123 classfound = class_hash_lookup (&nc, class -> hash, 124 (const char *)data.data, data.len, MDL); 125 126 #ifdef LDAP_CONFIGURATION 127 if (!classfound && find_subclass_in_ldap (class, &nc, &data)) 128 classfound = 1; 129 #endif 130 131 if (classfound) { 132 #if defined (DEBUG_CLASS_MATCHING) 133 log_info ("matches subclass %s.", 134 print_hex_1 (data.len, 135 data.data, 60)); 136 #endif 137 data_string_forget (&data, MDL); 138 classify (packet, nc); 139 matched = 1; 140 class_dereference (&nc, MDL); 141 continue; 142 } 143 if (!class -> spawning) { 144 data_string_forget (&data, MDL); 145 continue; 146 } 147 /* XXX Write out the spawned class? */ 148 #if defined (DEBUG_CLASS_MATCHING) 149 log_info ("spawning subclass %s.", 150 print_hex_1 (data.len, data.data, 60)); 151 #endif 152 status = class_allocate (&nc, MDL); 153 group_reference (&nc -> group, 154 class -> group, MDL); 155 class_reference (&nc -> superclass, 156 class, MDL); 157 nc -> lease_limit = class -> lease_limit; 158 nc -> dirty = 1; 159 if (nc -> lease_limit) { 160 nc -> billed_leases = 161 (dmalloc 162 (nc -> lease_limit * 163 sizeof (struct lease *), 164 MDL)); 165 if (!nc -> billed_leases) { 166 log_error ("no memory for%s", 167 " billing"); 168 data_string_forget 169 (&nc -> hash_string, 170 MDL); 171 class_dereference (&nc, MDL); 172 data_string_forget (&data, 173 MDL); 174 continue; 175 } 176 memset (nc -> billed_leases, 0, 177 (nc -> lease_limit * 178 sizeof (struct lease *))); 179 } 180 data_string_copy (&nc -> hash_string, &data, 181 MDL); 182 data_string_forget (&data, MDL); 183 if (!class -> hash) 184 class_new_hash(&class->hash, 185 SCLASS_HASH_SIZE, MDL); 186 class_hash_add (class -> hash, 187 (const char *) 188 nc -> hash_string.data, 189 nc -> hash_string.len, 190 nc, MDL); 191 classify (packet, nc); 192 class_dereference (&nc, MDL); 193 } 194 } 195 } 196 return matched; 197 } 198 199 void classify (packet, class) 200 struct packet *packet; 201 struct class *class; 202 { 203 if (packet -> class_count < PACKET_MAX_CLASSES) 204 class_reference (&packet -> classes [packet -> class_count++], 205 class, MDL); 206 else 207 log_error ("too many classes match %s", 208 print_hw_addr (packet -> raw -> htype, 209 packet -> raw -> hlen, 210 packet -> raw -> chaddr)); 211 } 212 213 214 isc_result_t unlink_class(struct class **class) { 215 struct collection *lp; 216 struct class *cp, *pp; 217 218 for (lp = collections; lp; lp = lp -> next) { 219 for (pp = 0, cp = lp -> classes; cp; pp = cp, cp = cp -> nic) 220 if (cp == *class) { 221 if (pp == 0) { 222 lp->classes = cp->nic; 223 } else { 224 pp->nic = cp->nic; 225 } 226 cp->nic = 0; 227 class_dereference(class, MDL); 228 229 return ISC_R_SUCCESS; 230 } 231 } 232 return ISC_R_NOTFOUND; 233 } 234 235 236 isc_result_t find_class (struct class **class, const char *name, 237 const char *file, int line) 238 { 239 struct collection *lp; 240 struct class *cp; 241 242 for (lp = collections; lp; lp = lp -> next) { 243 for (cp = lp -> classes; cp; cp = cp -> nic) 244 if (cp -> name && !strcmp (name, cp -> name)) { 245 return class_reference (class, cp, file, line); 246 } 247 } 248 return ISC_R_NOTFOUND; 249 } 250 251 /* Removes the billing class from a lease 252 * 253 * Note that because classes can be created and removed dynamically, it is 254 * possible that the class to which a lease was billed has since been deleted. 255 * To cover the case where the lease is the last reference to a deleted class 256 * we remove the lease reference from the class first, then the class from the 257 * lease. To protect ourselves from the reverse situation, where the class is 258 * the last reference to the lease (unlikely), we create a guard reference to 259 * the lease, then remove it at the end. 260 */ 261 void unbill_class (lease) 262 struct lease *lease; 263 { 264 int i; 265 struct class* class = lease->billing_class; 266 struct lease* refholder = NULL; 267 268 /* if there's no billing to remove, nothing to do */ 269 if (class == NULL) { 270 return; 271 } 272 273 /* Find the lease in the list of the class's billed leases */ 274 for (i = 0; i < class->lease_limit; i++) { 275 if (class->billed_leases[i] == lease) 276 break; 277 } 278 279 /* Create guard reference, so class cannot be last reference to lease */ 280 lease_reference(&refholder, lease, MDL); 281 282 /* If the class doesn't have the lease, then something is broken 283 * programmatically. We'll log it but skip the lease dereference. */ 284 if (i == class->lease_limit) { 285 log_error ("lease %s unbilled with no billing arrangement.", 286 piaddr(lease->ip_addr)); 287 } else { 288 /* Remove the lease from the class */ 289 lease_dereference(&class->billed_leases[i], MDL); 290 class->leases_consumed--; 291 } 292 293 /* Remove the class from the lease */ 294 class_dereference(&lease->billing_class, MDL); 295 296 /* Ditch our guard reference */ 297 lease_dereference(&refholder, MDL); 298 } 299 300 int bill_class (lease, class) 301 struct lease *lease; 302 struct class *class; 303 { 304 int i; 305 306 if (lease -> billing_class) { 307 log_error ("lease billed with existing billing arrangement."); 308 unbill_class (lease); 309 } 310 311 if (class -> leases_consumed == class -> lease_limit) 312 return 0; 313 314 for (i = 0; i < class -> lease_limit; i++) 315 if (!class -> billed_leases [i]) 316 break; 317 318 if (i == class -> lease_limit) { 319 log_error ("class billing consumption disagrees with leases."); 320 return 0; 321 } 322 323 lease_reference (&class -> billed_leases [i], lease, MDL); 324 class_reference (&lease -> billing_class, class, MDL); 325 class -> leases_consumed++; 326 return 1; 327 } 328