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