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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
ipgpc_create_action(ipp_action_id_t aid,nvlist_t ** nvlpp,ipp_flags_t flags)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
ipgpc_modify_action(ipp_action_id_t aid,nvlist_t ** nvlpp,ipp_flags_t flags)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
ipgpc_destroy_action(ipp_action_id_t aid,ipp_flags_t flags)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
ipgpc_info(ipp_action_id_t aid,int (* fn)(nvlist_t *,void *),void * arg,ipp_flags_t flags)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
ipgpc_invoke_action(ipp_action_id_t aid,ipp_packet_t * packet)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_inc_64(&ipgpc_epackets);
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 
451 	if (ill != NULL) {
452 		/*
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.
457 		 */
458 		if (IS_UNDER_IPMP(ill))
459 			pkt.if_index = ipmp_ill_get_ipmp_ifindex(ill);
460 		else
461 			pkt.if_index = ill->ill_phyint->phyint_ifindex;
462 		/* Got the field from the ILL, go ahead and refrele */
463 		ill_refrele(ill);
464 	} else {
465 		/* unknown if_index */
466 		pkt.if_index = IPGPC_UNSPECIFIED;
467 	}
468 
469 	if (ipgpc_debug > 5) {
470 		/* print pkt under high debug level */
471 #ifdef	IPGPC_DEBUG
472 		print_packet(af, &pkt);
473 #endif
474 	}
475 	if (ipgpc_debug > 3) {
476 		start = gethrtime(); /* start timer */
477 	}
478 
479 	/* classify this packet */
480 	out_class = ipgpc_classify(af, &pkt);
481 
482 	if (ipgpc_debug > 3) {
483 		end = gethrtime(); /* stop timer */
484 	}
485 
486 	/* ipgpc_classify will only return NULL if a memory error occured */
487 	if (out_class == NULL) {
488 		atomic_inc_64(&ipgpc_epackets);
489 		return (ENOMEM);
490 	}
491 
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)));
495 
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));
501 		return (rc);
502 	}
503 	return (ipp_packet_next(packet, IPP_ACTION_CONT));
504 }
505