xref: /minix/external/bsd/dhcp/dist/server/class.c (revision fb9c64b2)
1 /*	$NetBSD: class.c,v 1.1.1.3 2014/07/12 11:58:04 spz Exp $	*/
2 /* class.c
3 
4    Handling for client classes. */
5 
6 /*
7  * Copyright (c) 2009,2012-2014 by Internet Systems Consortium, Inc. ("ISC")
8  * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
9  * Copyright (c) 1998-2003 by Internet Software Consortium
10  *
11  * Permission to use, copy, modify, and distribute this software for any
12  * purpose with or without fee is hereby granted, provided that the above
13  * copyright notice and this permission notice appear in all copies.
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.1.1.3 2014/07/12 11:58:04 spz Exp $");
33 
34 #include "dhcpd.h"
35 
36 struct collection default_collection = {
37 	(struct collection *)0,
38 	"default",
39 	(struct class *)0,
40 };
41 
42 struct collection *collections = &default_collection;
43 struct executable_statement *default_classification_rules;
44 
45 int have_billing_classes;
46 
47 /* Build the default classification rule tree. */
48 
49 void classification_setup ()
50 {
51 	/* eval ... */
52 	default_classification_rules = (struct executable_statement *)0;
53 	if (!executable_statement_allocate (&default_classification_rules,
54 					    MDL))
55 		log_fatal ("Can't allocate check of default collection");
56 	default_classification_rules -> op = eval_statement;
57 
58 	/* check-collection "default" */
59 	if (!expression_allocate (&default_classification_rules -> data.eval,
60 				  MDL))
61 		log_fatal ("Can't allocate default check expression");
62 	default_classification_rules -> data.eval -> op = expr_check;
63 	default_classification_rules -> data.eval -> data.check =
64 		&default_collection;
65 }
66 
67 void classify_client (packet)
68 	struct packet *packet;
69 {
70 	execute_statements (NULL, packet, NULL, NULL, packet->options, NULL,
71 			    &global_scope, default_classification_rules, NULL);
72 }
73 
74 int check_collection (packet, lease, collection)
75 	struct packet *packet;
76 	struct lease *lease;
77 	struct collection *collection;
78 {
79 	struct class *class, *nc;
80 	struct data_string data;
81 	int matched = 0;
82 	int status;
83 	int ignorep;
84 	int classfound;
85 
86 	for (class = collection -> classes; class; class = class -> nic) {
87 #if defined (DEBUG_CLASS_MATCHING)
88 		log_info ("checking against class %s...", class -> name);
89 #endif
90 		memset (&data, 0, sizeof data);
91 
92 		/* If there is a "match if" expression, check it.   If
93 		   we get a match, and there's no subclass expression,
94 		   it's a match.   If we get a match and there is a subclass
95 		   expression, then we check the submatch.   If it's not a
96 		   match, that's final - we don't check the submatch. */
97 
98 		if (class -> expr) {
99 			status = (evaluate_boolean_expression_result
100 				  (&ignorep, packet, lease,
101 				   (struct client_state *)0,
102 				   packet -> options, (struct option_state *)0,
103 				   lease ? &lease -> scope : &global_scope,
104 				   class -> expr));
105 			if (status) {
106 				if (!class -> submatch) {
107 					matched = 1;
108 #if defined (DEBUG_CLASS_MATCHING)
109 					log_info ("matches class.");
110 #endif
111 					classify (packet, class);
112 					continue;
113 				}
114 			} else
115 				continue;
116 		}
117 
118 		/* Check to see if the client matches an existing subclass.
119 		   If it doesn't, and this is a spawning class, spawn a new
120 		   subclass and put the client in it. */
121 		if (class -> submatch) {
122 			status = (evaluate_data_expression
123 				  (&data, packet, lease,
124 				   (struct client_state *)0,
125 				   packet -> options, (struct option_state *)0,
126 				   lease ? &lease -> scope : &global_scope,
127 				   class -> submatch, MDL));
128 			if (status && data.len) {
129 				nc = (struct class *)0;
130 				classfound = class_hash_lookup (&nc, class -> hash,
131 					(const char *)data.data, data.len, MDL);
132 
133 #ifdef LDAP_CONFIGURATION
134 				if (!classfound && find_subclass_in_ldap (class, &nc, &data))
135 					classfound = 1;
136 #endif
137 
138 				if (classfound) {
139 #if defined (DEBUG_CLASS_MATCHING)
140 					log_info ("matches subclass %s.",
141 					      print_hex_1 (data.len,
142 							   data.data, 60));
143 #endif
144 					data_string_forget (&data, MDL);
145 					classify (packet, nc);
146 					matched = 1;
147 					class_dereference (&nc, MDL);
148 					continue;
149 				}
150 				if (!class -> spawning) {
151 					data_string_forget (&data, MDL);
152 					continue;
153 				}
154 				/* XXX Write out the spawned class? */
155 #if defined (DEBUG_CLASS_MATCHING)
156 				log_info ("spawning subclass %s.",
157 				      print_hex_1 (data.len, data.data, 60));
158 #endif
159 				status = class_allocate (&nc, MDL);
160 				group_reference (&nc -> group,
161 						 class -> group, MDL);
162 				class_reference (&nc -> superclass,
163 						 class, MDL);
164 				nc -> lease_limit = class -> lease_limit;
165 				nc -> dirty = 1;
166 				if (nc -> lease_limit) {
167 					nc -> billed_leases =
168 						(dmalloc
169 						 (nc -> lease_limit *
170 						  sizeof (struct lease *),
171 						  MDL));
172 					if (!nc -> billed_leases) {
173 						log_error ("no memory for%s",
174 							   " billing");
175 						data_string_forget
176 							(&nc -> hash_string,
177 							 MDL);
178 						class_dereference (&nc, MDL);
179 						data_string_forget (&data,
180 								    MDL);
181 						continue;
182 					}
183 					memset (nc -> billed_leases, 0,
184 						(nc -> lease_limit *
185 						 sizeof (struct lease *)));
186 				}
187 				data_string_copy (&nc -> hash_string, &data,
188 						  MDL);
189 				data_string_forget (&data, MDL);
190 				if (!class -> hash)
191 				    class_new_hash(&class->hash,
192 						   SCLASS_HASH_SIZE, MDL);
193 				class_hash_add (class -> hash,
194 						(const char *)
195 						nc -> hash_string.data,
196 						nc -> hash_string.len,
197 						nc, MDL);
198 				classify (packet, nc);
199 				class_dereference (&nc, MDL);
200 			}
201 		}
202 	}
203 	return matched;
204 }
205 
206 void classify (packet, class)
207 	struct packet *packet;
208 	struct class *class;
209 {
210 	if (packet -> class_count < PACKET_MAX_CLASSES)
211 		class_reference (&packet -> classes [packet -> class_count++],
212 				 class, MDL);
213 	else
214 		log_error ("too many classes match %s",
215 		      print_hw_addr (packet -> raw -> htype,
216 				     packet -> raw -> hlen,
217 				     packet -> raw -> chaddr));
218 }
219 
220 
221 isc_result_t unlink_class(struct class **class) {
222 	struct collection *lp;
223 	struct class *cp, *pp;
224 
225 	for (lp = collections; lp; lp = lp -> next) {
226 		for (pp = 0, cp = lp -> classes; cp; pp = cp, cp = cp -> nic)
227 			if (cp == *class) {
228 				if (pp == 0) {
229 					lp->classes = cp->nic;
230 				} else {
231 					pp->nic = cp->nic;
232 				}
233 				cp->nic = 0;
234 				class_dereference(class, MDL);
235 
236 				return ISC_R_SUCCESS;
237 			}
238 	}
239 	return ISC_R_NOTFOUND;
240 }
241 
242 
243 isc_result_t find_class (struct class **class, const char *name,
244 			 const char *file, int line)
245 {
246 	struct collection *lp;
247 	struct class *cp;
248 
249 	for (lp = collections; lp; lp = lp -> next) {
250 		for (cp = lp -> classes; cp; cp = cp -> nic)
251 			if (cp -> name && !strcmp (name, cp -> name)) {
252 				return class_reference (class, cp, file, line);
253 			}
254 	}
255 	return ISC_R_NOTFOUND;
256 }
257 
258 int unbill_class (lease, class)
259 	struct lease *lease;
260 	struct class *class;
261 {
262 	int i;
263 
264 	for (i = 0; i < class -> lease_limit; i++)
265 		if (class -> billed_leases [i] == lease)
266 			break;
267 	if (i == class -> lease_limit) {
268 		log_error ("lease %s unbilled with no billing arrangement.",
269 		      piaddr (lease -> ip_addr));
270 		return 0;
271 	}
272 	class_dereference (&lease -> billing_class, MDL);
273 	lease_dereference (&class -> billed_leases [i], MDL);
274 	class -> leases_consumed--;
275 	return 1;
276 }
277 
278 int bill_class (lease, class)
279 	struct lease *lease;
280 	struct class *class;
281 {
282 	int i;
283 
284 	if (lease -> billing_class) {
285 		log_error ("lease billed with existing billing arrangement.");
286 		unbill_class (lease, lease -> billing_class);
287 	}
288 
289 	if (class -> leases_consumed == class -> lease_limit)
290 		return 0;
291 
292 	for (i = 0; i < class -> lease_limit; i++)
293 		if (!class -> billed_leases [i])
294 			break;
295 
296 	if (i == class -> lease_limit) {
297 		log_error ("class billing consumption disagrees with leases.");
298 		return 0;
299 	}
300 
301 	lease_reference (&class -> billed_leases [i], lease, MDL);
302 	class_reference (&lease -> billing_class, class, MDL);
303 	class -> leases_consumed++;
304 	return 1;
305 }
306