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 2006 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 <stdio.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #define	RIPVERSION	RIPv2
37 #include <protocols/routed.h>
38 #include "snoop.h"
39 
40 static const char *show_cmd(int);
41 static int get_numtokens(unsigned int);
42 static const struct rip_sec_entry *rip_next_sec_entry(
43     const struct rip_sec_entry *, int);
44 
45 int
46 interpret_rip(int flags, struct rip *rip, int fraglen)
47 {
48 	const struct netinfo *nip;
49 	const struct entryinfo *ep;
50 	const struct netauth *nap;
51 	const struct rip_sec_entry *rsep, *rsn;
52 	const struct rip_emetric *rep;
53 	const uint32_t *tokp;
54 	int len, count;
55 	const char *cmdstr, *auth;
56 	struct in_addr dst;
57 	uint32_t mval;
58 	const struct sockaddr_in *sin;
59 	/* Room for IP destination + "/" + IP mask */
60 	char addrstr[15+1+15+1];
61 	/* Room for "RIPv" + uint8_t as %d */
62 	char ripvers[4+3+1];
63 
64 	/* RIP header is 4 octets long */
65 	if ((len = fraglen - 4) < 0)
66 		return (0);
67 
68 	if (flags & F_SUM) {
69 		switch (rip->rip_cmd) {
70 		case RIPCMD_REQUEST:	cmdstr = "C";		break;
71 		case RIPCMD_RESPONSE:	cmdstr = "R";		break;
72 		case RIPCMD_TRACEON:	cmdstr = "Traceon";	break;
73 		case RIPCMD_TRACEOFF:	cmdstr = "Traceoff";	break;
74 		case RIPCMD_POLL:	cmdstr = "Poll";	break;
75 		case RIPCMD_POLLENTRY:	cmdstr = "Poll entry";	break;
76 		case RIPCMD_SEC_RESPONSE: cmdstr = "R - SEC";	break;
77 		case RIPCMD_SEC_T_RESPONSE: cmdstr = "R - SEC_T"; break;
78 		default: cmdstr = "?"; break;
79 		}
80 
81 		if (rip->rip_vers == RIPv1)
82 			(void) strlcpy(ripvers, "RIP", sizeof (ripvers));
83 		else
84 			(void) snprintf(ripvers, sizeof (ripvers), "RIPv%d",
85 			    rip->rip_vers);
86 
87 		switch (rip->rip_cmd) {
88 		case RIPCMD_REQUEST:
89 		case RIPCMD_RESPONSE:
90 		case RIPCMD_POLL:
91 			nip = rip->rip_nets;
92 			auth = "";
93 			if (len >= sizeof (*nip) &&
94 			    nip->n_family == RIP_AF_AUTH) {
95 				nap = (struct netauth *)nip;
96 				len -= sizeof (*nip);
97 				if (nap->a_type == RIP_AUTH_MD5 &&
98 				    len >= ntohs(nap->au.a_md5.md5_auth_len))
99 					len -= ntohs(nap->au.a_md5.
100 					    md5_auth_len);
101 				auth = " +Auth";
102 			}
103 			count = len / sizeof (*nip);
104 			len %= sizeof (*nip);
105 			(void) snprintf(get_sum_line(), MAXLINE,
106 			    "%s %s (%d destinations%s%s)", ripvers, cmdstr,
107 			    count, (len != 0 ? "?" : ""), auth);
108 			break;
109 
110 		case RIPCMD_TRACEON:
111 		case RIPCMD_TRACEOFF:
112 			(void) snprintf(get_sum_line(), MAXLINE,
113 			    "%s %s File=\"%.*s\"", ripvers, cmdstr, len,
114 			    rip->rip_tracefile);
115 			len = 0;
116 			break;
117 
118 		case RIPCMD_SEC_RESPONSE:
119 		case RIPCMD_SEC_T_RESPONSE:
120 			if (len < sizeof (rip->rip_tsol.rip_generation))
121 				break;
122 			len -= sizeof (rip->rip_tsol.rip_generation);
123 			count = 0;
124 			rsep = rip->rip_tsol.rip_sec_entry;
125 			while (len > 0) {
126 				rsn = rip_next_sec_entry(rsep, len);
127 				if (rsn == NULL)
128 					break;
129 				len -= (const char *)rsn - (const char *)rsep;
130 				rsep = rsn;
131 				count++;
132 			}
133 			(void) snprintf(get_sum_line(), MAXLINE,
134 			    "%s %s (%d destinations%s)", ripvers, cmdstr,
135 			    count, (len != 0 ? "?" : ""));
136 			break;
137 
138 		default:
139 			(void) snprintf(get_sum_line(), MAXLINE,
140 			    "%s %d (%s)", ripvers, rip->rip_cmd, cmdstr);
141 			len = 0;
142 			break;
143 		}
144 	}
145 
146 	if (flags & F_DTAIL) {
147 
148 		len = fraglen - 4;
149 		show_header("RIP:  ", "Routing Information Protocol", fraglen);
150 		show_space();
151 		(void) snprintf(get_line(0, 0), get_line_remain(),
152 		    "Opcode = %d (%s)", rip->rip_cmd,
153 		    show_cmd(rip->rip_cmd));
154 		(void) snprintf(get_line(0, 0), get_line_remain(),
155 		    "Version = %d", rip->rip_vers);
156 
157 		switch (rip->rip_cmd) {
158 		case RIPCMD_REQUEST:
159 		case RIPCMD_RESPONSE:
160 		case RIPCMD_POLL:
161 			show_space();
162 			(void) snprintf(get_line(0, 0), get_line_remain(),
163 			    "Destination                     Next Hop        "
164 			    "Tag    Metric");
165 			for (nip = rip->rip_nets; len >= sizeof (*nip); nip++,
166 			    len -= sizeof (*nip)) {
167 				if (nip->n_family == RIP_AF_AUTH) {
168 					nap = (const struct netauth *)nip;
169 					if (nap->a_type == RIP_AUTH_NONE) {
170 						(void) snprintf(get_line
171 						    ((char *)nip - dlc_header,
172 							sizeof (*nip)),
173 						    get_line_remain(),
174 						    " *** Auth None");
175 					} else if (nap->a_type == RIP_AUTH_PW) {
176 						(void) snprintf(get_line
177 						    ((char *)nip - dlc_header,
178 							sizeof (*nip)),
179 						    get_line_remain(),
180 						    " *** Auth PW \"%.*s\"",
181 						    RIP_AUTH_PW_LEN,
182 						    nap->au.au_pw);
183 					} else if (nap->a_type ==
184 					    RIP_AUTH_MD5) {
185 						(void) snprintf(get_line(0, 0),
186 						    get_line_remain(),
187 						    " *** Auth MD5 pkt len %d, "
188 						    "keyid %d, sequence %08lX, "
189 						    "authlen %d",
190 						    ntohs(nap->au.a_md5.
191 							md5_pkt_len),
192 						    nap->au.a_md5.md5_keyid,
193 						    (long)ntohl(nap->au.a_md5.
194 							md5_seqno),
195 						    ntohs(nap->au.a_md5.
196 							md5_auth_len));
197 						if (len - sizeof (*nip) >=
198 						    ntohs(nap->au.a_md5.
199 						    md5_auth_len))
200 							len -= ntohs(nap->au.
201 							    a_md5.md5_auth_len);
202 						else
203 							len = sizeof (*nip);
204 					} else {
205 						(void) snprintf(get_line
206 						    ((char *)nip - dlc_header,
207 							sizeof (*nip)),
208 						    get_line_remain(),
209 						    " *** Auth Type %d?",
210 						    ntohs(nap->a_type));
211 					}
212 					continue;
213 				}
214 				if (nip->n_family == RIP_AF_UNSPEC &&
215 				    rip->rip_cmd == RIPCMD_REQUEST) {
216 					(void) snprintf(get_line(0, 0),
217 					    get_line_remain(),
218 					    " *** All routes");
219 					continue;
220 				}
221 				if (nip->n_family != RIP_AF_INET) {
222 					(void) snprintf(get_line(0, 0),
223 					    get_line_remain(),
224 					    " *** Address Family %d?",
225 					    ntohs(nip->n_family));
226 					continue;
227 				}
228 				if (nip->n_dst == htonl(RIP_DEFAULT)) {
229 					(void) strcpy(addrstr, "default");
230 				} else {
231 					dst.s_addr = nip->n_dst;
232 					(void) strlcpy(addrstr, inet_ntoa(dst),
233 					    sizeof (addrstr));
234 				}
235 				if (nip->n_dst != htonl(RIP_DEFAULT) &&
236 				    rip->rip_vers >= RIPv2) {
237 					count = strlen(addrstr);
238 					mval = ntohl(nip->n_mask);
239 					/* LINTED */
240 					if (mval == INADDR_ANY) {
241 						/* No mask */;
242 					} else if ((mval + (mval & -mval)) ==
243 					    0) {
244 						(void) snprintf(addrstr + count,
245 						    sizeof (addrstr) - count,
246 						    "/%d", 33 - ffs(mval));
247 					} else {
248 						dst.s_addr = nip->n_mask;
249 						(void) snprintf(addrstr + count,
250 						    sizeof (addrstr) - count,
251 						    "/%s", inet_ntoa(dst));
252 					}
253 				}
254 				dst.s_addr = nip->n_nhop;
255 				mval = ntohl(nip->n_metric);
256 				(void) snprintf(get_line(0, 0),
257 				    get_line_remain(),
258 				    "%-31s %-15s %-6d %d%s",
259 				    addrstr,
260 				    dst.s_addr == htonl(INADDR_ANY) ?
261 				    "--" : addrtoname(AF_INET, &dst),
262 				    ntohs(nip->n_tag),
263 				    mval,
264 				    (mval == HOPCNT_INFINITY ?
265 					" (not reachable)" : ""));
266 			}
267 			break;
268 
269 		case RIPCMD_POLLENTRY:
270 			if (len < sizeof (*ep))
271 				break;
272 			len -= sizeof (*ep);
273 			ep = (const struct entryinfo *)rip->rip_nets;
274 			/* LINTED */
275 			sin = (const struct sockaddr_in *)&ep->rtu_dst;
276 			(void) snprintf(get_line(0, 0), get_line_remain(),
277 			    "Destination = %s %s",
278 			    inet_ntoa(sin->sin_addr),
279 			    addrtoname(AF_INET, (void *)&sin->sin_addr));
280 			/* LINTED */
281 			sin = (const struct sockaddr_in *)&ep->rtu_router;
282 			(void) snprintf(get_line(0, 0), get_line_remain(),
283 			    "Router      = %s %s",
284 			    inet_ntoa(sin->sin_addr),
285 			    addrtoname(AF_INET, (void *)&sin->sin_addr));
286 			(void) snprintf(get_line(0, 0), get_line_remain(),
287 			    "Flags = %4x", (unsigned)ep->rtu_flags);
288 			(void) snprintf(get_line(0, 0), get_line_remain(),
289 			    "State = %d", ep->rtu_state);
290 			(void) snprintf(get_line(0, 0), get_line_remain(),
291 			    "Timer = %d", ep->rtu_timer);
292 			(void) snprintf(get_line(0, 0), get_line_remain(),
293 			    "Metric = %d", ep->rtu_metric);
294 			(void) snprintf(get_line(0, 0), get_line_remain(),
295 			    "Int flags = %8x", ep->int_flags);
296 			(void) snprintf(get_line(0, 0), get_line_remain(),
297 			    "Int name = \"%.*s\"", sizeof (ep->int_name),
298 			    ep->int_name);
299 			break;
300 
301 		case RIPCMD_SEC_RESPONSE:
302 		case RIPCMD_SEC_T_RESPONSE:
303 			if (len < sizeof (rip->rip_tsol.rip_generation))
304 				break;
305 			len -= sizeof (rip->rip_tsol.rip_generation);
306 			show_space();
307 			(void) snprintf(get_line(0, 0), get_line_remain(),
308 			    "Generation = %u",
309 			    (unsigned)ntohl(rip->rip_tsol.rip_generation));
310 			rsep = rip->rip_tsol.rip_sec_entry;
311 			(void) snprintf(get_line(0, 0), get_line_remain(),
312 			    "Address         E-METRIC");
313 			rsep = rip->rip_tsol.rip_sec_entry;
314 			while (len > 0) {
315 				char *cp;
316 				int blen, num;
317 
318 				rsn = rip_next_sec_entry(rsep, len);
319 				if (rsn == NULL)
320 					break;
321 				dst.s_addr = rsep->rip_dst;
322 				cp = get_line(0, 0);
323 				blen = get_line_remain();
324 				(void) snprintf(cp, blen, "%-16s ",
325 				    inet_ntoa(dst));
326 				cp += 17;
327 				blen -= 17;
328 				rep = rsep->rip_emetric;
329 				for (count = ntohl(rsep->rip_count); count > 0;
330 				    count--) {
331 					(void) snprintf(cp, blen, "metric=%d",
332 					    ntohs(rep->rip_metric));
333 					blen -= strlen(cp);
334 					cp += strlen(cp);
335 					tokp = rep->rip_token;
336 					num = get_numtokens(
337 					    ntohs(rep->rip_mask));
338 					/* advance to the next emetric */
339 					rep = (const struct rip_emetric *)
340 					    &rep->rip_token[num];
341 					if (num > 0) {
342 						(void) snprintf(cp, blen,
343 						    ",tokens=%lx",
344 						    (long)ntohl(*tokp));
345 						tokp++;
346 						num--;
347 					} else {
348 						(void) strlcpy(cp, ",no tokens",
349 						    blen);
350 					}
351 					while (num > 0) {
352 						blen -= strlen(cp);
353 						cp += strlen(cp);
354 						(void) snprintf(cp, blen,
355 						    ",%lx",
356 						    (long)ntohl(*tokp));
357 						tokp++;
358 						num--;
359 					}
360 					blen -= strlen(cp);
361 					cp += strlen(cp);
362 				}
363 				if (rsep->rip_count == 0) {
364 					(void) strlcpy(cp,
365 					    "NULL (not reachable)", blen);
366 				}
367 				len -= (const char *)rsn - (const char *)rsep;
368 				rsep = rsn;
369 			}
370 			break;
371 
372 		case RIPCMD_TRACEON:
373 		case RIPCMD_TRACEOFF:
374 			(void) snprintf(get_line(0, 0), get_line_remain(),
375 			    "Trace file = %.*s", len, rip->rip_tracefile);
376 			len = 0;
377 			break;
378 		}
379 	}
380 
381 	return (fraglen - len);
382 }
383 
384 static const char *
385 show_cmd(int c)
386 {
387 	switch (c) {
388 	case RIPCMD_REQUEST:
389 		return ("route request");
390 	case RIPCMD_RESPONSE:
391 		return ("route response");
392 	case RIPCMD_TRACEON:
393 		return ("route trace on");
394 	case RIPCMD_TRACEOFF:
395 		return ("route trace off");
396 	case RIPCMD_POLL:
397 		return ("route poll");
398 	case RIPCMD_POLLENTRY:
399 		return ("route poll entry");
400 	case RIPCMD_SEC_RESPONSE:
401 		return ("route sec response");
402 	case RIPCMD_SEC_T_RESPONSE:
403 		return ("route sec_t response");
404 	}
405 	return ("?");
406 }
407 
408 static int
409 get_numtokens(unsigned int mask)
410 {
411 	int num = 0;
412 
413 	while (mask != 0) {
414 		num++;
415 		mask &= mask - 1;
416 	}
417 	return (num);
418 }
419 
420 static const struct rip_sec_entry *
421 rip_next_sec_entry(const struct rip_sec_entry *rsep, int len)
422 {
423 	const struct rip_emetric *rep;
424 	const char *limit = (const char *)rsep + len;
425 	long count;
426 
427 	if ((const char *)(rep = rsep->rip_emetric) > limit)
428 		return (NULL);
429 	count = ntohl(rsep->rip_count);
430 	while (count > 0) {
431 		if ((const char *)rep->rip_token > limit)
432 			return (NULL);
433 		rep = (struct rip_emetric *)
434 		    &rep->rip_token[get_numtokens(ntohs(rep->rip_mask))];
435 		if ((const char *)rep > limit)
436 			return (NULL);
437 		count--;
438 	}
439 	return ((const struct rip_sec_entry *)rep);
440 }
441