xref: /netbsd-src/external/mpl/dhcp/dist/server/class.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: class.c,v 1.2 2018/04/07 22:37:30 christos Exp $	*/
2 
3 /* class.c
4 
5    Handling for client classes. */
6 
7 /*
8  * Copyright (c) 2004-2017 by 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  *   950 Charter Street
25  *   Redwood City, CA 94063
26  *   <info@isc.org>
27  *   https://www.isc.org/
28  *
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: class.c,v 1.2 2018/04/07 22:37:30 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 				data_string_forget (&data, MDL);
183 				if (!class -> hash)
184 				    class_new_hash(&class->hash,
185 						   SCLASS_HASH_SIZE, MDL);
186 				class_hash_add (class -> hash,
187 						(const char *)
188 						nc -> hash_string.data,
189 						nc -> hash_string.len,
190 						nc, MDL);
191 				classify (packet, nc);
192 				class_dereference (&nc, MDL);
193 			}
194 		}
195 	}
196 	return matched;
197 }
198 
199 void classify (packet, class)
200 	struct packet *packet;
201 	struct class *class;
202 {
203 	if (packet -> class_count < PACKET_MAX_CLASSES)
204 		class_reference (&packet -> classes [packet -> class_count++],
205 				 class, MDL);
206 	else
207 		log_error ("too many classes match %s",
208 		      print_hw_addr (packet -> raw -> htype,
209 				     packet -> raw -> hlen,
210 				     packet -> raw -> chaddr));
211 }
212 
213 
214 isc_result_t unlink_class(struct class **class) {
215 	struct collection *lp;
216 	struct class *cp, *pp;
217 
218 	for (lp = collections; lp; lp = lp -> next) {
219 		for (pp = 0, cp = lp -> classes; cp; pp = cp, cp = cp -> nic)
220 			if (cp == *class) {
221 				if (pp == 0) {
222 					lp->classes = cp->nic;
223 				} else {
224 					pp->nic = cp->nic;
225 				}
226 				cp->nic = 0;
227 				class_dereference(class, MDL);
228 
229 				return ISC_R_SUCCESS;
230 			}
231 	}
232 	return ISC_R_NOTFOUND;
233 }
234 
235 
236 isc_result_t find_class (struct class **class, const char *name,
237 			 const char *file, int line)
238 {
239 	struct collection *lp;
240 	struct class *cp;
241 
242 	for (lp = collections; lp; lp = lp -> next) {
243 		for (cp = lp -> classes; cp; cp = cp -> nic)
244 			if (cp -> name && !strcmp (name, cp -> name)) {
245 				return class_reference (class, cp, file, line);
246 			}
247 	}
248 	return ISC_R_NOTFOUND;
249 }
250 
251 /* Removes the billing class from a lease
252  *
253  * Note that because classes can be created and removed dynamically, it is
254  * possible that the class to which a lease was billed has since been deleted.
255  * To cover the case where the lease is the last reference to a deleted class
256  * we remove the lease reference from the class first, then the class from the
257  * lease.  To protect ourselves from the reverse situation, where the class is
258  * the last reference to the lease (unlikely), we create a guard reference to
259  * the lease, then remove it at the end.
260  */
261 void unbill_class (lease)
262 	struct lease *lease;
263 {
264 	int i;
265 	struct class* class = lease->billing_class;
266 	struct lease* refholder = NULL;
267 
268 	/* if there's no billing to remove, nothing to do */
269 	if (class == NULL) {
270 		return;
271 	}
272 
273 	/* Find the lease in the list of the class's billed leases */
274 	for (i = 0; i < class->lease_limit; i++) {
275 		if (class->billed_leases[i] == lease)
276 			break;
277 	}
278 
279 	/* Create guard reference, so class cannot be last reference to lease */
280 	lease_reference(&refholder, lease, MDL);
281 
282 	/* If the class doesn't have the lease, then something is broken
283 	 * programmatically.  We'll log it but skip the lease dereference. */
284 	if (i == class->lease_limit) {
285 		log_error ("lease %s unbilled with no billing arrangement.",
286 			   piaddr(lease->ip_addr));
287 	} else {
288 		/* Remove the lease from the class */
289 		lease_dereference(&class->billed_leases[i], MDL);
290 		class->leases_consumed--;
291 	}
292 
293 	/* Remove the class from the lease */
294 	class_dereference(&lease->billing_class, MDL);
295 
296 	/* Ditch our guard reference */
297 	lease_dereference(&refholder, MDL);
298 }
299 
300 int bill_class (lease, class)
301 	struct lease *lease;
302 	struct class *class;
303 {
304 	int i;
305 
306 	if (lease -> billing_class) {
307 		log_error ("lease billed with existing billing arrangement.");
308 		unbill_class (lease);
309 	}
310 
311 	if (class -> leases_consumed == class -> lease_limit)
312 		return 0;
313 
314 	for (i = 0; i < class -> lease_limit; i++)
315 		if (!class -> billed_leases [i])
316 			break;
317 
318 	if (i == class -> lease_limit) {
319 		log_error ("class billing consumption disagrees with leases.");
320 		return 0;
321 	}
322 
323 	lease_reference (&class -> billed_leases [i], lease, MDL);
324 	class_reference (&lease -> billing_class, class, MDL);
325 	class -> leases_consumed++;
326 	return 1;
327 }
328