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