xref: /illumos-gate/usr/src/uts/common/ipp/ipgpc/classifier.c (revision f63f7506be0210195779706f51c58646e568cc40)
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 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/kmem.h>
30 #include <sys/systm.h>
31 #include <sys/socket.h>
32 #include <sys/strsun.h>
33 #include <netinet/in.h>
34 #include <ipp/ipgpc/classifier.h>
35 #include <inet/ip.h>
36 #include <inet/ip6.h>
37 #include <net/if.h>
38 #include <inet/ipp_common.h>
39 
40 /* Implementation file for classifier used in ipgpc module */
41 
42 /*
43  * CHECK_MATCH_STATUS(match_status, slctrs_srchd, selector_mask)
44  *
45  * determines what the result of the selector search and what action needs to
46  * be taken next.
47  * if a NORMAL_MATCH occurs, business as usual NORMAL_MATCH
48  * if the selector was not searched because only DONTCARE keys are loaded,
49  * the selector is marked as not being searched
50  * otherwise, memory error occurred or no matches were found, classify()
51  * should return the error match status immediately
52  */
53 #define	CHECK_MATCH_STATUS(match_status, slctrs_srchd, selector_mask)	\
54 	(((match_status) == NORMAL_MATCH) ?			\
55 	(NORMAL_MATCH) :					\
56 	(((match_status) == DONTCARE_ONLY_MATCH) ?		\
57 	(*(slctrs_srchd) ^= (selector_mask), NORMAL_MATCH) :	\
58 	(match_status)))
59 
60 /* used to determine if an action instance already exists */
61 boolean_t ipgpc_action_exist = B_FALSE;
62 int ipgpc_debug = 0;		/* IPGPC debugging level */
63 
64 /* Statics */
65 static int common_classify(ipgpc_packet_t *, ht_match_t *, uint16_t *);
66 static void update_stats(int, uint_t);
67 static int bestmatch(ht_match_t *, uint16_t);
68 static void get_port_info(ipgpc_packet_t *, void *, int, mblk_t *);
69 
70 /*
71  * common_classify(packet, fid_table, slctrs_srchd)
72  *
73  * searches each of the common selectors
74  * - will return NORMAL_MATCH on sucess.  NO_MATCHES on error
75  */
76 static int
77 common_classify(ipgpc_packet_t *packet, ht_match_t *fid_table,
78     uint16_t *slctrs_srchd)
79 {
80 	int match_status;
81 	int if_grpnm_hv;
82 
83 	/* Find on packet direction */
84 	match_status =
85 	    ipgpc_findfilters(IPGPC_TABLE_DIR, packet->direction, fid_table);
86 	if (CHECK_MATCH_STATUS(match_status, slctrs_srchd,
87 	    ipgpc_table_list[DIR_IDX].info.mask) != NORMAL_MATCH) {
88 		return (match_status);
89 	}
90 
91 	/* Find on IF_INDEX of packet */
92 	match_status =
93 	    ipgpc_findfilters(IPGPC_TABLE_IF, packet->if_index, fid_table);
94 	if (CHECK_MATCH_STATUS(match_status, slctrs_srchd,
95 	    ipgpc_table_list[IF_IDX].info.mask) != NORMAL_MATCH) {
96 		return (match_status);
97 	}
98 
99 	/* Find on IF_GRPNM of packet */
100 	if (packet->if_groupname_len > 0) {
101 		if_grpnm_hv = name_hash(packet->if_groupname, TABLE_SIZE);
102 	} else {
103 		if_grpnm_hv = IPGPC_WILDCARD;
104 	}
105 	match_status =
106 	    ipgpc_findfilters(IPGPC_TABLE_IF_GRPNM, if_grpnm_hv, fid_table);
107 	if (CHECK_MATCH_STATUS(match_status, slctrs_srchd,
108 	    ipgpc_table_list[IF_GRPNM_IDX].info.mask) != NORMAL_MATCH) {
109 		return (match_status);
110 	}
111 
112 	/* Find on DS field */
113 	match_status =
114 	    ipgpc_findfilters(IPGPC_BA_DSID, packet->dsfield, fid_table);
115 	if (CHECK_MATCH_STATUS(match_status, slctrs_srchd,
116 	    ipgpc_ds_table_id.info.mask) != NORMAL_MATCH) {
117 		return (match_status);
118 	}
119 
120 	/* Find on UID of packet */
121 	match_status =
122 	    ipgpc_findfilters(IPGPC_TABLE_UID, packet->uid, fid_table);
123 	if (CHECK_MATCH_STATUS(match_status, slctrs_srchd,
124 	    ipgpc_table_list[UID_IDX].info.mask) != NORMAL_MATCH) {
125 		return (match_status);
126 	}
127 
128 	/* Find on PROJID of packet */
129 	match_status =
130 	    ipgpc_findfilters(IPGPC_TABLE_PROJID, packet->projid, fid_table);
131 	if (CHECK_MATCH_STATUS(match_status, slctrs_srchd,
132 	    ipgpc_table_list[PROJID_IDX].info.mask) != NORMAL_MATCH) {
133 		return (match_status);
134 	}
135 
136 	/* Find on IP Protocol field */
137 	if (packet->proto > 0) {
138 		match_status = ipgpc_findfilters(IPGPC_TABLE_PROTOID,
139 		    packet->proto, fid_table);
140 		if (CHECK_MATCH_STATUS(match_status, slctrs_srchd,
141 		    ipgpc_table_list[PROTOID_IDX].info.mask)
142 		    != NORMAL_MATCH) {
143 			return (match_status);
144 		}
145 	} else {
146 		/* skip search */
147 		*slctrs_srchd ^= ipgpc_table_list[PROTOID_IDX].info.mask;
148 	}
149 
150 	/* Find on IP Source Port field */
151 	if (packet->sport > 0) {
152 		match_status =
153 		    ipgpc_findfilters(IPGPC_TRIE_SPORTID, packet->sport,
154 			fid_table);
155 		if (CHECK_MATCH_STATUS(match_status, slctrs_srchd,
156 		    ipgpc_trie_list[IPGPC_TRIE_SPORTID].info.mask)
157 		    != NORMAL_MATCH) {
158 			return (match_status);
159 		}
160 	} else {
161 		/* skip search */
162 		*slctrs_srchd ^= ipgpc_trie_list[IPGPC_TRIE_SPORTID].info.mask;
163 	}
164 
165 	/* Find on IP Destination Port field */
166 	if (packet->dport > 0) {
167 		match_status =
168 		    ipgpc_findfilters(IPGPC_TRIE_DPORTID, packet->dport,
169 			fid_table);
170 		if (CHECK_MATCH_STATUS(match_status, slctrs_srchd,
171 		    ipgpc_trie_list[IPGPC_TRIE_DPORTID].info.mask)
172 		    != NORMAL_MATCH) {
173 			return (match_status);
174 		}
175 	} else {
176 		/* skip search */
177 		*slctrs_srchd ^= ipgpc_trie_list[IPGPC_TRIE_DPORTID].info.mask;
178 	}
179 	return (NORMAL_MATCH);
180 }
181 
182 /*
183  * update_stats(class_id, nbytes)
184  *
185  * if ipgpc_gather_stats == TRUE
186  * updates the statistics for class pointed to be the input classid
187  * and the global ipgpc kstats
188  * updates the last time the class was matched with the current hrtime value,
189  * number of packets and number of bytes with nbytes
190  */
191 static void
192 update_stats(int class_id, uint_t nbytes)
193 {
194 	if (ipgpc_gather_stats) {
195 		/* update global stats */
196 		BUMP_STATS(ipgpc_npackets);
197 		UPDATE_STATS(ipgpc_nbytes, nbytes);
198 		if (ipgpc_cid_list[class_id].aclass.gather_stats) {
199 			/* update per class stats */
200 			SET_STATS(ipgpc_cid_list[class_id].stats.last_match,
201 			    gethrtime());
202 			BUMP_STATS(ipgpc_cid_list[class_id].stats.npackets);
203 			UPDATE_STATS(ipgpc_cid_list[class_id].stats.nbytes,
204 			    nbytes);
205 		}
206 	}
207 }
208 
209 /*
210  * FREE_FID_TABLE(fid_table, p, q, i)
211  *
212  * searches fid_table for dynamically allocated memory and frees it
213  * p, q, i are temps
214  */
215 #define	FREE_FID_TABLE(fid_table, p, q, i)				\
216 	/* free all allocated memory in fid_table */			\
217 	for (i = 0; i < HASH_SIZE; ++i) {				\
218 		if (fid_table[i].next != NULL) {			\
219 			p = fid_table[i].next;				\
220 			while (p != NULL) {				\
221 				q = p;					\
222 				p = p->next;				\
223 				kmem_cache_free(ht_match_cache, q);	\
224 			}						\
225 		}							\
226 	}
227 
228 
229 /*
230  * ipgpc_classify(af, packet)
231  *
232  * The function that drives the packet classification algorithm.  Given a
233  * address family (either AF_INET or AF_INET6) the input packet structure
234  * is matched against all the selector structures.  For each search of
235  * a selector structure, all matched filters are collected.  Once all
236  * selectors are searched, the best match of all matched filters is
237  * determined.  Finally, the class associated with the best matching filter
238  * is returned.  If no filters were matched, the default class is returned.
239  * If a memory error occurred, NULL is returned.
240  */
241 ipgpc_class_t *
242 ipgpc_classify(int af, ipgpc_packet_t *packet)
243 {
244 	int match_status;
245 	uint16_t slctrs_srchd;
246 	int class_id;
247 	ht_match_t fid_table[HASH_SIZE];
248 	ht_match_t *p, *q;
249 	int i;
250 	int rc;
251 
252 	if (ipgpc_num_fltrs == 0) {
253 		/* zero filters are loaded, return default class */
254 		update_stats(ipgpc_def_class_id, packet->len);
255 		/*
256 		 * no need to free fid_table. Since zero selectors were
257 		 * searched and dynamic memory wasn't allocated.
258 		 */
259 		return (&ipgpc_cid_list[ipgpc_def_class_id].aclass);
260 	}
261 
262 	match_status = 0;
263 	slctrs_srchd = ALL_MATCH_MASK;
264 
265 	bzero(fid_table, sizeof (ht_match_t) * HASH_SIZE);
266 
267 	/* first search all address family independent selectors */
268 	if ((rc = common_classify(packet, fid_table, &slctrs_srchd)) !=
269 		NORMAL_MATCH) {
270 		/* free all dynamic allocated memory */
271 		FREE_FID_TABLE(fid_table, p, q, i);
272 		if (rc == NO_MATCHES) {
273 			update_stats(ipgpc_def_class_id, packet->len);
274 			return (&ipgpc_cid_list[ipgpc_def_class_id].aclass);
275 		} else {	/* memory error */
276 			return (NULL);
277 		}
278 	}
279 
280 	switch (af) {		/* switch off of address family */
281 	case AF_INET:
282 		/* Find on IPv4 Source Address field */
283 		match_status = ipgpc_findfilters(IPGPC_TRIE_SADDRID,
284 		    V4_PART_OF_V6(packet->saddr), fid_table);
285 		if (CHECK_MATCH_STATUS(match_status, &slctrs_srchd,
286 		    ipgpc_trie_list[IPGPC_TRIE_SADDRID].info.mask)
287 		    != NORMAL_MATCH) {
288 			/* free all dynamic allocated memory */
289 			FREE_FID_TABLE(fid_table, p, q, i);
290 			if (match_status == NO_MATCHES) {
291 				update_stats(ipgpc_def_class_id, packet->len);
292 				return (&ipgpc_cid_list[ipgpc_def_class_id].
293 				    aclass);
294 			} else { /* memory error */
295 				return (NULL);
296 			}
297 		}
298 		/* Find on IPv4 Destination Address field */
299 		match_status = ipgpc_findfilters(IPGPC_TRIE_DADDRID,
300 		    V4_PART_OF_V6(packet->daddr), fid_table);
301 		if (CHECK_MATCH_STATUS(match_status, &slctrs_srchd,
302 		    ipgpc_trie_list[IPGPC_TRIE_DADDRID].info.mask)
303 		    != NORMAL_MATCH) {
304 			/* free all dynamic allocated memory */
305 			FREE_FID_TABLE(fid_table, p, q, i);
306 			if (match_status == NO_MATCHES) {
307 				update_stats(ipgpc_def_class_id, packet->len);
308 				return (&ipgpc_cid_list[ipgpc_def_class_id].
309 				    aclass);
310 			} else { /* memory error */
311 				return (NULL);
312 			}
313 		}
314 		break;
315 	case AF_INET6:
316 		/* Find on IPv6 Source Address field */
317 		match_status = ipgpc_findfilters6(IPGPC_TRIE_SADDRID6,
318 		    packet->saddr, fid_table);
319 		if (CHECK_MATCH_STATUS(match_status, &slctrs_srchd,
320 		    ipgpc_trie_list[IPGPC_TRIE_SADDRID6].info.mask)
321 		    != NORMAL_MATCH) {
322 			/* free all dynamic allocated memory */
323 			FREE_FID_TABLE(fid_table, p, q, i);
324 			if (match_status == NO_MATCHES) {
325 				update_stats(ipgpc_def_class_id, packet->len);
326 				return (&ipgpc_cid_list[ipgpc_def_class_id].
327 				    aclass);
328 			} else { /* memory error */
329 				return (NULL);
330 			}
331 		}
332 		/* Find on IPv6 Destination Address field */
333 		match_status = ipgpc_findfilters6(IPGPC_TRIE_DADDRID6,
334 		    packet->daddr, fid_table);
335 		if (CHECK_MATCH_STATUS(match_status, &slctrs_srchd,
336 		    ipgpc_trie_list[IPGPC_TRIE_DADDRID6].info.mask)
337 		    != NORMAL_MATCH) {
338 			/* free all dynamic allocated memory */
339 			FREE_FID_TABLE(fid_table, p, q, i);
340 			if (match_status == NO_MATCHES) {
341 				update_stats(ipgpc_def_class_id, packet->len);
342 				return (&ipgpc_cid_list[ipgpc_def_class_id].
343 				    aclass);
344 			} else {
345 				return (NULL);
346 			}
347 		}
348 		break;
349 	default:
350 		ipgpc0dbg(("ipgpc_classify(): Unknown Address Family"));
351 		/* free all dynamic allocated memory */
352 		FREE_FID_TABLE(fid_table, p, q, i);
353 		return (NULL);
354 	}
355 
356 	/* zero selectors were searched, return default */
357 	if (slctrs_srchd == 0) {
358 		/*
359 		 * no need to free fid_table.  Since zero selectors were
360 		 * searched and dynamic memory wasn't allocated
361 		 */
362 		update_stats(ipgpc_def_class_id, packet->len);
363 		return (&ipgpc_cid_list[ipgpc_def_class_id].aclass);
364 	}
365 
366 	/* Perform best match search */
367 	class_id = bestmatch(fid_table, slctrs_srchd);
368 	/* free all dynamic allocated memory */
369 	FREE_FID_TABLE(fid_table, p, q, i);
370 
371 	update_stats(class_id, packet->len);
372 	return (&ipgpc_cid_list[class_id].aclass);
373 }
374 
375 /*
376  * bestmatch(fid_table, bestmask)
377  *
378  * determines the bestmatching filter in fid_table which matches the criteria
379  * described below and returns the class id
380  */
381 static int
382 bestmatch(ht_match_t *fid_table, uint16_t bestmask)
383 {
384 	int i, key;
385 	int bestmatch = -1;
386 	int oldbm = -1;
387 	uint32_t temp_prec;
388 	uint32_t temp_prio;
389 	uint64_t best_prio;
390 	uint64_t real_prio;
391 	ht_match_t *item;
392 
393 	for (i = 0; i < HASH_SIZE; ++i) {
394 		if (fid_table[i].key == 0) {
395 			continue;
396 		}
397 		for (item = &fid_table[i]; item != NULL; item = item->next) {
398 			/*
399 			 * BESTMATCH is:
400 			 * 1. Matches in all selectors searched
401 			 * 2. highest priority of filters that meet 1.
402 			 * 3. best precedence of filters that meet 2
403 			 *    with the same priority
404 			 */
405 			if ((key = item->key) == 0) {
406 				continue;
407 			}
408 			if (ipgpc_fid_list[key].info <= 0) {
409 				continue;
410 			}
411 
412 			/*
413 			 * check to see if fid has been inserted into a
414 			 * selector structure we did not search
415 			 * if so, then this filter is not a valid match
416 			 * and bestmatch() should continue
417 			 * this statement will == 0
418 			 * - a selector has been searched and this filter
419 			 *   either describes don't care or has inserted a
420 			 *   value into this selector structure
421 			 * - a selector has not been searched and this filter
422 			 *   has described don't care for this selector
423 			 */
424 			if (((~bestmask) & ipgpc_fid_list[key].insert_map)
425 			    != 0) {
426 				continue;
427 			}
428 
429 			/*
430 			 * tests to see if the map of selectors that
431 			 * were matched, equals the map of selectors
432 			 * structures this filter inserts into
433 			 */
434 			if (item->match_map != ipgpc_fid_list[key].insert_map) {
435 				continue;
436 			}
437 
438 			if (bestmatch == -1) { /* first matching filter */
439 				/* this filter becomes the bestmatch */
440 				temp_prio =
441 				    ipgpc_fid_list[key].filter.priority;
442 				temp_prec =
443 				    ipgpc_fid_list[key].filter.precedence;
444 				best_prio = ((uint64_t)temp_prio << 32) |
445 				    (uint64_t)~temp_prec;
446 				bestmatch = key;
447 				continue;
448 			}
449 
450 			/*
451 			 * calculate the real priority by combining priority
452 			 * and precedence
453 			 */
454 			real_prio =
455 			    ((uint64_t)ipgpc_fid_list[key].filter.priority
456 				<< 32) |
457 			    (uint64_t)~ipgpc_fid_list[key].filter.precedence;
458 
459 			/* check to see if this is the new bestmatch */
460 			if (real_prio > best_prio) {
461 				oldbm = bestmatch;
462 				ipgpc3dbg(("bestmatch: filter %s " \
463 				    "REJECTED because of better priority %d" \
464 				    " and/or precedence %d",
465 				    ipgpc_fid_list[oldbm].filter.filter_name,
466 				    ipgpc_fid_list[oldbm].filter.priority,
467 				    ipgpc_fid_list[oldbm].filter.precedence));
468 				best_prio = real_prio;
469 				bestmatch = key;
470 			} else {
471 				ipgpc3dbg(("bestmatch: filter %s " \
472 				    "REJECTED because of beter priority %d" \
473 				    " and/or precedence %d",
474 				    ipgpc_fid_list[key].filter.filter_name,
475 				    ipgpc_fid_list[key].filter.priority,
476 				    ipgpc_fid_list[key].filter.precedence));
477 			}
478 		}
479 	}
480 	if (bestmatch == -1) {	/* no best matches were found */
481 		ipgpc3dbg(("bestmatch: No filters ACCEPTED"));
482 		return (ipgpc_def_class_id);
483 	} else {
484 		ipgpc3dbg(("bestmatch: filter %s ACCEPTED with priority %d " \
485 		    "and precedence %d",
486 		    ipgpc_fid_list[bestmatch].filter.filter_name,
487 		    ipgpc_fid_list[bestmatch].filter.priority,
488 		    ipgpc_fid_list[bestmatch].filter.precedence));
489 		return (ipgpc_fid_list[bestmatch].class_id);
490 	}
491 }
492 
493 /*
494  * get_port_info(packet, iph, af, mp)
495  *
496  * Gets the source and destination ports from the ULP header, if present.
497  * If this is a fragment, don't try to get the port information even if this
498  * is the first fragment. The reason being we won't have this information
499  * in subsequent fragments and may end up classifying the first fragment
500  * differently than others. This is not desired.
501  * For IPv6 packets, step through the extension headers, if present, in
502  * order to get to the ULP header.
503  */
504 static void
505 get_port_info(ipgpc_packet_t *packet, void *iph, int af, mblk_t *mp)
506 {
507 	uint16_t *up;
508 
509 	if (af == AF_INET) {
510 		uint32_t u2, u1;
511 		uint_t iplen;
512 		ipha_t *ipha = (ipha_t *)iph;
513 
514 		u2 = ntohs(ipha->ipha_fragment_offset_and_flags);
515 		u1 = u2 & (IPH_MF | IPH_OFFSET);
516 		if (u1) {
517 			return;
518 		}
519 		iplen = (ipha->ipha_version_and_hdr_length & 0xF) << 2;
520 		up = (uint16_t *)(mp->b_rptr + iplen);
521 		packet->sport = (uint16_t)*up++;
522 		packet->dport = (uint16_t)*up;
523 	} else {	/* AF_INET6 */
524 		uint_t  length = IPV6_HDR_LEN;
525 		ip6_t *ip6h = (ip6_t *)iph;
526 		uint_t  ehdrlen;
527 		uint8_t *nexthdrp, *whereptr, *endptr;
528 		ip6_dest_t *desthdr;
529 		ip6_rthdr_t *rthdr;
530 		ip6_hbh_t *hbhhdr;
531 
532 		whereptr = ((uint8_t *)&ip6h[1]);
533 		endptr = mp->b_wptr;
534 		nexthdrp = &ip6h->ip6_nxt;
535 		while (whereptr < endptr) {
536 			switch (*nexthdrp) {
537 			case IPPROTO_HOPOPTS:
538 				hbhhdr = (ip6_hbh_t *)whereptr;
539 				ehdrlen = 8 * (hbhhdr->ip6h_len + 1);
540 				if ((uchar_t *)hbhhdr +  ehdrlen > endptr)
541 					return;
542 				nexthdrp = &hbhhdr->ip6h_nxt;
543 				break;
544 			case IPPROTO_DSTOPTS:
545 				desthdr = (ip6_dest_t *)whereptr;
546 				ehdrlen = 8 * (desthdr->ip6d_len + 1);
547 				if ((uchar_t *)desthdr +  ehdrlen > endptr)
548 					return;
549 				nexthdrp = &desthdr->ip6d_nxt;
550 				break;
551 			case IPPROTO_ROUTING:
552 				rthdr = (ip6_rthdr_t *)whereptr;
553 				ehdrlen =  8 * (rthdr->ip6r_len + 1);
554 				if ((uchar_t *)rthdr +  ehdrlen > endptr)
555 					return;
556 				nexthdrp = &rthdr->ip6r_nxt;
557 				break;
558 			case IPPROTO_FRAGMENT:
559 				return;
560 			case IPPROTO_TCP:
561 			case IPPROTO_UDP:
562 			case IPPROTO_SCTP:
563 				/*
564 				 * Verify we have at least ICMP_MIN_TP_HDR_LEN
565 				 * bytes of the ULP's header to get the port
566 				 * info.
567 				 */
568 				if (((uchar_t *)ip6h + length +
569 				    ICMP_MIN_TP_HDR_LEN)  > endptr) {
570 					return;
571 				}
572 				/* Get the protocol and the ports */
573 				packet->proto = *nexthdrp;
574 				up = (uint16_t *)((uchar_t *)ip6h + length);
575 				packet->sport = (uint16_t)*up++;
576 				packet->dport = (uint16_t)*up;
577 				return;
578 			case IPPROTO_ICMPV6:
579 			case IPPROTO_ENCAP:
580 			case IPPROTO_IPV6:
581 			case IPPROTO_ESP:
582 			case IPPROTO_AH:
583 				packet->proto = *nexthdrp;
584 				return;
585 			case IPPROTO_NONE:
586 			default:
587 				return;
588 			}
589 			length += ehdrlen;
590 			whereptr += ehdrlen;
591 		}
592 	}
593 }
594 
595 /*
596  * find_ids(packet, mp)
597  *
598  * attempt to discern the uid and projid of the originator of a packet by
599  * looking at the dblks making up the packet - yeuch!
600  *
601  * We do it by skipping any fragments with a credp of NULL (originated in
602  * kernel), taking the first value that isn't NULL to be the credp for the
603  * whole packet. We also suck the projid from the same fragment.
604  */
605 static void
606 find_ids(ipgpc_packet_t *packet, mblk_t *mp)
607 {
608 	cred_t *cr;
609 
610 	while (DB_CRED(mp) == NULL && mp->b_cont != NULL)
611 		mp = mp->b_cont;
612 
613 	if ((cr = DB_CRED(mp)) != NULL) {
614 		packet->uid = crgetuid(cr);
615 		packet->projid = crgetprojid(cr);
616 	} else {
617 		packet->uid = -1;
618 		packet->projid = -1;
619 	}
620 }
621 
622 /*
623  * parse_packet(packet, mp)
624  *
625  * parses the given message block into a ipgpc_packet_t structure
626  */
627 void
628 parse_packet(ipgpc_packet_t *packet, mblk_t *mp)
629 {
630 	ipha_t	*ipha;
631 
632 	/* parse message block for IP header and ports */
633 	ipha = (ipha_t *)mp->b_rptr; /* get ip header */
634 	V4_PART_OF_V6(packet->saddr) = (int32_t)ipha->ipha_src;
635 	V4_PART_OF_V6(packet->daddr) = (int32_t)ipha->ipha_dst;
636 	packet->dsfield = ipha->ipha_type_of_service;
637 	packet->proto = ipha->ipha_protocol;
638 	packet->sport = 0;
639 	packet->dport = 0;
640 	find_ids(packet, mp);
641 	packet->len = msgdsize(mp);
642 	/* parse out TCP/UDP ports, if appropriate */
643 	if ((packet->proto == IPPROTO_TCP) || (packet->proto == IPPROTO_UDP) ||
644 	    (packet->proto == IPPROTO_SCTP)) {
645 		get_port_info(packet, ipha, AF_INET, mp);
646 	}
647 }
648 
649 /*
650  * parse_packet6(packet, mp)
651  *
652  * parses the message block into a ipgpc_packet_t structure for IPv6 traffic
653  */
654 void
655 parse_packet6(ipgpc_packet_t *packet, mblk_t *mp)
656 {
657 	ip6_t *ip6h = (ip6_t *)mp->b_rptr;
658 
659 	/* parse message block for IP header and ports */
660 	bcopy(ip6h->ip6_src.s6_addr32, packet->saddr.s6_addr32,
661 	    sizeof (ip6h->ip6_src.s6_addr32));
662 	bcopy(ip6h->ip6_dst.s6_addr32, packet->daddr.s6_addr32,
663 	    sizeof (ip6h->ip6_dst.s6_addr32));
664 	/* Will be (re-)assigned in get_port_info */
665 	packet->proto = ip6h->ip6_nxt;
666 	packet->dsfield = __IPV6_TCLASS_FROM_FLOW(ip6h->ip6_vcf);
667 	find_ids(packet, mp);
668 	packet->len = msgdsize(mp);
669 	packet->sport = 0;
670 	packet->dport = 0;
671 	/* Need to pullup everything. */
672 	if (mp->b_cont != NULL) {
673 		if (!pullupmsg(mp, -1)) {
674 			ipgpc0dbg(("parse_packet6(): pullup error, can't " \
675 			    "find ports"));
676 			return;
677 		}
678 		ip6h = (ip6_t *)mp->b_rptr;
679 	}
680 	get_port_info(packet, ip6h, AF_INET6, mp);
681 }
682 
683 #ifdef	IPGPC_DEBUG
684 /*
685  * print_packet(af, packet)
686  *
687  * prints the contents of the packet structure for specified address family
688  */
689 void
690 print_packet(int af, ipgpc_packet_t *pkt)
691 {
692 	if (af == AF_INET) {
693 		char saddrbuf[INET_ADDRSTRLEN];
694 		char daddrbuf[INET_ADDRSTRLEN];
695 		ipgpc4dbg(("print_packet: saddr = %s, daddr = %s, sport = %u" \
696 		    ", dport = %u, proto = %u, dsfield = %x, uid = %d," \
697 		    " if_index = %d, if_groupname = %s, projid = %d, " \
698 		    "direction = %d",
699 		    inet_ntop(af, &V4_PART_OF_V6(pkt->saddr), saddrbuf,
700 			sizeof (saddrbuf)),
701 		    inet_ntop(af, &V4_PART_OF_V6(pkt->daddr), daddrbuf,
702 			sizeof (daddrbuf)),
703 		    ntohs(pkt->sport), ntohs(pkt->dport), pkt->proto,
704 		    pkt->dsfield, pkt->uid, pkt->if_index,
705 		    (pkt->if_groupname != NULL) ? pkt->if_groupname : "NULL",
706 		    pkt->projid, pkt->direction));
707 	} else if (af == AF_INET6) {
708 		char saddrbuf[INET6_ADDRSTRLEN];
709 		char daddrbuf[INET6_ADDRSTRLEN];
710 		ipgpc4dbg(("print_packet: saddr = %s, daddr = %s, sport = %u" \
711 		    ", dport = %u, proto = %u, dsfield = %x, uid = %d," \
712 		    " if_index = %d, if_groupname = %s, projid = %d, " \
713 		    "direction = %d",
714 		    inet_ntop(af, pkt->saddr.s6_addr32, saddrbuf,
715 			sizeof (saddrbuf)),
716 		    inet_ntop(af, pkt->daddr.s6_addr32, daddrbuf,
717 			sizeof (daddrbuf)),
718 		    ntohs(pkt->sport), ntohs(pkt->dport), pkt->proto,
719 		    pkt->dsfield, pkt->uid, pkt->if_index,
720 		    (pkt->if_groupname != NULL) ? pkt->if_groupname : "NULL",
721 		    pkt->projid, pkt->direction));
722 	}
723 }
724 #endif /* IPGPC_DEBUG */
725