xref: /illumos-gate/usr/src/uts/common/ipp/ipgpc/classifierddi.c (revision 99dda20867d903eec23291ba1ecb18a82d70096b)
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