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 }