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