]>
Commit | Line | Data |
---|---|---|
07378a2a TL |
1 | /* class.c |
2 | ||
3 | Handling for client classes. */ | |
4 | ||
5 | /* | |
6a39bcf0 | 6 | * Copyright (c) 2009,2012-2015 by Internet Systems Consortium, Inc. ("ISC") |
0f750c4f | 7 | * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC") |
98311e4b | 8 | * Copyright (c) 1998-2003 by Internet Software Consortium |
07378a2a | 9 | * |
98311e4b DH |
10 | * Permission to use, copy, modify, and distribute this software for any |
11 | * purpose with or without fee is hereby granted, provided that the above | |
12 | * copyright notice and this permission notice appear in all copies. | |
07378a2a | 13 | * |
98311e4b DH |
14 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES |
15 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
16 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR | |
17 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
20 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
07378a2a | 21 | * |
98311e4b DH |
22 | * Internet Systems Consortium, Inc. |
23 | * 950 Charter Street | |
24 | * Redwood City, CA 94063 | |
25 | * <info@isc.org> | |
2c85ac9b | 26 | * https://www.isc.org/ |
49733f31 | 27 | * |
07378a2a TL |
28 | */ |
29 | ||
07378a2a TL |
30 | #include "dhcpd.h" |
31 | ||
07378a2a TL |
32 | struct collection default_collection = { |
33 | (struct collection *)0, | |
34 | "default", | |
35 | (struct class *)0, | |
36 | }; | |
37 | ||
05129661 | 38 | struct collection *collections = &default_collection; |
adb7ed1a | 39 | struct executable_statement *default_classification_rules; |
07378a2a | 40 | |
ee7b5624 TL |
41 | int have_billing_classes; |
42 | ||
07378a2a TL |
43 | /* Build the default classification rule tree. */ |
44 | ||
45 | void classification_setup () | |
46 | { | |
07378a2a | 47 | /* eval ... */ |
d758ad8c TL |
48 | default_classification_rules = (struct executable_statement *)0; |
49 | if (!executable_statement_allocate (&default_classification_rules, | |
50 | MDL)) | |
8ae2d595 | 51 | log_fatal ("Can't allocate check of default collection"); |
d758ad8c | 52 | default_classification_rules -> op = eval_statement; |
07378a2a | 53 | |
dce08243 | 54 | /* check-collection "default" */ |
d758ad8c TL |
55 | if (!expression_allocate (&default_classification_rules -> data.eval, |
56 | MDL)) | |
dce08243 | 57 | log_fatal ("Can't allocate default check expression"); |
d758ad8c TL |
58 | default_classification_rules -> data.eval -> op = expr_check; |
59 | default_classification_rules -> data.eval -> data.check = | |
60 | &default_collection; | |
07378a2a TL |
61 | } |
62 | ||
63 | void classify_client (packet) | |
64 | struct packet *packet; | |
65 | { | |
a7341359 SR |
66 | execute_statements (NULL, packet, NULL, NULL, packet->options, NULL, |
67 | &global_scope, default_classification_rules, NULL); | |
07378a2a TL |
68 | } |
69 | ||
da38df14 | 70 | int check_collection (packet, lease, collection) |
07378a2a | 71 | struct packet *packet; |
da38df14 | 72 | struct lease *lease; |
07378a2a TL |
73 | struct collection *collection; |
74 | { | |
75 | struct class *class, *nc; | |
76 | struct data_string data; | |
77 | int matched = 0; | |
4af075a2 | 78 | int status; |
63f8b323 | 79 | int ignorep; |
33692791 | 80 | int classfound; |
07378a2a TL |
81 | |
82 | for (class = collection -> classes; class; class = class -> nic) { | |
74a2049e | 83 | #if defined (DEBUG_CLASS_MATCHING) |
8ae2d595 | 84 | log_info ("checking against class %s...", class -> name); |
74a2049e | 85 | #endif |
ee7b5624 | 86 | memset (&data, 0, sizeof data); |
56120203 TL |
87 | |
88 | /* If there is a "match if" expression, check it. If | |
89 | we get a match, and there's no subclass expression, | |
90 | it's a match. If we get a match and there is a subclass | |
91 | expression, then we check the submatch. If it's not a | |
92 | match, that's final - we don't check the submatch. */ | |
93 | ||
94 | if (class -> expr) { | |
95 | status = (evaluate_boolean_expression_result | |
96 | (&ignorep, packet, lease, | |
9e383163 | 97 | (struct client_state *)0, |
56120203 | 98 | packet -> options, (struct option_state *)0, |
eaa84150 TL |
99 | lease ? &lease -> scope : &global_scope, |
100 | class -> expr)); | |
56120203 TL |
101 | if (status) { |
102 | if (!class -> submatch) { | |
103 | matched = 1; | |
104 | #if defined (DEBUG_CLASS_MATCHING) | |
105 | log_info ("matches class."); | |
106 | #endif | |
107 | classify (packet, class); | |
108 | continue; | |
109 | } | |
110 | } else | |
111 | continue; | |
112 | } | |
113 | ||
114 | /* Check to see if the client matches an existing subclass. | |
115 | If it doesn't, and this is a spawning class, spawn a new | |
116 | subclass and put the client in it. */ | |
8b500185 | 117 | if (class -> submatch) { |
dce08243 TL |
118 | status = (evaluate_data_expression |
119 | (&data, packet, lease, | |
9e383163 | 120 | (struct client_state *)0, |
dce08243 | 121 | packet -> options, (struct option_state *)0, |
5b5fcc81 | 122 | lease ? &lease -> scope : &global_scope, |
d758ad8c | 123 | class -> submatch, MDL)); |
b767664e | 124 | if (status && data.len) { |
20916cae | 125 | nc = (struct class *)0; |
33692791 DH |
126 | classfound = class_hash_lookup (&nc, class -> hash, |
127 | (const char *)data.data, data.len, MDL); | |
128 | ||
129 | #ifdef LDAP_CONFIGURATION | |
130 | if (!classfound && find_subclass_in_ldap (class, &nc, &data)) | |
131 | classfound = 1; | |
132 | #endif | |
133 | ||
134 | if (classfound) { | |
74a2049e | 135 | #if defined (DEBUG_CLASS_MATCHING) |
8ae2d595 | 136 | log_info ("matches subclass %s.", |
ee7b5624 TL |
137 | print_hex_1 (data.len, |
138 | data.data, 60)); | |
139 | #endif | |
12c9957e | 140 | data_string_forget (&data, MDL); |
ee7b5624 TL |
141 | classify (packet, nc); |
142 | matched = 1; | |
a19e8a45 | 143 | class_dereference (&nc, MDL); |
ee7b5624 TL |
144 | continue; |
145 | } | |
12c9957e TL |
146 | if (!class -> spawning) { |
147 | data_string_forget (&data, MDL); | |
8b500185 | 148 | continue; |
12c9957e | 149 | } |
fea20b09 | 150 | /* XXX Write out the spawned class? */ |
ee7b5624 | 151 | #if defined (DEBUG_CLASS_MATCHING) |
8ae2d595 | 152 | log_info ("spawning subclass %s.", |
64e1dc1d | 153 | print_hex_1 (data.len, data.data, 60)); |
74a2049e | 154 | #endif |
a19e8a45 TL |
155 | status = class_allocate (&nc, MDL); |
156 | group_reference (&nc -> group, | |
157 | class -> group, MDL); | |
158 | class_reference (&nc -> superclass, | |
159 | class, MDL); | |
ee7b5624 TL |
160 | nc -> lease_limit = class -> lease_limit; |
161 | nc -> dirty = 1; | |
162 | if (nc -> lease_limit) { | |
163 | nc -> billed_leases = | |
164 | (dmalloc | |
165 | (nc -> lease_limit * | |
166 | sizeof (struct lease *), | |
12c9957e | 167 | MDL)); |
ee7b5624 | 168 | if (!nc -> billed_leases) { |
7e6f3635 TL |
169 | log_error ("no memory for%s", |
170 | " billing"); | |
ee7b5624 TL |
171 | data_string_forget |
172 | (&nc -> hash_string, | |
12c9957e | 173 | MDL); |
a19e8a45 | 174 | class_dereference (&nc, MDL); |
12c9957e TL |
175 | data_string_forget (&data, |
176 | MDL); | |
ee7b5624 TL |
177 | continue; |
178 | } | |
179 | memset (nc -> billed_leases, 0, | |
180 | (nc -> lease_limit * | |
0f750c4f | 181 | sizeof (struct lease *))); |
ee7b5624 TL |
182 | } |
183 | data_string_copy (&nc -> hash_string, &data, | |
12c9957e TL |
184 | MDL); |
185 | data_string_forget (&data, MDL); | |
ee7b5624 | 186 | if (!class -> hash) |
f7fdb216 DH |
187 | class_new_hash(&class->hash, |
188 | SCLASS_HASH_SIZE, MDL); | |
20916cae TL |
189 | class_hash_add (class -> hash, |
190 | (const char *) | |
191 | nc -> hash_string.data, | |
192 | nc -> hash_string.len, | |
193 | nc, MDL); | |
ee7b5624 | 194 | classify (packet, nc); |
d758ad8c | 195 | class_dereference (&nc, MDL); |
07378a2a TL |
196 | } |
197 | } | |
07378a2a TL |
198 | } |
199 | return matched; | |
200 | } | |
201 | ||
202 | void classify (packet, class) | |
203 | struct packet *packet; | |
204 | struct class *class; | |
205 | { | |
206 | if (packet -> class_count < PACKET_MAX_CLASSES) | |
20916cae TL |
207 | class_reference (&packet -> classes [packet -> class_count++], |
208 | class, MDL); | |
07378a2a | 209 | else |
d758ad8c | 210 | log_error ("too many classes match %s", |
07378a2a TL |
211 | print_hw_addr (packet -> raw -> htype, |
212 | packet -> raw -> hlen, | |
213 | packet -> raw -> chaddr)); | |
214 | } | |
215 | ||
899d754f JB |
216 | |
217 | isc_result_t unlink_class(struct class **class) { | |
218 | struct collection *lp; | |
219 | struct class *cp, *pp; | |
220 | ||
221 | for (lp = collections; lp; lp = lp -> next) { | |
222 | for (pp = 0, cp = lp -> classes; cp; pp = cp, cp = cp -> nic) | |
223 | if (cp == *class) { | |
224 | if (pp == 0) { | |
225 | lp->classes = cp->nic; | |
226 | } else { | |
227 | pp->nic = cp->nic; | |
228 | } | |
229 | cp->nic = 0; | |
230 | class_dereference(class, MDL); | |
231 | ||
232 | return ISC_R_SUCCESS; | |
233 | } | |
234 | } | |
235 | return ISC_R_NOTFOUND; | |
236 | } | |
237 | ||
6a39bcf0 | 238 | |
20916cae TL |
239 | isc_result_t find_class (struct class **class, const char *name, |
240 | const char *file, int line) | |
adb7ed1a TL |
241 | { |
242 | struct collection *lp; | |
243 | struct class *cp; | |
244 | ||
245 | for (lp = collections; lp; lp = lp -> next) { | |
246 | for (cp = lp -> classes; cp; cp = cp -> nic) | |
20916cae TL |
247 | if (cp -> name && !strcmp (name, cp -> name)) { |
248 | return class_reference (class, cp, file, line); | |
249 | } | |
adb7ed1a | 250 | } |
20916cae | 251 | return ISC_R_NOTFOUND; |
adb7ed1a | 252 | } |
ee7b5624 | 253 | |
6a39bcf0 TM |
254 | /* Removes the billing class from a lease |
255 | * | |
256 | * Note that because classes can be created and removed dynamically, it is | |
257 | * possible that the class to which a lease was billed has since been deleted. | |
258 | * To cover the case where the lease is the last reference to a deleted class | |
259 | * we remove the lease reference from the class first, then the class from the | |
260 | * lease. To protect ourselves from the reverse situation, where the class is | |
261 | * the last reference to the lease (unlikely), we create a guard reference to | |
262 | * the lease, then remove it at the end. | |
263 | */ | |
264 | void unbill_class (lease) | |
ee7b5624 | 265 | struct lease *lease; |
ee7b5624 TL |
266 | { |
267 | int i; | |
6a39bcf0 TM |
268 | struct class* class = lease->billing_class; |
269 | struct lease* refholder = NULL; | |
ee7b5624 | 270 | |
6a39bcf0 TM |
271 | /* if there's no billing to remove, nothing to do */ |
272 | if (class == NULL) { | |
273 | return; | |
274 | } | |
275 | ||
276 | /* Find the lease in the list of the class's billed leases */ | |
277 | for (i = 0; i < class->lease_limit; i++) { | |
278 | if (class->billed_leases[i] == lease) | |
ee7b5624 | 279 | break; |
6a39bcf0 TM |
280 | } |
281 | ||
282 | /* Create guard reference, so class cannot be last reference to lease */ | |
283 | lease_reference(&refholder, lease, MDL); | |
284 | ||
285 | /* If the class doesn't have the lease, then something is broken | |
286 | * programmatically. We'll log it but skip the lease dereference. */ | |
287 | if (i == class->lease_limit) { | |
8ae2d595 | 288 | log_error ("lease %s unbilled with no billing arrangement.", |
6a39bcf0 TM |
289 | piaddr(lease->ip_addr)); |
290 | } else { | |
291 | /* Remove the lease from the class */ | |
292 | lease_dereference(&class->billed_leases[i], MDL); | |
293 | class->leases_consumed--; | |
ee7b5624 | 294 | } |
6a39bcf0 TM |
295 | |
296 | /* Remove the class from the lease */ | |
297 | class_dereference(&lease->billing_class, MDL); | |
298 | ||
299 | /* Ditch our guard reference */ | |
300 | lease_dereference(&refholder, MDL); | |
ee7b5624 TL |
301 | } |
302 | ||
303 | int bill_class (lease, class) | |
304 | struct lease *lease; | |
305 | struct class *class; | |
306 | { | |
307 | int i; | |
308 | ||
bf7a9284 TL |
309 | if (lease -> billing_class) { |
310 | log_error ("lease billed with existing billing arrangement."); | |
6a39bcf0 | 311 | unbill_class (lease); |
bf7a9284 TL |
312 | } |
313 | ||
ee7b5624 TL |
314 | if (class -> leases_consumed == class -> lease_limit) |
315 | return 0; | |
316 | ||
317 | for (i = 0; i < class -> lease_limit; i++) | |
318 | if (!class -> billed_leases [i]) | |
319 | break; | |
320 | ||
321 | if (i == class -> lease_limit) { | |
8ae2d595 | 322 | log_error ("class billing consumption disagrees with leases."); |
ee7b5624 TL |
323 | return 0; |
324 | } | |
325 | ||
20916cae TL |
326 | lease_reference (&class -> billed_leases [i], lease, MDL); |
327 | class_reference (&lease -> billing_class, class, MDL); | |
ee7b5624 TL |
328 | class -> leases_consumed++; |
329 | return 1; | |
330 | } |