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