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