Home | History | Annotate | Line # | Download | only in server
class.c revision 1.1.1.3
      1 /*	$NetBSD: class.c,v 1.1.1.3 2022/04/03 01:08:44 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 (at) isc.org>
     27  *   https://www.isc.org/
     28  *
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __RCSID("$NetBSD: class.c,v 1.1.1.3 2022/04/03 01:08:44 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 				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 
    200 void 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 
    215 isc_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 
    237 isc_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  */
    262 void 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 
    301 int 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