]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/traceroute/findsaddr-mib.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / traceroute / findsaddr-mib.c
1 /*
2  * Copyright (c) 2000
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the Computer Systems
16  *      Engineering Group at Lawrence Berkeley Laboratory.
17  * 4. Neither the name of the University nor of the Laboratory may be used
18  *    to endorse or promote products derived from this software without
19  *    specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 /* Seriously complex Solaris mib2 code */
35
36 #ifndef lint
37 static const char rcsid[] =
38     "@(#) $Id: findsaddr-mib.c,v 1.2 2000/12/13 21:31:49 leres Exp $ (LBL)";
39 #endif
40
41 #include <sys/param.h>
42 #include <sys/file.h>
43 #include <sys/ioctl.h>
44 #include <sys/socket.h>
45 #ifdef HAVE_SYS_SOCKIO_H
46 #include <sys/sockio.h>
47 #endif
48 #include <sys/time.h>                           /* concession to AIX */
49 #include <sys/stream.h>
50 #include <sys/tihdr.h>
51 #include <sys/tiuser.h>
52
53 #if __STDC__
54 struct mbuf;
55 struct rtentry;
56 #endif
57
58 #include <net/if.h>
59 #include <net/route.h>
60
61 #include <netinet/in.h>
62
63 #include <inet/common.h>
64 #include <inet/mib2.h>
65 #include <inet/ip.h>
66 #include <inet/arp.h>
67
68 #include <arpa/inet.h>
69
70 #include <errno.h>
71 #include <fcntl.h>
72 #ifdef HAVE_MALLOC_H
73 #include <malloc.h>
74 #endif
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include <stropts.h>
79 #include <unistd.h>
80
81 #include "gnuc.h"
82 #ifdef HAVE_OS_PROTO_H
83 #include "os-proto.h"
84 #endif
85
86 #include "findsaddr.h"
87
88 /* Compatibility with older versions of Solaris */
89 #ifndef IRE_CACHE
90 #define IRE_CACHE IRE_ROUTE
91 #endif
92
93 #ifndef T_CURRENT
94 #define T_CURRENT MI_T_CURRENT
95 #endif
96
97 struct routelist {
98         struct routelist *next;
99         u_int32_t dest;
100         u_int32_t mask;
101         u_int32_t gate;
102         char ifname[64];
103 };
104
105 /* Forwards */
106 static struct routelist *getroutelist(char *);
107 static void freeroutelist(struct routelist *);
108
109 /*
110  * Return the source address for the given destination address
111  *
112  * Since solaris doesn't report the interface associated with every
113  * route, we have to make two passes over the routing table.  The
114  * first pass should yield a host, net, default or interface route.
115  * If we find an interface route we're done. If not, we need to
116  * make a second pass to find the interface route for the gateway
117  * in the host, net, default route we found in the first pass.
118  *
119  * So instead of making a single pass through the tables as they
120  * are retrieved from the kernel, we must build a linked list...
121  */
122 const char *
123 findsaddr(register const struct sockaddr_in *to,
124     register struct sockaddr_in *from)
125 {
126         register struct routelist *rl, *rl2, *routelist;
127         static char errbuf[512];
128         u_int32_t mask, gate;
129
130         /* Get the routing table */
131         routelist = getroutelist(errbuf);
132         if (routelist == NULL)
133                 return (errbuf);
134
135         /* First pass; look for a route that matches */
136         mask = 0;
137         rl2 = NULL;
138         for (rl = routelist; rl != NULL; rl = rl->next) {
139                 if ((to->sin_addr.s_addr & rl->mask) == rl->dest &&
140                     (rl->mask > mask || mask == 0) &&
141                     rl->gate != 0) {
142                         mask = rl->mask;
143                         rl2 = rl;
144                 }
145         }
146         if (rl2 == NULL) {
147                 freeroutelist(routelist);
148                 sprintf(errbuf, "%s: %.128s",
149                     inet_ntoa(to->sin_addr), strerror(EHOSTUNREACH));
150                 return (errbuf);
151         }
152
153         /* We're done if we got one with an interface */
154         if (rl2->ifname[0] != '\0') {
155                 freeroutelist(routelist);
156                 from->sin_addr.s_addr = rl2->gate;
157                 return (NULL);
158         }
159
160         /* First pass; look for a route that matches the gateway we found */
161         mask = 0;
162         gate = rl2->gate;
163         rl2 = NULL;
164         for (rl = routelist; rl != NULL; rl = rl->next) {
165                 if ((gate & rl->mask) == rl->dest &&
166                     (rl->mask > mask || mask == 0) &&
167                     rl->gate != 0 &&
168                     rl->ifname[0] != '\0') {
169                         mask = rl->mask;
170                         rl2 = rl;
171                 }
172         }
173         if (rl2 == NULL) {
174                 freeroutelist(routelist);
175                 sprintf(errbuf, "%s: %.128s (second pass)",
176                     inet_ntoa(to->sin_addr), strerror(EHOSTUNREACH));
177                 return (errbuf);
178         }
179
180         from->sin_addr.s_addr = rl2->gate;
181         freeroutelist(routelist);
182         return (NULL);
183 }
184
185 /* Request mib */
186 struct mibrq {
187         struct T_optmgmt_req req;
188         struct opthdr hdr;
189 };
190
191 /* Reply mib */
192 struct mibrep {
193         struct T_optmgmt_ack ack;
194         struct opthdr hdr;
195         char buf[512];
196 };
197
198 static struct mibrq mibrq = {
199         { T_OPTMGMT_REQ, sizeof(mibrq.hdr), sizeof(mibrq.req), T_CURRENT },
200         { MIB2_IP }
201 };
202
203 static struct mibrep mibrep = {
204         { 0, 0, 0 },
205         { 0 },
206         { 0 }
207 };
208
209 static struct strbuf rqbuf = {
210         0, sizeof(mibrq), (char *)&mibrq
211 };
212
213 static struct strbuf repbuf = {
214         sizeof(mibrep.buf), sizeof(mibrep.ack) + sizeof(mibrep.hdr),
215             (char *)&mibrep
216 };
217
218 static const char devip[] = "/dev/ip";
219
220 /*
221  * Construct the list of routes
222  */
223 static struct routelist *
224 getroutelist(char *errbuf)
225 {
226         register int s, stat, i;
227         register char *cp;
228         register struct T_optmgmt_ack *ackp;
229         register struct T_error_ack *eackp;
230         register struct opthdr *hp;
231         register mib2_ipRouteEntry_t *rp, *rp2;
232         register struct routelist *rl, *rl2, *routelist;
233         int flags;
234         struct strbuf repbuf2;
235
236         s = open(devip, O_RDWR, 0);
237         if (s < 0) {
238                 sprintf(errbuf, "open %s: %.128s", devip, strerror(errno));
239                 return (NULL);
240         }
241
242         if ((cp = "arp", ioctl(s, I_PUSH, cp) < 0) ||
243             (cp = "tcp", ioctl(s, I_PUSH, cp) < 0) ||
244             (cp = "udp", ioctl(s, I_PUSH, cp) < 0)) {
245                 sprintf(errbuf, "I_PUSH %s: %.128s", cp, strerror(errno));
246                 close(s);
247                 return (NULL);
248         }
249
250         flags = 0;
251         if (putmsg(s, &rqbuf, NULL, flags) < 0) {
252                 sprintf(errbuf, "putmsg: %.128s", strerror(errno));
253                 close(s);
254                 return (NULL);
255         }
256
257         routelist= NULL;
258         rl2 = NULL;
259
260         rp = NULL;
261
262         ackp = &mibrep.ack;
263         hp = &mibrep.hdr;
264         eackp = (struct T_error_ack *)ackp;
265         for (;;) {
266                 flags = 0;
267                 memset(repbuf.buf, 0, repbuf.len);
268                 stat = getmsg(s, &repbuf, NULL, &flags);
269                 if (stat < 0) {
270                         sprintf(errbuf, "getmsg: %.128s", strerror(errno));
271                         goto bail;
272                 }
273                 if (stat == 0 && repbuf.len >= sizeof(*ackp) &&
274                     ackp->PRIM_type == T_OPTMGMT_ACK &&
275                     ackp->MGMT_flags == T_SUCCESS &&
276                     hp->len == 0) {
277                         /* All done! */
278                         goto done;
279                 }
280                 if (repbuf.len >= sizeof(*eackp) &&
281                     eackp->PRIM_type == T_ERROR_ACK) {
282                         sprintf(errbuf, "getmsg err: %.128s",
283                             strerror((eackp->TLI_error == TSYSERR) ?
284                             eackp->UNIX_error : EPROTO));
285                         goto bail;
286                 }
287                 if (stat != MOREDATA ||
288                     repbuf.len < sizeof(*ackp) ||
289                     ackp->PRIM_type != T_OPTMGMT_ACK ||
290                     ackp->MGMT_flags != T_SUCCESS) {
291                         strcpy(errbuf, "unknown getmsg err");
292                         goto bail;
293                 }
294
295                 memset(&repbuf2, 0, sizeof(repbuf2));
296                 repbuf2.maxlen = hp->len;
297                 rp = malloc(hp->len);
298                 if (rp == NULL) {
299                         sprintf(errbuf, "malloc: %.128s", strerror(errno));
300                         goto bail;
301                 }
302                 repbuf2.buf = (char *)rp;
303
304                 flags = 0;
305                 memset(repbuf2.buf, 0, repbuf2.len);
306                 stat = getmsg(s, NULL, &repbuf2, &flags);
307                 if (stat < 0) {
308                         sprintf(errbuf, "getmsg2: %.128s", strerror(errno));
309                         goto bail;
310                 }
311
312                 /* Spin through the routes */
313                 rp2 = rp;
314                 for (rp2 = rp; (char *)rp2 < (char *)rp + repbuf2.len; ++rp2) {
315                         if (hp->level != MIB2_IP || hp->name != MIB2_IP_21)
316                                 continue;
317
318                         if (rp2->ipRouteInfo.re_ire_type == IRE_CACHE ||
319                             rp2->ipRouteInfo.re_ire_type == IRE_BROADCAST)
320                             continue;
321
322                         /* Got one we want to keep */
323                         rl = malloc(sizeof(*rl));
324                         if (rl == NULL) {
325                                 sprintf(errbuf,
326                                     "malloc 2: %.128s", strerror(errno));
327                                 goto bail;
328                         }
329                         memset(rl, 0, sizeof(*rl));
330
331                         rl->mask = rp2->ipRouteMask;
332                         rl->dest = rp2->ipRouteDest;
333                         rl->gate = rp2->ipRouteNextHop;
334                         if (rp2->ipRouteIfIndex.o_length > 0) {
335                                 i = rp2->ipRouteIfIndex.o_length;
336                                 if (i > sizeof(rl->ifname) - 1)
337                                         i = sizeof(rl->ifname) - 1;
338                                 strncpy(rl->ifname,
339                                     rp2->ipRouteIfIndex.o_bytes, i);
340                                 rl->ifname[i] = '\0';
341                         }
342
343                         /* Keep in order (just for fun) */
344                         if (routelist == NULL)
345                                 routelist = rl;
346                         if (rl2 != NULL)
347                                 rl2->next = rl;
348                         rl2 = rl;
349                 }
350                 free(rp);
351                 rp = NULL;
352         }
353
354         strcpy(errbuf, "failed!");
355
356 bail:
357         if (routelist != NULL) {
358                 freeroutelist(routelist);
359                 routelist = NULL;
360         }
361 done:
362         if (rp != NULL)
363                 free(rp);
364         close(s);
365         return (routelist);
366 }
367
368 static void
369 freeroutelist(register struct routelist *rl)
370 {
371         register struct routelist *rl2;
372
373         while (rl != NULL) {
374                 rl2 = rl->next;
375                 free(rl);
376                 rl = rl2;
377         }
378 }