1234353Sdim/* $NetBSD: class.c,v 1.4 2022/04/03 01:10:59 christos Exp $ */ 2218885Sdim 3218885Sdim/* class.c 4218885Sdim 5218885Sdim Handling for client classes. */ 6218885Sdim 7218885Sdim/* 8218885Sdim * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC") 9218885Sdim * Copyright (c) 1998-2003 by Internet Software Consortium 10218885Sdim * 11218885Sdim * This Source Code Form is subject to the terms of the Mozilla Public 12218885Sdim * License, v. 2.0. If a copy of the MPL was not distributed with this 13218885Sdim * file, You can obtain one at http://mozilla.org/MPL/2.0/. 14218885Sdim * 15218885Sdim * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 16218885Sdim * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17218885Sdim * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 18218885Sdim * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19218885Sdim * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20218885Sdim * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 21218885Sdim * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22218885Sdim * 23243830Sdim * Internet Systems Consortium, Inc. 24218885Sdim * PO Box 360 25218885Sdim * Newmarket, NH 03857 USA 26218885Sdim * <info@isc.org> 27218885Sdim * https://www.isc.org/ 28218885Sdim * 29218885Sdim */ 30218885Sdim 31218885Sdim#include <sys/cdefs.h> 32234353Sdim__RCSID("$NetBSD: class.c,v 1.4 2022/04/03 01:10:59 christos Exp $"); 33218885Sdim 34218885Sdim#include "dhcpd.h" 35218885Sdim 36218885Sdimstruct executable_statement *default_classification_rules; 37218885Sdim 38218885Sdimint have_billing_classes; 39218885Sdim 40218885Sdim/* Build the default classification rule tree. */ 41218885Sdim 42218885Sdimvoid classification_setup () 43218885Sdim{ 44218885Sdim /* eval ... */ 45218885Sdim default_classification_rules = (struct executable_statement *)0; 46218885Sdim if (!executable_statement_allocate (&default_classification_rules, 47218885Sdim MDL)) 48218885Sdim log_fatal ("Can't allocate check of default collection"); 49218885Sdim default_classification_rules -> op = eval_statement; 50218885Sdim 51218885Sdim /* check-collection "default" */ 52218885Sdim if (!expression_allocate (&default_classification_rules -> data.eval, 53218885Sdim MDL)) 54218885Sdim log_fatal ("Can't allocate default check expression"); 55218885Sdim default_classification_rules -> data.eval -> op = expr_check; 56218885Sdim default_classification_rules -> data.eval -> data.check = 57218885Sdim &default_collection; 58218885Sdim} 59218885Sdim 60218885Sdimvoid classify_client (packet) 61218885Sdim struct packet *packet; 62218885Sdim{ 63218885Sdim execute_statements (NULL, packet, NULL, NULL, packet->options, NULL, 64218885Sdim &global_scope, default_classification_rules, NULL); 65218885Sdim} 66218885Sdim 67218885Sdimint check_collection (packet, lease, collection) 68218885Sdim struct packet *packet; 69218885Sdim struct lease *lease; 70218885Sdim struct collection *collection; 71218885Sdim{ 72218885Sdim struct class *class, *nc; 73218885Sdim struct data_string data; 74218885Sdim int matched = 0; 75218885Sdim int status; 76218885Sdim int ignorep; 77218885Sdim int classfound; 78218885Sdim 79218885Sdim for (class = collection -> classes; class; class = class -> nic) { 80218885Sdim#if defined (DEBUG_CLASS_MATCHING) 81218885Sdim log_info ("checking against class %s...", class -> name); 82218885Sdim#endif 83218885Sdim memset (&data, 0, sizeof data); 84218885Sdim 85218885Sdim /* If there is a "match if" expression, check it. If 86218885Sdim we get a match, and there's no subclass expression, 87218885Sdim it's a match. If we get a match and there is a subclass 88218885Sdim expression, then we check the submatch. If it's not a 89218885Sdim match, that's final - we don't check the submatch. */ 90218885Sdim 91218885Sdim if (class -> expr) { 92218885Sdim status = (evaluate_boolean_expression_result 93218885Sdim (&ignorep, packet, lease, 94218885Sdim (struct client_state *)0, 95218885Sdim packet -> options, (struct option_state *)0, 96218885Sdim lease ? &lease -> scope : &global_scope, 97218885Sdim class -> expr)); 98218885Sdim if (status) { 99218885Sdim if (!class -> submatch) { 100218885Sdim matched = 1; 101218885Sdim#if defined (DEBUG_CLASS_MATCHING) 102218885Sdim log_info ("matches class."); 103218885Sdim#endif 104218885Sdim classify (packet, class); 105218885Sdim continue; 106218885Sdim } 107218885Sdim } else 108218885Sdim continue; 109218885Sdim } 110218885Sdim 111218885Sdim /* Check to see if the client matches an existing subclass. 112218885Sdim If it doesn't, and this is a spawning class, spawn a new 113218885Sdim subclass and put the client in it. */ 114218885Sdim if (class -> submatch) { 115218885Sdim status = (evaluate_data_expression 116218885Sdim (&data, packet, lease, 117218885Sdim (struct client_state *)0, 118218885Sdim packet -> options, (struct option_state *)0, 119218885Sdim lease ? &lease -> scope : &global_scope, 120218885Sdim class -> submatch, MDL)); 121218885Sdim if (status && data.len) { 122218885Sdim nc = (struct class *)0; 123218885Sdim classfound = class_hash_lookup (&nc, class -> hash, 124218885Sdim (const char *)data.data, data.len, MDL); 125218885Sdim 126218885Sdim#ifdef LDAP_CONFIGURATION 127218885Sdim if (!classfound && find_subclass_in_ldap (class, &nc, &data)) 128218885Sdim classfound = 1; 129218885Sdim#endif 130218885Sdim 131218885Sdim if (classfound) { 132218885Sdim#if defined (DEBUG_CLASS_MATCHING) 133218885Sdim log_info ("matches subclass %s.", 134218885Sdim print_hex_1 (data.len, 135218885Sdim data.data, 60)); 136218885Sdim#endif 137218885Sdim data_string_forget (&data, MDL); 138218885Sdim classify (packet, nc); 139218885Sdim matched = 1; 140218885Sdim class_dereference (&nc, MDL); 141218885Sdim continue; 142218885Sdim } 143234353Sdim if (!class -> spawning) { 144218885Sdim data_string_forget (&data, MDL); 145218885Sdim continue; 146218885Sdim } 147218885Sdim /* XXX Write out the spawned class? */ 148218885Sdim#if defined (DEBUG_CLASS_MATCHING) 149218885Sdim log_info ("spawning subclass %s.", 150218885Sdim print_hex_1 (data.len, data.data, 60)); 151218885Sdim#endif 152218885Sdim status = class_allocate (&nc, MDL); 153218885Sdim group_reference (&nc -> group, 154218885Sdim class -> group, MDL); 155218885Sdim class_reference (&nc -> superclass, 156218885Sdim class, MDL); 157218885Sdim nc -> lease_limit = class -> lease_limit; 158218885Sdim nc -> dirty = 1; 159218885Sdim if (nc -> lease_limit) { 160218885Sdim nc -> billed_leases = 161218885Sdim (dmalloc 162218885Sdim (nc -> lease_limit * 163218885Sdim sizeof (struct lease *), 164218885Sdim MDL)); 165218885Sdim if (!nc -> billed_leases) { 166218885Sdim log_error ("no memory for%s", 167218885Sdim " billing"); 168218885Sdim data_string_forget 169218885Sdim (&nc -> hash_string, 170218885Sdim MDL); 171218885Sdim class_dereference (&nc, MDL); 172218885Sdim data_string_forget (&data, 173218885Sdim MDL); 174218885Sdim continue; 175218885Sdim } 176218885Sdim memset (nc -> billed_leases, 0, 177218885Sdim (nc -> lease_limit * 178218885Sdim sizeof (struct lease *))); 179218885Sdim } 180218885Sdim data_string_copy (&nc -> hash_string, &data, 181218885Sdim MDL); 182218885Sdim if (!class -> hash) 183218885Sdim class_new_hash(&class->hash, 184218885Sdim SCLASS_HASH_SIZE, MDL); 185218885Sdim class_hash_add (class -> hash, 186218885Sdim (const char *) 187218885Sdim nc -> hash_string.data, 188218885Sdim nc -> hash_string.len, 189218885Sdim nc, MDL); 190218885Sdim classify (packet, nc); 191218885Sdim class_dereference (&nc, MDL); 192218885Sdim } 193218885Sdim 194218885Sdim data_string_forget (&data, MDL); 195218885Sdim } 196218885Sdim } 197218885Sdim return matched; 198218885Sdim} 199218885Sdim 200218885Sdimvoid classify (packet, class) 201218885Sdim struct packet *packet; 202218885Sdim struct class *class; 203218885Sdim{ 204218885Sdim if (packet -> class_count < PACKET_MAX_CLASSES) 205218885Sdim class_reference (&packet -> classes [packet -> class_count++], 206218885Sdim class, MDL); 207218885Sdim else 208218885Sdim log_error ("too many classes match %s", 209218885Sdim print_hw_addr (packet -> raw -> htype, 210218885Sdim packet -> raw -> hlen, 211218885Sdim packet -> raw -> chaddr)); 212218885Sdim} 213218885Sdim 214218885Sdim 215218885Sdimisc_result_t unlink_class(struct class **class) { 216218885Sdim struct collection *lp; 217218885Sdim struct class *cp, *pp; 218218885Sdim 219218885Sdim for (lp = collections; lp; lp = lp -> next) { 220218885Sdim for (pp = 0, cp = lp -> classes; cp; pp = cp, cp = cp -> nic) 221218885Sdim if (cp == *class) { 222218885Sdim if (pp == 0) { 223218885Sdim lp->classes = cp->nic; 224243830Sdim } else { 225243830Sdim pp->nic = cp->nic; 226243830Sdim } 227243830Sdim cp->nic = 0; 228243830Sdim class_dereference(class, MDL); 229243830Sdim 230243830Sdim return ISC_R_SUCCESS; 231243830Sdim } 232243830Sdim } 233243830Sdim return ISC_R_NOTFOUND; 234243830Sdim} 235243830Sdim 236243830Sdim 237243830Sdimisc_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