xref: /netbsd/external/mpl/dhcp/dist/server/class.c (revision 13df4856)
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 
36 struct executable_statement *default_classification_rules;
37 
38 int have_billing_classes;
39 
40 /* Build the default classification rule tree. */
41 
classification_setup()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 
classify_client(packet)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 
check_collection(packet,lease,collection)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 
classify(packet,class)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 
unlink_class(struct class ** class)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 
find_class(struct class ** class,const char * name,const char * file,int line)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  */
unbill_class(lease)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 
bill_class(lease,class)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