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