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