1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/systm.h> 30 #include <sys/socket.h> 31 #include <netinet/in.h> 32 #include <sys/modctl.h> 33 #include <sys/sunddi.h> 34 #include <ipp/ipp.h> 35 #include <ipp/ipp_config.h> 36 #include <ipp/ipgpc/classifier.h> 37 #include <inet/ip.h> 38 #include <net/if.h> 39 #include <inet/ip_if.h> 40 #include <inet/ipp_common.h> 41 42 /* DDI file for ipgpc ipp module */ 43 44 /* protects against multiple configs */ 45 static kmutex_t ipgpc_config_lock; 46 47 static int ipgpc_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t); 48 static int ipgpc_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t); 49 static int ipgpc_destroy_action(ipp_action_id_t, ipp_flags_t); 50 static int ipgpc_info(ipp_action_id_t aid, int (*)(nvlist_t *, void *), void *, 51 ipp_flags_t); 52 static int ipgpc_invoke_action(ipp_action_id_t, ipp_packet_t *); 53 54 ipp_ops_t ipgpc_ops = { 55 IPPO_REV, 56 ipgpc_create_action, /* ippo_action_create */ 57 ipgpc_modify_action, /* ippo_action_modify */ 58 ipgpc_destroy_action, /* ippo_action_destroy */ 59 ipgpc_info, /* ippo_action_info */ 60 ipgpc_invoke_action /* ippo_action_invoke */ 61 }; 62 63 extern struct mod_ops mod_ippops; 64 65 /* 66 * Module linkage information for the kernel. 67 */ 68 static struct modlipp modlipp = { 69 &mod_ippops, 70 "IP Generic Packet Classifier (ipgpc) module 1.0", 71 &ipgpc_ops 72 }; 73 74 static struct modlinkage modlinkage = { 75 MODREV_1, 76 (void *)&modlipp, 77 NULL 78 }; 79 80 #define __FN__ "_init" 81 int 82 _init( 83 void) 84 { 85 int rc; 86 87 if (ipgpc_action_exist) { 88 return (EBUSY); 89 } 90 /* init mutexes */ 91 mutex_init(&ipgpc_config_lock, NULL, MUTEX_DRIVER, NULL); 92 mutex_init(&ipgpc_fid_list_lock, NULL, MUTEX_DRIVER, NULL); 93 mutex_init(&ipgpc_cid_list_lock, NULL, MUTEX_DRIVER, NULL); 94 mutex_init(&ipgpc_table_list_lock, NULL, MUTEX_DRIVER, NULL); 95 mutex_init(&ipgpc_ds_table_id.lock, NULL, MUTEX_DRIVER, NULL); 96 97 if ((rc = mod_install(&modlinkage)) != 0) { 98 /* clean up after fail */ 99 mutex_destroy(&ipgpc_config_lock); 100 mutex_destroy(&ipgpc_fid_list_lock); 101 mutex_destroy(&ipgpc_cid_list_lock); 102 mutex_destroy(&ipgpc_table_list_lock); 103 mutex_destroy(&ipgpc_ds_table_id.lock); 104 } 105 106 return (rc); 107 } 108 #undef __FN__ 109 110 #define __FN__ "_fini" 111 int 112 _fini( 113 void) 114 { 115 int rc; 116 117 if (ipgpc_action_exist) { 118 return (EBUSY); 119 } 120 121 if ((rc = mod_remove(&modlinkage)) != 0) { 122 return (rc); 123 } 124 /* destroy mutexes */ 125 mutex_destroy(&ipgpc_config_lock); 126 mutex_destroy(&ipgpc_fid_list_lock); 127 mutex_destroy(&ipgpc_cid_list_lock); 128 mutex_destroy(&ipgpc_table_list_lock); 129 mutex_destroy(&ipgpc_ds_table_id.lock); 130 return (rc); 131 } 132 #undef __FN__ 133 134 #define __FN__ "_info" 135 int 136 _info( 137 struct modinfo *modinfop) 138 { 139 return (mod_info(&modlinkage, modinfop)); 140 } 141 #undef __FN__ 142 143 /* 144 * ipgpc_create_action(aid, nvlpp, flags) 145 * 146 * creates a single instance of ipgpc, if one does not exist. If an action 147 * instance already exists, fail with EBUSY 148 * 149 * if nvlpp contains the name IPP_ACTION_STATS_ENABLE, then process it and 150 * determine if global stats should be collected 151 * 152 * the ipgpc_config_lock is taken to block out any other creates or destroys 153 * the are issued while the create is taking place 154 */ 155 /* ARGSUSED */ 156 static int 157 ipgpc_create_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags) 158 { 159 int rc; 160 uint32_t stat; 161 nvlist_t *nvlp; 162 163 nvlp = *nvlpp; 164 *nvlpp = NULL; /* nvlist should be NULL when this returns */ 165 166 /* only one ipgpc action instance can be loaded at once */ 167 if (ipgpc_action_exist) { 168 nvlist_free(nvlp); 169 return (EBUSY); 170 } else { 171 mutex_enter(&ipgpc_config_lock); 172 if (ipgpc_action_exist) { 173 nvlist_free(nvlp); 174 mutex_exit(&ipgpc_config_lock); 175 return (EBUSY); 176 } 177 /* check for action param IPP_ACTION_STATS_ENABLE */ 178 if ((rc = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, 179 &stat)) != 0) { 180 ipgpc_gather_stats = B_FALSE; /* disabled by default */ 181 } else { 182 ipgpc_gather_stats = (boolean_t)stat; 183 } 184 if ((rc = ipgpc_initialize(aid)) != 0) { 185 ipgpc0dbg(("ipgpc_create_action: ipgpc_intialize " \ 186 "error %d", rc)); 187 ipgpc_destroy(IPP_DESTROY_REF); 188 ipgpc_action_exist = B_FALSE; 189 nvlist_free(nvlp); 190 mutex_exit(&ipgpc_config_lock); 191 return (rc); 192 } 193 ipgpc_action_exist = B_TRUE; 194 nvlist_free(nvlp); 195 mutex_exit(&ipgpc_config_lock); 196 return (0); 197 } 198 } 199 200 /* 201 * ipgpc_modify_action 202 * 203 * modify an instance of ipgpc 204 * 205 * nvlpp will contain the configuration type to switch off of. Use this 206 * to determine what modification should be made. If the modification fails, 207 * return the appropriate error. 208 */ 209 /* ARGSUSED */ 210 static int 211 ipgpc_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags) 212 { 213 nvlist_t *nvlp; 214 int rc = 0; 215 uint8_t config_type; 216 uint32_t stat; 217 char *name; 218 int32_t filter_instance; 219 ipgpc_filter_t *filter; 220 ipgpc_class_t *aclass; 221 222 nvlp = *nvlpp; 223 *nvlpp = NULL; /* nvlist should be NULL when this returns */ 224 225 if ((rc = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type)) 226 != 0) { 227 nvlist_free(nvlp); 228 ipgpc0dbg(("ipgpc_modify_action: invalid configuration type")); 229 return (EINVAL); 230 } 231 232 switch (config_type) { 233 case IPP_SET: /* set an action parameter */ 234 if ((rc = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, 235 &stat)) != 0) { 236 nvlist_free(nvlp); 237 ipgpc0dbg(("ipgpc_modify_action: invalid IPP_SET " \ 238 "parameter")); 239 return (EINVAL); 240 } else { 241 ipgpc_gather_stats = (boolean_t)stat; 242 } 243 break; 244 case CLASSIFIER_ADD_FILTER: /* add a filter */ 245 filter = kmem_zalloc(sizeof (ipgpc_filter_t), KM_SLEEP); 246 if ((rc = ipgpc_parse_filter(filter, nvlp)) != 0) { 247 ipgpc0dbg(("ipgpc_modify_action: invalid filter")); 248 ipgpc_filter_destructor(filter); 249 kmem_free(filter, sizeof (ipgpc_filter_t)); 250 break; 251 } 252 /* parse class name */ 253 if ((rc = nvlist_lookup_string(nvlp, CLASSIFIER_CLASS_NAME, 254 &name)) != 0) { 255 ipgpc0dbg(("ipgpc_modify_action: class name missing")); 256 ipgpc_filter_destructor(filter); 257 kmem_free(filter, sizeof (ipgpc_filter_t)); 258 break; 259 } 260 rc = ipgpc_addfilter(filter, name, flags); 261 if (rc != 0) { 262 ipgpc_filter_destructor(filter); 263 } 264 kmem_free(filter, sizeof (ipgpc_filter_t)); 265 break; 266 case CLASSIFIER_ADD_CLASS: /* add a class */ 267 aclass = kmem_zalloc(sizeof (ipgpc_class_t), KM_SLEEP); 268 if ((rc = ipgpc_parse_class(aclass, nvlp)) != 0) { 269 ipgpc0dbg(("ipgpc_modify_action: invalid class")); 270 kmem_free(aclass, sizeof (ipgpc_class_t)); 271 break; 272 } 273 rc = ipgpc_addclass(aclass, flags); 274 kmem_free(aclass, sizeof (ipgpc_class_t)); 275 break; 276 case CLASSIFIER_REMOVE_FILTER: /* remove a filter */ 277 /* parse filter name */ 278 if ((rc = nvlist_lookup_string(nvlp, CLASSIFIER_FILTER_NAME, 279 &name)) != 0) { 280 ipgpc0dbg(("ipgpc_modify_action: filtername missing")); 281 break; 282 } 283 /* parse optional filter_instance */ 284 if (nvlist_lookup_int32(nvlp, IPGPC_FILTER_INSTANCE, 285 &filter_instance) != 0) { 286 filter_instance = -1; 287 } 288 rc = ipgpc_removefilter(name, filter_instance, flags); 289 break; 290 case CLASSIFIER_REMOVE_CLASS: /* remove a class */ 291 /* parse class name */ 292 if ((rc = nvlist_lookup_string(nvlp, CLASSIFIER_CLASS_NAME, 293 &name)) != 0) { 294 ipgpc0dbg(("ipgpc_modify_action: class name missing")); 295 break; 296 } 297 rc = ipgpc_removeclass(name, flags); 298 break; 299 case CLASSIFIER_MODIFY_FILTER: /* modify a filter */ 300 rc = ipgpc_modifyfilter(&nvlp, flags); 301 break; 302 case CLASSIFIER_MODIFY_CLASS: /* modify a class */ 303 rc = ipgpc_modifyclass(&nvlp, flags); 304 break; 305 default: /* invalid config type */ 306 nvlist_free(nvlp); 307 ipgpc0dbg(("ipgpc_modify_action:invalid configuration type %u", 308 config_type)); 309 return (EINVAL); 310 } 311 nvlist_free(nvlp); /* free the list */ 312 return (rc); /* nvlist is passed back NULL */ 313 } 314 315 /* 316 * ipgpc_destroy_action(aid, flags) 317 * 318 * action destructor for ipgpc 319 * 320 * Destroys an instance of the ipgpc action, if one exists. The 321 * ipgpc_action_lock is taken to block out any other destroys or creates 322 * that might be issued while the action is being destroyed 323 */ 324 /* ARGSUSED */ 325 static int 326 ipgpc_destroy_action(ipp_action_id_t aid, ipp_flags_t flags) 327 { 328 /* only destroy action if it exists */ 329 if (ipgpc_action_exist == B_TRUE) { 330 mutex_enter(&ipgpc_config_lock); 331 if (ipgpc_action_exist == B_FALSE) { 332 mutex_exit(&ipgpc_config_lock); 333 return (EBUSY); 334 } 335 ipgpc_action_exist = B_FALSE; 336 ipgpc_destroy(flags); 337 mutex_exit(&ipgpc_config_lock); 338 } 339 return (0); 340 } 341 342 /* 343 * ipgpc_info(aid, fn, arg) 344 * 345 * configuration quering function for ipgpc 346 * 347 * passes back the configuration of ipgpc through allocated nvlists 348 * all action paramaters, classes and filters are built into nvlists 349 * and passed to the function pointer fn with arg 350 */ 351 /* ARGSUSED */ 352 static int 353 ipgpc_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg, 354 ipp_flags_t flags) 355 { 356 int rc; 357 358 /* set parameters */ 359 if ((rc = ipgpc_params_info(fn, arg)) != 0) { 360 return (rc); 361 } 362 363 /* set all classes */ 364 if ((rc = ipgpc_classes_info(fn, arg)) != 0) { 365 return (rc); 366 } 367 368 /* set all filters */ 369 if ((rc = ipgpc_filters_info(fn, arg)) != 0) { 370 return (rc); 371 } 372 return (0); 373 } 374 375 /* 376 * ipgpc_invoke_action(aid, packet) 377 * 378 * packet processing function for ipgpc 379 * 380 * given packet the selector information is parsed and the classify 381 * function is called with those selectors. The classify function will 382 * return either a class or NULL, which represents a memory error and 383 * ENOMEM is returned. If the class returned is not NULL, the class and next 384 * action, associated with that class, are added to packet 385 */ 386 /* ARGSUSED */ 387 static int 388 ipgpc_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet) 389 { 390 ipgpc_class_t *out_class; 391 hrtime_t start, end; 392 mblk_t *mp = NULL; 393 ip_priv_t *priv = NULL; 394 ill_t *ill = NULL; 395 ipha_t *ipha; 396 ip_proc_t callout_pos; 397 int af; 398 int rc; 399 ipgpc_packet_t pkt; 400 uint_t ill_idx; 401 402 /* extract packet data */ 403 mp = ipp_packet_get_data(packet); 404 ASSERT(mp != NULL); 405 406 priv = (ip_priv_t *)ipp_packet_get_private(packet); 407 ASSERT(priv != NULL); 408 409 callout_pos = priv->proc; 410 ill_idx = priv->ill_index; 411 412 /* If we don't get an M_DATA, then return an error */ 413 if (mp->b_datap->db_type != M_DATA) { 414 if ((mp->b_cont != NULL) && 415 (mp->b_cont->b_datap->db_type == M_DATA)) { 416 mp = mp->b_cont; /* jump over the M_CTL into M_DATA */ 417 } else { 418 ipgpc0dbg(("ipgpc_invoke_action: no data\n")); 419 atomic_add_64(&ipgpc_epackets, 1); 420 return (EINVAL); 421 } 422 } 423 424 /* 425 * Translate the callout_pos into the direction the packet is traveling 426 */ 427 if (callout_pos != IPP_LOCAL_IN) { 428 if (callout_pos & IPP_LOCAL_OUT) { 429 callout_pos = IPP_LOCAL_OUT; 430 } else if (callout_pos & IPP_FWD_IN) { 431 callout_pos = IPP_FWD_IN; 432 } else { /* IPP_FWD_OUT */ 433 callout_pos = IPP_FWD_OUT; 434 } 435 } 436 437 /* The ill_index could be 0 when called from forwarding (read) path */ 438 if (ill_idx > 0) { 439 ill = ill_lookup_on_ifindex(ill_idx, B_FALSE, NULL, NULL, 440 NULL, NULL); 441 } 442 443 /* parse the packet from the message block */ 444 ipha = (ipha_t *)mp->b_rptr; 445 /* Determine IP Header Version */ 446 if (IPH_HDR_VERSION(ipha) == IPV4_VERSION) { 447 parse_packet(&pkt, mp); 448 af = AF_INET; 449 } else { 450 parse_packet6(&pkt, mp); 451 af = AF_INET6; 452 } 453 454 pkt.direction = callout_pos; /* set packet direction */ 455 456 if (ill != NULL) { 457 pkt.if_index = ill->ill_phyint->phyint_ifindex; 458 pkt.if_groupname_len = 459 ill->ill_phyint->phyint_groupname_len; 460 if (pkt.if_groupname_len > 0) { 461 pkt.if_groupname = 462 ill->ill_phyint->phyint_groupname; 463 } else { 464 pkt.if_groupname = NULL; 465 } 466 /* Got the fields from the ILL, go ahead and refrele */ 467 ill_refrele(ill); 468 } else { 469 /* unknown if_index and if_group */ 470 pkt.if_index = IPGPC_UNSPECIFIED; 471 pkt.if_groupname = NULL; 472 pkt.if_groupname_len = 0; 473 } 474 475 if (ipgpc_debug > 5) { 476 /* print pkt under high debug level */ 477 #ifdef IPGPC_DEBUG 478 print_packet(af, &pkt); 479 #endif 480 } 481 if (ipgpc_debug > 3) { 482 start = gethrtime(); /* start timer */ 483 } 484 485 /* classify this packet */ 486 out_class = ipgpc_classify(af, &pkt); 487 488 if (ipgpc_debug > 3) { 489 end = gethrtime(); /* stop timer */ 490 } 491 492 /* ipgpc_classify will only return NULL if a memory error occured */ 493 if (out_class == NULL) { 494 atomic_add_64(&ipgpc_epackets, 1); 495 return (ENOMEM); 496 } 497 498 ipgpc1dbg(("ipgpc_invoke_action: class = %s", out_class->class_name)); 499 /* print time to classify(..) */ 500 ipgpc2dbg(("ipgpc_invoke_action: time = %lld nsec\n", (end - start))); 501 502 if ((rc = ipp_packet_add_class(packet, out_class->class_name, 503 out_class->next_action)) != 0) { 504 atomic_add_64(&ipgpc_epackets, 1); 505 ipgpc0dbg(("ipgpc_invoke_action: ipp_packet_add_class " \ 506 "failed with error %d", rc)); 507 return (rc); 508 } 509 return (ipp_packet_next(packet, IPP_ACTION_CONT)); 510 } 511