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