/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2013 Vincent Bernat * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "lldpd.h" #include #include #include /* Solaris comes with libdladm which seems to be handy to get all the necessary * information. Unfortunately, this library needs a special device file and a * Unix socket to a daemon. This is a bit difficult to use it in a * privilege-separated daemon. Therefore, we keep using ioctl(). This should * also improve compatibility with older versions of Solaris. */ static void ifsolaris_extract(struct lldpd *cfg, struct interfaces_device_list *interfaces, struct interfaces_address_list *addresses, struct lifreq *lifr) { int flags = 0; int index = 0; struct interfaces_address *address = NULL; struct interfaces_device *device = NULL; sa_family_t lifr_af = lifr->lifr_addr.ss_family; struct lifreq lifrl = { .lifr_name = {} }; strlcpy(lifrl.lifr_name, lifr->lifr_name, sizeof(lifrl.lifr_name)); /* Flags */ if (ioctl(cfg->g_sock, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) { log_warn("interfaces", "unable to get flags for %s", lifrl.lifr_name); return; } flags = lifrl.lifr_flags; /* Index */ if (ioctl(cfg->g_sock, SIOCGLIFINDEX, (caddr_t)&lifrl) < 0) { log_warn("interfaces", "unable to get index for %s", lifrl.lifr_name); return; } index = lifrl.lifr_index; /* Record the address */ if ((address = malloc(sizeof(struct interfaces_address))) == NULL) { log_warn("interfaces", "not enough memory for a new IP address on %s", lifrl.lifr_name); return; } address->flags = flags; address->index = index; memcpy(&address->address, &lifr->lifr_addr, (lifr_af == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); TAILQ_INSERT_TAIL(addresses, address, next); /* Hardware address */ if (ioctl(cfg->g_sock, SIOCGLIFHWADDR, (caddr_t)&lifrl) < 0) { log_debug("interfaces", "unable to get hardware address for %s", lifrl.lifr_name); return; } struct sockaddr_dl *saddrdl = (struct sockaddr_dl *)&lifrl.lifr_addr; if (saddrdl->sdl_type != 4) { log_debug("interfaces", "skip %s: not an ethernet device (%d)", lifrl.lifr_name, saddrdl->sdl_type); return; } /* Handle the interface */ if ((device = calloc(1, sizeof(struct interfaces_device))) == NULL) { log_warn("interfaces", "unable to allocate memory for %s", lifrl.lifr_name); return; } device->name = strdup(lifrl.lifr_name); device->flags = flags; device->index = index; device->type = IFACE_PHYSICAL_T; device->address = malloc(ETHER_ADDR_LEN); if (device->address) memcpy(device->address, LLADDR(saddrdl), ETHER_ADDR_LEN); /* MTU */ if (ioctl(cfg->g_sock, SIOCGLIFMTU, (caddr_t)&lifrl) < 0) { log_debug("interfaces", "unable to get MTU for %s", lifrl.lifr_name); } else device->mtu = lifrl.lifr_mtu; TAILQ_INSERT_TAIL(interfaces, device, next); } extern struct lldpd_ops bpf_ops; void interfaces_update(struct lldpd *cfg) { struct lldpd_hardware *hardware; caddr_t buffer = NULL; struct interfaces_device_list *interfaces; struct interfaces_address_list *addresses; interfaces = malloc(sizeof(struct interfaces_device_list)); addresses = malloc(sizeof(struct interfaces_address_list)); if (interfaces == NULL || addresses == NULL) { log_warnx("interfaces", "unable to allocate memory"); goto end; } TAILQ_INIT(interfaces); TAILQ_INIT(addresses); struct lifnum lifn = { .lifn_family = AF_UNSPEC, .lifn_flags = LIFC_ENABLED }; if (ioctl(cfg->g_sock, SIOCGLIFNUM, &lifn) < 0) { log_warn("interfaces", "unable to get the number of interfaces"); goto end; } size_t bufsize = lifn.lifn_count * sizeof(struct lifreq); if ((buffer = malloc(bufsize)) == NULL) { log_warn("interfaces", "unable to allocate buffer to get interfaces"); goto end; } struct lifconf lifc = { .lifc_family = AF_UNSPEC, .lifc_flags = LIFC_ENABLED, .lifc_len = bufsize, .lifc_buf = buffer }; if (ioctl(cfg->g_sock, SIOCGLIFCONF, (char *)&lifc) < 0) { log_warn("interfaces", "unable to get the network interfaces"); goto end; } int num = lifc.lifc_len / sizeof(struct lifreq); if (num > lifn.lifn_count) num = lifn.lifn_count; log_debug("interfaces", "got %d interfaces", num); struct lifreq *lifrp = (struct lifreq *)buffer; for (int n = 0; n < num; n++, lifrp++) ifsolaris_extract(cfg, interfaces, addresses, lifrp); interfaces_helper_allowlist(cfg, interfaces); interfaces_helper_physical(cfg, interfaces, &bpf_ops, ifbpf_phys_init); interfaces_helper_mgmt(cfg, addresses, interfaces); interfaces_helper_chassis(cfg, interfaces); /* Mac/PHY */ TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) { if (!hardware->h_flags) continue; /* TODO: mac/phy for Solaris */ interfaces_helper_promisc(cfg, hardware); } end: free(buffer); interfaces_free_devices(interfaces); interfaces_free_addresses(addresses); } void interfaces_cleanup(struct lldpd *cfg) { }