diff --git a/libnozzle/libnozzle.c b/libnozzle/libnozzle.c index 05bf0c4b..f04f4161 100644 --- a/libnozzle/libnozzle.c +++ b/libnozzle/libnozzle.c @@ -1,1190 +1,1197 @@ /* * Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KNET_LINUX #include /* * libnl3 < 3.3 includes kernel headers directly * causing conflicts with net/if.h included above */ #ifdef LIBNL3_WORKAROUND #define _LINUX_IF_H 1 #endif #include #include #include #include #endif #ifdef KNET_BSD #include #endif #include "libnozzle.h" #include "internals.h" /* * internal functions are all _unlocked_ * locking should be handled at external API functions */ static int lib_init = 0; static struct nozzle_lib_config lib_cfg; static pthread_mutex_t config_mutex = PTHREAD_MUTEX_INITIALIZER; /* * internal helpers */ static void lib_fini(void) { if (lib_cfg.head == NULL) { #ifdef KNET_LINUX nl_close(lib_cfg.nlsock); nl_socket_free(lib_cfg.nlsock); #endif close(lib_cfg.ioctlfd); lib_init = 0; } } static int is_valid_nozzle(const nozzle_t nozzle) { nozzle_t temp; if (!nozzle) { return 0; } if (!lib_init) { return 0; } temp = lib_cfg.head; while (temp != NULL) { if (nozzle == temp) return 1; temp = temp->next; } return 0; } static void destroy_iface(nozzle_t nozzle) { #ifdef KNET_BSD struct ifreq ifr; #endif if (!nozzle) return; if (nozzle->fd) close(nozzle->fd); #ifdef KNET_BSD memset(&ifr, 0, sizeof(struct ifreq)); strncpy(ifname, nozzle->name, IFNAMSIZ); ioctl(lib_cfg.ioctlfd, SIOCIFDESTROY, &ifr); #endif free(nozzle); lib_fini(); return; } static int get_iface_mtu(const nozzle_t nozzle) { int err = 0, savederrno = 0; struct ifreq ifr; memset(&ifr, 0, sizeof(struct ifreq)); strncpy(ifname, nozzle->name, IFNAMSIZ); err = ioctl(lib_cfg.ioctlfd, SIOCGIFMTU, &ifr); if (err) { savederrno = errno; goto out_clean; } err = ifr.ifr_mtu; out_clean: errno = savederrno; return err; } static int get_iface_mac(const nozzle_t nozzle, char **ether_addr) { int err = 0, savederrno = 0; struct ifreq ifr; char mac[MACADDR_CHAR_MAX]; #ifdef KNET_BSD struct ifaddrs *ifap = NULL; struct ifaddrs *ifa; int found = 0; #endif memset(&mac, 0, MACADDR_CHAR_MAX); memset(&ifr, 0, sizeof(struct ifreq)); strncpy(ifname, nozzle->name, IFNAMSIZ); #ifdef KNET_LINUX err = ioctl(lib_cfg.ioctlfd, SIOCGIFHWADDR, &ifr); if (err) { savederrno = errno; goto out_clean; } ether_ntoa_r((struct ether_addr *)ifr.ifr_hwaddr.sa_data, mac); #endif #ifdef KNET_BSD /* * there is no ioctl to get the ether address of an interface on FreeBSD * (not to be confused with hwaddr). Use workaround described here: * https://lists.freebsd.org/pipermail/freebsd-hackers/2004-June/007394.html */ err = getifaddrs(&ifap); if (err < 0) { savederrno = errno; goto out_clean; } ifa = ifap; while (ifa) { if (!strncmp(nozzle->name, ifa->ifa_name, IFNAMSIZ)) { found = 1; break; } ifa=ifa->ifa_next; } if (found) { ether_ntoa_r((struct ether_addr *)LLADDR((struct sockaddr_dl *)ifa->ifa_addr), mac); } else { errno = EINVAL; err = -1; } freeifaddrs(ifap); if (err) { goto out_clean; } #endif *ether_addr = strdup(mac); if (!*ether_addr) { savederrno = errno; err = -1; } out_clean: errno = savederrno; return err; } #define IP_ADD 1 #define IP_DEL 2 static int _set_ip(nozzle_t nozzle, int command, const char *ipaddr, const char *prefix, int secondary) { int fam; char *broadcast = NULL; int err = 0; #ifdef KNET_LINUX struct rtnl_addr *addr = NULL; struct nl_addr *local_addr = NULL; struct nl_addr *bcast_addr = NULL; struct nl_cache *cache = NULL; int ifindex; #endif #ifdef KNET_BSD char cmdline[4096]; char proto[6]; char *error_string = NULL; #endif if (!strchr(ipaddr, ':')) { fam = AF_INET; broadcast = generate_v4_broadcast(ipaddr, prefix); if (!broadcast) { errno = EINVAL; return -1; } } else { fam = AF_INET6; } #ifdef KNET_LINUX addr = rtnl_addr_alloc(); if (!addr) { errno = ENOMEM; return -1; } if (rtnl_link_alloc_cache(lib_cfg.nlsock, AF_UNSPEC, &cache) < 0) { errno = ENOMEM; err = -1; goto out; } ifindex = rtnl_link_name2i(cache, nozzle->name); if (ifindex == 0) { errno = ENOENT; err = -1; goto out; } rtnl_addr_set_ifindex(addr, ifindex); if (nl_addr_parse(ipaddr, fam, &local_addr) < 0) { errno = EINVAL; err = -1; goto out; } if (rtnl_addr_set_local(addr, local_addr) < 0) { errno = EINVAL; err = -1; goto out; } if (broadcast) { if (nl_addr_parse(broadcast, fam, &bcast_addr) < 0) { errno = EINVAL; err = -1; goto out; } if (rtnl_addr_set_broadcast(addr, bcast_addr) < 0) { errno = EINVAL; err = -1; goto out; } } rtnl_addr_set_prefixlen(addr, atoi(prefix)); if (command == IP_ADD) { if (rtnl_addr_add(lib_cfg.nlsock, addr, 0) < 0) { errno = EINVAL; err = -1; goto out; } } else { if (rtnl_addr_delete(lib_cfg.nlsock, addr, 0) < 0) { errno = EINVAL; err = -1; goto out; } } out: if (addr) { rtnl_addr_put(addr); } if (local_addr) { nl_addr_put(local_addr); } if (bcast_addr) { nl_addr_put(bcast_addr); } if (cache) { nl_cache_put(cache); } if (broadcast) { free(broadcast); } return err; #endif #ifdef KNET_BSD /* * TODO: port to use ioctl and such, drop shell forking here */ memset(cmdline, 0, sizeof(cmdline)); if (fam == AF_INET) { snprintf(proto, sizeof(proto), "inet"); } else { snprintf(proto, sizeof(proto), "inet6"); } if (command == IP_ADD) { snprintf(cmdline, sizeof(cmdline)-1, "ifconfig %s %s %s/%s", nozzle->name, proto, ipaddr, prefix); if (broadcast) { snprintf(cmdline + strlen(cmdline), sizeof(cmdline) - strlen(cmdline) -1, " broadcast %s", broadcast); } if ((secondary) && (fam == AF_INET)) { snprintf(cmdline + strlen(cmdline), sizeof(cmdline) - strlen(cmdline) -1, " alias"); } } else { snprintf(cmdline, sizeof(cmdline)-1, "ifconfig %s %s %s/%s delete", nozzle->name, proto, ipaddr, prefix); } if (broadcast) { free(broadcast); } /* * temporary workaround as we port libnozzle to BSD ioctl * for IP address management */ err = execute_bin_sh_command(cmdline, &error_string); if (error_string) { free(error_string); error_string = NULL; } return err; #endif } /* * Exported public API */ nozzle_t nozzle_open(char *devname, size_t devname_size, const char *updownpath) { int savederrno = 0; nozzle_t nozzle = NULL; char *temp_mac = NULL; #ifdef KNET_LINUX struct ifreq ifr; #endif #ifdef KNET_BSD uint16_t i; long int nozzlenum = 0; char curnozzle[IFNAMSIZ]; #endif if (devname == NULL) { errno = EINVAL; return NULL; } if (devname_size < IFNAMSIZ) { errno = EINVAL; return NULL; } if (strlen(devname) > IFNAMSIZ) { errno = E2BIG; return NULL; } #ifdef KNET_BSD /* * BSD does not support named devices like Linux * but it is possible to force a nozzleX device number * where X is 0 to 255. */ if (strlen(devname)) { if (strncmp(devname, "tap", 3)) { errno = EINVAL; return NULL; } errno = 0; nozzlenum = strtol(devname+3, NULL, 10); if (errno) { errno = EINVAL; return NULL; } if ((nozzlenum < 0) || (nozzlenum > 255)) { errno = EINVAL; return NULL; } } #endif if (updownpath) { /* only absolute paths */ if (updownpath[0] != '/') { errno = EINVAL; return NULL; } if (strlen(updownpath) >= UPDOWN_PATH_MAX) { errno = E2BIG; return NULL; } } savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return NULL; } if (!lib_init) { lib_cfg.head = NULL; #ifdef KNET_LINUX lib_cfg.nlsock = nl_socket_alloc(); if (!lib_cfg.nlsock) { savederrno = errno; goto out_error; } if (nl_connect(lib_cfg.nlsock, NETLINK_ROUTE) < 0) { savederrno = EBUSY; goto out_error; } lib_cfg.ioctlfd = socket(AF_INET, SOCK_STREAM, 0); #endif #ifdef KNET_BSD lib_cfg.ioctlfd = socket(AF_LOCAL, SOCK_DGRAM, 0); #endif if (lib_cfg.ioctlfd < 0) { savederrno = errno; goto out_error; } lib_init = 1; } nozzle = malloc(sizeof(struct nozzle_iface)); if (!nozzle) { savederrno = ENOMEM; goto out_error; } memset(nozzle, 0, sizeof(struct nozzle_iface)); #ifdef KNET_BSD if (!strlen(devname)) { for (i = 0; i < 256; i++) { snprintf(curnozzle, sizeof(curnozzle) - 1, "/dev/tap%u", i); nozzle->fd = open(curnozzle, O_RDWR); savederrno = errno; if (nozzle->fd > 0) { break; } } snprintf(curnozzle, sizeof(curnozzle) -1 , "tap%u", i); } else { snprintf(curnozzle, sizeof(curnozzle) - 1, "/dev/%s", devname); nozzle->fd = open(curnozzle, O_RDWR); savederrno = errno; snprintf(curnozzle, sizeof(curnozzle) - 1, "%s", devname); } if (nozzle->fd < 0) { savederrno = EBUSY; goto out_error; } strncpy(devname, curnozzle, IFNAMSIZ); strncpy(nozzle->name, curnozzle, IFNAMSIZ); #endif #ifdef KNET_LINUX if ((nozzle->fd = open("/dev/net/tun", O_RDWR)) < 0) { savederrno = errno; goto out_error; } memset(&ifr, 0, sizeof(struct ifreq)); memmove(ifname, devname, IFNAMSIZ); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; if (ioctl(nozzle->fd, TUNSETIFF, &ifr) < 0) { savederrno = errno; goto out_error; } if ((strlen(devname) > 0) && (strcmp(devname, ifname) != 0)) { savederrno = EBUSY; goto out_error; } strncpy(devname, ifname, IFNAMSIZ); strncpy(nozzle->name, ifname, IFNAMSIZ); #endif nozzle->default_mtu = get_iface_mtu(nozzle); if (nozzle->default_mtu < 0) { savederrno = errno; goto out_error; } if (get_iface_mac(nozzle, &temp_mac) < 0) { savederrno = errno; goto out_error; } strncpy(nozzle->default_mac, temp_mac, 18); free(temp_mac); if (updownpath) { int len = strlen(updownpath); strcpy(nozzle->updownpath, updownpath); if (nozzle->updownpath[len-1] != '/') { nozzle->updownpath[len] = '/'; } nozzle->hasupdown = 1; } nozzle->next = lib_cfg.head; lib_cfg.head = nozzle; pthread_mutex_unlock(&config_mutex); errno = savederrno; return nozzle; out_error: destroy_iface(nozzle); pthread_mutex_unlock(&config_mutex); errno = savederrno; return NULL; } int nozzle_close(nozzle_t nozzle) { int err = 0, savederrno = 0; nozzle_t temp = lib_cfg.head; nozzle_t prev = lib_cfg.head; struct nozzle_ip *ip, *ip_next; savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = EINVAL; err = -1; goto out_clean; } while ((temp) && (temp != nozzle)) { prev = temp; temp = temp->next; } if (nozzle == prev) { lib_cfg.head = nozzle->next; } else { prev->next = nozzle->next; } ip = nozzle->ip; while (ip) { ip_next = ip->next; free(ip); ip = ip_next; } destroy_iface(nozzle); out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return err; } int nozzle_run_updown(const nozzle_t nozzle, uint8_t action, char **exec_string) { int err = 0, savederrno = 0; char command[PATH_MAX]; const char *action_str = NULL; struct stat sb; if (action > NOZZLE_POSTDOWN) { errno = EINVAL; return -1; } if (!exec_string) { errno = EINVAL; return -1; } savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = EINVAL; err = -1; goto out_clean; } if (!nozzle->hasupdown) { savederrno = EINVAL; err = -1; goto out_clean; } switch(action) { case NOZZLE_PREUP: action_str = "pre-up.d"; break; case NOZZLE_UP: action_str = "up.d"; break; case NOZZLE_DOWN: action_str = "down.d"; break; case NOZZLE_POSTDOWN: action_str = "post-down.d"; break; } memset(command, 0, PATH_MAX); - snprintf(command, PATH_MAX, "%s%s/%s", nozzle->updownpath, action_str, nozzle->name); + snprintf(command, PATH_MAX, "%s/%s/%s", nozzle->updownpath, action_str, nozzle->name); err = stat(command, &sb); if (err) { savederrno = errno; goto out_clean; } + /* + * clear errno from previous calls as there is no errno + * returned from execute_bin_sh_command + */ + savederrno = 0; err = execute_bin_sh_command(command, exec_string); - savederrno = errno; + if (err) { + err = -2; + } out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return err; } int nozzle_set_up(nozzle_t nozzle) { int err = 0, savederrno = 0; struct ifreq ifr; savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = EINVAL; err = -1; goto out_clean; } if (nozzle->up) { goto out_clean; } memset(&ifr, 0, sizeof(struct ifreq)); strncpy(ifname, nozzle->name, IFNAMSIZ); err = ioctl(lib_cfg.ioctlfd, SIOCGIFFLAGS, &ifr); if (err) { savederrno = errno; goto out_clean; } ifr.ifr_flags |= IFF_UP | IFF_RUNNING; err = ioctl(lib_cfg.ioctlfd, SIOCSIFFLAGS, &ifr); if (err) { savederrno = errno; goto out_clean; } nozzle->up = 1; out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return err; } int nozzle_set_down(nozzle_t nozzle) { int err = 0, savederrno = 0; struct ifreq ifr; savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = EINVAL; err = -1; goto out_clean; } if (!nozzle->up) { goto out_clean; } memset(&ifr, 0, sizeof(struct ifreq)); strncpy(ifname, nozzle->name, IFNAMSIZ); err = ioctl(lib_cfg.ioctlfd, SIOCGIFFLAGS, &ifr); if (err) { savederrno = errno; goto out_clean; } ifr.ifr_flags &= ~IFF_UP; err = ioctl(lib_cfg.ioctlfd, SIOCSIFFLAGS, &ifr); if (err) { savederrno = errno; goto out_clean; } nozzle->up = 0; out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return err; } int nozzle_get_mtu(const nozzle_t nozzle) { int err = 0, savederrno = 0; savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = EINVAL; err = -1; goto out_clean; } err = get_iface_mtu(nozzle); savederrno = errno; out_clean: pthread_mutex_unlock(&config_mutex); savederrno = errno; return err; } int nozzle_get_mac(const nozzle_t nozzle, char **ether_addr) { int err = 0, savederrno = 0; if (!ether_addr) { errno = EINVAL; return -1; } savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = EINVAL; err = -1; goto out_clean; } err = get_iface_mac(nozzle, ether_addr); out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return err; } int nozzle_set_mac(nozzle_t nozzle, const char *ether_addr) { int err = 0, savederrno = 0; struct ifreq ifr; if (!ether_addr) { errno = EINVAL; return -1; } savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = EINVAL; err = -1; goto out_clean; } memset(&ifr, 0, sizeof(struct ifreq)); strncpy(ifname, nozzle->name, IFNAMSIZ); #ifdef KNET_LINUX err = ioctl(lib_cfg.ioctlfd, SIOCGIFHWADDR, &ifr); if (err) { savederrno = errno; goto out_clean; } memmove(ifr.ifr_hwaddr.sa_data, ether_aton(ether_addr), ETH_ALEN); err = ioctl(lib_cfg.ioctlfd, SIOCSIFHWADDR, &ifr); savederrno = errno; #endif #ifdef KNET_BSD err = ioctl(lib_cfg.ioctlfd, SIOCGIFADDR, &ifr); if (err) { savederrno = errno; goto out_clean; } memmove(ifr.ifr_addr.sa_data, ether_aton(ether_addr), ETHER_ADDR_LEN); ifr.ifr_addr.sa_len = ETHER_ADDR_LEN; err = ioctl(lib_cfg.ioctlfd, SIOCSIFLLADDR, &ifr); savederrno = errno; #endif out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return err; } int nozzle_reset_mac(nozzle_t nozzle) { return nozzle_set_mac(nozzle, nozzle->default_mac); } nozzle_t nozzle_get_handle_by_name(const char *devname) { int savederrno = 0; nozzle_t nozzle; if ((devname == NULL) || (strlen(devname) > IFNAMSIZ)) { errno = EINVAL; return NULL; } savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return NULL; } nozzle = lib_cfg.head; while (nozzle != NULL) { if (!strcmp(devname, nozzle->name)) break; nozzle = nozzle->next; } if (!nozzle) { savederrno = ENOENT; } pthread_mutex_unlock(&config_mutex); errno = savederrno; return nozzle; } const char *nozzle_get_name_by_handle(const nozzle_t nozzle) { int savederrno = 0; char *name = NULL; savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return NULL; } if (!is_valid_nozzle(nozzle)) { savederrno = ENOENT; goto out_clean; } name = nozzle->name; out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return name; } int nozzle_get_fd(const nozzle_t nozzle) { int fd = -1, savederrno = 0; savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = ENOENT; fd = -1; goto out_clean; } fd = nozzle->fd; out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return fd; } int nozzle_set_mtu(nozzle_t nozzle, const int mtu) { int err = 0, savederrno = 0; struct nozzle_ip *tmp_ip; struct ifreq ifr; if (!mtu) { errno = EINVAL; return -1; } savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = EINVAL; err = -1; goto out_clean; } err = nozzle->current_mtu = get_iface_mtu(nozzle); if (err < 0) { savederrno = errno; goto out_clean; } memset(&ifr, 0, sizeof(struct ifreq)); strncpy(ifname, nozzle->name, IFNAMSIZ); ifr.ifr_mtu = mtu; err = ioctl(lib_cfg.ioctlfd, SIOCSIFMTU, &ifr); if (err) { savederrno = errno; goto out_clean; } if ((nozzle->current_mtu < 1280) && (mtu >= 1280)) { tmp_ip = nozzle->ip; while(tmp_ip) { if (tmp_ip->domain == AF_INET6) { err = _set_ip(nozzle, IP_ADD, tmp_ip->ipaddr, tmp_ip->prefix, 0); if (err) { savederrno = errno; err = -1; goto out_clean; } } tmp_ip = tmp_ip->next; } } out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return err; } int nozzle_reset_mtu(nozzle_t nozzle) { return nozzle_set_mtu(nozzle, nozzle->default_mtu); } int nozzle_add_ip(nozzle_t nozzle, const char *ipaddr, const char *prefix) { int err = 0, savederrno = 0; int found = 0; struct nozzle_ip *ip = NULL, *ip_prev = NULL, *ip_last = NULL; int secondary = 0; if ((!ipaddr) || (!prefix)) { errno = EINVAL; return -1; } savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = EINVAL; err = -1; goto out_clean; } found = find_ip(nozzle, ipaddr, prefix, &ip, &ip_prev); if (found) { goto out_clean; } ip = malloc(sizeof(struct nozzle_ip)); if (!ip) { savederrno = errno; err = -1 ; goto out_clean; } memset(ip, 0, sizeof(struct nozzle_ip)); strncpy(ip->ipaddr, ipaddr, IPADDR_CHAR_MAX); strncpy(ip->prefix, prefix, PREFIX_CHAR_MAX); if (!strchr(ip->ipaddr, ':')) { ip->domain = AF_INET; } else { ip->domain = AF_INET6; } /* * if user asks for an IPv6 address, but MTU < 1280 * store the IP and bring it up later if and when MTU > 1280 */ if ((ip->domain == AF_INET6) && (get_iface_mtu(nozzle) < 1280)) { err = 0; } else { if (nozzle->ip) { secondary = 1; } err = _set_ip(nozzle, IP_ADD, ipaddr, prefix, secondary); savederrno = errno; } if (err) { free(ip); goto out_clean; } if (nozzle->ip) { ip_last = nozzle->ip; while (ip_last->next != NULL) { ip_last = ip_last->next; } ip_last->next = ip; } else { nozzle->ip = ip; } out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return err; } int nozzle_del_ip(nozzle_t nozzle, const char *ipaddr, const char *prefix) { int err = 0, savederrno = 0; int found = 0; struct nozzle_ip *ip = NULL, *ip_prev = NULL; if ((!ipaddr) || (!prefix)) { errno = EINVAL; return -1; } savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { savederrno = EINVAL; err = -1; goto out_clean; } found = find_ip(nozzle, ipaddr, prefix, &ip, &ip_prev); if (!found) { goto out_clean; } err = _set_ip(nozzle, IP_DEL, ipaddr, prefix, 0); savederrno = errno; if (!err) { if (ip == ip_prev) { nozzle->ip = ip->next; } else { ip_prev->next = ip->next; } free(ip); } out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return err; } int nozzle_get_ips(const nozzle_t nozzle, struct nozzle_ip **nozzle_ip) { int err = 0, savederrno = 0; if (!nozzle_ip) { errno = EINVAL; return -1; } savederrno = pthread_mutex_lock(&config_mutex); if (savederrno) { errno = savederrno; return -1; } if (!is_valid_nozzle(nozzle)) { err = -1; savederrno = EINVAL; goto out_clean; } *nozzle_ip = nozzle->ip; out_clean: pthread_mutex_unlock(&config_mutex); errno = savederrno; return err; } diff --git a/libnozzle/libnozzle.h b/libnozzle/libnozzle.h index df3d9fb5..6baf09ee 100644 --- a/libnozzle/libnozzle.h +++ b/libnozzle/libnozzle.h @@ -1,314 +1,315 @@ /* * Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #ifndef __LIBNOZZLE_H__ #define __LIBNOZZLE_H__ #include #include /** * * @file libnozzle.h * @brief tap interfaces management API include file * @copyright Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. * * nozzle is a commodity library to manage tap (ethernet) interfaces */ typedef struct nozzle_iface *nozzle_t; /** * nozzle_open * @brief create a new tap device on the system. * * devname - pointer to device name of at least size IFNAMSIZ. * if the dev strlen is 0, then the system will assign a name automatically. * if a string is specified, the system will try to create a device with * the specified name. * NOTE: on FreeBSD the tap device names can only be tapX where X is a * number from 0 to 255. On Linux such limitation does not apply. * The name must be unique to the system. If an interface with the same * name is already configured on the system, an error will be returned. * * devname_size - length of the buffer provided in dev (has to be at least IFNAMSIZ). * * updownpath - nozzle supports the typical filesystem structure to execute * actions for: down.d post-down.d pre-up.d up.d * in the form of: * updownpath// * updownpath specifies where to find those directories on the * filesystem and it must be an absolute path. * * @return * nozzle_open returns * a pointer to a nozzle struct on success * NULL on error and errno is set. */ nozzle_t nozzle_open(char *devname, size_t devname_size, const char *updownpath); /** * nozzle_close * @brief deconfigure and destroy a nozzle device * * nozzle - pointer to the nozzle struct to destroy * * @return * 0 on success * -1 on error and errno is set. */ int nozzle_close(nozzle_t nozzle); #define NOZZLE_PREUP 0 #define NOZZLE_UP 1 #define NOZZLE_DOWN 2 #define NOZZLE_POSTDOWN 3 /** * nozzle_run_updown * @brief execute updown commands associated with a nozzle device. It is * the application responsibility to call helper scripts * before or after creating/destroying interfaces or IP addresses. * * nozzle - pointer to the nozzle struct * * action - pre-up.d / up.d / down.d / post-down.d (see defines above) * * exec_string - pointers to string to record executing action stdout/stderr. * The string is malloc'ed, the caller needs to free the buffer. * If the script generates no output this string might be NULL. * * @return * 0 on success - * -1 on error and errno is set. + * -1 on error and errno is set (sanity checks and internal calls. + * -2 on error from executing the shell scripts, and no errno is set. */ int nozzle_run_updown(const nozzle_t nozzle, uint8_t action, char **exec_string); /** * nozzle_set_up * @brief equivalent of ifconfig up * * nozzle - pointer to the nozzle struct * * @return * 0 on success * -1 on error and errno is set. */ int nozzle_set_up(nozzle_t nozzle); /** * nozzle_set_down * @brief equivalent of ifconfig down * * nozzle - pointer to the nozzle struct * * @return * 0 on success * -1 on error and errno is set. */ int nozzle_set_down(nozzle_t nozzle); /** * nozzle_add_ip * @brief equivalent of ip addr or ifconfig * * nozzle - pointer to the nozzle struct * * ipaddr - string containing either an IPv4 or an IPv6 address. * Please note that Linux will automatically remove any IPv6 addresses from an interface * with MTU < 1280. libnozzle will cache those IPs and re-instate them when MTU is > 1280. * MTU must be set via nozzle_set_mtu for IPv6 to be re-instated. * * prefix - 24, 64 or any valid network prefix for the requested address. * * @return * 0 on success * -1 on error and errno is set. */ int nozzle_add_ip(nozzle_t nozzle, const char *ipaddr, const char *prefix); /** * nozzle_del_ip * @brief equivalent of ip addr del or ifconfig del * * nozzle - pointer to the nozzle struct * * ipaddr - string containing either an IPv4 or an IPv6 address. * * prefix - 24, 64 or any valid network prefix for the requested address. * * @return * 0 on success * -1 on error and errno is set. */ int nozzle_del_ip(nozzle_t nozzle, const char *ipaddr, const char *prefix); #define IPADDR_CHAR_MAX 128 #define PREFIX_CHAR_MAX 4 struct nozzle_ip { char ipaddr[IPADDR_CHAR_MAX + 1]; char prefix[PREFIX_CHAR_MAX + 1]; int domain; /* AF_INET or AF_INET6 */ struct nozzle_ip *next; }; /** * nozzle_get_ips * @brief retrive the list of all configured ips for a given interface * * nozzle - pointer to the nozzle struct * * nozzle_ip - pointer to the head of a list of nozzle_ip structs. * The last IP will have next = NULL. * nozzle_ip can be NULL if there are no IP addresses * associated with this nozzle device. * *DO NOT* free those structs as they are used internally * for IP address tracking. * * @return * 0 on success * -1 on error and errno is set. * */ int nozzle_get_ips(const nozzle_t nozzle, struct nozzle_ip **nozzle_ip); /** * nozzle_get_mtu * @brief retrive mtu on a given nozzle interface * * nozzle - pointer to the nozzle struct * * @return * MTU on success * -1 on error and errno is set. */ int nozzle_get_mtu(const nozzle_t nozzle); /** * nozzle_set_mtu * @brief set mtu on a given nozzle interface * * nozzle - pointer to the nozzle struct * * mtu - new MTU value * * @return * 0 on success * -1 on error and errno is set. */ int nozzle_set_mtu(nozzle_t nozzle, const int mtu); /** * nozzle_reset_mtu * @brief reset mtu on a given nozzle interface to the system default * * nozzle - pointer to the nozzle struct * * @return * 0 on success * -1 on error and errno is set. */ int nozzle_reset_mtu(nozzle_t nozzle); /** * nozzle_get_mac * @brief retrive mac address on a given nozzle interface * * nozzle - pointer to the nozzle struct * * ether_addr - pointers to string containing the current mac address. * The string is malloc'ed, the caller needs to free this buffer. * @return * 0 on success. * -1 on error and errno is set. */ int nozzle_get_mac(const nozzle_t nozzle, char **ether_addr); /** * nozzle_set_mac * @brief set mac address on a given nozzle interface * * nozzle - pointer to the nozzle struct * * ether_addr - pointers to string containing the new mac address. * * @return * 0 on success. * -1 on error and errno is set. */ int nozzle_set_mac(nozzle_t nozzle, const char *ether_addr); /** * nozzle_reset_mac * @brief reset mac address on a given nozzle interface to system default * * nozzle - pointer to the nozzle struct * * @return * 0 on success. * -1 on error and errno is set. */ int nozzle_reset_mac(nozzle_t nozzle); /** * nozzle_get_handle_by_name * @brief find a nozzle handle by device name * * devname - string containing the name of the interface * * @return * handle on success. * NULL on error and errno is set. */ nozzle_t nozzle_get_handle_by_name(const char *devname); /** * nozzle_get_name_by_handle * @brief retrive nozzle interface name by handle * * nozzle - pointer to the nozzle struct * * @return * pointer to the interface name * NULL on error and errno is set. */ const char *nozzle_get_name_by_handle(const nozzle_t nozzle); /** * nozzle_get_fd * @brief * * nozzle - pointer to the nozzle struct * * @return * fd associated to a given nozzle on success. * -1 on error and errno is set. */ int nozzle_get_fd(const nozzle_t nozzle); #endif diff --git a/libnozzle/tests/Makefile.am b/libnozzle/tests/Makefile.am index 4a53ea44..64af8388 100644 --- a/libnozzle/tests/Makefile.am +++ b/libnozzle/tests/Makefile.am @@ -1,112 +1,117 @@ # # Copyright (C) 2017-2018 Red Hat, Inc. All rights reserved. # # Author: Fabio M. Di Nitto # # This software licensed under GPL-2.0+, LGPL-2.0+ # MAINTAINERCLEANFILES = Makefile.in include $(top_srcdir)/build-aux/check.mk EXTRA_DIST = \ - tap_updown_bad \ - tap_updown_good \ + nozzle_run_updown_exit_true \ + nozzle_run_updown_exit_false \ api-test-coverage noinst_HEADERS = \ test-common.h if BUILD_LIBNOZZLE api_checks = \ api_nozzle_open_test \ api_nozzle_close_test \ api_nozzle_set_up_test \ api_nozzle_set_down_test \ api_nozzle_get_mtu_test \ api_nozzle_set_mtu_test \ api_nozzle_get_mac_test \ api_nozzle_set_mac_test \ api_nozzle_get_handle_by_name_test \ api_nozzle_get_name_by_handle_test \ - api_nozzle_get_fd_test + api_nozzle_get_fd_test \ + api_nozzle_run_updown_test int_checks = \ int_execute_bin_sh_command_test \ nozzle_test fun_checks = benchmarks = check_PROGRAMS = \ $(api_checks) \ $(int_checks) \ $(fun_checks) noinst_PROGRAMS = \ $(benchmarks) \ $(check_PROGRAMS) noinst_SCRIPTS = \ api-test-coverage TESTS = $(check_PROGRAMS) check-local: check-api-test-coverage check-api-test-coverage: chmod u+x $(top_srcdir)/libnozzle/tests/api-test-coverage $(top_srcdir)/libnozzle/tests/api-test-coverage $(top_srcdir) $(top_builddir) -AM_CPPFLAGS = -I$(top_srcdir)/libnozzle -DABSBUILDDIR=\"$(abs_builddir)\" +AM_CPPFLAGS = -I$(top_srcdir)/libnozzle -DABSBUILDDIR=\"$(abs_builddir)\" -DABSSRCDIR=\"$(abs_srcdir)\" AM_CFLAGS += $(PTHREAD_CFLAGS) $(libnl_CFLAGS) LIBS += $(top_builddir)/libnozzle/libnozzle.la $(PTHREAD_LIBS) $(libnl_LIBS) api_nozzle_open_test_SOURCES = api_nozzle_open.c \ test-common.c api_nozzle_close_test_SOURCES = api_nozzle_close.c \ test-common.c api_nozzle_set_up_test_SOURCES = api_nozzle_set_up.c \ test-common.c \ ../internals.c api_nozzle_set_down_test_SOURCES = api_nozzle_set_down.c \ test-common.c \ ../internals.c api_nozzle_get_mtu_test_SOURCES = api_nozzle_get_mtu.c \ test-common.c api_nozzle_set_mtu_test_SOURCES = api_nozzle_set_mtu.c \ test-common.c \ ../internals.c api_nozzle_get_mac_test_SOURCES = api_nozzle_get_mac.c \ test-common.c api_nozzle_set_mac_test_SOURCES = api_nozzle_set_mac.c \ test-common.c api_nozzle_get_handle_by_name_test_SOURCES = api_nozzle_get_handle_by_name.c \ test-common.c api_nozzle_get_name_by_handle_test_SOURCES = api_nozzle_get_name_by_handle.c \ test-common.c api_nozzle_get_fd_test_SOURCES = api_nozzle_get_fd.c \ test-common.c +api_nozzle_run_updown_test_SOURCES = api_nozzle_run_updown.c \ + test-common.c \ + ../internals.c + int_execute_bin_sh_command_test_SOURCES = int_execute_bin_sh_command.c \ test-common.c \ ../internals.c nozzle_test_SOURCES = nozzle_test.c \ test-common.c \ ../internals.c endif diff --git a/libnozzle/tests/api_nozzle_run_updown.c b/libnozzle/tests/api_nozzle_run_updown.c new file mode 100644 index 00000000..89bf9095 --- /dev/null +++ b/libnozzle/tests/api_nozzle_run_updown.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. + * + * Author: Fabio M. Di Nitto + * + * This software licensed under GPL-2.0+, LGPL-2.0+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test-common.h" + +static int test(void) +{ + char device_name[IFNAMSIZ]; + size_t size = IFNAMSIZ; + int err=0; + nozzle_t nozzle = NULL; + char *error_string = NULL; + char *tmpdir = NULL; + char tmpdirsrc[PATH_MAX]; + char tmpstr[PATH_MAX*2]; + char srcfile[PATH_MAX]; + char dstfile[PATH_MAX]; + + /* + * create a tmp dir for storing up/down scripts. + * we cannot create symlinks src dir + */ + strcpy(tmpdirsrc, ABSBUILDDIR "/nozzle_test_XXXXXX"); + + tmpdir = mkdtemp(tmpdirsrc); + if (!tmpdir) { + printf("Unable to create temporary directory %s for testing: %s\n", tmpdirsrc, strerror(errno)); + return -1; + } + + printf("Created temporary test dir: %s\n", tmpdir); + + printf("Populating test dir...\n"); + + snprintf(tmpstr, sizeof(tmpstr) - 1, "%s/pre-up.d", tmpdir); + if (mkdir(tmpstr, 0700) < 0) { + printf("Unable to create %s/pre-up.d: %s", tmpdir, strerror(errno)); + err = -1; + goto out_clean; + } + + snprintf(tmpstr, sizeof(tmpstr) - 1, "%s/up.d", tmpdir); + if (mkdir(tmpstr, 0700) < 0) { + printf("Unable to create %s/up.d: %s", tmpdir, strerror(errno)); + err = -1; + goto out_clean; + } + + snprintf(tmpstr, sizeof(tmpstr) - 1, "%s/down.d", tmpdir); + if (mkdir(tmpstr, 0700) < 0) { + printf("Unable to create %s/down.d: %s", tmpdir, strerror(errno)); + err = -1; + goto out_clean; + } + + snprintf(tmpstr, sizeof(tmpstr) - 1, "%s/post-down.d", tmpdir); + if (mkdir(tmpstr, 0700) < 0) { + printf("Unable to create %s/post-down.d: %s", tmpdir, strerror(errno)); + err = -1; + goto out_clean; + } + + printf("Testing error conditions\n"); + + printf("Init nozzle device with no path\n"); + + memset(device_name, 0, size); + nozzle = nozzle_open(device_name, size, NULL); + if (!nozzle) { + printf("Unable to init %s\n", device_name); + err = -1; + goto out_clean; + } + + err = nozzle_run_updown(nozzle, NOZZLE_POSTDOWN, &error_string); + if ((!err) || (errno != EINVAL)) { + printf("nozzle_run_updown sanity check failed\n"); + err = -1; + goto out_clean; + } + + nozzle_close(nozzle); + + printf("Init nozzle device with path\n"); + + memset(device_name, 0, size); + nozzle = nozzle_open(device_name, size, tmpdir); + if (!nozzle) { + printf("Unable to init %s\n", device_name); + err = -1; + goto out_clean; + } + + printf("Testing invalid nozzle handle\n"); + + err = nozzle_run_updown(NULL, NOZZLE_POSTDOWN, &error_string); + if ((!err) || (errno != EINVAL)) { + printf("nozzle_run_updown sanity check failed\n"); + err = -1; + goto out_clean; + } + + printf("Testing invalid action\n"); + + err = nozzle_run_updown(nozzle, NOZZLE_POSTDOWN + 1, &error_string); + if ((!err) || (errno != EINVAL)) { + printf("nozzle_run_updown sanity check failed\n"); + err = -1; + goto out_clean; + } + + printf("Testing invalid error string\n"); + + err = nozzle_run_updown(nozzle, NOZZLE_POSTDOWN + 1, NULL); + if ((!err) || (errno != EINVAL)) { + printf("nozzle_run_updown sanity check failed\n"); + err = -1; + goto out_clean; + } + + printf("Testing interface pre-up/up/down/post-down (no scripts installed)\n"); + + err = nozzle_run_updown(nozzle, NOZZLE_PREUP, &error_string); + if (!err) { + printf("nozzle_run_updown failed to detect lack of script in pre-up.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + err = nozzle_run_updown(nozzle, NOZZLE_UP, &error_string); + if (!err) { + printf("nozzle_run_updown failed to detect lack of script in up.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + err = nozzle_run_updown(nozzle, NOZZLE_DOWN, &error_string); + if (!err) { + printf("nozzle_run_updown failed to detect lack of script in down.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + err = nozzle_run_updown(nozzle, NOZZLE_POSTDOWN, &error_string); + if (!err) { + printf("nozzle_run_updown failed to detect lack of script in post-down.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + printf("Populating test dir with fail scripts\n"); + + snprintf(srcfile, sizeof(srcfile) - 1, "%s/nozzle_run_updown_exit_false", ABSSRCDIR); + + snprintf(dstfile, sizeof(dstfile) - 1, "%s/pre-up.d/%s", tmpdir, device_name); + if (link(srcfile, dstfile) < 0) { + printf("unable to create symlink\n"); + err = -1; + goto out_clean; + } + + snprintf(dstfile, sizeof(dstfile) - 1, "%s/up.d/%s", tmpdir, device_name); + if (link(srcfile, dstfile) < 0) { + printf("unable to create symlink\n"); + err = -1; + goto out_clean; + } + + snprintf(dstfile, sizeof(dstfile) - 1, "%s/down.d/%s", tmpdir, device_name); + if (link(srcfile, dstfile) < 0) { + printf("unable to create symlink\n"); + err = -1; + goto out_clean; + } + + snprintf(dstfile, sizeof(dstfile) - 1, "%s/post-down.d/%s", tmpdir, device_name); + if (link(srcfile, dstfile) < 0) { + printf("unable to create symlink\n"); + err = -1; + goto out_clean; + } + + printf("Testing interface pre-up/up/down/post-down (FAIL scripts installed)\n"); + + err = nozzle_run_updown(nozzle, NOZZLE_PREUP, &error_string); + if (err != -2) { + printf("nozzle_run_updown failed to detect script failure in pre-up.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + err = nozzle_run_updown(nozzle, NOZZLE_UP, &error_string); + if (err != -2) { + printf("nozzle_run_updown failed to detect script failure in up.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + err = nozzle_run_updown(nozzle, NOZZLE_DOWN, &error_string); + if (err != -2) { + printf("nozzle_run_updown failed to detect script failure in down.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + err = nozzle_run_updown(nozzle, NOZZLE_POSTDOWN, &error_string); + if (err != -2) { + printf("nozzle_run_updown failed to detect script failure in post-down.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + printf("Populating test dir with true scripts\n"); + + snprintf(srcfile, sizeof(srcfile) - 1, "%s/nozzle_run_updown_exit_true", ABSSRCDIR); + + snprintf(dstfile, sizeof(dstfile) - 1, "%s/pre-up.d/%s", tmpdir, device_name); + if (unlink(dstfile) < 0) { + printf("unable to remove old symlink\n"); + err = -1; + goto out_clean; + } + if (link(srcfile, dstfile) < 0) { + printf("unable to create symlink\n"); + err = -1; + goto out_clean; + } + + snprintf(dstfile, sizeof(dstfile) - 1, "%s/up.d/%s", tmpdir, device_name); + if (unlink(dstfile) < 0) { + printf("unable to remove old symlink\n"); + err = -1; + goto out_clean; + } + if (link(srcfile, dstfile) < 0) { + printf("unable to create symlink\n"); + err = -1; + goto out_clean; + } + + snprintf(dstfile, sizeof(dstfile) - 1, "%s/down.d/%s", tmpdir, device_name); + if (unlink(dstfile) < 0) { + printf("unable to remove old symlink\n"); + err = -1; + goto out_clean; + } + if (link(srcfile, dstfile) < 0) { + printf("unable to create symlink\n"); + err = -1; + goto out_clean; + } + + snprintf(dstfile, sizeof(dstfile) - 1, "%s/post-down.d/%s", tmpdir, device_name); + if (unlink(dstfile) < 0) { + printf("unable to remove old symlink\n"); + err = -1; + goto out_clean; + } + if (link(srcfile, dstfile) < 0) { + printf("unable to create symlink\n"); + err = -1; + goto out_clean; + } + + printf("Testing interface pre-up/up/down/post-down (TRUE scripts installed)\n"); + + err = nozzle_run_updown(nozzle, NOZZLE_PREUP, &error_string); + if (err) { + printf("nozzle_run_updown failed to execute true script in pre-up.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + err = nozzle_run_updown(nozzle, NOZZLE_UP, &error_string); + if (err) { + printf("nozzle_run_updown failed to execute true script in up.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + err = nozzle_run_updown(nozzle, NOZZLE_DOWN, &error_string); + if (err) { + printf("nozzle_run_updown failed to execite true script in down.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + err = nozzle_run_updown(nozzle, NOZZLE_POSTDOWN, &error_string); + if (err) { + printf("nozzle_run_updown failed to execute true script in post-down.d\n"); + err = -1; + goto out_clean; + } else { + if (error_string) { + free(error_string); + error_string = NULL; + } + } + + +out_clean: + if (tmpdir) { + snprintf(tmpstr, sizeof(tmpstr) - 1, "rm -rf %s", tmpdir); + printf("Removing temporary dir: %s\n", tmpstr); + err = execute_bin_sh_command(tmpstr, &error_string); + if (err) { + printf("Error removing directory: %s\n", error_string); + } + if (error_string) { + free(error_string); + } + } + if (nozzle) { + nozzle_close(nozzle); + } + + return err; +} + +int main(void) +{ + need_root(); + + if (test() < 0) + return FAIL; + + return PASS; +} diff --git a/libnozzle/tests/int_execute_bin_sh_command.c b/libnozzle/tests/int_execute_bin_sh_command.c index b1b0a42f..c5a7e5d4 100644 --- a/libnozzle/tests/int_execute_bin_sh_command.c +++ b/libnozzle/tests/int_execute_bin_sh_command.c @@ -1,124 +1,121 @@ /* * Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include #include #include #include "test-common.h" static int test(void) { int err = 0; - char command[4096]; char *error_string = NULL; - memset(command, 0, sizeof(command)); - printf("Testing execute_bin_sh_command\n"); printf("command true\n"); err = execute_bin_sh_command("true", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err) { printf("Unable to execute true ?!?!\n"); goto out_clean; } printf("command false\n"); err = execute_bin_sh_command("false", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Can we really execute false successfully?!?!\n"); err = -1; goto out_clean; } printf("command that outputs to stdout (enforcing redirect)\n"); err = execute_bin_sh_command("grep -h 2>&1", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Can we really execute grep -h successfully?!?\n"); err = -1; goto out_clean; } printf("command that outputs to stderr\n"); err = execute_bin_sh_command("grep -h", &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Can we really execute grep -h successfully?!?\n"); err = -1; goto out_clean; } printf("Testing ERROR conditions\n"); printf("empty command\n"); err = execute_bin_sh_command(NULL, &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if ((!err) || (errno != EINVAL)) { printf("execute_bin_sh_command returned incorrect error or incorrect errno!\n"); err = -1; goto out_clean; } printf("empty error\n"); err = execute_bin_sh_command("true", NULL); if ((!err) || (errno != EINVAL)) { printf("execute_bin_sh_command returned incorrect error or incorrect errno!\n"); err = -1; goto out_clean; } err = 0; out_clean: return err; } int main(void) { need_root(); if (test() < 0) return FAIL; return PASS; } diff --git a/libnozzle/tests/tap_updown_bad/down.d/kronostest b/libnozzle/tests/nozzle_run_updown_exit_false similarity index 100% rename from libnozzle/tests/tap_updown_bad/down.d/kronostest rename to libnozzle/tests/nozzle_run_updown_exit_false diff --git a/libnozzle/tests/tap_updown_good/down.d/kronostest b/libnozzle/tests/nozzle_run_updown_exit_true similarity index 100% rename from libnozzle/tests/tap_updown_good/down.d/kronostest rename to libnozzle/tests/nozzle_run_updown_exit_true diff --git a/libnozzle/tests/nozzle_test.c b/libnozzle/tests/nozzle_test.c index ad1ab79d..475035d0 100644 --- a/libnozzle/tests/nozzle_test.c +++ b/libnozzle/tests/nozzle_test.c @@ -1,478 +1,241 @@ /* * Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include #include #include #include "test-common.h" char testipv4_1[IPBUFSIZE]; char testipv4_2[IPBUFSIZE]; char testipv6_1[IPBUFSIZE]; char testipv6_2[IPBUFSIZE]; -static int check_knet_up_down(void) -{ - char verifycmd[1024]; - char device_name[IFNAMSIZ]; - size_t size = IFNAMSIZ; - int err=0; - nozzle_t nozzle; - char *error_string = NULL; - - printf("Testing interface up/down\n"); - - memset(device_name, 0, size); - nozzle = nozzle_open(device_name, size, NULL); - if (!nozzle) { - printf("Unable to init %s\n", device_name); - return -1; - } - - printf("Put the interface up\n"); - - err = nozzle_set_up(nozzle); - if (err < 0) { - printf("Unable to set interface up\n"); - err = -1; - goto out_clean; - } - - memset(verifycmd, 0, sizeof(verifycmd)); - snprintf(verifycmd, sizeof(verifycmd)-1, -#ifdef KNET_LINUX - "ip addr show dev %s | grep -q UP", nozzle->name); -#endif -#ifdef KNET_BSD - "ifconfig %s | grep -q UP", nozzle->name); -#endif - err = execute_bin_sh_command(verifycmd, &error_string); - if (error_string) { - printf("Error string: %s\n", error_string); - free(error_string); - error_string = NULL; - } - if (err < 0) { - printf("Unable to verify inteface UP\n"); - err = -1; - goto out_clean; - } - - printf("Put the interface down\n"); - - err = nozzle_set_down(nozzle); - if (err < 0) { - printf("Unable to put the interface down\n"); - err = -1; - goto out_clean; - } - - memset(verifycmd, 0, sizeof(verifycmd)); - snprintf(verifycmd, sizeof(verifycmd)-1, -#ifdef KNET_LINUX - "ip addr show dev %s | grep -q UP", nozzle->name); -#endif -#ifdef KNET_BSD - "ifconfig %s | grep -q UP", nozzle->name); -#endif - err = execute_bin_sh_command(verifycmd, &error_string); - if (error_string) { - printf("Error string: %s\n", error_string); - free(error_string); - error_string = NULL; - } - if (!err) { - printf("Unable to verify inteface DOWN\n"); - err = -1; - goto out_clean; - } - - nozzle_close(nozzle); - - printf("Testing interface pre-up/up/down/post-down (exec errors)\n"); - - memset(device_name, 0, size); - nozzle = nozzle_open(device_name, size, ABSBUILDDIR "/nozzle_updown_bad"); - if (!nozzle) { - printf("Unable to init %s\n", device_name); - return -1; - } - - printf("Put the interface up\n"); - - err = nozzle_run_updown(nozzle, NOZZLE_PREUP, &error_string); - if (err) { - printf("nozzle_run_updown NOZZLE_PREUP error: %s\n", strerror(errno)); - } - if (error_string) { - printf("preup output: %s\n", error_string); - free(error_string); - error_string = NULL; - } - err = nozzle_set_up(nozzle); - if (err < 0) { - printf("Unable to put the interface up\n"); - err = -1; - goto out_clean; - } - err = nozzle_run_updown(nozzle, NOZZLE_UP, &error_string); - if (err) { - printf("nozzle_run_updown NOZZLE_UP error: %s\n", strerror(errno)); - } - if (error_string) { - printf("up output: %s\n", error_string); - free(error_string); - error_string = NULL; - } - - printf("Put the interface down\n"); - - err = nozzle_run_updown(nozzle, NOZZLE_DOWN, &error_string); - if (err) { - printf("nozzle_run_updown NOZZLE_DOWN error: %s\n", strerror(errno)); - } - if (error_string) { - printf("down output: %s\n", error_string); - free(error_string); - error_string = NULL; - } - err = nozzle_set_down(nozzle); - if (err < 0) { - printf("Unable to put the interface down\n"); - err = -1; - goto out_clean; - } - err = nozzle_run_updown(nozzle, NOZZLE_POSTDOWN, &error_string); - if (err) { - printf("nozzle_run_updown NOZZLE_POSTDOWN error: %s\n", strerror(errno)); - } - if (error_string) { - printf("postdown output: %s\n", error_string); - free(error_string); - error_string = NULL; - } - - nozzle_close(nozzle); - - printf("Testing interface pre-up/up/down/post-down\n"); - - memset(device_name, 0, size); - - nozzle = nozzle_open(device_name, size, ABSBUILDDIR "/nozzle_updown_good"); - if (!nozzle) { - printf("Unable to init %s\n", device_name); - return -1; - } - - printf("Put the interface up\n"); - - err = nozzle_run_updown(nozzle, NOZZLE_PREUP, &error_string); - if (err) { - printf("nozzle_run_updown NOZZLE_PREUP error: %s\n", strerror(errno)); - } - if (error_string) { - printf("preup output: %s\n", error_string); - free(error_string); - error_string = NULL; - } - err = nozzle_set_up(nozzle); - if (err < 0) { - printf("Unable to put the interface up\n"); - err = -1; - goto out_clean; - } - err = nozzle_run_updown(nozzle, NOZZLE_UP, &error_string); - if (err) { - printf("nozzle_run_updown NOZZLE_UP error: %s\n", strerror(errno)); - } - if (error_string) { - printf("up output: %s\n", error_string); - free(error_string); - error_string = NULL; - } - - printf("Put the interface down\n"); - - err = nozzle_run_updown(nozzle, NOZZLE_DOWN, &error_string); - if (err) { - printf("nozzle_run_updown NOZZLE_DOWN error: %s\n", strerror(errno)); - } - if (error_string) { - printf("down output: %s\n", error_string); - free(error_string); - error_string = NULL; - } - err = nozzle_set_down(nozzle); - if (err < 0) { - printf("Unable to put the interface down\n"); - err = -1; - goto out_clean; - } - err = nozzle_run_updown(nozzle, NOZZLE_POSTDOWN, &error_string); - if (err) { - printf("nozzle_run_updown NOZZLE_POSTDOWN error: %s\n", strerror(errno)); - } - if (error_string) { - printf("postdown output: %s\n", error_string); - free(error_string); - error_string = NULL; - } - - nozzle_close(nozzle); - - printf("Test ERROR conditions\n"); - - printf("Pass NULL to nozzle set_up\n"); - err = 0; - errno = 0; - if ((nozzle_set_up(NULL) >= 0) || (errno != EINVAL)) { - printf("Something is wrong in nozzle_set_up sanity checks\n"); - err = -1; - goto out_clean; - } - - printf("Pass NULL to nozzle set_down\n"); - errno = 0; - if ((nozzle_set_down(NULL) >= 0) || (errno != EINVAL)) { - printf("Something is wrong in nozzle_set_down sanity checks\n"); - err = -1; - goto out_clean; - } - -out_clean: - nozzle_close(nozzle); - - return err; -} - static int check_knet_set_del_ip(void) { char device_name[IFNAMSIZ]; size_t size = IFNAMSIZ; char verifycmd[2048]; int err = 0; nozzle_t nozzle; struct nozzle_ip *ip_list = NULL, *ip_list_tmp = NULL; int ip_list_entries = 0; char *error_string = NULL; printf("Testing interface add/remove ip\n"); memset(device_name, 0, size); nozzle = nozzle_open(device_name, size, NULL); if (!nozzle) { printf("Unable to init %s\n", device_name); return -1; } printf("Adding ip: %s/24\n", testipv4_1); err = nozzle_add_ip(nozzle, testipv4_1, "24"); if (err < 0) { printf("Unable to assign IP address\n"); err=-1; goto out_clean; } printf("Adding ip: %s/24\n", testipv4_2); err = nozzle_add_ip(nozzle, testipv4_2, "24"); if (err < 0) { printf("Unable to assign IP address\n"); err=-1; goto out_clean; } printf("Adding duplicate ip: %s/24\n", testipv4_1); err = nozzle_add_ip(nozzle, testipv4_1, "24"); if (err < 0) { printf("Unable to find IP address in libnozzle db\n"); err=-1; goto out_clean; } printf("Checking ip: %s/24\n", testipv4_1); memset(verifycmd, 0, sizeof(verifycmd)); snprintf(verifycmd, sizeof(verifycmd)-1, #ifdef KNET_LINUX "ip addr show dev %s | grep -q %s/24", nozzle->name, testipv4_1); #endif #ifdef KNET_BSD "ifconfig %s | grep -q %s", nozzle->name, testipv4_1); #endif err = execute_bin_sh_command(verifycmd, &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } printf("Get ip list from libnozzle:\n"); if (nozzle_get_ips(nozzle, &ip_list) < 0) { printf("Not enough mem?\n"); err=-1; goto out_clean; } ip_list_tmp = ip_list; ip_list_entries = 0; while(ip_list_tmp) { ip_list_entries++; printf("Found IP %s %s in libnozzle db\n", ip_list_tmp->ipaddr, ip_list_tmp->prefix); ip_list_tmp = ip_list_tmp->next; } if (ip_list_entries != 2) { printf("Didn't get enough ip back from libnozzle?\n"); err=-1; goto out_clean; } printf("Deleting ip: %s/24\n", testipv4_1); err = nozzle_del_ip(nozzle, testipv4_1, "24"); if (err < 0) { printf("Unable to delete IP address\n"); err=-1; goto out_clean; } printf("Deleting ip: %s/24\n", testipv4_2); err = nozzle_del_ip(nozzle, testipv4_2, "24"); if (err < 0) { printf("Unable to delete IP address\n"); err=-1; goto out_clean; } printf("Deleting again ip: %s/24\n", testipv4_1); err = nozzle_del_ip(nozzle, testipv4_1, "24"); if (err < 0) { printf("Unable to delete IP address\n"); err=-1; goto out_clean; } memset(verifycmd, 0, sizeof(verifycmd)); snprintf(verifycmd, sizeof(verifycmd)-1, #ifdef KNET_LINUX "ip addr show dev %s | grep -q %s/24", nozzle->name, testipv4_1); #endif #ifdef KNET_BSD "ifconfig %s | grep -q %s", nozzle->name, testipv4_1); #endif err = execute_bin_sh_command(verifycmd, &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } printf("Adding ip: %s/64\n", testipv6_1); err = nozzle_add_ip(nozzle, testipv6_1, "64"); if (err < 0) { printf("Unable to assign IP address\n"); err=-1; goto out_clean; } memset(verifycmd, 0, sizeof(verifycmd)); snprintf(verifycmd, sizeof(verifycmd)-1, #ifdef KNET_LINUX "ip addr show dev %s | grep -q %s/64", nozzle->name, testipv6_1); #endif #ifdef KNET_BSD "ifconfig %s | grep -q %s", nozzle->name, testipv6_1); #endif err = execute_bin_sh_command(verifycmd, &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } printf("Deleting ip: %s/64\n", testipv6_1); err = nozzle_del_ip(nozzle, testipv6_1, "64"); if (err) { printf("Unable to delete IP address\n"); err=-1; goto out_clean; } memset(verifycmd, 0, sizeof(verifycmd)); snprintf(verifycmd, sizeof(verifycmd)-1, #ifdef KNET_LINUX "ip addr show dev %s | grep -q %s/64", nozzle->name, testipv6_1); #endif #ifdef KNET_BSD "ifconfig %s | grep -q %s", nozzle->name, testipv6_1); #endif err = execute_bin_sh_command(verifycmd, &error_string); if (error_string) { printf("Error string: %s\n", error_string); free(error_string); error_string = NULL; } if (!err) { printf("Unable to verify IP address\n"); err=-1; goto out_clean; } out_clean: nozzle_close(nozzle); return err; } int main(void) { need_root(); make_local_ips(testipv4_1, testipv4_2, testipv6_1, testipv6_2); - if (check_knet_up_down() < 0) - return -1; - if (check_knet_set_del_ip() < 0) return -1; return 0; } diff --git a/libnozzle/tests/tap_updown_bad/post-down.d/kronostest b/libnozzle/tests/tap_updown_bad/post-down.d/kronostest deleted file mode 100755 index 721ecb6e..00000000 --- a/libnozzle/tests/tap_updown_bad/post-down.d/kronostest +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. -# -# Author: Fabio M. Di Nitto -# -# This software licensed under GPL-2.0+, LGPL-2.0+ -# - -exit 1 diff --git a/libnozzle/tests/tap_updown_bad/pre-up.d/kronostest b/libnozzle/tests/tap_updown_bad/pre-up.d/kronostest deleted file mode 100755 index 721ecb6e..00000000 --- a/libnozzle/tests/tap_updown_bad/pre-up.d/kronostest +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. -# -# Author: Fabio M. Di Nitto -# -# This software licensed under GPL-2.0+, LGPL-2.0+ -# - -exit 1 diff --git a/libnozzle/tests/tap_updown_bad/up.d/kronostest b/libnozzle/tests/tap_updown_bad/up.d/kronostest deleted file mode 100755 index 721ecb6e..00000000 --- a/libnozzle/tests/tap_updown_bad/up.d/kronostest +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. -# -# Author: Fabio M. Di Nitto -# -# This software licensed under GPL-2.0+, LGPL-2.0+ -# - -exit 1 diff --git a/libnozzle/tests/tap_updown_good/post-down.d/kronostest b/libnozzle/tests/tap_updown_good/post-down.d/kronostest deleted file mode 100755 index 6e28da18..00000000 --- a/libnozzle/tests/tap_updown_good/post-down.d/kronostest +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. -# -# Author: Fabio M. Di Nitto -# -# This software licensed under GPL-2.0+, LGPL-2.0+ -# - -exit 0 diff --git a/libnozzle/tests/tap_updown_good/pre-up.d/kronostest b/libnozzle/tests/tap_updown_good/pre-up.d/kronostest deleted file mode 100755 index 6e28da18..00000000 --- a/libnozzle/tests/tap_updown_good/pre-up.d/kronostest +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. -# -# Author: Fabio M. Di Nitto -# -# This software licensed under GPL-2.0+, LGPL-2.0+ -# - -exit 0 diff --git a/libnozzle/tests/tap_updown_good/up.d/kronostest b/libnozzle/tests/tap_updown_good/up.d/kronostest deleted file mode 100755 index 6e28da18..00000000 --- a/libnozzle/tests/tap_updown_good/up.d/kronostest +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# -# Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. -# -# Author: Fabio M. Di Nitto -# -# This software licensed under GPL-2.0+, LGPL-2.0+ -# - -exit 0