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