]> git.sommitrealweird.co.uk Git - quagga-debian.git/blob - zebra/if_ioctl_solaris.c
New upstream version 1.2.4
[quagga-debian.git] / zebra / if_ioctl_solaris.c
1 /*
2  * Interface looking up by ioctl () on Solaris.
3  * Copyright (C) 1997, 98 Kunihiro Ishiguro
4  *
5  * This file is part of GNU Zebra.
6  *
7  * GNU Zebra is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; either version 2, or (at your option) any
10  * later version.
11  *
12  * GNU Zebra is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Zebra; see the file COPYING.  If not, write to the Free
19  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20  * 02111-1307, USA.  
21  */
22
23 #include <zebra.h>
24
25 #include "if.h"
26 #include "sockunion.h"
27 #include "prefix.h"
28 #include "ioctl.h"
29 #include "connected.h"
30 #include "memory.h"
31 #include "log.h"
32 #include "privs.h"
33 #include "vrf.h"
34 #include "vty.h"
35
36 #include "zebra/interface.h"
37 #include "zebra/ioctl_solaris.h"
38 #include "zebra/rib.h"
39
40 static int if_get_addr (struct interface *, struct sockaddr *, const char *);
41 static void interface_info_ioctl (struct interface *);
42 extern struct zebra_privs_t zserv_privs;
43
44 static int
45 interface_list_ioctl (int af)
46 {
47   int ret;
48   int sock;
49 #define IFNUM_BASE 32
50   struct lifnum lifn;
51   int ifnum;
52   struct lifreq *lifreq;
53   struct lifconf lifconf;
54   struct interface *ifp;
55   int n;
56   int save_errno;
57   size_t needed, lastneeded = 0;
58   char *buf = NULL;
59
60   if (zserv_privs.change(ZPRIVS_RAISE))
61     zlog (NULL, LOG_ERR, "Can't raise privileges");
62   
63   sock = socket (af, SOCK_DGRAM, 0);
64   if (sock < 0)
65     {
66       zlog_warn ("Can't make %s socket stream: %s",
67                  (af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno));
68                  
69       if (zserv_privs.change(ZPRIVS_LOWER))
70         zlog (NULL, LOG_ERR, "Can't lower privileges");
71         
72       return -1;
73     }
74
75 calculate_lifc_len:     /* must hold privileges to enter here */
76   lifn.lifn_family = af;
77   lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */
78   ret = ioctl (sock, SIOCGLIFNUM, &lifn);
79   save_errno = errno;
80   
81   if (zserv_privs.change(ZPRIVS_LOWER))
82     zlog (NULL, LOG_ERR, "Can't lower privileges");
83  
84   if (ret < 0)
85     {
86       zlog_warn ("interface_list_ioctl: SIOCGLIFNUM failed %s",
87                  safe_strerror (save_errno));
88       close (sock);
89       return -1;
90     }
91   ifnum = lifn.lifn_count;
92
93   /*
94    * When calculating the buffer size needed, add a small number
95    * of interfaces to those we counted.  We do this to capture
96    * the interface status of potential interfaces which may have
97    * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
98    */
99   needed = (ifnum + 4) * sizeof (struct lifreq);
100   if (needed > lastneeded || needed < lastneeded / 2)
101     {
102       if (buf != NULL)
103         XFREE (MTYPE_TMP, buf);
104       if ((buf = XMALLOC (MTYPE_TMP, needed)) == NULL)
105         {
106           zlog_warn ("interface_list_ioctl: malloc failed");
107           close (sock);
108           return -1;
109         }
110     }
111   lastneeded = needed;
112
113   lifconf.lifc_family = af;
114   lifconf.lifc_flags = LIFC_NOXMIT;
115   lifconf.lifc_len = needed;
116   lifconf.lifc_buf = buf;
117
118   if (zserv_privs.change(ZPRIVS_RAISE))
119     zlog (NULL, LOG_ERR, "Can't raise privileges");
120     
121   ret = ioctl (sock, SIOCGLIFCONF, &lifconf);
122
123   if (ret < 0)
124     {
125       if (errno == EINVAL)
126         goto calculate_lifc_len; /* deliberately hold privileges */
127
128       zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno));
129
130       if (zserv_privs.change(ZPRIVS_LOWER))
131         zlog (NULL, LOG_ERR, "Can't lower privileges");
132
133       goto end;
134     }
135
136   if (zserv_privs.change(ZPRIVS_LOWER))
137     zlog (NULL, LOG_ERR, "Can't lower privileges");
138     
139   /* Allocate interface. */
140   lifreq = lifconf.lifc_req;
141
142   for (n = 0; n < lifconf.lifc_len; n += sizeof (struct lifreq))
143     {
144       /* we treat Solaris logical interfaces as addresses, because that is
145        * how PF_ROUTE on Solaris treats them. Hence we can not directly use
146        * the lifreq_name to get the ifp.  We need to normalise the name
147        * before attempting get.
148        *
149        * Solaris logical interface names are in the form of:
150        * <interface name>:<logical interface id>
151        */
152       unsigned int normallen = 0;
153       uint64_t lifflags;
154       
155       /* We should exclude ~IFF_UP interfaces, as we'll find out about them
156        * coming up later through RTM_NEWADDR message on the route socket.
157        */
158       if (if_get_flags_direct (lifreq->lifr_name, &lifflags,
159                            lifreq->lifr_addr.ss_family)
160           || !CHECK_FLAG (lifflags, IFF_UP))
161         {
162           lifreq++;
163           continue;
164         }
165       
166       /* Find the normalised name */
167       while ( (normallen < sizeof(lifreq->lifr_name))
168              && ( *(lifreq->lifr_name + normallen) != '\0')
169              && ( *(lifreq->lifr_name + normallen) != ':') )
170         normallen++;
171       
172       ifp = if_get_by_name_len(lifreq->lifr_name, normallen);
173
174       if (lifreq->lifr_addr.ss_family == AF_INET)
175         ifp->flags |= IFF_IPV4;
176
177       if (lifreq->lifr_addr.ss_family == AF_INET6)
178         {
179 #ifdef HAVE_IPV6
180           ifp->flags |= IFF_IPV6;
181 #else
182           lifreq++;
183           continue;
184 #endif /* HAVE_IPV6 */
185         }
186         
187       if_add_update (ifp);
188
189       interface_info_ioctl (ifp);
190       
191       /* If a logical interface pass the full name so it can be
192        * as a label on the address
193        */
194       if ( *(lifreq->lifr_name + normallen) != '\0')
195         if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr,
196                      lifreq->lifr_name);
197       else
198         if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr, NULL);
199         
200       /* Poke the interface flags. Lets IFF_UP mangling kick in */
201       if_flags_update (ifp, ifp->flags);
202       
203       lifreq++;
204     }
205
206 end:
207   close (sock);
208   XFREE (MTYPE_TMP, lifconf.lifc_buf);
209   return ret;
210 }
211
212 /* Get interface's index by ioctl. */
213 static int
214 if_get_index (struct interface *ifp)
215 {
216   int ret;
217   struct lifreq lifreq;
218
219   lifreq_set_name (&lifreq, ifp->name);
220
221   if (ifp->flags & IFF_IPV4)
222     ret = AF_IOCTL (AF_INET, SIOCGLIFINDEX, (caddr_t) & lifreq);
223   else if (ifp->flags & IFF_IPV6)
224     ret = AF_IOCTL (AF_INET6, SIOCGLIFINDEX, (caddr_t) & lifreq);
225   else
226     ret = -1;
227
228   if (ret < 0)
229     {
230       zlog_warn ("SIOCGLIFINDEX(%s) failed", ifp->name);
231       return ret;
232     }
233
234   /* OK we got interface index. */
235 #ifdef ifr_ifindex
236   ifp->ifindex = lifreq.lifr_ifindex;
237 #else
238   ifp->ifindex = lifreq.lifr_index;
239 #endif
240   return ifp->ifindex;
241
242 }
243
244
245 /* Interface address lookup by ioctl.  This function only looks up
246    IPv4 address. */
247 #define ADDRLEN(sa) (((sa)->sa_family == AF_INET ?  \
248                 sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)))
249
250 #define SIN(s) ((struct sockaddr_in *)(s))
251 #define SIN6(s) ((struct sockaddr_in6 *)(s))
252
253 /* Retrieve address information for the given ifp */
254 static int
255 if_get_addr (struct interface *ifp, struct sockaddr *addr, const char *label)
256 {
257   int ret;
258   struct lifreq lifreq;
259   struct sockaddr_storage mask, dest;
260   char *dest_pnt = NULL;
261   u_char prefixlen = 0;
262   afi_t af;
263   int flags = 0;
264
265   /* Interface's name and address family.
266    * We need to use the logical interface name / label, if we've been
267    * given one, in order to get the right address
268    */
269   strncpy (lifreq.lifr_name, (label ? label : ifp->name), IFNAMSIZ);
270
271   /* Interface's address. */
272   memcpy (&lifreq.lifr_addr, addr, ADDRLEN (addr));
273   af = addr->sa_family;
274
275   /* Point to point or broad cast address pointer init. */
276   dest_pnt = NULL;
277
278   if (AF_IOCTL (af, SIOCGLIFDSTADDR, (caddr_t) & lifreq) >= 0)
279     {
280       memcpy (&dest, &lifreq.lifr_dstaddr, ADDRLEN (addr));
281       if (af == AF_INET)
282         dest_pnt = (char *) &(SIN (&dest)->sin_addr);
283       else
284         dest_pnt = (char *) &(SIN6 (&dest)->sin6_addr);
285       flags = ZEBRA_IFA_PEER;
286     }
287
288   if (af == AF_INET)
289     {
290       ret = if_ioctl (SIOCGLIFNETMASK, (caddr_t) & lifreq);
291       
292       if (ret < 0)
293         {
294           if (errno != EADDRNOTAVAIL)
295             {
296               zlog_warn ("SIOCGLIFNETMASK (%s) fail: %s", ifp->name,
297                          safe_strerror (errno));
298               return ret;
299             }
300           return 0;
301         }
302       memcpy (&mask, &lifreq.lifr_addr, ADDRLEN (addr));
303
304       prefixlen = ip_masklen (SIN (&mask)->sin_addr);
305       if (!dest_pnt && (if_ioctl (SIOCGLIFBRDADDR, (caddr_t) & lifreq) >= 0))
306         {
307           memcpy (&dest, &lifreq.lifr_broadaddr, sizeof (struct sockaddr_in));
308           dest_pnt = (char *) &SIN (&dest)->sin_addr;
309         }
310     }
311 #ifdef HAVE_IPV6
312   else if (af == AF_INET6)
313     {
314       if (if_ioctl_ipv6 (SIOCGLIFSUBNET, (caddr_t) & lifreq) < 0)
315         {
316           if (ifp->flags & IFF_POINTOPOINT)
317             prefixlen = IPV6_MAX_BITLEN;
318           else
319             zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s",
320                        ifp->name, safe_strerror (errno));
321         }
322       else
323         {
324           prefixlen = lifreq.lifr_addrlen;
325         }
326     }
327 #endif /* HAVE_IPV6 */
328
329   /* Set address to the interface. */
330   if (af == AF_INET)
331     connected_add_ipv4 (ifp, flags, &SIN (addr)->sin_addr, prefixlen,
332                         (struct in_addr *) dest_pnt, label);
333 #ifdef HAVE_IPV6
334   else if (af == AF_INET6)
335     connected_add_ipv6 (ifp, flags, &SIN6 (addr)->sin6_addr, prefixlen,
336                         (struct in6_addr *) dest_pnt, label);
337 #endif /* HAVE_IPV6 */
338
339   return 0;
340 }
341
342 /* Fetch interface information via ioctl(). */
343 static void
344 interface_info_ioctl (struct interface *ifp)
345 {
346   if_get_index (ifp);
347   if_get_flags (ifp);
348   if_get_mtu (ifp);
349   if_get_metric (ifp);
350 }
351
352 /* Lookup all interface information. */
353 void
354 interface_list (struct zebra_vrf *zvrf)
355 {
356   if (zvrf->vrf_id != VRF_DEFAULT)
357     {
358       zlog_warn ("interface_list: ignore VRF %u", zvrf->vrf_id);
359       return;
360     }
361   interface_list_ioctl (AF_INET);
362   interface_list_ioctl (AF_INET6);
363   interface_list_ioctl (AF_UNSPEC);
364 }
365
366 struct connected *
367 if_lookup_linklocal (struct interface *ifp)
368 {
369 #ifdef HAVE_IPV6
370   struct listnode *node;
371   struct connected *ifc;
372
373   if (ifp == NULL)
374     return NULL;
375
376   for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
377     {
378       if ((ifc->address->family == AF_INET6) &&
379           (IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6)))
380         return ifc;
381     }
382 #endif /* HAVE_IPV6 */
383
384   return NULL;
385 }