Merge from vendor branch OPENPAM:
[dragonfly.git] / contrib / dhcp-3.0 / server / class.c
1 /* class.c
2
3    Handling for client classes. */
4
5 /*
6  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7  * Copyright (c) 1998-2003 by Internet Software Consortium
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  *
21  *   Internet Systems Consortium, Inc.
22  *   950 Charter Street
23  *   Redwood City, CA 94063
24  *   <info@isc.org>
25  *   http://www.isc.org/
26  *
27  * This software has been written for Internet Systems Consortium
28  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29  * To learn more about Internet Systems Consortium, see
30  * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
31  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
32  * ``http://www.nominum.com''.
33  */
34
35 #ifndef lint
36 static char copyright[] =
37 "$Id: class.c,v 1.29.2.4 2004/06/10 17:59:51 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
38
39 #endif /* not lint */
40
41 #include "dhcpd.h"
42
43 struct collection default_collection = {
44         (struct collection *)0,
45         "default",
46         (struct class *)0,
47 };
48
49 struct collection *collections = &default_collection;
50 struct executable_statement *default_classification_rules;
51
52 int have_billing_classes;
53
54 /* Build the default classification rule tree. */
55
56 void classification_setup ()
57 {
58         /* eval ... */
59         default_classification_rules = (struct executable_statement *)0;
60         if (!executable_statement_allocate (&default_classification_rules,
61                                             MDL))
62                 log_fatal ("Can't allocate check of default collection");
63         default_classification_rules -> op = eval_statement;
64
65         /* check-collection "default" */
66         if (!expression_allocate (&default_classification_rules -> data.eval,
67                                   MDL))
68                 log_fatal ("Can't allocate default check expression");
69         default_classification_rules -> data.eval -> op = expr_check;
70         default_classification_rules -> data.eval -> data.check =
71                 &default_collection;
72 }
73
74 void classify_client (packet)
75         struct packet *packet;
76 {
77         execute_statements ((struct binding_value **)0, packet,
78                             (struct lease *)0, (struct client_state *)0,
79                             packet -> options, (struct option_state *)0,
80                             &global_scope, default_classification_rules);
81 }
82
83 int check_collection (packet, lease, collection)
84         struct packet *packet;
85         struct lease *lease;
86         struct collection *collection;
87 {
88         struct class *class, *nc;
89         struct data_string data;
90         int matched = 0;
91         int status;
92         int ignorep;
93
94         for (class = collection -> classes; class; class = class -> nic) {
95 #if defined (DEBUG_CLASS_MATCHING)
96                 log_info ("checking against class %s...", class -> name);
97 #endif
98                 memset (&data, 0, sizeof data);
99
100                 /* If there is a "match if" expression, check it.   If
101                    we get a match, and there's no subclass expression,
102                    it's a match.   If we get a match and there is a subclass
103                    expression, then we check the submatch.   If it's not a
104                    match, that's final - we don't check the submatch. */
105
106                 if (class -> expr) {
107                         status = (evaluate_boolean_expression_result
108                                   (&ignorep, packet, lease,
109                                    (struct client_state *)0,
110                                    packet -> options, (struct option_state *)0,
111                                    lease ? &lease -> scope : &global_scope,
112                                    class -> expr));
113                         if (status) {
114                                 if (!class -> submatch) {
115                                         matched = 1;
116 #if defined (DEBUG_CLASS_MATCHING)
117                                         log_info ("matches class.");
118 #endif
119                                         classify (packet, class);
120                                         continue;
121                                 }
122                         } else
123                                 continue;
124                 }
125
126                 /* Check to see if the client matches an existing subclass.
127                    If it doesn't, and this is a spawning class, spawn a new
128                    subclass and put the client in it. */
129                 if (class -> submatch) {
130                         status = (evaluate_data_expression
131                                   (&data, packet, lease,
132                                    (struct client_state *)0,
133                                    packet -> options, (struct option_state *)0,
134                                    lease ? &lease -> scope : &global_scope,
135                                    class -> submatch, MDL));
136                         if (status && data.len) {
137                                 nc = (struct class *)0;
138                                 if (class_hash_lookup (&nc, class -> hash,
139                                                        (const char *)data.data,
140                                                        data.len, MDL)) {
141 #if defined (DEBUG_CLASS_MATCHING)
142                                         log_info ("matches subclass %s.",
143                                               print_hex_1 (data.len,
144                                                            data.data, 60));
145 #endif
146                                         data_string_forget (&data, MDL);
147                                         classify (packet, nc);
148                                         matched = 1;
149                                         class_dereference (&nc, MDL);
150                                         continue;
151                                 }
152                                 if (!class -> spawning) {
153                                         data_string_forget (&data, MDL);
154                                         continue;
155                                 }
156                                 /* XXX Write out the spawned class? */
157 #if defined (DEBUG_CLASS_MATCHING)
158                                 log_info ("spawning subclass %s.",
159                                       print_hex_1 (data.len, data.data, 60));
160 #endif
161                                 status = class_allocate (&nc, MDL);
162                                 group_reference (&nc -> group,
163                                                  class -> group, MDL);
164                                 class_reference (&nc -> superclass,
165                                                  class, MDL);
166                                 nc -> lease_limit = class -> lease_limit;
167                                 nc -> dirty = 1;
168                                 if (nc -> lease_limit) {
169                                         nc -> billed_leases =
170                                                 (dmalloc
171                                                  (nc -> lease_limit *
172                                                   sizeof (struct lease *),
173                                                   MDL));
174                                         if (!nc -> billed_leases) {
175                                                 log_error ("no memory for%s",
176                                                            " billing");
177                                                 data_string_forget
178                                                         (&nc -> hash_string,
179                                                          MDL);
180                                                 class_dereference (&nc, MDL);
181                                                 data_string_forget (&data,
182                                                                     MDL);
183                                                 continue;
184                                         }
185                                         memset (nc -> billed_leases, 0,
186                                                 (nc -> lease_limit *
187                                                  sizeof nc -> billed_leases));
188                                 }
189                                 data_string_copy (&nc -> hash_string, &data,
190                                                   MDL);
191                                 data_string_forget (&data, MDL);
192                                 if (!class -> hash)
193                                     class_new_hash (&class -> hash, 0, MDL);
194                                 class_hash_add (class -> hash,
195                                                 (const char *)
196                                                 nc -> hash_string.data,
197                                                 nc -> hash_string.len,
198                                                 nc, MDL);
199                                 classify (packet, nc);
200                                 class_dereference (&nc, MDL);
201                         }
202                 }
203         }
204         return matched;
205 }
206
207 void classify (packet, class)
208         struct packet *packet;
209         struct class *class;
210 {
211         if (packet -> class_count < PACKET_MAX_CLASSES)
212                 class_reference (&packet -> classes [packet -> class_count++],
213                                  class, MDL);
214         else
215                 log_error ("too many classes match %s",
216                       print_hw_addr (packet -> raw -> htype,
217                                      packet -> raw -> hlen,
218                                      packet -> raw -> chaddr));
219 }
220
221 isc_result_t find_class (struct class **class, const char *name,
222                          const char *file, int line)
223 {
224         struct collection *lp;
225         struct class *cp;
226
227         for (lp = collections; lp; lp = lp -> next) {
228                 for (cp = lp -> classes; cp; cp = cp -> nic)
229                         if (cp -> name && !strcmp (name, cp -> name)) {
230                                 return class_reference (class, cp, file, line);
231                         }
232         }
233         return ISC_R_NOTFOUND;
234 }
235
236 int unbill_class (lease, class)
237         struct lease *lease;
238         struct class *class;
239 {
240         int i;
241
242         for (i = 0; i < class -> lease_limit; i++)
243                 if (class -> billed_leases [i] == lease)
244                         break;
245         if (i == class -> lease_limit) {
246                 log_error ("lease %s unbilled with no billing arrangement.",
247                       piaddr (lease -> ip_addr));
248                 return 0;
249         }
250         class_dereference (&lease -> billing_class, MDL);
251         lease_dereference (&class -> billed_leases [i], MDL);
252         class -> leases_consumed--;
253         return 1;
254 }
255
256 int bill_class (lease, class)
257         struct lease *lease;
258         struct class *class;
259 {
260         int i;
261
262         if (lease -> billing_class) {
263                 log_error ("lease billed with existing billing arrangement.");
264                 unbill_class (lease, lease -> billing_class);
265         }
266
267         if (class -> leases_consumed == class -> lease_limit)
268                 return 0;
269
270         for (i = 0; i < class -> lease_limit; i++)
271                 if (!class -> billed_leases [i])
272                         break;
273
274         if (i == class -> lease_limit) {
275                 log_error ("class billing consumption disagrees with leases.");
276                 return 0;
277         }
278
279         lease_reference (&class -> billed_leases [i], lease, MDL);
280         class_reference (&lease -> billing_class, class, MDL);
281         class -> leases_consumed++;
282         return 1;
283 }