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