1/* $NetBSD: class.c,v 1.4 2022/04/03 01:10:59 christos Exp $ */ 2 3/* class.c 4 5 Handling for client classes. */ 6 7/* 8 * Copyright (C) 2004-2022 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 * PO Box 360 25 * Newmarket, NH 03857 USA 26 * <info@isc.org> 27 * https://www.isc.org/ 28 * 29 */ 30 31#include <sys/cdefs.h> 32__RCSID("$NetBSD: class.c,v 1.4 2022/04/03 01:10:59 christos Exp $"); 33 34#include "dhcpd.h" 35 36struct executable_statement *default_classification_rules; 37 38int have_billing_classes; 39 40/* Build the default classification rule tree. */ 41 42void 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 60void 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 67int 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 if (!class -> hash) 183 class_new_hash(&class->hash, 184 SCLASS_HASH_SIZE, MDL); 185 class_hash_add (class -> hash, 186 (const char *) 187 nc -> hash_string.data, 188 nc -> hash_string.len, 189 nc, MDL); 190 classify (packet, nc); 191 class_dereference (&nc, MDL); 192 } 193 194 data_string_forget (&data, MDL); 195 } 196 } 197 return matched; 198} 199 200void classify (packet, class) 201 struct packet *packet; 202 struct class *class; 203{ 204 if (packet -> class_count < PACKET_MAX_CLASSES) 205 class_reference (&packet -> classes [packet -> class_count++], 206 class, MDL); 207 else 208 log_error ("too many classes match %s", 209 print_hw_addr (packet -> raw -> htype, 210 packet -> raw -> hlen, 211 packet -> raw -> chaddr)); 212} 213 214 215isc_result_t unlink_class(struct class **class) { 216 struct collection *lp; 217 struct class *cp, *pp; 218 219 for (lp = collections; lp; lp = lp -> next) { 220 for (pp = 0, cp = lp -> classes; cp; pp = cp, cp = cp -> nic) 221 if (cp == *class) { 222 if (pp == 0) { 223 lp->classes = cp->nic; 224 } else { 225 pp->nic = cp->nic; 226 } 227 cp->nic = 0; 228 class_dereference(class, MDL); 229 230 return ISC_R_SUCCESS; 231 } 232 } 233 return ISC_R_NOTFOUND; 234} 235 236 237isc_result_t find_class (struct class **class, const char *name, 238 const char *file, int line) 239{ 240 struct collection *lp; 241 struct class *cp; 242 243 for (lp = collections; lp; lp = lp -> next) { 244 for (cp = lp -> classes; cp; cp = cp -> nic) 245 if (cp -> name && !strcmp (name, cp -> name)) { 246 return class_reference (class, cp, file, line); 247 } 248 } 249 return ISC_R_NOTFOUND; 250} 251 252/* Removes the billing class from a lease 253 * 254 * Note that because classes can be created and removed dynamically, it is 255 * possible that the class to which a lease was billed has since been deleted. 256 * To cover the case where the lease is the last reference to a deleted class 257 * we remove the lease reference from the class first, then the class from the 258 * lease. To protect ourselves from the reverse situation, where the class is 259 * the last reference to the lease (unlikely), we create a guard reference to 260 * the lease, then remove it at the end. 261 */ 262void unbill_class (lease) 263 struct lease *lease; 264{ 265 int i; 266 struct class* class = lease->billing_class; 267 struct lease* refholder = NULL; 268 269 /* if there's no billing to remove, nothing to do */ 270 if (class == NULL) { 271 return; 272 } 273 274 /* Find the lease in the list of the class's billed leases */ 275 for (i = 0; i < class->lease_limit; i++) { 276 if (class->billed_leases[i] == lease) 277 break; 278 } 279 280 /* Create guard reference, so class cannot be last reference to lease */ 281 lease_reference(&refholder, lease, MDL); 282 283 /* If the class doesn't have the lease, then something is broken 284 * programmatically. We'll log it but skip the lease dereference. */ 285 if (i == class->lease_limit) { 286 log_error ("lease %s unbilled with no billing arrangement.", 287 piaddr(lease->ip_addr)); 288 } else { 289 /* Remove the lease from the class */ 290 lease_dereference(&class->billed_leases[i], MDL); 291 class->leases_consumed--; 292 } 293 294 /* Remove the class from the lease */ 295 class_dereference(&lease->billing_class, MDL); 296 297 /* Ditch our guard reference */ 298 lease_dereference(&refholder, MDL); 299} 300 301int bill_class (lease, class) 302 struct lease *lease; 303 struct class *class; 304{ 305 int i; 306 307 if (lease -> billing_class) { 308 log_error ("lease billed with existing billing arrangement."); 309 unbill_class (lease); 310 } 311 312 if (class -> leases_consumed == class -> lease_limit) 313 return 0; 314 315 for (i = 0; i < class -> lease_limit; i++) 316 if (!class -> billed_leases [i]) 317 break; 318 319 if (i == class -> lease_limit) { 320 log_error ("class billing consumption disagrees with leases."); 321 return 0; 322 } 323 324 lease_reference (&class -> billed_leases [i], lease, MDL); 325 class_reference (&lease -> billing_class, class, MDL); 326 class -> leases_consumed++; 327 return 1; 328} 329