xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_igmp.c (revision 2aeafac3612e19716bf8164f89c3c9196342979c)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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 <stdio.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <net/if.h>
34 #include <sys/stropts.h>
35 #include <sys/sysmacros.h>
36 #include <netinet/in_systm.h>
37 #include <netinet/in.h>
38 #include <netinet/ip.h>
39 #include <netinet/igmp.h>
40 #include <inet/ip.h>
41 #include <arpa/inet.h>
42 #include <netdb.h>
43 #include "snoop.h"
44 
45 static void interpret_igmpv3qry(struct igmp *, int);
46 static void interpret_igmpv3rpt(struct igmp *, int);
47 
48 
49 /*ARGSUSED*/
50 void
51 interpret_igmp(int flags, char *data, int iplen, int ilen)
52 {
53 	const char *pt;
54 	char *line;
55 	struct igmp *igmp = (struct igmp *)data;
56 	char addrstr[INET_ADDRSTRLEN];
57 
58 	if (ilen < IGMP_MINLEN) {
59 		/* incomplete header */
60 		line = get_sum_line();
61 		(void) snprintf(line, MAXLINE, "Malformed IGMP packet");
62 		return;
63 	}
64 
65 	switch (igmp->igmp_type) {
66 	case IGMP_MEMBERSHIP_QUERY:
67 		if (ilen == IGMP_MINLEN) {
68 			if (igmp->igmp_code == 0)
69 				pt = "v1 membership query";
70 			else
71 				pt = "v2 membership query";
72 		} else if (ilen >= IGMP_V3_QUERY_MINLEN) {
73 			pt = "v3 membership query";
74 		} else {
75 			pt = "Unknown membership query";
76 		}
77 		break;
78 	case IGMP_V1_MEMBERSHIP_REPORT:
79 		pt = "v1 membership report";
80 		break;
81 	case IGMP_V2_MEMBERSHIP_REPORT:
82 		pt = "v2 membership report";
83 		break;
84 	case IGMP_V3_MEMBERSHIP_REPORT:
85 		pt = "v3 membership report";
86 		break;
87 	case IGMP_V2_LEAVE_GROUP:
88 		pt = "v2 leave group";
89 		break;
90 
91 	default:
92 		pt = "Unknown";
93 		break;
94 	}
95 
96 	if (flags & F_SUM) {
97 		line = get_sum_line();
98 		(void) snprintf(line, MAXLINE, "IGMP %s", pt);
99 	}
100 
101 	if (flags & F_DTAIL) {
102 		show_header("IGMP:  ", "IGMP Header", ilen);
103 		show_space();
104 		(void) snprintf(get_line(0, 0), get_line_remain(),
105 		    "Type = %d (%s)", igmp->igmp_type, pt);
106 		(void) snprintf(get_line(0, 0), get_line_remain(),
107 		    "Max Response Time = %d", igmp->igmp_code);
108 		(void) snprintf(get_line(0, 0), get_line_remain(),
109 		    "Checksum = %x", ntohs(igmp->igmp_cksum));
110 
111 		if (igmp->igmp_type == IGMP_MEMBERSHIP_QUERY &&
112 		    ilen >= IGMP_V3_QUERY_MINLEN) {
113 			interpret_igmpv3qry(igmp, ilen);
114 		} else if (igmp->igmp_type == IGMP_V3_MEMBERSHIP_REPORT) {
115 			interpret_igmpv3rpt(igmp, ilen);
116 		} else {
117 			(void) snprintf(get_line(0, 0), get_line_remain(),
118 			    "Group = %s",
119 			    inet_ntop(AF_INET, &igmp->igmp_group.s_addr,
120 			    addrstr, INET_ADDRSTRLEN));
121 		}
122 
123 		show_space();
124 	}
125 }
126 
127 static void
128 interpret_igmpv3qry(struct igmp *igmp, int ilen)
129 {
130 	struct igmp3q *qry;
131 	struct in_addr *src;
132 	int rem = ilen;
133 	int srccnt;
134 	char addrstr[INET_ADDRSTRLEN];
135 
136 	if (ilen < sizeof (*qry)) {
137 		(void) snprintf(get_line(0, 0), get_line_remain(),
138 		    "Malformed IGMP Query");
139 		return;
140 	}
141 
142 	qry = (struct igmp3q *)igmp;
143 	rem -= sizeof (*qry);
144 	srccnt = ntohs(qry->igmp3q_numsrc);
145 	(void) snprintf(get_line(0, 0), get_line_remain(),
146 	    "Group = %s", inet_ntop(AF_INET, &qry->igmp3q_group, addrstr,
147 	    INET_ADDRSTRLEN));
148 	(void) snprintf(get_line(0, 0), get_line_remain(),
149 	    "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
150 
151 	src = (struct in_addr *)&qry[1];
152 	while (srccnt > 0 && rem >= sizeof (*src)) {
153 		rem -= sizeof (*src);
154 
155 		(void) snprintf(get_line(0, 0), get_line_remain(), "    %s",
156 		    inet_ntop(AF_INET, &src->s_addr, addrstr, INET_ADDRSTRLEN));
157 
158 		srccnt--;
159 		src++;
160 	}
161 }
162 
163 #define	MAX_IGMPV3_REPORT_TYPE	6
164 
165 const char *igmpv3rpt_types[] = {
166 	"<unknown>",
167 	"MODE_IS_INCLUDE",
168 	"MODE_IS_EXCLUDE",
169 	"CHANGE_TO_INCLUDE",
170 	"CHANGE_TO_EXCLUDE",
171 	"ALLOW_NEW_SOURCES",
172 	"BLOCK_OLD_SOURCES",
173 };
174 
175 static void
176 interpret_igmpv3rpt(struct igmp *igmp, int ilen)
177 {
178 	struct igmp3r *rpt;
179 	struct grphdr *grh;
180 	struct in_addr *src;
181 	int rem = ilen, auxlen;
182 	uint16_t grhcnt, srccnt;
183 	char addrstr[INET_ADDRSTRLEN];
184 
185 	if (ilen < sizeof (*rpt)) {
186 		(void) snprintf(get_line(0, 0), get_line_remain(),
187 		    "Malformed IGMPv3 Report");
188 		return;
189 	}
190 
191 	rpt = (struct igmp3r *)igmp;
192 	grh = (struct grphdr *)&rpt[1];
193 	grhcnt = ntohs(rpt->igmp3r_numrec);
194 	(void) snprintf(get_line(0, 0), get_line_remain(),
195 	    "%d Group Record%s:", grhcnt, (grhcnt == 1) ? "" : "s");
196 	rem -= sizeof (*rpt);
197 	while (grhcnt > 0 && rem >= sizeof (*grh)) {
198 		rem -= sizeof (*grh);
199 
200 		(void) snprintf(get_line(0, 0), get_line_remain(),
201 		    "Group = %s  type = %s", inet_ntop(AF_INET,
202 		    &grh->grphdr_group.s_addr, addrstr, INET_ADDRSTRLEN),
203 		    (grh->grphdr_type > MAX_IGMPV3_REPORT_TYPE) ?
204 		    "<unknown>" : igmpv3rpt_types[grh->grphdr_type]);
205 		srccnt = ntohs(grh->grphdr_numsrc);
206 		(void) snprintf(get_line(0, 0), get_line_remain(),
207 		    "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
208 
209 		src = (struct in_addr *)&grh[1];
210 		while (srccnt > 0 && rem >= sizeof (*src)) {
211 			rem -= sizeof (*src);
212 
213 			(void) snprintf(get_line(0, 0), get_line_remain(),
214 			    "    %s", inet_ntop(AF_INET, &src->s_addr, addrstr,
215 			    INET_ADDRSTRLEN));
216 
217 			srccnt--;
218 			src++;
219 		}
220 
221 		grhcnt--;
222 		auxlen = grh->grphdr_auxlen * 4;
223 		rem -= auxlen;
224 		grh = (struct grphdr *)((uint8_t *)src + auxlen);
225 	}
226 }
227