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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
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>
32 #include <ipp/ipp_config.h>
33 #include <ipp/ipgpc/classifier.h>
36 #include <inet/ip_if.h>
37 #include <inet/ipp_common.h>
39 /* DDI file for ipgpc ipp module */
41 /* protects against multiple configs */
42 static kmutex_t ipgpc_config_lock
;
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 *,
49 static int ipgpc_invoke_action(ipp_action_id_t
, ipp_packet_t
*);
51 ipp_ops_t ipgpc_ops
= {
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 */
60 extern struct mod_ops mod_ippops
;
63 * Module linkage information for the kernel.
65 static struct modlipp modlipp
= {
67 "IP Generic Packet Classifier (ipgpc) module 1.0",
71 static struct modlinkage modlinkage
= {
77 #define __FN__ "_init"
84 if (ipgpc_action_exist
) {
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
);
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
);
107 #define __FN__ "_fini"
114 if (ipgpc_action_exist
) {
118 if ((rc
= mod_remove(&modlinkage
)) != 0) {
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
);
131 #define __FN__ "_info"
134 struct modinfo
*modinfop
)
136 return (mod_info(&modlinkage
, modinfop
));
141 * ipgpc_create_action(aid, nvlpp, flags)
143 * creates a single instance of ipgpc, if one does not exist. If an action
144 * instance already exists, fail with EBUSY
146 * if nvlpp contains the name IPP_ACTION_STATS_ENABLE, then process it and
147 * determine if global stats should be collected
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
154 ipgpc_create_action(ipp_action_id_t aid
, nvlist_t
**nvlpp
, ipp_flags_t flags
)
161 *nvlpp
= NULL
; /* nvlist should be NULL when this returns */
163 /* only one ipgpc action instance can be loaded at once */
164 if (ipgpc_action_exist
) {
168 mutex_enter(&ipgpc_config_lock
);
169 if (ipgpc_action_exist
) {
171 mutex_exit(&ipgpc_config_lock
);
174 /* check for action param IPP_ACTION_STATS_ENABLE */
175 if ((rc
= nvlist_lookup_uint32(nvlp
, IPP_ACTION_STATS_ENABLE
,
177 ipgpc_gather_stats
= B_FALSE
; /* disabled by default */
179 ipgpc_gather_stats
= (boolean_t
)stat
;
181 if ((rc
= ipgpc_initialize(aid
)) != 0) {
182 ipgpc0dbg(("ipgpc_create_action: ipgpc_intialize " \
184 ipgpc_destroy(IPP_DESTROY_REF
);
185 ipgpc_action_exist
= B_FALSE
;
187 mutex_exit(&ipgpc_config_lock
);
190 ipgpc_action_exist
= B_TRUE
;
192 mutex_exit(&ipgpc_config_lock
);
198 * ipgpc_modify_action
200 * modify an instance of ipgpc
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.
208 ipgpc_modify_action(ipp_action_id_t aid
, nvlist_t
**nvlpp
, ipp_flags_t flags
)
215 int32_t filter_instance
;
216 ipgpc_filter_t
*filter
;
217 ipgpc_class_t
*aclass
;
220 *nvlpp
= NULL
; /* nvlist should be NULL when this returns */
222 if ((rc
= nvlist_lookup_byte(nvlp
, IPP_CONFIG_TYPE
, &config_type
))
225 ipgpc0dbg(("ipgpc_modify_action: invalid configuration type"));
229 switch (config_type
) {
230 case IPP_SET
: /* set an action parameter */
231 if ((rc
= nvlist_lookup_uint32(nvlp
, IPP_ACTION_STATS_ENABLE
,
234 ipgpc0dbg(("ipgpc_modify_action: invalid IPP_SET " \
238 ipgpc_gather_stats
= (boolean_t
)stat
;
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
));
249 /* parse class name */
250 if ((rc
= nvlist_lookup_string(nvlp
, CLASSIFIER_CLASS_NAME
,
252 ipgpc0dbg(("ipgpc_modify_action: class name missing"));
253 ipgpc_filter_destructor(filter
);
254 kmem_free(filter
, sizeof (ipgpc_filter_t
));
257 rc
= ipgpc_addfilter(filter
, name
, flags
);
259 ipgpc_filter_destructor(filter
);
261 kmem_free(filter
, sizeof (ipgpc_filter_t
));
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
));
270 rc
= ipgpc_addclass(aclass
, flags
);
271 kmem_free(aclass
, sizeof (ipgpc_class_t
));
273 case CLASSIFIER_REMOVE_FILTER
: /* remove a filter */
274 /* parse filter name */
275 if ((rc
= nvlist_lookup_string(nvlp
, CLASSIFIER_FILTER_NAME
,
277 ipgpc0dbg(("ipgpc_modify_action: filtername missing"));
280 /* parse optional filter_instance */
281 if (nvlist_lookup_int32(nvlp
, IPGPC_FILTER_INSTANCE
,
282 &filter_instance
) != 0) {
283 filter_instance
= -1;
285 rc
= ipgpc_removefilter(name
, filter_instance
, flags
);
287 case CLASSIFIER_REMOVE_CLASS
: /* remove a class */
288 /* parse class name */
289 if ((rc
= nvlist_lookup_string(nvlp
, CLASSIFIER_CLASS_NAME
,
291 ipgpc0dbg(("ipgpc_modify_action: class name missing"));
294 rc
= ipgpc_removeclass(name
, flags
);
296 case CLASSIFIER_MODIFY_FILTER
: /* modify a filter */
297 rc
= ipgpc_modifyfilter(&nvlp
, flags
);
299 case CLASSIFIER_MODIFY_CLASS
: /* modify a class */
300 rc
= ipgpc_modifyclass(&nvlp
, flags
);
302 default: /* invalid config type */
304 ipgpc0dbg(("ipgpc_modify_action:invalid configuration type %u",
308 nvlist_free(nvlp
); /* free the list */
309 return (rc
); /* nvlist is passed back NULL */
313 * ipgpc_destroy_action(aid, flags)
315 * action destructor for ipgpc
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
323 ipgpc_destroy_action(ipp_action_id_t aid
, ipp_flags_t flags
)
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
);
332 ipgpc_action_exist
= B_FALSE
;
333 ipgpc_destroy(flags
);
334 mutex_exit(&ipgpc_config_lock
);
340 * ipgpc_info(aid, fn, arg)
342 * configuration quering function for ipgpc
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
350 ipgpc_info(ipp_action_id_t aid
, int (*fn
)(nvlist_t
*, void *), void *arg
,
356 if ((rc
= ipgpc_params_info(fn
, arg
)) != 0) {
360 /* set all classes */
361 if ((rc
= ipgpc_classes_info(fn
, arg
)) != 0) {
365 /* set all filters */
366 if ((rc
= ipgpc_filters_info(fn
, arg
)) != 0) {
373 * ipgpc_invoke_action(aid, packet)
375 * packet processing function for ipgpc
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
385 ipgpc_invoke_action(ipp_action_id_t aid
, ipp_packet_t
*packet
)
387 ipgpc_class_t
*out_class
;
390 ip_priv_t
*priv
= NULL
;
393 ip_proc_t callout_pos
;
399 /* extract packet data */
400 mp
= ipp_packet_get_data(packet
);
403 priv
= (ip_priv_t
*)ipp_packet_get_private(packet
);
404 ASSERT(priv
!= NULL
);
406 callout_pos
= priv
->proc
;
407 ill_idx
= priv
->ill_index
;
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 */
415 ipgpc0dbg(("ipgpc_invoke_action: no data\n"));
416 atomic_inc_64(&ipgpc_epackets
);
422 * Translate the callout_pos into the direction the packet is traveling
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
;
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
);
441 parse_packet6(&pkt
, mp
);
445 pkt
.direction
= callout_pos
; /* set packet direction */
447 /* The ill_index could be 0 when called from forwarding (read) path */
449 ill
= ill_lookup_on_ifindex_global_instance(ill_idx
, B_FALSE
);
453 * Since all IPP actions in an IPMP group are performed
454 * relative to the IPMP group interface, if this is an
455 * underlying interface in an IPMP group, use the IPMP
456 * group interface's index.
458 if (IS_UNDER_IPMP(ill
))
459 pkt
.if_index
= ipmp_ill_get_ipmp_ifindex(ill
);
461 pkt
.if_index
= ill
->ill_phyint
->phyint_ifindex
;
462 /* Got the field from the ILL, go ahead and refrele */
465 /* unknown if_index */
466 pkt
.if_index
= IPGPC_UNSPECIFIED
;
469 if (ipgpc_debug
> 5) {
470 /* print pkt under high debug level */
472 print_packet(af
, &pkt
);
475 if (ipgpc_debug
> 3) {
476 start
= gethrtime(); /* start timer */
479 /* classify this packet */
480 out_class
= ipgpc_classify(af
, &pkt
);
482 if (ipgpc_debug
> 3) {
483 end
= gethrtime(); /* stop timer */
486 /* ipgpc_classify will only return NULL if a memory error occured */
487 if (out_class
== NULL
) {
488 atomic_inc_64(&ipgpc_epackets
);
492 ipgpc1dbg(("ipgpc_invoke_action: class = %s", out_class
->class_name
));
493 /* print time to classify(..) */
494 ipgpc2dbg(("ipgpc_invoke_action: time = %lld nsec\n", (end
- start
)));
496 if ((rc
= ipp_packet_add_class(packet
, out_class
->class_name
,
497 out_class
->next_action
)) != 0) {
498 atomic_inc_64(&ipgpc_epackets
);
499 ipgpc0dbg(("ipgpc_invoke_action: ipp_packet_add_class " \
500 "failed with error %d", rc
));
503 return (ipp_packet_next(packet
, IPP_ACTION_CONT
));