Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:12.2:ARM
open-fcoe
fcoe-utils-rc2-update
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File fcoe-utils-rc2-update of Package open-fcoe
updates to open-fcoe commit:- From: Vasu Dev <vasu.dev@intel.com> commit 95becb29bce2d8d94687f7c83d5f009e945be3de Author: Chris Leech <christopher.leech@intel.com> Date: Wed Mar 17 12:02:51 2010 -0700 fipvlan: make output able to show complete interface names up to IFNAMSIZ This update adds auto fipvlan creations beside few minor fixes & improvements. Signed-off-by: Vasu Dev <vasu.dev@intel.com> --- Makefile.am | 20 + QUICKSTART | 3 configure.ac | 3 debug/fcoedump.sh | 67 +++- etc/cfg-ethx | 5 fcoe_utils.c | 191 ----------- fcoe_utils.h | 85 ----- fcoe_utils_version.h.in | 6 fcoemon.c | 200 ++++++++++- fcoemon.h | 7 fcoemon_utils.c | 688 --------------------------------------- fcoemon_utils.h | 268 --------------- fipvlan.c | 683 ++++++++++++++++----------------------- include/fcoe_utils.h | 85 +++++ include/fcoe_utils_version.h.in | 6 include/fcoemon_utils.h | 270 +++++++++++++++ include/fip.h | 25 + include/list.h | 444 ------------------------- include/log.h | 15 - include/rtnetlink.h | 63 ++++ lib/Makefile.am | 3 lib/fcoe_utils.c | 191 +++++++++++ lib/fip.c | 155 +++++++++ lib/rtnetlink.c | 429 ++++++++++++++++++++++++ lib/sa_log.c | 256 +++++++++++++++ lib/sa_other.c | 69 ++++ lib/sa_select.c | 213 ++++++++++++ lib/sa_timer.c | 226 +++++++++++++ log.c | 87 ----- 29 files changed, 2530 insertions(+), 2233 deletions(-) delete mode 100644 fcoe_utils.c delete mode 100644 fcoe_utils.h delete mode 100644 fcoe_utils_version.h.in delete mode 100644 fcoemon_utils.c delete mode 100644 fcoemon_utils.h create mode 100644 include/fcoe_utils.h create mode 100644 include/fcoe_utils_version.h.in create mode 100644 include/fcoemon_utils.h delete mode 100644 include/list.h delete mode 100644 include/log.h create mode 100644 include/rtnetlink.h create mode 100644 lib/Makefile.am create mode 100644 lib/fcoe_utils.c create mode 100644 lib/fip.c create mode 100644 lib/rtnetlink.c create mode 100644 lib/sa_log.c create mode 100644 lib/sa_other.c create mode 100644 lib/sa_select.c create mode 100644 lib/sa_timer.c delete mode 100644 log.c diff --git a/Makefile.am b/Makefile.am index e533cf7..bdd510f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,6 @@ +# build libutil first +SUBDIRS = lib . + ## target programs, to be built and installed in $(prefix)/sbin sbin_PROGRAMS = fcoeadm fcping fipvlan fcnsq fcrls if WITH_DCB @@ -12,9 +15,10 @@ AM_CFLAGS = -DSYSCONFDIR="\"${sysconfdir}\"" ## rules for building fcoeadm ## only listed sources get packaged, so must list all headers too -fcoeadm_SOURCES = fcoeadm_display.c fcoeadm.c fcoeadm.h fcoe_utils.c \ -fcoe_utils.h fcoe_utils_version.h include/fc_scsi.h include/fc_types.h \ +fcoeadm_SOURCES = fcoeadm_display.c fcoeadm.c fcoeadm.h \ +include/fc_scsi.h include/fc_types.h \ include/net_types.h fcoe_clif.h +fcoeadm_LDADD = lib/libutil.a ## fcoeadm uses HBAAPI, so get the right flags for compiling and linking fcoeadm_CFLAGS = $(HBAAPI_CFLAGS) @@ -22,7 +26,8 @@ fcoeadm_LDFLAGS = $(HBAAPI_LIBS) ## rules for building fcping ## only listed sources get packaged, so must list all headers too -fcping_SOURCES = fcping.c fcoe_utils.c fcoe_utils.h +fcping_SOURCES = fcping.c +fcping_LDADD = lib/libutil.a ## fcping uses HBAAPI, so get the right flags for compiling and linking fcping_CFLAGS = $(HBAAPI_CFLAGS) @@ -30,9 +35,10 @@ fcping_LDFLAGS = $(HBAAPI_LIBS) -lrt ## rules for building fcoemon ## only listed sources get packaged, so must list all headers too -fcoemon_SOURCES = fcoemon_utils.c fcoemon.c fcoemon.h fcoemon_utils.h \ -fcoe_utils.c fcoe_utils.h fcoe_utils_version.h include/fc_scsi.h \ +fcoemon_SOURCES = fcoemon.c fcoemon.h \ +include/fc_scsi.h \ include/fc_types.h include/net_types.h fcoe_clif.h +fcoemon_LDADD = lib/libutil.a ## fcoemon needs headers from dcbd, get the right include path for them fcoemon_CFLAGS = $(DCBD_CFLAGS) @@ -40,8 +46,8 @@ fcoemon_LDFLAGS = -lrt ## rules for building fipvlan ## only listed sources get packaged, so must list all headers too -fipvlan_SOURCES = fipvlan.c fcoe_utils_version.h include/fip.h \ -log.c include/log.h include/list.h +fipvlan_SOURCES = fipvlan.c include/fip.h +fipvlan_LDADD = lib/libutil.a ## install configuration file in $(prefix)/etc/fcoe fcoe_configdir = ${sysconfdir}/fcoe diff --git a/QUICKSTART b/QUICKSTART index 766c068..e1d0e00 100644 --- a/QUICKSTART +++ b/QUICKSTART @@ -54,6 +54,9 @@ git://open-fcoe.org/openfc/fcoe-utils.git LibFCoE FCoE + Enable the block layer + Block layer SG support v4 + Device Drivers -> Network Device Support -> Ethernet (10000 Mbit) Intel(R) 10GbE PCI Express adapters support Data Center Bridging (DCB) Support diff --git a/configure.ac b/configure.ac index 517b9c2..db4e55c 100644 --- a/configure.ac +++ b/configure.ac @@ -3,6 +3,7 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AC_PROG_CC AM_PROG_CC_C_O +AC_PROG_RANLIB PKG_CHECK_MODULES([HBAAPI], [HBAAPI]) AC_SUBST([HBAAPI_CFLAGS]) @@ -19,6 +20,6 @@ AS_IF([test "X$with_dcb" != Xno], [PKG_CHECK_MODULES([DCBD], [dcbd]) AC_SUBST([DCBD_CFLAGS])]) -AC_CONFIG_FILES([Makefile fcoe-utils.spec fcoe_utils_version.h]) +AC_CONFIG_FILES([Makefile lib/Makefile fcoe-utils.spec include/fcoe_utils_version.h]) AC_OUTPUT diff --git a/debug/fcoedump.sh b/debug/fcoedump.sh index 3c4bac6..44e858a 100755 --- a/debug/fcoedump.sh +++ b/debug/fcoedump.sh @@ -1,3 +1,4 @@ +#!/bin/bash ############################################################################### # # fcoe_debug: print debugging information for fcoe @@ -17,24 +18,51 @@ else PHYSDEV=$DEVICE fi -kernel_info () { +kernel_info() +{ echo -e "\n###KERNEL INFO###" uname -a + + if [ -f "/proc/config.gz" ] + then + echo -e "\nzcat /proc/config.gz" + zcat /proc/config.gz + else + echo -e "\n/proc/config.gz does not exist." + fi } -system_info () { +system_info() +{ echo -e "\n###System Info###" - echo -e "#lsscsi:" + + echo -e "#lsscsi" lsscsi - echo -e "#lspci:" + + echo -e "#lspci" lspci - echo -e "#check symbols:" + echo -e "#grep dcbnl_init /proc/kallsyms" grep dcbnl_init /proc/kallsyms - grep fcoe /proc/kallsyms + + echo -e "#grep scsi_transport_fc /proc/kallsyms" + grep scsi_transport_fc /proc/kallsyms + + echo -e "#grep libfc /proc/kallsyms" + grep libfc /proc/kallsyms | grep -v libfcoe + + echo -e "#grep libfcoe /proc/kallsyms" + grep libfcoe /proc/kallsyms + + echo -e "#grep fcoe /proc/kallsyms" + grep fcoe /proc/kallsyms | grep -v libfcoe + + echo -e "#lsmod" + lsmod } -adapter_info () { +adapter_info() +{ if [ $DEVICE != $PHYSDEV ] then echo -e "\n###Adapter INFO VLAN $DEVICE" @@ -63,7 +91,8 @@ adapter_info () { ifconfig $PHYSDEV } -dcb_info () { +dcb_info() +{ echo -e "\n###DCB INFO" echo -e "#tc config" tc qdisc @@ -99,7 +128,8 @@ dcb_info () { dcbtool gp $PHYSDEV ll:0 } -fcoe_info () { +fcoe_info() +{ echo -e "\n###FCOE Info" echo -e "#service fcoe status" service fcoe status @@ -111,8 +141,16 @@ fcoe_info () { fcoeadm -t } -sysfs_dump () { - echo -e "###SYSFS dump" +bsg_info() +{ + echo -e "\n###BSG Info" + echo -e "#find /dev/bsg/" + find /dev/bsg/ 2>&1 +} + +sysfs_dump() +{ + echo -e "\n###SYSFS dump" echo -e "#sysfs fc_host dump" find /sys/class/fc_host/host*/ -type f -print -exec cat '{}' \; echo -e "#sysfs fc_transport dump" @@ -123,7 +161,8 @@ sysfs_dump () { find /sys/class/fc_vports/*/ -type f -print -exec cat '{}' \; } -logfile_dump() { +logfile_dump() +{ echo "###LOGFILES" echo "#/var/log/messages" cat /var/log/messages @@ -132,12 +171,14 @@ logfile_dump() { dmesg } -fcoe_debug () { +fcoe_debug() +{ kernel_info system_info adapter_info dcb_info fcoe_info + bsg_info sysfs_dump logfile_dump } diff --git a/etc/cfg-ethx b/etc/cfg-ethx index 96eca59..b7274ac 100644 --- a/etc/cfg-ethx +++ b/etc/cfg-ethx @@ -7,3 +7,8 @@ FCOE_ENABLE="yes" ## Default: yes # Indicate if DCB service is required at the Ethernet port DCB_REQUIRED="yes" + +## Type: yes/no +## Default: yes +# Indicate if VLAN discovery should be handled by fcoemon +AUTO_VLAN="yes" diff --git a/fcoe_utils.c b/fcoe_utils.c deleted file mode 100644 index 506356d..0000000 --- a/fcoe_utils.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright(c) 2010 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Maintained at www.Open-FCoE.org - */ - -#include "fcoe_utils.h" - -static int fcoe_sysfs_read(char *buf, int size, const char *path) -{ - FILE *fp; - int i, rc = -EINVAL; - - fp = fopen(path, "r"); - if (fp) { - if (fgets(buf, size, fp)) { - /* - * Strip trailing newline by replacing - * any '\r' or '\n' instances with '\0'. - * It's not as elegant as it could be, but - * we know that the symbolic name won't - * have either of those characters until - * the end of the line. - */ - for (i = 0; i < strlen(buf); i++) { - if (buf[i] == '\n' || - buf[i] == '\r') { - buf[i] = '\0'; - break; - } - } - rc = 0; - } - - fclose(fp); - } - - return rc; -} - -static int fcoe_check_fchost(const char *ifname, const char *dname) -{ - char buf[MAX_STR_LEN]; - char path[MAX_PATH_LEN]; - int rc = -EINVAL; - - sprintf(path, "%s/%s/symbolic_name", SYSFS_FCHOST, dname); - - if (!fcoe_sysfs_read(buf, MAX_STR_LEN, path)) - rc = check_symbolic_name_for_interface(buf, ifname); - - return rc; -} - -enum fcoe_err fcoe_find_fchost(char *ifname, char *fchost, int len) -{ - int n, dname_len; - struct dirent **namelist; - int rc = ENOFCOECONN; - - n = scandir(SYSFS_FCHOST, &namelist, 0, alphasort); - - for (n-- ; n >= 0 ; n--) { - if (rc) { - /* check symbolic name */ - if (!fcoe_check_fchost(ifname, namelist[n]->d_name)) { - dname_len = strnlen(namelist[n]->d_name, len); - - if (len > dname_len) { - strncpy(fchost, namelist[n]->d_name, - dname_len + 1); - /* rc = 0 indicates found */ - rc = NOERR; - } else { - /* - * The fc_host is too large - * for the buffer. - */ - rc = EINTERR; - } - } - } - free(namelist[n]); - - } - free(namelist); - - return rc; -} - -enum fcoe_err fcoe_validate_interface(char *ifname) -{ - enum fcoe_err rc = NOERR; - char path[MAX_PATH_LEN]; - - - if (!strlen(ifname)) - rc = ENOETHDEV; - - /* - * TODO: Is there a better way to check if the - * interface name is correct? - */ - sprintf(path, "%s/%s", SYSFS_NET, ifname); - if (!rc && fcoe_checkdir(path)) - rc = ENOETHDEV; - - return rc; -} - -/* - * Validate an existing instance for an FC interface - */ -enum fcoe_err fcoe_validate_fcoe_conn(char *ifname) -{ - char fchost[FCHOSTBUFLEN]; - enum fcoe_err rc = NOERR; - - rc = fcoe_validate_interface(ifname); - - if (!rc) - rc = fcoe_find_fchost(ifname, fchost, FCHOSTBUFLEN); - - return rc; -} - -/* - * Open and close to check if directory exists - */ -int fcoe_checkdir(char *dir) -{ - DIR *d = NULL; - - if (!dir) - return -EINVAL; - /* check if we have sysfs */ - d = opendir(dir); - if (!d) - return -EINVAL; - closedir(d); - return 0; -} - -char *get_ifname_from_symbolic_name(const char *symbolic_name) -{ - int symbolic_name_len = strlen(symbolic_name); - int lead_len = strlen(SYMB_NAME_LEAD); - - if (lead_len < symbolic_name_len) - return (char *)(symbolic_name + lead_len); - - return NULL; -} - -int check_symbolic_name_for_interface(const char *symbolic_name, - const char *ifname) -{ - int rc = -EINVAL; - char *symb; - - if (!ifname) - return rc; - - symb = get_ifname_from_symbolic_name(symbolic_name); - - /* - * It's important to use the length of the ifname - * from the symbolic_name here. If the ifname length - * were used then if the user passed in a substring - * of the the interface name it would match because - * we'd only be looking for the first few characters, - * not the whole string. - */ - if (symb && !strncmp(ifname, symb, strlen(symb))) - rc = 0; - - return rc; -} diff --git a/fcoe_utils.h b/fcoe_utils.h deleted file mode 100644 index 3c43304..0000000 --- a/fcoe_utils.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright(c) 2010 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Maintained at www.Open-FCoE.org - */ - -#ifndef _FCOE_UTILS_H_ -#define _FCOE_UTILS_H_ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> -#include <libgen.h> -#include <dirent.h> -#include <errno.h> - -/* - * Used when trying to get the interface name from the symbolic_name. - * Not very elegant as this code will need to change if fcoe.ko changes - * its version. - */ -#define FCOE_MODULE_VERSION "v0.1" -#define SYMB_NAME_LEAD "fcoe " FCOE_MODULE_VERSION " over " - -#define MAX_STR_LEN 512 -#define MAX_PATH_LEN MAX_STR_LEN - -#define SYSFS_MOUNT "/sys" -#define SYSFS_NET SYSFS_MOUNT "/class/net" -#define SYSFS_FCHOST SYSFS_MOUNT "/class/fc_host" -#define SYSFS_FCOE SYSFS_MOUNT "/module/fcoe/parameters" - -#define FCHOSTBUFLEN 64 - -/* - * This macro assumes that progname has been set - */ -#define FCOE_LOG_ERR(fmt, args...) \ - do { \ - fprintf(stderr, "%s: " fmt, progname, ##args); \ - } while (0) - - -enum fcoe_err { - NOERR = 0, /* No error */ - EFCOECONN, /* FCoE connection already exists */ - ENOFCOECONN, /* No FCoE connection on interface */ - EINTERR, /* Internal error */ - EINVALARG, /* Invalid argument */ - EBADNUMARGS, /* Invalid number of arguments */ - EIGNORE, /* Ignore this error value */ - ENOSYSFS, /* sysfs is not present */ - ENOETHDEV, /* Not a valid Ethernet interface */ - ENOMONCONN, /* Not connected to fcoemon */ - ECONNTMOUT, /* Connection to fcoemon timed out */ - EHBAAPIERR, /* Error using HBAAPI/libhbalinux */ -}; - -enum fcoe_err fcoe_validate_interface(char *ifname); -enum fcoe_err fcoe_validate_fcoe_conn(char *ifname); -enum fcoe_err fcoe_find_fchost(char *ifname, char *fchost, int len); -int fcoe_checkdir(char *dir); -int check_symbolic_name_for_interface(const char *symbolic_name, - const char *ifname); -char *get_ifname_from_symbolic_name(const char *symbolic_name); - -#endif /* _FCOE_UTILS_H_ */ diff --git a/fcoe_utils_version.h.in b/fcoe_utils_version.h.in deleted file mode 100644 index 642f37d..0000000 --- a/fcoe_utils_version.h.in +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _FCOE_UTILS_VERSION_H_ -#define _FCOE_UTILS_VERSION_H_ - -#define FCOE_UTILS_VERSION "@VERSION@" - -#endif /* _FCOE_UTILS_VERSION_H_ */ diff --git a/fcoemon.c b/fcoemon.c index 8cb31ea..5af8507 100644 --- a/fcoemon.c +++ b/fcoemon.c @@ -55,6 +55,9 @@ #include "fcoemon.h" #include "fcoe_clif.h" +#include "fip.h" +#include "rtnetlink.h" + #ifndef SYSCONFDIR #define SYSCONFDIR "/etc" #endif @@ -103,12 +106,16 @@ struct fcoe_port { is a VLAN */ int fcoe_enable; int dcb_required; + int auto_vlan; /* following track data required to manage FCoE interface state */ enum fcp_action action; /* current state */ enum fcp_action last_action; /* last action */ int last_msg_type; /* last rtnetlink msg type received on if name */ struct sock_info *sock_reply; + + int ifindex; + unsigned char mac[ETHER_ADDR_LEN]; }; enum fcoeport_ifname { @@ -153,6 +160,8 @@ static void fcm_link_getlink(void); static int fcm_link_buf_check(size_t); static void clear_dcbd_info(struct fcm_netif *ff); +static int fcm_fip_socket; + /* * Table for getopt_long(3). */ @@ -350,9 +359,21 @@ static int fcm_read_config_files(void) continue; } /* if not found, default to "no" */ - if (!strncasecmp(val, "yes", 3) && rc == 1) { + if (!strncasecmp(val, "yes", 3) && rc == 1) next->dcb_required = 1; + + /* AUTO_VLAN */ + rc = fcm_read_config_variable(file, val, sizeof(val), + fp, "AUTO_VLAN"); + if (rc < 0) { + FCM_LOG("%s invalid format for AUTO_VLAN setting"); + fclose(fp); + free(next); + continue; } + /* if not found, default to "no" */ + if (!strncasecmp(val, "yes", 3) && rc == 1) + next->auto_vlan = 1; fclose(fp); @@ -460,6 +481,92 @@ static int fcm_link_init(void) return 0; } +static struct fcoe_port *fcm_port_create(char *ifname, int cmd); + +void fcm_new_vlan(int ifindex, int vid) +{ + char real_name[IFNAMSIZ]; + char vlan_name[IFNAMSIZ]; + + FCM_LOG_DBG("Auto VLAN Found FCF on VID %d\n", vid); + + if (rtnl_find_vlan(ifindex, vid, vlan_name)) { + rtnl_get_linkname(ifindex, real_name); + snprintf(vlan_name, IFNAMSIZ, "%s.%d-fcoe", real_name, vid); + vlan_create(ifindex, vid, vlan_name); + } + rtnl_set_iff_up(0, vlan_name); + fcm_port_create(vlan_name, FCP_CREATE_IF); +} + + +int fcm_vlan_disc_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg) +{ + int vid; + unsigned char mac[ETHER_ADDR_LEN]; + int len = ntohs(fh->fip_desc_len); + struct fip_tlv_hdr *tlv = (struct fip_tlv_hdr *)(fh + 1); + + FCM_LOG_DBG("%s", __func__); + + if (ntohs(fh->fip_proto) != FIP_PROTO_VLAN) { + FCM_LOG_DBG("ignoring FIP frame that is not of type VLAN"); + return -1; + } + + if (fh->fip_subcode != FIP_VLAN_NOTE) { + FCM_LOG_DBG("ignoring FIP VLAN Discovery Request"); + return -1; + } + + while (len > 0) { + switch (tlv->tlv_type) { + case FIP_TLV_MAC_ADDR: + memcpy(mac, ((struct fip_tlv_mac_addr *)tlv)->mac_addr, + ETHER_ADDR_LEN); + break; + /* + * this expects to see the MAC_ADDR TLV first, + * and is broken if not + */ + case FIP_TLV_VLAN: + if (tlv->tlv_len != 1) { + FCM_LOG_ERR(EINVAL, "bad length on VLAN TLV"); + break; + } + vid = ntohs(((struct fip_tlv_vlan *)tlv)->vlan); + fcm_new_vlan(sa->sll_ifindex, vid); + break; + default: + /* unexpected or unrecognized descriptor */ + FCM_LOG_DBG("ignoring TLV type %d", tlv->tlv_type); + break; + } + len -= tlv->tlv_len; + tlv = ((void *) tlv) + (tlv->tlv_len << 2); + }; + return 0; +} + +static void fcm_fip_recv(void *arg) +{ + fip_recv(fcm_fip_socket, fcm_vlan_disc_handler, NULL); +} + +static int fcm_vlan_disc_init(void) +{ + int fd; + + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_FIP)); + if (fd < 0) { + FCM_LOG_ERR(errno, "socket error"); + return fd; + } + fcm_fip_socket = fd; + sa_select_add_fd(fd, fcm_fip_recv, NULL, NULL, NULL); + return 0; +} + /* fcm_vlan_dev_real_dev - query vlan real_dev * @vlan_ifname - vlan device ifname to find real interface name for @@ -542,7 +649,10 @@ static void fcp_set_next_action(struct fcoe_port *p, enum fcp_action action) p->action = action; break; case FCP_ACTIVATE_IF: - p->action = FCP_ENABLE_IF; + if (p->auto_vlan) + p->action = FCP_VLAN_DISC; + else + p->action = FCP_ENABLE_IF; break; default: p->action = FCP_WAIT; @@ -552,10 +662,11 @@ static void fcp_set_next_action(struct fcoe_port *p, enum fcp_action action) case FCP_DESTROY_IF: switch (action) { case FCP_CREATE_IF: - p->action = action; - break; case FCP_ACTIVATE_IF: - p->action = FCP_CREATE_IF; + if (p->auto_vlan) + p->action = FCP_VLAN_DISC; + else + p->action = FCP_CREATE_IF; break; default: p->action = FCP_WAIT; @@ -578,12 +689,15 @@ static void fcp_set_next_action(struct fcoe_port *p, enum fcp_action action) case FCP_DISABLE_IF: switch (action) { case FCP_DESTROY_IF: - case FCP_ENABLE_IF: case FCP_RESET_IF: p->action = action; break; + case FCP_ENABLE_IF: case FCP_ACTIVATE_IF: - p->action = FCP_ENABLE_IF; + if (p->auto_vlan) + p->action = FCP_VLAN_DISC; + else + p->action = FCP_ENABLE_IF; break; default: p->action = FCP_WAIT; @@ -594,14 +708,30 @@ static void fcp_set_next_action(struct fcoe_port *p, enum fcp_action action) case FCP_SCAN_IF: switch (action) { case FCP_DESTROY_IF: - case FCP_ENABLE_IF: case FCP_DISABLE_IF: case FCP_RESET_IF: case FCP_SCAN_IF: p->action = action; break; + case FCP_ENABLE_IF: case FCP_ACTIVATE_IF: - p->action = FCP_ENABLE_IF; + if (p->auto_vlan) + p->action = FCP_VLAN_DISC; + else + p->action = FCP_ENABLE_IF; + break; + default: + p->action = FCP_WAIT; + break; + } + break; + case FCP_VLAN_DISC: + switch (action) { + case FCP_DESTROY_IF: + case FCP_DISABLE_IF: + case FCP_RESET_IF: + case FCP_SCAN_IF: + p->action = action; break; default: p->action = FCP_WAIT; @@ -727,12 +857,15 @@ void fcm_process_link_msg(struct ifinfomsg *ip, int len, unsigned type) char ifname[IFNAMSIZ]; char real_dev[IFNAMSIZ]; u_int8_t operstate; - u_int64_t mac; + unsigned char mac[ETHER_ADDR_LEN]; int is_vlan; + int ifindex; - mac = is_vlan = 0; + is_vlan = 0; operstate = IF_OPER_UNKNOWN; + ifindex = ip->ifi_index; + if (ip->ifi_type != ARPHRD_ETHER) return; @@ -742,7 +875,7 @@ void fcm_process_link_msg(struct ifinfomsg *ip, int len, unsigned type) switch (ap->rta_type) { case IFLA_ADDRESS: if (RTA_PAYLOAD(ap) == 6) - mac = net48_get(RTA_DATA(ap)); + memcpy(mac, RTA_DATA(ap), ETHER_ADDR_LEN); break; case IFLA_IFNAME: @@ -772,6 +905,12 @@ void fcm_process_link_msg(struct ifinfomsg *ip, int len, unsigned type) if (!p) return; + p->ifindex = ifindex; + memcpy(p->mac, mac, ETHER_ADDR_LEN); + + /* don't do VLAN discovery on a VLAN */ + p->auto_vlan = 0; + /* try to find the real device name */ real_dev[0] = '\0'; fcm_vlan_dev_real_dev(ifname, real_dev); @@ -784,6 +923,8 @@ void fcm_process_link_msg(struct ifinfomsg *ip, int len, unsigned type) * an FCoE interface configured on it. */ if (p) { + p->ifindex = ifindex; + memcpy(p->mac, mac, ETHER_ADDR_LEN); strncpy(p->real_ifname, ifname, strlen(ifname)+1); update_fcoe_port_state(p, type, operstate, FCP_REAL_IFNAME); @@ -1795,6 +1936,13 @@ err_out: return ret; } +int fcm_start_vlan_disc(struct fcoe_port *p) +{ + FCM_LOG_DBG("%s", __func__); + fip_send_vlan_request(fcm_fip_socket, p->ifindex, p->mac); + return 0; +} + /* * * Input: action = 1 Destroy the FCoE interface @@ -1857,6 +2005,10 @@ static void fcm_fcoe_action(struct fcm_netif *ff, struct fcoe_port *p) SYSFS_FCHOST, fchost, fchost); rc = fcm_fcoe_if_action(path, "- - -"); break; + case FCP_VLAN_DISC: + FCM_LOG_DBG("OP: VLAN DISC %s\n", p->ifname); + rc = fcm_start_vlan_disc(p); + break; default: return; break; @@ -2070,7 +2222,7 @@ static void fcm_pidfile_create(void) } } -static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r) +static struct fcoe_port *fcm_port_create(char *ifname, int cmd) { struct fcoe_port *p; struct fcoe_port *curr; @@ -2081,11 +2233,10 @@ static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r) if (!p->fcoe_enable) { p->fcoe_enable = 1; fcp_set_next_action(p, cmd); - p->sock_reply = *r; if (p->dcb_required) { ff = fcm_netif_lookup(p->real_ifname); if (!ff) - return fcm_success; + return p; fcm_dcbd_state_set(ff, FCD_GET_DCB_STATE); if (ff->ff_dcbd_state == FCD_GET_DCB_STATE) fcp_set_next_action(p, FCP_WAIT); @@ -2094,13 +2245,13 @@ static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r) p->fcoe_enable = 1; fcp_set_next_action(p, cmd); } - return fcm_success; + return p; } p = alloc_fcoe_port(ifname); if (!p) { FCM_LOG_ERR(errno, "fail to allocate fcoe_port %s", ifname); - return fcm_fail; + return NULL; } fcm_vlan_dev_real_dev(ifname, p->real_ifname); @@ -2109,7 +2260,6 @@ static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r) p->fcoe_enable = 1; p->dcb_required = 0; fcp_set_next_action(p, cmd); - p->sock_reply = *r; p->next = NULL; if (!fcoe_config.port) @@ -2125,9 +2275,19 @@ static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r) ff = fcm_netif_lookup_create(p->real_ifname); if (!ff) { FCM_LOG_ERR(errno, "fail to allocate fcm_netif %s", ifname); - return fcm_fail; + return NULL; } + return p; +} + +static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r) +{ + struct fcoe_port *p; + p = fcm_port_create(ifname, cmd); + if (!p) + return fcm_fail; + p->sock_reply = *r; return fcm_success; } @@ -2350,6 +2510,7 @@ int main(int argc, char **argv) break; case 'd': fcoe_config.debug = 1; + enable_debug_log(1); break; case 's': fcoe_config.use_syslog = 1; @@ -2421,6 +2582,7 @@ int main(int argc, char **argv) fcm_fcoe_init(); fcm_link_init(); /* NETLINK_ROUTE protocol */ fcm_dcbd_init(); + fcm_vlan_disc_init(); fcm_srv_create(&srv_info); sa_select_set_callback(fcm_handle_changes); diff --git a/fcoemon.h b/fcoemon.h index a36128d..9917d76 100644 --- a/fcoemon.h +++ b/fcoemon.h @@ -43,14 +43,12 @@ struct fcoe_config { #define FCM_LOG_DBG(fmt, args...) \ do { \ - if (fcoe_config.debug) \ - sa_log(fmt, ##args); \ + sa_log_debug(fmt, ##args); \ } while (0) #define FCM_LOG_DEV_DBG(fcm, fmt, args...) \ do { \ - if (fcoe_config.debug) \ - sa_log("%s, " fmt, fcm->ifname, ##args); \ + sa_log_debug("%s, " fmt, fcm->ifname, ##args); \ } while (0) #define FCM_LOG_DEV(fcm, fmt, args...) \ @@ -95,6 +93,7 @@ enum fcp_action { FCP_DISABLE_IF, /* disable FCoE interface */ FCP_ACTIVATE_IF, /* create or enable FCoE interface */ FCP_ERROR, /* error condition */ + FCP_VLAN_DISC, /* start VLAN discovery */ }; #define FCM_DCBD_STATES { \ diff --git a/fcoemon_utils.c b/fcoemon_utils.c deleted file mode 100644 index 5f1cd10..0000000 --- a/fcoemon_utils.c +++ /dev/null @@ -1,688 +0,0 @@ -/* - * Copyright(c) 2009 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Maintained at www.Open-FCoE.org - */ - -#include "fcoemon_utils.h" -#include "net_types.h" -#include "fc_types.h" - -u_char libsa_lock_hier; /* for lock debugging non-log related */ - -int use_syslog; - -/* - * Size of on-stack line buffers. - * These shouldn't be to large for a kernel stack frame. - */ -#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ - -void enable_syslog(int enable) -{ - use_syslog = enable; -} - -/* - * log with a variable argument list. - */ -static void -sa_log_va(const char *func, const char *format, va_list arg) -{ - size_t len; - size_t flen; - int add_newline; - char sa_buf[SA_LOG_BUF_LEN]; - char *bp; - - /* - * If the caller didn't provide a newline at the end, we will. - */ - len = strlen(format); - add_newline = 0; - if (!len || format[len - 1] != '\n') - add_newline = 1; - bp = sa_buf; - len = sizeof(sa_buf); - if (func) { - flen = snprintf(bp, len, "%s: ", func); - len -= flen; - bp += flen; - } - flen = vsnprintf(bp, len, format, arg); - if (add_newline && flen < len) { - bp += flen; - *bp++ = '\n'; - *bp = '\0'; - } - sa_log_output(sa_buf); -} - -/* - * log - */ -void -sa_log(const char *format, ...) -{ - va_list arg; - - va_start(arg, format); - sa_log_va(NULL, format, arg); - va_end(arg); -} - -/* - * log with error number. - */ -void -sa_log_err(int error, const char *func, const char *format, ...) -{ - va_list arg; - char buf[SA_LOG_BUF_LEN]; - - if (func) - sa_log("%s: error %d %s", func, error, - strerror_r(error, buf, sizeof(buf))); - else - sa_log("error %d %s", error, - strerror_r(error, buf, sizeof(buf))); - va_start(arg, format); - sa_log_va(func, format, arg); - va_end(arg); -} - -/* - * Size of on-stack line buffers. - * These shouldn't be to large for a kernel stack frame. - */ -#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ - -/* - * Assert failures. - */ -void -assert_failed(const char *format, ...) -{ - va_list arg; - char buf[SA_LOG_BUF_LEN]; - - va_start(arg, format); - vsnprintf(buf, sizeof(buf), format, arg); - va_end(arg); - sa_log_abort(buf); -} - -/* - * Log options. - * These may be set directly by callers. - */ -u_int sa_log_flags; /* timestamp and other option flags */ -int sa_log_time_delta_min = 1; /* minimum diff to print in millisec */ -char *sa_log_prefix; /* string to print before any message */ - -void -sa_log_set_option(u_int flags) -{ - sa_log_flags = flags; -} - -/* - * Put timestamp on front of each log line, as controlled by tunables above. - */ -static void -sa_log_timestamp(void) -{ - static struct timeval tlast; - char ctime_buf[30]; - struct timeval t; - struct timeval diff; - - gettimeofday(&t, NULL); - if (sa_log_flags & SA_LOGF_TIME) { - ctime_r(&t.tv_sec, ctime_buf); - ctime_buf[11 + 8] = '\0'; /* trim ctime after seconds */ - fprintf(stderr, "%s.%3.3ld ", - ctime_buf + 11, t.tv_usec / 1000); - } - if (sa_log_flags & SA_LOGF_DELTA) { - if (tlast.tv_sec == 0) - tlast = t; - timersub(&t, &tlast, &diff); - tlast = t; - if (diff.tv_sec != 0 || - diff.tv_usec >= sa_log_time_delta_min * 1000) - fprintf(stderr, "%4ld.%3.3ld ", - diff.tv_sec, diff.tv_usec / 1000); - else - fprintf(stderr, "%8s ", ""); - } - if (sa_log_prefix) - fprintf(stderr, "%s: ", sa_log_prefix); -} - -void -sa_log_output(const char *buf) -{ - if (use_syslog) { - syslog(LOG_INFO, "%s", buf); - return; - } - sa_log_timestamp(); - fprintf(stderr, "%s", buf); - fflush(stderr); -} - -void -sa_log_abort(const char *buf) -{ - sa_log_output(buf); - abort(); -} - -/* - * Make a printable NUL-terminated copy of the string. - * The source buffer might not be NUL-terminated. - */ -char * -sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len) -{ - char *dp = dest; - const char *sp = src; - - while (len-- > 1 && src_len-- > 0 && *sp != '\0') { - *dp++ = isprint(*sp) ? *sp : (isspace(*sp) ? ' ' : '.'); - sp++; - } - *dp = '\0'; - - /* - * Take off trailing blanks. - */ - while (--dp >= dest && isspace(*dp)) - *dp = '\0'; - return dest; -} - -/** sa_enum_decode(buf, len, tp, val) - * - * @param buf buffer for result (may be used or not). - * @param len size of buffer (at least 32 bytes recommended). - * @param tp pointer to table of names and values, struct sa_nameval. - * @param val value to be decoded into a name. - * @returns pointer to name string. Unknown values are put into buffer in hex. - */ -const char * -sa_enum_decode(char *buf, size_t len, const struct sa_nameval *tp, u_int val) -{ - for (; tp->nv_name != NULL; tp++) { - if (tp->nv_val == val) - return tp->nv_name; - } - snprintf(buf, len, "Unknown (code 0x%X)", val); - return buf; -} - -#define SA_TIMER_HZ (1000 * 1000 * 1000ULL) /* nanoseconds per second */ -#define SA_TIMER_FUZZ (500 * 1000ULL) /* 500 microseconds is close enough */ - -static struct sa_timer *sa_timer_head; /* queue of scheduled events */ -static u_int64_t sa_timer_nsec; /* nanoseconds since start */ - -/* - * Initialize a timer structure. Set handler. - */ -void -sa_timer_init(struct sa_timer *tm, void (*handler)(void *), void *arg) -{ - ASSERT(handler != NULL); - memset(tm, 0, sizeof(*tm)); - tm->tm_handler = handler; - tm->tm_handler_arg = arg; -} - -/* - * Allocate a timer structure. Set handler. - */ -struct sa_timer * -sa_timer_alloc(void (*handler)(void *arg), void *arg) -{ - struct sa_timer *tm; - - tm = malloc(sizeof(*tm)); - if (tm) - sa_timer_init(tm, handler, arg); - return tm; -} - -u_int64_t -sa_timer_get(void) -{ - u_int64_t nsec; -#ifndef _POSIX_TIMERS - struct timeval tv; - - gettimeofday(&tv, NULL); /* XXX want monotonic time, not TOD */ - nsec = tv.tv_sec * SA_TIMER_HZ + tv.tv_usec * 1000; -#else /* _POSIX_TIMERS */ - struct timespec ts; - int rc; - - rc = clock_gettime(CLOCK_MONOTONIC, &ts); - ASSERT_NOTIMPL(rc == 0); - nsec = ts.tv_sec * SA_TIMER_HZ + ts.tv_nsec; -#endif /* _POSIX_TIMERS */ - -#if 0 /* XXX */ - ASSERT(nsec >= sa_timer_nsec); /* really must be monotonic */ -#else - if (nsec < sa_timer_nsec) - sa_log("sa_timer_get: negative time lapse " - "old %qud new %qud diff %qd nsec\n", - (long long unsigned int) sa_timer_nsec, - (long long unsigned int) nsec, - (long long int) (nsec - sa_timer_nsec)); -#endif - sa_timer_nsec = nsec; - return nsec; -} - -/* - * Get monotonic time since some arbitrary time in the past. - * If _POSIX_MONOTONIC_CLOCK isn't available, we'll use time of day. - */ -u_int -sa_timer_get_secs(void) -{ - u_int sec; - -#ifndef _POSIX_TIMERS - struct timeval tv; - - gettimeofday(&tv, NULL); /* XXX want monotonic time, not TOD */ - sec = tv.tv_sec; -#else /* _POSIX_TIMERS */ - struct timespec ts; - int rc; - - rc = clock_gettime(CLOCK_MONOTONIC, &ts); - ASSERT_NOTIMPL(rc == 0); - sec = ts.tv_sec; -#endif /* _POSIX_TIMERS */ - return sec; -} - -/* - * Set timer to fire. Delta is in microseconds from now. - */ -void -sa_timer_set(struct sa_timer *tm, u_long delta_usec) -{ - struct sa_timer *cur; - struct sa_timer **prev; - - ASSERT(delta_usec != 0); - ASSERT(tm->tm_handler != NULL); - sa_timer_cancel(tm); - ASSERT(sa_timer_active(tm) == 0); - tm->tm_nsec = - sa_timer_get() + delta_usec * SA_TIMER_HZ / SA_TIMER_UNITS; - ASSERT(tm->tm_nsec != 0); - - /* - * Insert timer into sorted linked list. - * Find insertion point, before cur. - */ - for (prev = &sa_timer_head; - (cur = *prev) != NULL && cur->tm_nsec <= tm->tm_nsec; - prev = &cur->tm_next) - ; - *prev = tm; - tm->tm_next = cur; -} - -/* - * Cancel timer if it is active. - */ -void -sa_timer_cancel(struct sa_timer *tm) -{ - struct sa_timer *cur; - struct sa_timer **prev; - - if (sa_timer_active(tm)) { - for (prev = &sa_timer_head; (cur = *prev) != NULL; - prev = &cur->tm_next) - if (cur == tm) { - tm->tm_nsec = 0; - *prev = tm->tm_next; - break; - } - ASSERT(cur == tm); - } -} - -/* - * Free (and cancel) timer. - */ -void -sa_timer_free(struct sa_timer *tm) -{ - if (sa_timer_active(tm)) - sa_timer_cancel(tm); - free(tm); -} - -/* - * Handle timer checks. Called from select loop or other periodic function. - * - * The struct timeval is set before returning to the maximum amount of time - * that should elapse before the next call. - * - * Returns 1 if any timer functions were called, 0 otherwise. - */ -int -sa_timer_check(struct timeval *tv) -{ - u_int64_t now = 0; - u_int64_t next_due = 0; - struct sa_timer *tm; - int ret = 0; - - /* - * Remember, the list may change during the handler. - */ - for (;;) { - now = sa_timer_get(); - tm = sa_timer_head; - if (tm == NULL) { - next_due = now; - break; - } - - next_due = tm->tm_nsec; - if (next_due > now + SA_TIMER_FUZZ) - break; - - /* - * Remove this element from the list. - */ - sa_timer_head = tm->tm_next; - tm->tm_next = NULL; - - /* - * Mark cancelled and call handler. - */ - tm->tm_nsec = 0; - ASSERT(tm->tm_handler != NULL); - (*tm->tm_handler)(tm->tm_handler_arg); - ret = 1; - } - - ASSERT(next_due >= now); - next_due -= now; - tv->tv_sec = (time_t) (next_due / SA_TIMER_HZ); - tv->tv_usec = (long) (next_due % SA_TIMER_HZ) / 1000; - - return ret; -} - -#define NFC_NFDS 64 - -/* - * Deferred procedure call. - */ -struct sa_defer_ent { - TAILQ_ENTRY(sa_defer_ent) de_next; - void (*de_func)(void *arg); - void *de_arg; -}; - -/* - * Static module state. - */ -static struct sa_sel_state { - fd_set ts_rx_fds; - fd_set ts_tx_fds; - fd_set ts_ex_fds; - int ts_max_fd; - u_char ts_exit; - struct sa_sel_fd { - void (*ts_rx_handler)(void *); - void (*ts_tx_handler)(void *); - void (*ts_ex_handler)(void *); - void *ts_handler_arg; - } ts_fd[NFC_NFDS]; - void (*ts_callback)(void); - TAILQ_HEAD(, sa_defer_ent) ts_defer_list; -} sa_sel_state; - -int sa_select_loop(void) -{ - struct sa_sel_state *ss = &sa_sel_state; - struct sa_sel_fd *fp; - fd_set rx_fds; - fd_set tx_fds; - fd_set ex_fds; - struct timeval tval; - struct timeval *tvp; - int rv, i; - - ss->ts_exit = 0; - while (ss->ts_exit == 0) { - sa_timer_check(&tval); - if (ss->ts_exit) - break; - if (tval.tv_sec == 0 && tval.tv_usec == 0) - tvp = NULL; - else - tvp = &tval; - rx_fds = ss->ts_rx_fds; - tx_fds = ss->ts_tx_fds; - ex_fds = ss->ts_ex_fds; - rv = select(ss->ts_max_fd + 1, &rx_fds, &tx_fds, &ex_fds, tvp); - if (rv == -1) { - if (errno == EINTR) - continue; - return errno; - } - - fp = ss->ts_fd; - for (i = 0; rv > 0 && i <= sa_sel_state.ts_max_fd; i++, fp++) { - if (FD_ISSET(i, &rx_fds)) { - if (fp->ts_rx_handler != NULL) - (*fp->ts_rx_handler) - (fp->ts_handler_arg); - else - ASSERT(!FD_ISSET(i, &ss->ts_rx_fds)); - --rv; - } - if (FD_ISSET(i, &tx_fds)) { - if (fp->ts_tx_handler != NULL) - (*fp->ts_tx_handler) - (fp->ts_handler_arg); - else - ASSERT(!FD_ISSET(i, &ss->ts_tx_fds)); - --rv; - } - if (FD_ISSET(i, &ex_fds)) { - if (fp->ts_ex_handler != NULL) - (*fp->ts_ex_handler) - (fp->ts_handler_arg); - else - ASSERT(!FD_ISSET(i, &ss->ts_ex_fds)); - --rv; - } - } - if (ss->ts_callback != NULL) - (*ss->ts_callback)(); - } - return 0; -} - -void -sa_select_add_fd(int fd, - void (*rx_handler)(void *), - void (*tx_handler)(void *), - void (*ex_handler)(void *), - void *arg) -{ - struct sa_sel_state *ss = &sa_sel_state; - struct sa_sel_fd *fp; - - ASSERT_NOTIMPL(fd < NFC_NFDS); - ASSERT(rx_handler != NULL || tx_handler != NULL || ex_handler != NULL); - if (ss->ts_max_fd < fd) - ss->ts_max_fd = fd; - fp = &ss->ts_fd[fd]; - fp->ts_handler_arg = arg; - if (rx_handler != NULL) { - fp->ts_rx_handler = rx_handler; - FD_SET(fd, &ss->ts_rx_fds); - } - if (tx_handler != NULL) { - fp->ts_tx_handler = tx_handler; - FD_SET(fd, &ss->ts_tx_fds); - } - if (ex_handler != NULL) { - fp->ts_ex_handler = ex_handler; - FD_SET(fd, &ss->ts_ex_fds); - } -} - -void -sa_select_set_rx(int fd, void (*handler)(void *)) -{ - struct sa_sel_state *ss = &sa_sel_state; - - ASSERT(fd <= ss->ts_max_fd); - ss->ts_fd[fd].ts_rx_handler = handler; - if (handler != NULL) - FD_SET(fd, &ss->ts_rx_fds); - else - FD_CLR(fd, &ss->ts_rx_fds); -} - -void -sa_select_set_tx(int fd, void (*handler)(void *)) -{ - struct sa_sel_state *ss = &sa_sel_state; - - ASSERT(fd <= ss->ts_max_fd); - ss->ts_fd[fd].ts_tx_handler = handler; - if (handler != NULL) - FD_SET(fd, &ss->ts_tx_fds); - else - FD_CLR(fd, &ss->ts_tx_fds); -} - -void -sa_select_set_ex(int fd, void (*handler)(void *)) -{ - struct sa_sel_state *ss = &sa_sel_state; - - ASSERT(fd <= ss->ts_max_fd); - ss->ts_fd[fd].ts_ex_handler = handler; - if (handler != NULL) - FD_SET(fd, &ss->ts_ex_fds); - else - FD_CLR(fd, &ss->ts_ex_fds); -} - -void -sa_select_rem_fd(int fd) -{ - struct sa_sel_state *ss = &sa_sel_state; - struct sa_sel_fd *fp; - - ASSERT_NOTIMPL(fd < NFC_NFDS); - FD_CLR(fd, &ss->ts_rx_fds); - FD_CLR(fd, &ss->ts_tx_fds); - FD_CLR(fd, &ss->ts_ex_fds); - fp = &ss->ts_fd[fd]; - fp->ts_rx_handler = NULL; - fp->ts_tx_handler = NULL; - fp->ts_ex_handler = NULL; - fp->ts_handler_arg = NULL; -} - -/* - * Set callback for every time through the select loop. - */ -void -sa_select_set_callback(void (*cb)(void)) -{ - sa_sel_state.ts_callback = cb; -} - -/* - * Cause select loop to exit. - * This is invoked from a handler which wants the select loop to return - * after the handler is finished. For example, during receipt of a network - * packet, the program may decide to clean up and exit, but in order to do - * this cleanly, all lower-level protocol handlers should return first. - */ -void -sa_select_exit(void) -{ - sa_sel_state.ts_exit = 1; -} - -/* - * Convert 48-bit IEEE MAC address to 64-bit FC WWN. - */ -fc_wwn_t -fc_wwn_from_mac(u_int64_t mac, u_int scheme, u_int port) -{ - fc_wwn_t wwn; - - ASSERT(mac < (1ULL << 48)); - wwn = mac | ((fc_wwn_t) scheme << 60); - switch (scheme) { - case 1: - ASSERT(port == 0); - break; - case 2: - ASSERT(port < 0xfff); - wwn |= (fc_wwn_t) port << 48; - break; - default: - ASSERT_NOTREACHED; - break; - } - return wwn; -} - -/* assumes input is pointer to two hex digits */ -/* returns -1 on error */ -int -hex2int(char *b) -{ - int i; - int n = 0; - int m; - - for (i = 0, m = 1; i < 2; i++, m--) { - if (isxdigit(*(b+i))) { - if (*(b+i) <= '9') - n |= (*(b+i) & 0x0f) << (4*m); - else - n |= ((*(b+i) & 0x0f) + 9) << (4*m); - } else - return -1; - } - return n; -} - diff --git a/fcoemon_utils.h b/fcoemon_utils.h deleted file mode 100644 index 35ab19e..0000000 --- a/fcoemon_utils.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright(c) 2009 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Maintained at www.Open-FCoE.org - */ - -#ifndef _FCOEMON_UTILS_H_ -#define _FCOEMON_UTILS_H_ - -#include <sys/types.h> -#include <sys/time.h> -#include <sys/socket.h> -#include <sys/queue.h> -#include <malloc.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <time.h> -#include <stdarg.h> -#include <ctype.h> -#include <time.h> -#include <unistd.h> -#include <signal.h> -#include <errno.h> -#include <syslog.h> - -#include "fc_types.h" - -void sa_log(const char *format, ...); -void sa_log_err(int, const char *func, const char *format, ...); - -/* - * These functions can be provided outside of libsa for those environments - * that want to redirect them. - */ -void sa_log_output(const char *); /* log message */ -void sa_log_abort(const char *); /* log message and abort */ - -#define __SA_STRING(x) #x - -/* - * Logging options. - */ -#define SA_LOGF_TIME 0x0001 /* include timestamp in message */ -#define SA_LOGF_DELTA 0x0002 /* include time since last message */ - -extern u_int sa_log_flags; /* timestamp and other option flags */ -extern int sa_log_time_delta_min; /* minimum diff to print in millisec */ -extern char *sa_log_prefix; /* string to print before any message */ - -extern void assert_failed(const char *s, ...) - __attribute__ ((format(printf, 1, 2))); - -#ifndef UNLIKELY -#define UNLIKELY(_x) (_x) -#endif /* UNLIKELY */ - -/* - * ASSERT macros - * - * ASSERT(expr) - this calls assert_failed() if expr is false. This variant - * is not present in production code or if DEBUG_ASSERTS is not defined. - * Be careful not to rely on expr being evaluated. - */ -#if defined(DEBUG_ASSERTS) -#define ASSERT(_x) do { \ - if (UNLIKELY(!(_x))) { \ - assert_failed("ASSERT FAILED (%s) @ %s:%d\n", \ - "" #_x, __FILE__, __LINE__); \ - } \ - } while (0) -#else -#define ASSERT(_x) -#endif /* DEBUG_ASSERTS */ - -/* - * ASSERT_NOTIMPL(expr) - this calls assert_failed() if expr is false. - * The implication is that the condition is not handled by the current - * implementation, and work should be done eventually to handle this. - */ -#define ASSERT_NOTIMPL(_x) do { \ - if (UNLIKELY(!(_x))) { \ - assert_failed("ASSERT (NOT IMPL) " \ - "(%s) @ %s:%d\n", \ - "" #_x, __FILE__, __LINE__); \ - } \ - } while (0) - -/* - * ASSERT_NOTREACHED - this is the same as ASSERT_NOTIMPL(0). - */ -#define ASSERT_NOTREACHED do { \ - assert_failed("ASSERT (NOT REACHED) @ %s:%d\n", \ - __FILE__, __LINE__); \ - } while (0) - -/* - * ASSERT_BUG(bugno, expr). This variant is used when a bug number has - * been assigned to any one of the other assertion failures. It is always - * present in code. It gives the bug number which helps locate - * documentation and helps prevent duplicate bug filings. - */ -#define ASSERT_BUG(_bugNr, _x) do { \ - if (UNLIKELY(!(_x))) { \ - assert_failed("ASSERT (BUG %d) (%s) @ %s:%d\n", \ - (_bugNr), #_x, __FILE__, __LINE__); \ - } \ - } while (0) - -#ifndef LIBSA_USE_DANGEROUS_ROUTINES -#define strcpy DONT_USE_strcpy -#define strcat DONT_USE_strcat -#define gets DONT_USE_gets -#endif /* LIBSA_USE_DANGEROUS_ROUTINES */ - -char *sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len); -char *sa_hex_format(char *buf, size_t buflen, - const unsigned char *data, size_t data_len, - unsigned int group_len, char *inter_group_sep); - -/* - * Structure for tables encoding and decoding name-value pairs such as enums. - */ -struct sa_nameval { - char *nv_name; - u_int nv_val; -}; - -const char *sa_enum_decode(char *, size_t, const struct sa_nameval *, u_int); -int sa_enum_encode(const struct sa_nameval *tp, const char *, u_int *); -const char *sa_flags_decode(char *, size_t, const struct sa_nameval *, u_int); - -/* - * Timer facility. - */ - -struct sa_timer { - struct sa_timer *tm_next; - u_int64_t tm_nsec; /* relative time to event (nSec) */ - void (*tm_handler)(void *arg); - void *tm_handler_arg; - struct sa_timer **timer_head; -}; - - -#define SA_TIMER_UNITS (1000 * 1000UL) /* number of timer ticks per second */ - -/* - * Initialize a pre-allocated timer structure. - */ -void sa_timer_init(struct sa_timer *, void (*handler)(void *), void *arg); - -/* - * Test whether the timer is active. - */ -static inline int sa_timer_active(struct sa_timer *tm) -{ - return tm->tm_nsec != 0; -} - -/* - * Allocate a timer structure. Set handler. - */ -struct sa_timer *sa_timer_alloc(void (*)(void *arg), void *arg); - -/* - * Set timer to fire. Delta is in microseconds from now. - */ -void sa_timer_set(struct sa_timer *, u_long delta); - -/* - * Cancel timer. - */ -void sa_timer_cancel(struct sa_timer *); - -/* - * Free (and cancel) timer. - */ -void sa_timer_free(struct sa_timer *); - - -/* - * Handle timer checks. Called from select loop or other periodic function. - * - * The struct timeval passed in indicates how much time has passed since - * the last call, and is set before returning to the maximum amount of time - * that should elapse before the next call. - * - * Returns 1 if any timer handlers were invoked, 0 otherwise. - */ -int sa_timer_check(struct timeval *); - -/* - * Get time in nanoseconds since some arbitrary time. - */ -u_int64_t sa_timer_get(void); - -/* - * Get time in seconds since some arbitrary time. - */ -u_int sa_timer_get_secs(void); - -/* - * sa_select - Server Array select facility. - * - * This is a thin layer to poll files with a select loop. - */ - -/* - * Enter the polling loop which never exits. - */ -int sa_select_loop(void); - -/* - * Set callback for every time through the select loop. - */ -void sa_select_set_callback(void (*)(void)); - -/* - * Add a callback to handle files which are ready for receive, transmit, - * or to handle exceptions. - */ -void sa_select_add_fd(int fd, void (*rx_handler)(void *), - void (*tx_handler)(void *), - void (*ex_handler)(void *), void *arg); - -/* - * Change a single callback for a descriptor that's already been added. - */ -void sa_select_set_rx(int fd, void (*handler)(void *)); -void sa_select_set_tx(int fd, void (*handler)(void *)); -void sa_select_set_ex(int fd, void (*handler)(void *)); - -/* - * Remove all callbacks for a file descriptor. - */ -void sa_select_rem_fd(int fd); - -/* - * Cause select loop to return. - */ -void sa_select_exit(void); - -/* - * Convert 48-bit IEEE MAC address to 64-bit FC WWN. - */ -extern fc_wwn_t -fc_wwn_from_mac(u_int64_t, u_int32_t scheme, u_int32_t port); - -extern int hex2int(char *b); - -int use_syslog; -void enable_syslog(int); - -#endif /* _FCOEMON_UTILS_H_ */ diff --git a/fipvlan.c b/fipvlan.c index cc8a785..5b428e2 100644 --- a/fipvlan.c +++ b/fipvlan.c @@ -22,6 +22,7 @@ #include <stdio.h> #include <stdarg.h> #include <stdint.h> +#include <stdbool.h> #include <unistd.h> #include <errno.h> #include <getopt.h> @@ -29,6 +30,7 @@ #include <signal.h> #include <sys/ioctl.h> #include <sys/socket.h> +#include <sys/queue.h> #include <net/if.h> #include <net/if_arp.h> #include <net/ethernet.h> @@ -36,103 +38,99 @@ #include <arpa/inet.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> + +#include <sys/stat.h> +#include <fcntl.h> + #include "fcoe_utils_version.h" #include "fip.h" -#include "log.h" -#include "list.h" +#include "fcoemon_utils.h" +#include "fcoe_utils.h" +#include "rtnetlink.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define FIP_LOG(...) sa_log(__VA_ARGS__) +#define FIP_LOG_ERR(error, ...) sa_log_err(error, __func__, __VA_ARGS__) +#define FIP_LOG_ERRNO(...) sa_log_err(errno, __func__, __VA_ARGS__) +#define FIP_LOG_DBG(...) sa_log_debug(__VA_ARGS__) + /* global configuration */ +struct { + char **namev; + int namec; + bool automode; + bool create; + bool start; +} config = { + .namev = NULL, + .namec = 0, + .automode = false, + .create = false, +}; + char *exe; +TAILQ_HEAD(iff_list_head, iff); + struct iff { int ifindex; - char *ifname; + int iflink; + char ifname[IFNAMSIZ]; unsigned char mac_addr[ETHER_ADDR_LEN]; - struct list_head list; + bool is_vlan; + short int vid; + TAILQ_ENTRY(iff) list_node; + struct iff_list_head vlans; }; -LIST_HEAD(interfaces); +struct iff_list_head interfaces = TAILQ_HEAD_INITIALIZER(interfaces); + +TAILQ_HEAD(fcf_list_head, fcf); struct fcf { - struct iff *interface; + int ifindex; uint16_t vlan; unsigned char mac_addr[ETHER_ADDR_LEN]; - struct list_head list; + TAILQ_ENTRY(fcf) list_node; }; -LIST_HEAD(fcfs); +struct fcf_list_head fcfs = TAILQ_HEAD_INITIALIZER(fcfs); -/** - * packet_socket - create a packet socket bound to the FIP ethertype - */ -int packet_socket(void) +struct iff *lookup_iff(int ifindex, char *ifname) { - int s; + struct iff *iff; - log_debug(1, "creating ETH_P_FIP packet socket"); - s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_FIP)); - if (s < 0) - log_errno("packet socket error"); + if (!ifindex && !ifname) + return NULL; - return s; + TAILQ_FOREACH(iff, &interfaces, list_node) + if ((!ifindex || ifindex == iff->ifindex) && + (!ifname || strcmp(ifname, iff->ifname) == 0)) + return iff; + return NULL; } -/** - * fip_send_vlan_request - send a FIP VLAN request - * @s: ETH_P_FIP packet socket - * @iff: network interface to send from - * - * Note: sends to FIP_ALL_FCF_MACS - */ -ssize_t fip_send_vlan_request(int s, struct iff *iff) +struct iff *lookup_vlan(int ifindex, short int vid) { - struct sockaddr_ll sa = { - .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_FIP), - .sll_ifindex = iff->ifindex, - .sll_hatype = ARPHRD_ETHER, - .sll_pkttype = PACKET_MULTICAST, - .sll_halen = ETHER_ADDR_LEN, - .sll_addr = FIP_ALL_FCF_MACS, - }; - struct fiphdr fh = { - .fip_version = FIP_VERSION(1), - .fip_proto = htons(FIP_PROTO_VLAN), - .fip_subcode = FIP_VLAN_REQ, - .fip_desc_len = htons(2), - .fip_flags = 0, - }; - struct { - struct fip_tlv_mac_addr mac; - } tlvs = { - .mac = { - .hdr.tlv_type = FIP_TLV_MAC_ADDR, - .hdr.tlv_len = 2, - }, - }; - struct iovec iov[] = { - { .iov_base = &fh, .iov_len = sizeof(fh), }, - { .iov_base = &tlvs, .iov_len = sizeof(tlvs), }, - }; - struct msghdr msg = { - .msg_name = &sa, - .msg_namelen = sizeof(sa), - .msg_iov = iov, - .msg_iovlen = ARRAY_SIZE(iov), - }; - int rc; - - memcpy(tlvs.mac.mac_addr, iff->mac_addr, ETHER_ADDR_LEN); - - log_debug(1, "sending FIP VLAN request"); - rc = sendmsg(s, &msg, 0); - if (rc < 0) - log_errno("sendmsg error"); + struct iff *real_dev, *vlan; + TAILQ_FOREACH(real_dev, &interfaces, list_node) + if (real_dev->ifindex == ifindex) + TAILQ_FOREACH(vlan, &real_dev->vlans, list_node) + if (vlan->vid == vid) + return vlan; + return NULL; +} - return rc; +struct iff *find_vlan_real_dev(struct iff *vlan) +{ + struct iff *real_dev; + TAILQ_FOREACH(real_dev, &interfaces, list_node) { + if (real_dev->ifindex == vlan->iflink) + return real_dev; + } + return NULL; } struct fip_tlv_ptrs { @@ -146,7 +144,7 @@ struct fip_tlv_ptrs { #define TLV_LEN_CHECK(t, l) ({ \ int _tlc = ((t)->tlv_len != (l)) ? 1 : 0; \ if (_tlc) \ - log_warn("bad length for TLV of type %d", (t)->tlv_type); \ + FIP_LOG("bad length for TLV of type %d", (t)->tlv_type); \ _tlc; \ }) @@ -178,7 +176,7 @@ unsigned int fip_parse_tlvs(void *ptr, int len, struct fip_tlv_ptrs *tlv_ptrs) break; default: /* unexpected or unrecognized descriptor */ - log_warn("unrecognized TLV type %d", tlv->tlv_type); + FIP_LOG("unrecognized TLV type %d", tlv->tlv_type); break; } len -= tlv->tlv_len; @@ -190,180 +188,62 @@ unsigned int fip_parse_tlvs(void *ptr, int len, struct fip_tlv_ptrs *tlv_ptrs) /** * fip_recv_vlan_note - parse a FIP VLAN Notification * @fh: FIP header, the beginning of the received FIP frame - * @len: total length of the received FIP frame - * @iff: interface this notification was received on + * @ifindex: index of interface this was received on */ -int fip_recv_vlan_note(struct fiphdr *fh, ssize_t len, struct iff *iff) +int fip_recv_vlan_note(struct fiphdr *fh, int ifindex) { struct fip_tlv_ptrs tlvs; struct fcf *fcf; unsigned int bitmap, required_tlvs; - int desc_len; + int len; int i; - log_debug(1, "received FIP VLAN Notification"); + FIP_LOG_DBG("received FIP VLAN Notification"); - desc_len = ntohs(fh->fip_desc_len); - if (len < (sizeof(*fh) + (desc_len << 2))) - return -1; + len = ntohs(fh->fip_desc_len); required_tlvs = (1 << FIP_TLV_MAC_ADDR) | (1 << FIP_TLV_VLAN); - bitmap = fip_parse_tlvs((fh + 1), desc_len, &tlvs); + bitmap = fip_parse_tlvs((fh + 1), len, &tlvs); if ((bitmap & required_tlvs) != required_tlvs) return -1; for (i = 0; i < tlvs.vlanc; i++) { fcf = malloc(sizeof(*fcf)); if (!fcf) { - log_errno("malloc failed"); + FIP_LOG_ERRNO("malloc failed"); break; } memset(fcf, 0, sizeof(*fcf)); - fcf->interface = iff; + fcf->ifindex = ifindex; fcf->vlan = ntohs(tlvs.vlan[i]->vlan); memcpy(fcf->mac_addr, tlvs.mac->mac_addr, ETHER_ADDR_LEN); - list_add_tail(&fcf->list, &fcfs); + TAILQ_INSERT_TAIL(&fcfs, fcf, list_node); } return 0; } -/** - * fip_recv - receive from a FIP packet socket - * @s: packet socket with data ready to be received - */ -int fip_recv(int s) +int fip_vlan_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg) { - char buf[4096]; - struct sockaddr_ll sa; - struct iovec iov[] = { - { .iov_base = &buf[0], .iov_len = sizeof(buf), }, - }; - struct msghdr msg = { - .msg_name = &sa, - .msg_namelen = sizeof(sa), - .msg_iov = iov, - .msg_iovlen = ARRAY_SIZE(iov), - }; - struct fiphdr *fh; - struct iff *iff; - ssize_t len; - - log_debug(1, "%s", __func__); - - len = recvmsg(s, &msg, 0); - if (len < 0) { - log_errno("packet socket recv error"); - return len; - } - - if (len < sizeof(*fh)) { - log_err("received packed smaller that FIP header length"); - return -1; - } - - fh = (struct fiphdr *) buf; + int rc = -1; /* We only care about VLAN Notifications */ if (ntohs(fh->fip_proto) != FIP_PROTO_VLAN) { - log_debug(1, "ignoring FIP packet, protocol %d", - ntohs(fh->fip_proto)); - return -1; - } - - list_for_each_entry(iff, &interfaces, list) { - if (iff->ifindex == sa.sll_ifindex) - break; - } - if (&iff->list == &interfaces) { - log_warn("received packet on unexpected interface"); + FIP_LOG_DBG("ignoring FIP packet, protocol %d", + ntohs(fh->fip_proto)); return -1; } switch (fh->fip_subcode) { case FIP_VLAN_NOTE: - fip_recv_vlan_note(fh, len, iff); + rc = fip_recv_vlan_note(fh, sa->sll_ifindex); break; default: - log_warn("FIP packet with unknown subcode %d", fh->fip_subcode); - return -1; - } - - return 0; -} - -/** - * rtnl_socket - create and bind a routing netlink socket - */ -int rtnl_socket(void) -{ - struct sockaddr_nl sa = { - .nl_family = AF_NETLINK, - .nl_pid = getpid(), - .nl_groups = RTMGRP_LINK, - }; - int s; - int rc; - - log_debug(1, "creating netlink socket"); - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - log_errno("netlink socket error"); - return s; - } - - rc = bind(s, (struct sockaddr *) &sa, sizeof(sa)); - if (rc < 0) { - log_errno("netlink bind error"); - close(s); - return rc; + FIP_LOG_DBG("ignored FIP VLAN packet with subcode %d", + fh->fip_subcode); + break; } - - return s; -} - -/** - * send_getlink_dump - send an RTM_GETLINK dump request to list all interfaces - * @s: routing netlink socket to use - */ -ssize_t send_getlink_dump(int s) -{ - struct sockaddr_nl sa = { - .nl_family = AF_NETLINK, - .nl_pid = 0, - }; - struct { - struct nlmsghdr nh; - struct ifinfomsg ifm; - } req = { - .nh = { - .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), - .nlmsg_type = RTM_GETLINK, - .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, - .nlmsg_pid = 0, - }, - .ifm = { - .ifi_family = AF_UNSPEC, - .ifi_type = ARPHRD_ETHER, - }, - }; - struct iovec iov[] = { - { .iov_base = &req, .iov_len = sizeof(req), }, - }; - struct msghdr msg = { - .msg_name = &sa, - .msg_namelen = sizeof(sa), - .msg_iov = iov, - .msg_iovlen = ARRAY_SIZE(iov), - }; - int rc; - - log_debug(1, "sending RTM_GETLINK dump request"); - rc = sendmsg(s, &msg, 0); - if (rc < 0) - log_errno("netlink sendmsg error"); - return rc; } @@ -373,125 +253,64 @@ ssize_t send_getlink_dump(int s) */ void rtnl_recv_newlink(struct nlmsghdr *nh) { - struct ifinfomsg *ifm; - struct rtattr *rta; - struct iff *iff; - unsigned int len; - - log_debug(1, "RTM_NEWLINK"); + struct ifinfomsg *ifm = NLMSG_DATA(nh); + struct rtattr *ifla[__IFLA_MAX]; + struct rtattr *linkinfo[__IFLA_INFO_MAX]; + struct rtattr *vlan[__IFLA_VLAN_MAX]; + struct iff *iff, *real_dev; - ifm = NLMSG_DATA(nh); - log_debug(1, "ifindex %d, type %d", ifm->ifi_index, ifm->ifi_type); + FIP_LOG_DBG("RTM_NEWLINK: ifindex %d, type %d", + ifm->ifi_index, ifm->ifi_type); /* We only deal with Ethernet interfaces */ if (ifm->ifi_type != ARPHRD_ETHER) return; - /* if there's no link, we're not going to wait for it */ - if ((ifm->ifi_flags & IFF_RUNNING) != IFF_RUNNING) - return; - iff = malloc(sizeof(*iff)); if (!iff) { - log_errno("malloc failed"); + FIP_LOG_ERRNO("malloc failed"); return; } memset(iff, 0, sizeof(*iff)); + TAILQ_INIT(&iff->vlans); - iff->ifindex = ifm->ifi_index; - - len = IFLA_PAYLOAD(nh); - for (rta = IFLA_RTA(ifm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { - switch (rta->rta_type) { - case IFLA_ADDRESS: - memcpy(iff->mac_addr, RTA_DATA(rta), ETHER_ADDR_LEN); - log_debug(1, "\tIFLA_ADDRESS\t%x:%x:%x:%x:%x:%x", - iff->mac_addr[0], iff->mac_addr[1], - iff->mac_addr[2], iff->mac_addr[3], - iff->mac_addr[4], iff->mac_addr[5]); - break; - case IFLA_IFNAME: - iff->ifname = strdup(RTA_DATA(rta)); - log_debug(1, "\tIFLA_IFNAME\t%s", iff->ifname); - break; - default: - /* other attributes don't matter */ - break; - } - } - - list_add_tail(&iff->list, &interfaces); -} + parse_ifinfo(ifla, nh); -#define NLMSG(c) ((struct nlmsghdr *) (c)) - -/** - * rtnl_recv - receive from a routing netlink socket - * @s: routing netlink socket with data ready to be received - * - * Returns: 0 when NLMSG_DONE is received - * <0 on error - * >0 when more data is expected - */ -int rtnl_recv(int s) -{ - char buf[8192]; - struct sockaddr_nl sa; - struct iovec iov[] = { - [0] = { .iov_base = buf, .iov_len = sizeof(buf), }, - }; - struct msghdr msg = { - .msg_name = &sa, - .msg_namelen = sizeof(sa), - .msg_iov = iov, - .msg_iovlen = ARRAY_SIZE(iov), - }; - struct nlmsghdr *nh; - int len; - int rc; - - log_debug(1, "%s", __func__); - - len = recvmsg(s, &msg, 0); - if (len < 0) { - log_errno("netlink recvmsg error"); - return len; - } - - rc = 1; - for (nh = NLMSG(buf); NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { - switch (nh->nlmsg_type) { - case RTM_NEWLINK: - rtnl_recv_newlink(nh); - break; - case NLMSG_DONE: - log_debug(1, "NLMSG_DONE"); - break; - case NLMSG_ERROR: - log_debug(1, "NLMSG_ERROR"); - break; - default: - log_warn("unexpected netlink message type %d", - nh->nlmsg_type); - break; - } - - if (nh->nlmsg_type == NLMSG_DONE) { - rc = 0; - break; + iff->ifindex = ifm->ifi_index; + if (ifla[IFLA_LINK]) + iff->iflink = *(int *)RTA_DATA(ifla[IFLA_LINK]); + else + iff->iflink = iff->ifindex; + memcpy(iff->mac_addr, RTA_DATA(ifla[IFLA_ADDRESS]), ETHER_ADDR_LEN); + strncpy(iff->ifname, RTA_DATA(ifla[IFLA_IFNAME]), IFNAMSIZ); + + if (ifla[IFLA_LINKINFO]) { + parse_linkinfo(linkinfo, ifla[IFLA_LINKINFO]); + if (linkinfo[IFLA_INFO_KIND] && + !strcmp(RTA_DATA(linkinfo[IFLA_INFO_KIND]), "vlan")) { + iff->is_vlan = true; + parse_vlaninfo(vlan, linkinfo[IFLA_INFO_DATA]); + iff->vid = *(int *)RTA_DATA(vlan[IFLA_VLAN_ID]); + real_dev = find_vlan_real_dev(iff); + if (!real_dev) { + FIP_LOG_ERR(ENODEV, "VLAN found without parent"); + return; + } + TAILQ_INSERT_TAIL(&real_dev->vlans, iff, list_node); + return; } - if (!(nh->nlmsg_flags & NLM_F_MULTI)) - break; } - return rc; + TAILQ_INSERT_TAIL(&interfaces, iff, list_node); } /* command line arguments */ -#define GETOPT_STR "ahv" +#define GETOPT_STR "acshv" static const struct option long_options[] = { { "auto", no_argument, NULL, 'a' }, + { "create", no_argument, NULL, 'c' }, + { "start", no_argument, NULL, 's' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } @@ -503,6 +322,8 @@ static void help(int status) "Usage: %s [ options ] [ network interfaces ]\n" "Options:\n" " -a, --auto Auto select Ethernet interfaces\n" +" -c, --create Create system VLAN devices\n" +" -s, --start Start FCoE login automatically\n" " -h, --help Display this help and exit\n" " -v, --version Display version information and exit\n", exe); @@ -510,15 +331,9 @@ static void help(int status) exit(status); } -/* array of interface names to use */ -char **namev; -/* length of namev */ -int namec; - -int parse_cmdline(int argc, char **argv) +void parse_cmdline(int argc, char **argv) { char c; - int automode = 0; while (1) { c = getopt_long(argc, argv, GETOPT_STR, long_options, NULL); @@ -526,7 +341,13 @@ int parse_cmdline(int argc, char **argv) break; switch (c) { case 'a': - automode = 1; + config.automode = true; + break; + case 'c': + config.create = true; + break; + case 's': + config.start = true; break; case 'h': help(0); @@ -542,137 +363,174 @@ int parse_cmdline(int argc, char **argv) } } - if ((optind == argc) && (!automode)) + if ((optind == argc) && (!config.automode)) help(1); - namev = &argv[optind]; - namec = argc - optind; - return automode; + config.namev = &argv[optind]; + config.namec = argc - optind; } -/* exit after waiting 2 seconds without receiving anything */ -#define TIMEOUT 2000 +int rtnl_listener_handler(struct nlmsghdr *nh, void *arg) +{ + switch (nh->nlmsg_type) { + case RTM_NEWLINK: + rtnl_recv_newlink(nh); + return 0; + } + return -1; +} -int autodetect() +void create_missing_vlans() { - struct pollfd pfd[1]; - int ns; + struct fcf *fcf; + struct iff *real_dev, *vlan; + char vlan_name[IFNAMSIZ]; int rc; - ns = rtnl_socket(); - if (ns < 0) - return ns; - - send_getlink_dump(ns); - pfd[0].fd = ns; - pfd[0].events = POLLIN; + if (!config.create) + return; - while (1) { - rc = poll(pfd, ARRAY_SIZE(pfd), TIMEOUT); - log_debug(1, "return from poll %d", rc); - if (rc == 0) /* timeout */ - break; - if (rc == -1) { - log_errno("poll error"); - break; + TAILQ_FOREACH(fcf, &fcfs, list_node) { + vlan = lookup_vlan(fcf->ifindex, fcf->vlan); + if (vlan) { + FIP_LOG_DBG("VLAN %s.%d already exists as %s", + fcf->ifindex, fcf->vlan, vlan->ifname); + continue; } - if (pfd[0].revents) { - rc = rtnl_recv(pfd[0].fd); - if (rc == 0) - break; + real_dev = lookup_iff(fcf->ifindex, NULL); + if (!real_dev) { + FIP_LOG_ERR(ENODEV, "lost device %d with discoved FCF?", + fcf->ifindex); + continue; } - pfd[0].revents = 0; + snprintf(vlan_name, IFNAMSIZ, "%s.%d-fcoe", + real_dev->ifname, fcf->vlan); + rc = vlan_create(fcf->ifindex, fcf->vlan, vlan_name); + if (rc < 0) + printf("Failed to crate VLAN device %s\n\t%s\n", + vlan_name, strerror(-rc)); + else + printf("Created VLAN device %s\n", vlan_name); + rtnl_set_iff_up(0, vlan_name); } - close(ns); - return 0; + printf("\n"); } -int check_interface(char *name, int ps) +int fcoe_instance_start(char *ifname) { - struct ifreq ifr; - struct iff *iff; - - iff = malloc(sizeof(*iff)); - if (!iff) { - log_errno("malloc failed"); - return -1; + int fd, rc; + FIP_LOG_DBG("%s on %s\n", __func__, ifname); + fd = open(SYSFS_FCOE "/create", O_WRONLY); + if (fd < 0) { + FIP_LOG_ERRNO("failed to open fcoe create file"); + return fd; } - memset(iff, 0, sizeof(*iff)); + rc = write(fd, ifname, strlen(ifname)); + close(fd); + return rc < 0 ? rc : 0; +} - strncpy(ifr.ifr_name, name, IFNAMSIZ); - if (ioctl(ps, SIOCGIFINDEX, &ifr) != 0) { - log_errno("SIOCGIFINDEX"); - goto err; - } - iff->ifname = strdup(ifr.ifr_name); - iff->ifindex = ifr.ifr_ifindex; +void start_fcoe() +{ + struct fcf *fcf; + struct iff *iff; - if (ioctl(ps, SIOCGIFHWADDR, &ifr) != 0) { - log_errno("SIOCGIFHWADDR"); - goto err; - } - if (ifr.ifr_addr.sa_family != ARPHRD_ETHER) { - log_err("%s is not an Ethernet interface", name); - goto err; + TAILQ_FOREACH(fcf, &fcfs, list_node) { + iff = lookup_vlan(fcf->ifindex, fcf->vlan); + if (!iff) { + FIP_LOG_ERR(ENODEV, + "Cannot start FCoE on VLAN %d, ifindex %d, " + "because the VLAN device does not exist", + fcf->vlan, fcf->ifindex); + continue; + } + printf("Starting FCoE on interface %s\n", iff->ifname); + fcoe_instance_start(iff->ifname); } - memcpy(iff->mac_addr, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN); - - list_add_tail(&iff->list, &interfaces); - return 0; -err: - free(iff); - return -1; } void print_results() { + struct iff *iff; struct fcf *fcf; - if (list_empty(&fcfs)) { + if (TAILQ_EMPTY(&fcfs)) { printf("No Fibre Channel Forwarders Found\n"); return; } printf("Fibre Channel Forwarders Discovered\n"); - printf("%-10.10s| %-5.5s| %-10.10s\n", "interface", "VLAN", "FCF MAC"); - printf("------------------------------------\n"); - list_for_each_entry(fcf, &fcfs, list) { - printf("%-10.10s| %-5d| %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", - fcf->interface->ifname, fcf->vlan, + printf("%-16.16s| %-5.5s| %-17.17s\n", "interface", "VLAN", "FCF MAC"); + printf("------------------------------------------\n"); + TAILQ_FOREACH(fcf, &fcfs, list_node) { + iff = lookup_iff(fcf->ifindex, NULL); + printf("%-16.16s| %-5d| %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", + iff->ifname, fcf->vlan, fcf->mac_addr[0], fcf->mac_addr[1], fcf->mac_addr[2], fcf->mac_addr[3], fcf->mac_addr[4], fcf->mac_addr[5]); } + printf("\n"); } -void recv_loop(int ps) +/* exit after waiting 1 second without receiving anything */ +#define TIMEOUT 1000 + +void recv_loop(int ns, int ps) { - struct pollfd pfd[1] = { - [0].fd = ps, + struct pollfd pfd[2] = { + [0].fd = ns, [0].events = POLLIN, + [1].fd = ps, + [1].events = POLLIN, }; int rc; while (1) { rc = poll(pfd, ARRAY_SIZE(pfd), TIMEOUT); - log_debug(1, "return from poll %d", rc); + FIP_LOG_DBG("return from poll %d", rc); if (rc == 0) /* timeout */ break; if (rc == -1) { - log_errno("poll error"); + FIP_LOG_ERRNO("poll error"); break; } if (pfd[0].revents) - fip_recv(pfd[0].fd); + rtnl_recv(ns, rtnl_listener_handler, NULL); + if (pfd[1].revents) + fip_recv(ps, fip_vlan_handler, NULL); pfd[0].revents = 0; + pfd[1].revents = 0; } } -int main(int argc, char **argv) +void find_interfaces(int ns) +{ + send_getlink_dump(ns); + rtnl_recv(ns, rtnl_listener_handler, NULL); +} + +void send_vlan_requests(int ps) { - int ps; struct iff *iff; int i; - int automode; + + if (config.automode) { + TAILQ_FOREACH(iff, &interfaces, list_node) + fip_send_vlan_request(ps, iff->ifindex, iff->mac_addr); + } else { + for (i = 0; i < config.namec; i++) { + iff = lookup_iff(0, config.namev[i]); + if (!iff) + continue; + fip_send_vlan_request(ps, iff->ifindex, iff->mac_addr); + } + } +} + +int main(int argc, char **argv) +{ + int ps, ns; + int rc = 0; exe = strrchr(argv[0], '/'); if (exe) @@ -680,33 +538,48 @@ int main(int argc, char **argv) else exe = argv[0]; - automode = parse_cmdline(argc, argv); - log_start(exe, 0, 0); - - ps = packet_socket(); + parse_cmdline(argc, argv); + sa_log_prefix = exe; + sa_log_flags = 0; + enable_debug_log(0); - if (automode) { - autodetect(); - } else { - for (i = 0; i < namec; i++) - check_interface(namev[i], ps); + ps = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_FIP)); + if (ps < 0) { + rc = ps; + goto ps_err; + } + ns = rtnl_socket(); + if (ns < 0) { + rc = ns; + goto ns_err; } - if (list_empty(&interfaces)) { - log_err("no interfaces to perform discovery on"); + find_interfaces(ns); + + if (TAILQ_EMPTY(&interfaces)) { + FIP_LOG_ERR(ENODEV, "no interfaces to perform discovery on"); close(ps); - log_stop(); exit(1); } - list_for_each_entry(iff, &interfaces, list) - fip_send_vlan_request(ps, iff); + send_vlan_requests(ps); + recv_loop(ns, ps); - recv_loop(ps); print_results(); - + if (config.create) { + create_missing_vlans(); + /* + * need to listen for the RTM_NETLINK messages + * about the new VLAN devices + */ + recv_loop(ns, ps); + } + if (config.start) + start_fcoe(); + close(ns); +ns_err: close(ps); - log_stop(); - exit(0); +ps_err: + exit(rc); } diff --git a/include/fcoe_utils.h b/include/fcoe_utils.h new file mode 100644 index 0000000..3c43304 --- /dev/null +++ b/include/fcoe_utils.h @@ -0,0 +1,85 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FCOE_UTILS_H_ +#define _FCOE_UTILS_H_ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <libgen.h> +#include <dirent.h> +#include <errno.h> + +/* + * Used when trying to get the interface name from the symbolic_name. + * Not very elegant as this code will need to change if fcoe.ko changes + * its version. + */ +#define FCOE_MODULE_VERSION "v0.1" +#define SYMB_NAME_LEAD "fcoe " FCOE_MODULE_VERSION " over " + +#define MAX_STR_LEN 512 +#define MAX_PATH_LEN MAX_STR_LEN + +#define SYSFS_MOUNT "/sys" +#define SYSFS_NET SYSFS_MOUNT "/class/net" +#define SYSFS_FCHOST SYSFS_MOUNT "/class/fc_host" +#define SYSFS_FCOE SYSFS_MOUNT "/module/fcoe/parameters" + +#define FCHOSTBUFLEN 64 + +/* + * This macro assumes that progname has been set + */ +#define FCOE_LOG_ERR(fmt, args...) \ + do { \ + fprintf(stderr, "%s: " fmt, progname, ##args); \ + } while (0) + + +enum fcoe_err { + NOERR = 0, /* No error */ + EFCOECONN, /* FCoE connection already exists */ + ENOFCOECONN, /* No FCoE connection on interface */ + EINTERR, /* Internal error */ + EINVALARG, /* Invalid argument */ + EBADNUMARGS, /* Invalid number of arguments */ + EIGNORE, /* Ignore this error value */ + ENOSYSFS, /* sysfs is not present */ + ENOETHDEV, /* Not a valid Ethernet interface */ + ENOMONCONN, /* Not connected to fcoemon */ + ECONNTMOUT, /* Connection to fcoemon timed out */ + EHBAAPIERR, /* Error using HBAAPI/libhbalinux */ +}; + +enum fcoe_err fcoe_validate_interface(char *ifname); +enum fcoe_err fcoe_validate_fcoe_conn(char *ifname); +enum fcoe_err fcoe_find_fchost(char *ifname, char *fchost, int len); +int fcoe_checkdir(char *dir); +int check_symbolic_name_for_interface(const char *symbolic_name, + const char *ifname); +char *get_ifname_from_symbolic_name(const char *symbolic_name); + +#endif /* _FCOE_UTILS_H_ */ diff --git a/include/fcoe_utils_version.h.in b/include/fcoe_utils_version.h.in new file mode 100644 index 0000000..642f37d --- /dev/null +++ b/include/fcoe_utils_version.h.in @@ -0,0 +1,6 @@ +#ifndef _FCOE_UTILS_VERSION_H_ +#define _FCOE_UTILS_VERSION_H_ + +#define FCOE_UTILS_VERSION "@VERSION@" + +#endif /* _FCOE_UTILS_VERSION_H_ */ diff --git a/include/fcoemon_utils.h b/include/fcoemon_utils.h new file mode 100644 index 0000000..8680f46 --- /dev/null +++ b/include/fcoemon_utils.h @@ -0,0 +1,270 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FCOEMON_UTILS_H_ +#define _FCOEMON_UTILS_H_ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <malloc.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <stdarg.h> +#include <ctype.h> +#include <time.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <syslog.h> + +#include "fc_types.h" + +void sa_log(const char *format, ...); +void sa_log_debug(const char *format, ...); +void sa_log_err(int, const char *func, const char *format, ...); + +/* + * These functions can be provided outside of libsa for those environments + * that want to redirect them. + */ +void sa_log_output(const char *); /* log message */ +void sa_log_abort(const char *); /* log message and abort */ + +#define __SA_STRING(x) #x + +/* + * Logging options. + */ +#define SA_LOGF_TIME 0x0001 /* include timestamp in message */ +#define SA_LOGF_DELTA 0x0002 /* include time since last message */ + +extern u_int sa_log_flags; /* timestamp and other option flags */ +extern int sa_log_time_delta_min; /* minimum diff to print in millisec */ +extern char *sa_log_prefix; /* string to print before any message */ + +extern void assert_failed(const char *s, ...) + __attribute__ ((format(printf, 1, 2))); + +#ifndef UNLIKELY +#define UNLIKELY(_x) (_x) +#endif /* UNLIKELY */ + +/* + * ASSERT macros + * + * ASSERT(expr) - this calls assert_failed() if expr is false. This variant + * is not present in production code or if DEBUG_ASSERTS is not defined. + * Be careful not to rely on expr being evaluated. + */ +#if defined(DEBUG_ASSERTS) +#define ASSERT(_x) do { \ + if (UNLIKELY(!(_x))) { \ + assert_failed("ASSERT FAILED (%s) @ %s:%d\n", \ + "" #_x, __FILE__, __LINE__); \ + } \ + } while (0) +#else +#define ASSERT(_x) +#endif /* DEBUG_ASSERTS */ + +/* + * ASSERT_NOTIMPL(expr) - this calls assert_failed() if expr is false. + * The implication is that the condition is not handled by the current + * implementation, and work should be done eventually to handle this. + */ +#define ASSERT_NOTIMPL(_x) do { \ + if (UNLIKELY(!(_x))) { \ + assert_failed("ASSERT (NOT IMPL) " \ + "(%s) @ %s:%d\n", \ + "" #_x, __FILE__, __LINE__); \ + } \ + } while (0) + +/* + * ASSERT_NOTREACHED - this is the same as ASSERT_NOTIMPL(0). + */ +#define ASSERT_NOTREACHED do { \ + assert_failed("ASSERT (NOT REACHED) @ %s:%d\n", \ + __FILE__, __LINE__); \ + } while (0) + +/* + * ASSERT_BUG(bugno, expr). This variant is used when a bug number has + * been assigned to any one of the other assertion failures. It is always + * present in code. It gives the bug number which helps locate + * documentation and helps prevent duplicate bug filings. + */ +#define ASSERT_BUG(_bugNr, _x) do { \ + if (UNLIKELY(!(_x))) { \ + assert_failed("ASSERT (BUG %d) (%s) @ %s:%d\n", \ + (_bugNr), #_x, __FILE__, __LINE__); \ + } \ + } while (0) + +#ifndef LIBSA_USE_DANGEROUS_ROUTINES +#define strcpy DONT_USE_strcpy +#define strcat DONT_USE_strcat +#define gets DONT_USE_gets +#endif /* LIBSA_USE_DANGEROUS_ROUTINES */ + +char *sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len); +char *sa_hex_format(char *buf, size_t buflen, + const unsigned char *data, size_t data_len, + unsigned int group_len, char *inter_group_sep); + +/* + * Structure for tables encoding and decoding name-value pairs such as enums. + */ +struct sa_nameval { + char *nv_name; + u_int nv_val; +}; + +const char *sa_enum_decode(char *, size_t, const struct sa_nameval *, u_int); +int sa_enum_encode(const struct sa_nameval *tp, const char *, u_int *); +const char *sa_flags_decode(char *, size_t, const struct sa_nameval *, u_int); + +/* + * Timer facility. + */ + +struct sa_timer { + struct sa_timer *tm_next; + u_int64_t tm_nsec; /* relative time to event (nSec) */ + void (*tm_handler)(void *arg); + void *tm_handler_arg; + struct sa_timer **timer_head; +}; + + +#define SA_TIMER_UNITS (1000 * 1000UL) /* number of timer ticks per second */ + +/* + * Initialize a pre-allocated timer structure. + */ +void sa_timer_init(struct sa_timer *, void (*handler)(void *), void *arg); + +/* + * Test whether the timer is active. + */ +static inline int sa_timer_active(struct sa_timer *tm) +{ + return tm->tm_nsec != 0; +} + +/* + * Allocate a timer structure. Set handler. + */ +struct sa_timer *sa_timer_alloc(void (*)(void *arg), void *arg); + +/* + * Set timer to fire. Delta is in microseconds from now. + */ +void sa_timer_set(struct sa_timer *, u_long delta); + +/* + * Cancel timer. + */ +void sa_timer_cancel(struct sa_timer *); + +/* + * Free (and cancel) timer. + */ +void sa_timer_free(struct sa_timer *); + + +/* + * Handle timer checks. Called from select loop or other periodic function. + * + * The struct timeval passed in indicates how much time has passed since + * the last call, and is set before returning to the maximum amount of time + * that should elapse before the next call. + * + * Returns 1 if any timer handlers were invoked, 0 otherwise. + */ +int sa_timer_check(struct timeval *); + +/* + * Get time in nanoseconds since some arbitrary time. + */ +u_int64_t sa_timer_get(void); + +/* + * Get time in seconds since some arbitrary time. + */ +u_int sa_timer_get_secs(void); + +/* + * sa_select - Server Array select facility. + * + * This is a thin layer to poll files with a select loop. + */ + +/* + * Enter the polling loop which never exits. + */ +int sa_select_loop(void); + +/* + * Set callback for every time through the select loop. + */ +void sa_select_set_callback(void (*)(void)); + +/* + * Add a callback to handle files which are ready for receive, transmit, + * or to handle exceptions. + */ +void sa_select_add_fd(int fd, void (*rx_handler)(void *), + void (*tx_handler)(void *), + void (*ex_handler)(void *), void *arg); + +/* + * Change a single callback for a descriptor that's already been added. + */ +void sa_select_set_rx(int fd, void (*handler)(void *)); +void sa_select_set_tx(int fd, void (*handler)(void *)); +void sa_select_set_ex(int fd, void (*handler)(void *)); + +/* + * Remove all callbacks for a file descriptor. + */ +void sa_select_rem_fd(int fd); + +/* + * Cause select loop to return. + */ +void sa_select_exit(void); + +/* + * Convert 48-bit IEEE MAC address to 64-bit FC WWN. + */ +extern fc_wwn_t +fc_wwn_from_mac(u_int64_t, u_int32_t scheme, u_int32_t port); + +extern int hex2int(char *b); + +extern int use_syslog; +void enable_syslog(int); +void enable_debug_log(int); + +#endif /* _FCOEMON_UTILS_H_ */ diff --git a/include/fip.h b/include/fip.h index 91a017f..5f39763 100644 --- a/include/fip.h +++ b/include/fip.h @@ -20,6 +20,9 @@ #ifndef FIP_H #define FIP_H +#include <stdint.h> +#include <net/ethernet.h> + #define ETH_P_FCOE 0x8906 #define ETH_P_FIP 0x8914 @@ -123,4 +126,26 @@ struct fip_tlv_vlan { uint16_t vlan; /* only lower 12 bits matter */ }; + +/* libutil / fip.c functionality */ + +/* FIP message handler, passed into fip_recv */ +typedef int fip_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg); + +/** + * fip_recv - receive from a FIP packet socket + * @s: packet socket with data ready to be received + */ +int fip_recv(int s, fip_handler *fn, void *arg); + +/** + * fip_send_vlan_request - send a FIP VLAN request + * @s: ETH_P_FIP packet socket to send on + * @ifindex: network interface index to send on + * @mac: mac address of the netif + * + * Note: sends to FIP_ALL_FCF_MACS + */ +ssize_t fip_send_vlan_request(int s, int ifindex, unsigned char *mac); + #endif /* FIP_H */ diff --git a/include/list.h b/include/list.h deleted file mode 100644 index 35da9fd..0000000 --- a/include/list.h +++ /dev/null @@ -1,444 +0,0 @@ -/* Adapted from the Linux kernel, under the terms of the GPLv2 */ - -#ifndef LIST_H -#define LIST_H - -#include <stddef.h> - -#define container_of(ptr, type, member) ({ \ - const typeof(((type *) 0)->member) * __mptr = (ptr); \ - (type *)((char *) __mptr - offsetof(type, member)); \ -}) - -/* - * Simple doubly linked list implementation. - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ - -struct list_head { - struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -static inline void INIT_LIST_HEAD(struct list_head *list) -{ - list->next = list; - list->prev = list; -} - -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -static inline void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} - -/** - * list_add_tail - add a new entry - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) -{ - __list_add(new, head->prev, head); -} - -/* - * Delete a list entry by making the prev/next entries - * point to each other. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_del(struct list_head *prev, struct list_head *next) -{ - next->prev = prev; - prev->next = next; -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * Note: list_empty() on entry does not return true after this, the entry is - * in an undefined state. - */ -static inline void list_del(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - entry->next = NULL; - entry->prev = NULL; -} - -/** - * list_replace - replace old entry by new one - * @old : the element to be replaced - * @new : the new element to insert - * - * If @old was empty, it will be overwritten. - */ -static inline void list_replace(struct list_head *old, - struct list_head *new) -{ - new->next = old->next; - new->next->prev = new; - new->prev = old->prev; - new->prev->next = new; -} - -static inline void list_replace_init(struct list_head *old, - struct list_head *new) -{ - list_replace(old, new); - INIT_LIST_HEAD(old); -} - -/** - * list_del_init - deletes entry from list and reinitialize it. - * @entry: the element to delete from the list. - */ -static inline void list_del_init(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - INIT_LIST_HEAD(entry); -} - -/** - * list_move - delete from one list and add as another's head - * @list: the entry to move - * @head: the head that will precede our entry - */ -static inline void list_move(struct list_head *list, struct list_head *head) -{ - __list_del(list->prev, list->next); - list_add(list, head); -} - -/** - * list_move_tail - delete from one list and add as another's tail - * @list: the entry to move - * @head: the head that will follow our entry - */ -static inline void list_move_tail(struct list_head *list, - struct list_head *head) -{ - __list_del(list->prev, list->next); - list_add_tail(list, head); -} - -/** - * list_is_last - tests whether @list is the last entry in list @head - * @list: the entry to test - * @head: the head of the list - */ -static inline int list_is_last(const struct list_head *list, - const struct list_head *head) -{ - return list->next == head; -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static inline int list_empty(const struct list_head *head) -{ - return head->next == head; -} - -/** - * list_empty_careful - tests whether a list is empty and not being modified - * @head: the list to test - * - * Description: - * tests whether a list is empty _and_ checks that no other CPU might be - * in the process of modifying either member (next or prev) - * - * NOTE: using list_empty_careful() without synchronization - * can only be safe if the only activity that can happen - * to the list entry is list_del_init(). Eg. it cannot be used - * if another CPU could re-list_add() it. - */ -static inline int list_empty_careful(const struct list_head *head) -{ - struct list_head *next = head->next; - return (next == head) && (next == head->prev); -} - -/** - * list_is_singular - tests whether a list has just one entry. - * @head: the list to test. - */ -static inline int list_is_singular(const struct list_head *head) -{ - return !list_empty(head) && (head->next == head->prev); -} - -static inline void __list_splice(const struct list_head *list, - struct list_head *head) -{ - struct list_head *first = list->next; - struct list_head *last = list->prev; - struct list_head *at = head->next; - - first->prev = head; - head->next = first; - - last->next = at; - at->prev = last; -} - -/** - * list_splice - join two lists - * @list: the new list to add. - * @head: the place to add it in the first list. - */ -static inline void list_splice(const struct list_head *list, - struct list_head *head) -{ - if (!list_empty(list)) - __list_splice(list, head); -} - -/** - * list_splice_init - join two lists and reinitialise the emptied list. - * @list: the new list to add. - * @head: the place to add it in the first list. - * - * The list at @list is reinitialised - */ -static inline void list_splice_init(struct list_head *list, - struct list_head *head) -{ - if (!list_empty(list)) { - __list_splice(list, head); - INIT_LIST_HEAD(list); - } -} - -/** - * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. - */ -#define list_entry(ptr, type, member) \ - container_of(ptr, type, member) - -/** - * list_first_entry - get the first element from a list - * @ptr: the list head to take the element from. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. - * - * Note, that list is expected to be not empty. - */ -#define list_first_entry(ptr, type, member) \ - list_entry((ptr)->next, type, member) - -/** - * list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - */ -#define list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) - -/** - * list_for_each_prev - iterate over a list backwards - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - */ -#define list_for_each_prev(pos, head) \ - for (pos = (head)->prev; pos != (head); pos = pos->prev) - -/** - * list_for_each_safe - iterate over a list safe against removal of list entry - * @pos: the &struct list_head to use as a loop cursor. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. - */ -#define list_for_each_safe(pos, n, head) \ - for (pos = (head)->next, n = pos->next; \ - pos != (head); \ - pos = n, n = pos->next) - -/** - * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry - * @pos: the &struct list_head to use as a loop cursor. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. - */ -#define list_for_each_prev_safe(pos, n, head) \ - for (pos = (head)->prev, n = pos->prev; \ - pos != (head); \ - pos = n, n = pos->prev) - -/** - * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry(pos, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_reverse - iterate backwards over list of given type. - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry_reverse(pos, head, member) \ - for (pos = list_entry((head)->prev, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.prev, typeof(*pos), member)) - -/** - * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() - * @pos: the type * to use as a start point - * @head: the head of the list - * @member: the name of the list_struct within the struct. - * - * Prepares a pos entry for use as a start point in - * list_for_each_entry_continue(). - */ -#define list_prepare_entry(pos, head, member) \ - ((pos) ? : list_entry(head, typeof(*pos), member)) - -/** - * list_for_each_entry_continue - continue iteration over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Continue to iterate over list of given type, continuing after - * the current position. - */ -#define list_for_each_entry_continue(pos, head, member) \ - for (pos = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_continue_reverse - iterate backwards from the given point - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Start to iterate over list of given type backwards, continuing after - * the current position. - */ -#define list_for_each_entry_continue_reverse(pos, head, member) \ - for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.prev, typeof(*pos), member)) - -/** - * list_for_each_entry_from - iterate over list of given type from the current point - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate over list of given type, continuing from current position. - */ -#define list_for_each_entry_from(pos, head, member) \ - for (; &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_continue - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate over list of given type, continuing after current point, - * safe against removal of list entry. - */ -#define list_for_each_entry_safe_continue(pos, n, head, member) \ - for (pos = list_entry(pos->member.next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_from - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate over list of given type from current point, safe against - * removal of list entry. - */ -#define list_for_each_entry_safe_from(pos, n, head, member) \ - for (n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_reverse - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate backwards over list of given type, safe against removal - * of list entry. - */ -#define list_for_each_entry_safe_reverse(pos, n, head, member) \ - for (pos = list_entry((head)->prev, typeof(*pos), member), \ - n = list_entry(pos->member.prev, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.prev, typeof(*n), member)) - -#endif /* LIST_H */ diff --git a/include/log.h b/include/log.h deleted file mode 100644 index 7a2a138..0000000 --- a/include/log.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef LOG_H -#define LOG_H - -void log_start(char *program, int daemon, int level); -void log_stop(); -void do_log(int priority, const char *fmt, va_list ap); -void log_debug(int level, char *fmt, ...); -void log_warn(char *fmt, ...); -void log_err(char *fmt, ...); -void _log_errno(const char *func, char *call, int errnum); - -#define log_errno(s) _log_errno(__func__, s, errno) - -#endif /* LOG_H */ - diff --git a/include/rtnetlink.h b/include/rtnetlink.h new file mode 100644 index 0000000..167748e --- /dev/null +++ b/include/rtnetlink.h @@ -0,0 +1,63 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _RTNETLINK_ +#define _RTNETLINK_ + +int rtnl_socket(void); +typedef int rtnl_handler(struct nlmsghdr *nh, void *arg); +int rtnl_recv(int s, rtnl_handler *fn, void *arg); +ssize_t send_getlink_dump(int s); +int rtnl_set_iff_up(int ifindex, char *ifname); +int vlan_create(int ifindex, int vid, char *name); +int rtnl_find_vlan(int ifindex, int vid, char *ifname); +int rtnl_get_linkname(int ifindex, char *name); + +static inline void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max) + tb[rta->rta_type] = rta; + rta = RTA_NEXT(rta, len); + } +} + +static inline void parse_nested_rtattr(struct rtattr *tb[], int max, struct rtattr *rta) +{ + parse_rtattr(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta)); +} + +static inline void parse_ifinfo(struct rtattr *tb[], struct nlmsghdr *nh) +{ + struct ifinfomsg *ifm = NLMSG_DATA(nh); + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), IFLA_PAYLOAD(nh)); +} + +static inline void parse_linkinfo(struct rtattr *tb[], struct rtattr *linkinfo) +{ + parse_nested_rtattr(tb, IFLA_INFO_MAX, linkinfo); +} + +static inline void parse_vlaninfo(struct rtattr *tb[], struct rtattr *vlan) +{ + parse_nested_rtattr(tb, IFLA_VLAN_MAX, vlan); +} + +#endif /* _RTNETLINK_ */ diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..ebe259f --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,3 @@ +AM_CPPFLAGS = -I${top_srcdir}/include +noinst_LIBRARIES = libutil.a +libutil_a_SOURCES = fcoe_utils.c sa_log.c sa_select.c sa_timer.c sa_other.c fip.c rtnetlink.c diff --git a/lib/fcoe_utils.c b/lib/fcoe_utils.c new file mode 100644 index 0000000..506356d --- /dev/null +++ b/lib/fcoe_utils.c @@ -0,0 +1,191 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include "fcoe_utils.h" + +static int fcoe_sysfs_read(char *buf, int size, const char *path) +{ + FILE *fp; + int i, rc = -EINVAL; + + fp = fopen(path, "r"); + if (fp) { + if (fgets(buf, size, fp)) { + /* + * Strip trailing newline by replacing + * any '\r' or '\n' instances with '\0'. + * It's not as elegant as it could be, but + * we know that the symbolic name won't + * have either of those characters until + * the end of the line. + */ + for (i = 0; i < strlen(buf); i++) { + if (buf[i] == '\n' || + buf[i] == '\r') { + buf[i] = '\0'; + break; + } + } + rc = 0; + } + + fclose(fp); + } + + return rc; +} + +static int fcoe_check_fchost(const char *ifname, const char *dname) +{ + char buf[MAX_STR_LEN]; + char path[MAX_PATH_LEN]; + int rc = -EINVAL; + + sprintf(path, "%s/%s/symbolic_name", SYSFS_FCHOST, dname); + + if (!fcoe_sysfs_read(buf, MAX_STR_LEN, path)) + rc = check_symbolic_name_for_interface(buf, ifname); + + return rc; +} + +enum fcoe_err fcoe_find_fchost(char *ifname, char *fchost, int len) +{ + int n, dname_len; + struct dirent **namelist; + int rc = ENOFCOECONN; + + n = scandir(SYSFS_FCHOST, &namelist, 0, alphasort); + + for (n-- ; n >= 0 ; n--) { + if (rc) { + /* check symbolic name */ + if (!fcoe_check_fchost(ifname, namelist[n]->d_name)) { + dname_len = strnlen(namelist[n]->d_name, len); + + if (len > dname_len) { + strncpy(fchost, namelist[n]->d_name, + dname_len + 1); + /* rc = 0 indicates found */ + rc = NOERR; + } else { + /* + * The fc_host is too large + * for the buffer. + */ + rc = EINTERR; + } + } + } + free(namelist[n]); + + } + free(namelist); + + return rc; +} + +enum fcoe_err fcoe_validate_interface(char *ifname) +{ + enum fcoe_err rc = NOERR; + char path[MAX_PATH_LEN]; + + + if (!strlen(ifname)) + rc = ENOETHDEV; + + /* + * TODO: Is there a better way to check if the + * interface name is correct? + */ + sprintf(path, "%s/%s", SYSFS_NET, ifname); + if (!rc && fcoe_checkdir(path)) + rc = ENOETHDEV; + + return rc; +} + +/* + * Validate an existing instance for an FC interface + */ +enum fcoe_err fcoe_validate_fcoe_conn(char *ifname) +{ + char fchost[FCHOSTBUFLEN]; + enum fcoe_err rc = NOERR; + + rc = fcoe_validate_interface(ifname); + + if (!rc) + rc = fcoe_find_fchost(ifname, fchost, FCHOSTBUFLEN); + + return rc; +} + +/* + * Open and close to check if directory exists + */ +int fcoe_checkdir(char *dir) +{ + DIR *d = NULL; + + if (!dir) + return -EINVAL; + /* check if we have sysfs */ + d = opendir(dir); + if (!d) + return -EINVAL; + closedir(d); + return 0; +} + +char *get_ifname_from_symbolic_name(const char *symbolic_name) +{ + int symbolic_name_len = strlen(symbolic_name); + int lead_len = strlen(SYMB_NAME_LEAD); + + if (lead_len < symbolic_name_len) + return (char *)(symbolic_name + lead_len); + + return NULL; +} + +int check_symbolic_name_for_interface(const char *symbolic_name, + const char *ifname) +{ + int rc = -EINVAL; + char *symb; + + if (!ifname) + return rc; + + symb = get_ifname_from_symbolic_name(symbolic_name); + + /* + * It's important to use the length of the ifname + * from the symbolic_name here. If the ifname length + * were used then if the user passed in a substring + * of the the interface name it would match because + * we'd only be looking for the first few characters, + * not the whole string. + */ + if (symb && !strncmp(ifname, symb, strlen(symb))) + rc = 0; + + return rc; +} diff --git a/lib/fip.c b/lib/fip.c new file mode 100644 index 0000000..a5430c9 --- /dev/null +++ b/lib/fip.c @@ -0,0 +1,155 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +/* Routines for automatic FIP VLAN discovery and creation */ +/* Shared by fcoemon and fipvlan */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <errno.h> +#include <getopt.h> +#include <poll.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <netpacket/packet.h> +#include <arpa/inet.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include "fip.h" +#include "fcoemon_utils.h" + +#define FIP_LOG(...) sa_log(__VA_ARGS__) +#define FIP_LOG_ERR(error, ...) sa_log_err(error, __func__, __VA_ARGS__) +#define FIP_LOG_ERRNO(...) sa_log_err(errno, __func__, __VA_ARGS__) +#define FIP_LOG_DBG(...) sa_log_debug(__VA_ARGS__) + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/** + * fip_send_vlan_request - send a FIP VLAN request + * @s: ETH_P_FIP packet socket to send on + * @ifindex: network interface index to send on + * @mac: mac address of the sending network interface + * + * Note: sends to FIP_ALL_FCF_MACS + */ +ssize_t fip_send_vlan_request(int s, int ifindex, unsigned char *mac) +{ + struct sockaddr_ll sa = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_FIP), + .sll_ifindex = ifindex, + .sll_hatype = ARPHRD_ETHER, + .sll_pkttype = PACKET_MULTICAST, + .sll_halen = ETHER_ADDR_LEN, + .sll_addr = FIP_ALL_FCF_MACS, + }; + struct fiphdr fh = { + .fip_version = FIP_VERSION(1), + .fip_proto = htons(FIP_PROTO_VLAN), + .fip_subcode = FIP_VLAN_REQ, + .fip_desc_len = htons(2), + .fip_flags = 0, + }; + struct { + struct fip_tlv_mac_addr mac; + } tlvs = { + .mac = { + .hdr.tlv_type = FIP_TLV_MAC_ADDR, + .hdr.tlv_len = 2, + }, + }; + struct iovec iov[] = { + { .iov_base = &fh, .iov_len = sizeof(fh), }, + { .iov_base = &tlvs, .iov_len = sizeof(tlvs), }, + }; + struct msghdr msg = { + .msg_name = &sa, + .msg_namelen = sizeof(sa), + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + }; + int rc; + + memcpy(tlvs.mac.mac_addr, mac, ETHER_ADDR_LEN); + + FIP_LOG_DBG("sending FIP VLAN request"); + rc = sendmsg(s, &msg, 0); + if (rc < 0) { + rc = -errno; + FIP_LOG_ERRNO("sendmsg error"); + } + return rc; +} + +/** + * fip_recv - receive from a FIP packet socket + * @s: packet socket with data ready to be received + * @fn: FIP receive callback to process the payload + * @arg: argument to pass through to @fn + */ +int fip_recv(int s, fip_handler *fn, void *arg) +{ + char buf[4096]; + struct sockaddr_ll sa; + struct iovec iov[] = { + { .iov_base = buf, .iov_len = sizeof(buf), }, + }; + struct msghdr msg = { + .msg_name = &sa, + .msg_namelen = sizeof(sa), + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + }; + struct fiphdr *fh; + ssize_t len, desc_len; + + FIP_LOG_DBG("%s", __func__); + + len = recvmsg(s, &msg, 0); + if (len < 0) { + FIP_LOG_ERRNO("packet socket recv error"); + return len; + } + + if (len < sizeof(*fh)) { + FIP_LOG_ERR(EINVAL, "received packed smaller that FIP header"); + return -1; + } + + fh = (struct fiphdr *) buf; + + desc_len = ntohs(fh->fip_desc_len); + if (len < (sizeof(*fh) + (desc_len << 2))) { + FIP_LOG_ERR(EINVAL, "received data less that FIP descriptor"); + return -1; + } + + if (fn) + return fn(fh, &sa, arg); + return 0; +} diff --git a/lib/rtnetlink.c b/lib/rtnetlink.c new file mode 100644 index 0000000..b7a32f2 --- /dev/null +++ b/lib/rtnetlink.c @@ -0,0 +1,429 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <errno.h> +#include <getopt.h> +#include <poll.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <netpacket/packet.h> +#include <arpa/inet.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include "rtnetlink.h" +#include "fcoemon_utils.h" + +#define RTNL_LOG(...) sa_log(__VA_ARGS__) +#define RTNL_LOG_ERR(error, ...) sa_log_err(error, __func__, __VA_ARGS__) +#define RTNL_LOG_ERRNO(...) sa_log_err(errno, __func__, __VA_ARGS__) +#define RTNL_LOG_DBG(...) sa_log_debug(__VA_ARGS__) + +/** + * rtnl_socket - create and bind a routing netlink socket + */ +int rtnl_socket(void) +{ + struct sockaddr_nl sa = { + .nl_family = AF_NETLINK, + .nl_groups = RTMGRP_LINK, + }; + int s; + int rc; + + RTNL_LOG_DBG("creating netlink socket"); + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + RTNL_LOG_ERRNO("netlink socket error"); + return s; + } + + rc = bind(s, (struct sockaddr *) &sa, sizeof(sa)); + if (rc < 0) { + RTNL_LOG_ERRNO("netlink bind error"); + close(s); + return rc; + } + + return s; +} + +/** + * send_getlink_dump - send an RTM_GETLINK dump request to list all interfaces + * @s: routing netlink socket to use + */ +ssize_t send_getlink_dump(int s) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg ifm; + } req = { + .nh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_GETLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + }, + .ifm = { + .ifi_type = ARPHRD_ETHER, + }, + }; + int rc; + + RTNL_LOG_DBG("sending RTM_GETLINK dump request"); + rc = send(s, &req, req.nh.nlmsg_len, 0); + if (rc < 0) + RTNL_LOG_ERRNO("netlink sendmsg error"); + + return rc; +} + +#define NLMSG(c) ((struct nlmsghdr *) (c)) + +/** + * rtnl_recv - receive from a routing netlink socket + * @s: routing netlink socket with data ready to be received + * + * Returns: 0 when NLMSG_DONE is received + * <0 on error + * >0 when more data is expected + */ +int rtnl_recv(int s, rtnl_handler *fn, void *arg) +{ + char buf[8192]; + struct nlmsghdr *nh; + int len; + int rc = 0; + bool more = false; + + RTNL_LOG_DBG("%s", __func__); +more: + len = recv(s, buf, sizeof(buf), 0); + if (len < 0) { + RTNL_LOG_ERRNO("netlink recvmsg error"); + return len; + } + + for (nh = NLMSG(buf); NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { + if (nh->nlmsg_flags & NLM_F_MULTI) + more = true; + + switch (nh->nlmsg_type) { + case NLMSG_NOOP: + RTNL_LOG_DBG("NLMSG_NOOP"); + break; + case NLMSG_ERROR: + rc = ((struct nlmsgerr *)NLMSG_DATA(nh))->error; + RTNL_LOG_DBG("NLMSG_ERROR (%d) %s", rc, strerror(-rc)); + break; + case NLMSG_DONE: + more = false; + RTNL_LOG_DBG("NLMSG_DONE"); + break; + default: + if (!fn || fn(nh, arg) < 0) + RTNL_LOG("unexpected netlink message type %d", + nh->nlmsg_type); + break; + } + } + if (more) + goto more; + return rc; +} + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *)(((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +static void add_rtattr(struct nlmsghdr *n, int type, const void *data, int alen) +{ + struct rtattr *rta = NLMSG_TAIL(n); + int len = RTA_LENGTH(alen); + + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); +} + +static struct rtattr *add_rtattr_nest(struct nlmsghdr *n, int type) +{ + struct rtattr *nest = NLMSG_TAIL(n); + + add_rtattr(n, type, NULL, 0); + return nest; +} + +static void end_rtattr_nest(struct nlmsghdr *n, struct rtattr *nest) +{ + nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest; +} + +static ssize_t rtnl_send_set_iff_up(int s, int ifindex, char *ifname) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg ifm; + char attrbuf[RTA_SPACE(IFNAMSIZ)]; + } req = { + .nh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_SETLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + }, + .ifm = { + .ifi_index = ifindex, + .ifi_flags = IFF_UP, + .ifi_change = IFF_UP, + }, + }; + int rc; + + if (ifname) + add_rtattr(&req.nh, IFLA_IFNAME, ifname, strlen(ifname)); + + RTNL_LOG_DBG("sending RTM_SETLINK request"); + rc = send(s, &req, req.nh.nlmsg_len, 0); + if (rc < 0) + RTNL_LOG_ERRNO("netlink send error"); + + return rc; +} + +int rtnl_set_iff_up(int ifindex, char *ifname) +{ + int s; + int rc; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (s < 0) + return s; + rc = rtnl_send_set_iff_up(s, ifindex, ifname); + if (rc < 0) + goto out; + rc = rtnl_recv(s, NULL, NULL); +out: + close(s); + return rc; +} + +static ssize_t rtnl_send_vlan_newlink(int s, int ifindex, int vid, char *name) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg ifm; + char attrbuf[1024]; + } req = { + .nh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_NEWLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | + NLM_F_EXCL | NLM_F_ACK, + }, + }; + struct rtattr *linkinfo, *data; + int rc; + + add_rtattr(&req.nh, IFLA_LINK, &ifindex, 4); + add_rtattr(&req.nh, IFLA_IFNAME, name, strlen(name)); + linkinfo = add_rtattr_nest(&req.nh, IFLA_LINKINFO); + add_rtattr(&req.nh, IFLA_INFO_KIND, "vlan", strlen("vlan")); + data = add_rtattr_nest(&req.nh, IFLA_INFO_DATA); + add_rtattr(&req.nh, IFLA_VLAN_ID, &vid, 2); + end_rtattr_nest(&req.nh, data); + end_rtattr_nest(&req.nh, linkinfo); + + RTNL_LOG_DBG("sending RTM_NEWLINK request"); + rc = send(s, &req, req.nh.nlmsg_len, 0); + if (rc < 0) + RTNL_LOG_ERRNO("netlink send error"); + + return rc; +} + +int vlan_create(int ifindex, int vid, char *name) +{ + int s; + int rc; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (s < 0) + return s; + + rc = rtnl_send_vlan_newlink(s, ifindex, vid, name); + if (rc < 0) + goto out; + rc = rtnl_recv(s, NULL, NULL); +out: + close(s); + return rc; +} + +static ssize_t rtnl_send_getlink(int s, int ifindex, char *name) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg ifm; + char attrbuf[RTA_SPACE(IFNAMSIZ)]; + } req = { + .nh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_GETLINK, + .nlmsg_flags = NLM_F_REQUEST, + }, + .ifm = { + .ifi_family = AF_UNSPEC, + .ifi_index = ifindex, + }, + }; + int rc; + + if (!ifindex && !name) + return -1; + + if (name) + add_rtattr(&req.nh, IFLA_IFNAME, name, strlen(name)); + + RTNL_LOG_DBG("sending RTM_GETLINK"); + rc = send(s, &req, req.nh.nlmsg_len, 0); + if (rc < 0) + RTNL_LOG_ERRNO("netlink send error"); + + return rc; +} + +static int rtnl_getlinkname_handler(struct nlmsghdr *nh, void *arg) +{ + char *name = arg; + struct ifinfomsg *ifm; + struct rtattr *ifla[__IFLA_MAX]; + + switch (nh->nlmsg_type) { + case RTM_NEWLINK: + ifm = NLMSG_DATA(nh); + parse_ifinfo(ifla, nh); + strncpy(name, RTA_DATA(ifla[IFLA_IFNAME]), IFNAMSIZ); + return 0; + } + return -1; +} + +int rtnl_get_linkname(int ifindex, char *name) +{ + int s; + int rc; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (s < 0) + return s; + rc = rtnl_send_getlink(s, ifindex, NULL); + if (rc < 0) + return rc; + rc = rtnl_recv(s, rtnl_getlinkname_handler, name); + if (rc < 0) + goto out; +out: + close(s); + return rc; +} + +struct vlan_identifier { + int ifindex; + int vid; + int found; + unsigned char ifname[IFNAMSIZ]; +}; + +static int rtnl_find_vlan_handler(struct nlmsghdr *nh, void *arg) +{ + struct vlan_identifier *vlan = arg; + struct ifinfomsg *ifm; + struct rtattr *ifla[__IFLA_MAX]; + struct rtattr *linkinfo[__IFLA_INFO_MAX]; + struct rtattr *vlaninfo[__IFLA_VLAN_MAX]; + + switch (nh->nlmsg_type) { + case RTM_NEWLINK: + ifm = NLMSG_DATA(nh); + parse_ifinfo(ifla, nh); + if (!ifla[IFLA_LINK]) + break; + if (vlan->ifindex != *(int *)RTA_DATA(ifla[IFLA_LINK])) + break; + if (!ifla[IFLA_LINKINFO]) + break; + parse_linkinfo(linkinfo, ifla[IFLA_LINKINFO]); + if (!linkinfo[IFLA_INFO_KIND]) + break; + if (strcmp(RTA_DATA(linkinfo[IFLA_INFO_KIND]), "vlan")) + break; + if (!linkinfo[IFLA_INFO_DATA]) + break; + parse_vlaninfo(vlaninfo, linkinfo[IFLA_INFO_DATA]); + if (!vlaninfo[IFLA_VLAN_ID]) + break; + if (vlan->vid != *(int *)RTA_DATA(vlaninfo[IFLA_VLAN_ID])) + break; + if (!ifla[IFLA_IFNAME]) + break; + vlan->found = 1; + memcpy(vlan->ifname, RTA_DATA(ifla[IFLA_IFNAME]), IFNAMSIZ); + } + return 0; +} + +int rtnl_find_vlan(int ifindex, int vid, char *ifname) +{ + int s; + int rc; + struct vlan_identifier vlan = { + .ifindex = ifindex, + .vid = vid, + .found = 0, + }; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (s < 0) + return s; + rc = send_getlink_dump(s); + if (rc < 0) + goto out; + rc = rtnl_recv(s, rtnl_find_vlan_handler, &vlan); + if (rc < 0) + goto out; + if (vlan.found) { + memcpy(ifname, vlan.ifname, IFNAMSIZ); + rc = 0; + } else { + rc = -ENODEV; + } +out: + close(s); + return rc; +} diff --git a/lib/sa_log.c b/lib/sa_log.c new file mode 100644 index 0000000..91c256f --- /dev/null +++ b/lib/sa_log.c @@ -0,0 +1,256 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include "fcoemon_utils.h" +#include "net_types.h" +#include "fc_types.h" + +int use_syslog; +static int debug; + +/* + * Size of on-stack line buffers. + * These shouldn't be to large for a kernel stack frame. + */ +#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ + +void enable_syslog(int enable) +{ + use_syslog = enable; +} + +void enable_debug_log(int enable) +{ + debug = enable; +} + +/* + * log with a variable argument list. + */ +static void +sa_log_va(const char *func, const char *format, va_list arg) +{ + size_t len; + size_t flen; + int add_newline; + char sa_buf[SA_LOG_BUF_LEN]; + char *bp; + + /* + * If the caller didn't provide a newline at the end, we will. + */ + len = strlen(format); + add_newline = 0; + if (!len || format[len - 1] != '\n') + add_newline = 1; + bp = sa_buf; + len = sizeof(sa_buf); + if (func) { + flen = snprintf(bp, len, "%s: ", func); + len -= flen; + bp += flen; + } + flen = vsnprintf(bp, len, format, arg); + if (add_newline && flen < len) { + bp += flen; + *bp++ = '\n'; + *bp = '\0'; + } + sa_log_output(sa_buf); +} + +/* + * log + */ +void +sa_log(const char *format, ...) +{ + va_list arg; + + va_start(arg, format); + sa_log_va(NULL, format, arg); + va_end(arg); +} + +/* + * debug log, controlled by static debug flag + */ +void +sa_log_debug(const char *format, ...) +{ + va_list arg; + + if (!debug) + return; + + va_start(arg, format); + sa_log_va(NULL, format, arg); + va_end(arg); +} + +/* + * log with error number. + */ +void +sa_log_err(int error, const char *func, const char *format, ...) +{ + va_list arg; + char buf[SA_LOG_BUF_LEN]; + + if (func) + sa_log("%s: error %d %s", func, error, + strerror_r(error, buf, sizeof(buf))); + else + sa_log("error %d %s", error, + strerror_r(error, buf, sizeof(buf))); + va_start(arg, format); + sa_log_va(func, format, arg); + va_end(arg); +} + +/* + * Size of on-stack line buffers. + * These shouldn't be to large for a kernel stack frame. + */ +#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ + +/* + * Assert failures. + */ +void +assert_failed(const char *format, ...) +{ + va_list arg; + char buf[SA_LOG_BUF_LEN]; + + va_start(arg, format); + vsnprintf(buf, sizeof(buf), format, arg); + va_end(arg); + sa_log_abort(buf); +} + +/* + * Log options. + * These may be set directly by callers. + */ +u_int sa_log_flags; /* timestamp and other option flags */ +int sa_log_time_delta_min = 1; /* minimum diff to print in millisec */ +char *sa_log_prefix; /* string to print before any message */ + +void +sa_log_set_option(u_int flags) +{ + sa_log_flags = flags; +} + +/* + * Put timestamp on front of each log line, as controlled by tunables above. + */ +static void +sa_log_timestamp(void) +{ + static struct timeval tlast; + char ctime_buf[30]; + struct timeval t; + struct timeval diff; + + gettimeofday(&t, NULL); + if (sa_log_flags & SA_LOGF_TIME) { + ctime_r(&t.tv_sec, ctime_buf); + ctime_buf[11 + 8] = '\0'; /* trim ctime after seconds */ + fprintf(stderr, "%s.%3.3ld ", + ctime_buf + 11, t.tv_usec / 1000); + } + if (sa_log_flags & SA_LOGF_DELTA) { + if (tlast.tv_sec == 0) + tlast = t; + timersub(&t, &tlast, &diff); + tlast = t; + if (diff.tv_sec != 0 || + diff.tv_usec >= sa_log_time_delta_min * 1000) + fprintf(stderr, "%4ld.%3.3ld ", + diff.tv_sec, diff.tv_usec / 1000); + else + fprintf(stderr, "%8s ", ""); + } + if (sa_log_prefix) + fprintf(stderr, "%s: ", sa_log_prefix); +} + +void +sa_log_output(const char *buf) +{ + if (use_syslog) { + syslog(LOG_INFO, "%s", buf); + return; + } + sa_log_timestamp(); + fprintf(stderr, "%s", buf); + fflush(stderr); +} + +void +sa_log_abort(const char *buf) +{ + sa_log_output(buf); + abort(); +} + +/* + * Make a printable NUL-terminated copy of the string. + * The source buffer might not be NUL-terminated. + */ +char * +sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len) +{ + char *dp = dest; + const char *sp = src; + + while (len-- > 1 && src_len-- > 0 && *sp != '\0') { + *dp++ = isprint(*sp) ? *sp : (isspace(*sp) ? ' ' : '.'); + sp++; + } + *dp = '\0'; + + /* + * Take off trailing blanks. + */ + while (--dp >= dest && isspace(*dp)) + *dp = '\0'; + return dest; +} + +/** sa_enum_decode(buf, len, tp, val) + * + * @param buf buffer for result (may be used or not). + * @param len size of buffer (at least 32 bytes recommended). + * @param tp pointer to table of names and values, struct sa_nameval. + * @param val value to be decoded into a name. + * @returns pointer to name string. Unknown values are put into buffer in hex. + */ +const char * +sa_enum_decode(char *buf, size_t len, const struct sa_nameval *tp, u_int val) +{ + for (; tp->nv_name != NULL; tp++) { + if (tp->nv_val == val) + return tp->nv_name; + } + snprintf(buf, len, "Unknown (code 0x%X)", val); + return buf; +} diff --git a/lib/sa_other.c b/lib/sa_other.c new file mode 100644 index 0000000..af53a54 --- /dev/null +++ b/lib/sa_other.c @@ -0,0 +1,69 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include "fcoemon_utils.h" +#include "net_types.h" +#include "fc_types.h" + +/* + * Convert 48-bit IEEE MAC address to 64-bit FC WWN. + */ +fc_wwn_t +fc_wwn_from_mac(u_int64_t mac, u_int scheme, u_int port) +{ + fc_wwn_t wwn; + + ASSERT(mac < (1ULL << 48)); + wwn = mac | ((fc_wwn_t) scheme << 60); + switch (scheme) { + case 1: + ASSERT(port == 0); + break; + case 2: + ASSERT(port < 0xfff); + wwn |= (fc_wwn_t) port << 48; + break; + default: + ASSERT_NOTREACHED; + break; + } + return wwn; +} + +/* assumes input is pointer to two hex digits */ +/* returns -1 on error */ +int +hex2int(char *b) +{ + int i; + int n = 0; + int m; + + for (i = 0, m = 1; i < 2; i++, m--) { + if (isxdigit(*(b+i))) { + if (*(b+i) <= '9') + n |= (*(b+i) & 0x0f) << (4*m); + else + n |= ((*(b+i) & 0x0f) + 9) << (4*m); + } else + return -1; + } + return n; +} + diff --git a/lib/sa_select.c b/lib/sa_select.c new file mode 100644 index 0000000..1b18a6e --- /dev/null +++ b/lib/sa_select.c @@ -0,0 +1,213 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include "fcoemon_utils.h" +#include "net_types.h" +#include "fc_types.h" + +#define NFC_NFDS 64 + +/* +* Static module state. +*/ +static struct sa_sel_state { + fd_set ts_rx_fds; + fd_set ts_tx_fds; + fd_set ts_ex_fds; + int ts_max_fd; + u_char ts_exit; + struct sa_sel_fd { + void (*ts_rx_handler)(void *); + void (*ts_tx_handler)(void *); + void (*ts_ex_handler)(void *); + void *ts_handler_arg; + } ts_fd[NFC_NFDS]; + void (*ts_callback)(void); +} sa_sel_state; + +int sa_select_loop(void) +{ + struct sa_sel_state *ss = &sa_sel_state; + struct sa_sel_fd *fp; + fd_set rx_fds; + fd_set tx_fds; + fd_set ex_fds; + struct timeval tval; + struct timeval *tvp; + int rv, i; + + ss->ts_exit = 0; + while (ss->ts_exit == 0) { + sa_timer_check(&tval); + if (ss->ts_exit) + break; + if (tval.tv_sec == 0 && tval.tv_usec == 0) + tvp = NULL; + else + tvp = &tval; + rx_fds = ss->ts_rx_fds; + tx_fds = ss->ts_tx_fds; + ex_fds = ss->ts_ex_fds; + rv = select(ss->ts_max_fd + 1, &rx_fds, &tx_fds, &ex_fds, tvp); + if (rv == -1) { + if (errno == EINTR) + continue; + return errno; + } + + fp = ss->ts_fd; + for (i = 0; rv > 0 && i <= sa_sel_state.ts_max_fd; i++, fp++) { + if (FD_ISSET(i, &rx_fds)) { + if (fp->ts_rx_handler != NULL) + (*fp->ts_rx_handler) + (fp->ts_handler_arg); + else + ASSERT(!FD_ISSET(i, &ss->ts_rx_fds)); + --rv; + } + if (FD_ISSET(i, &tx_fds)) { + if (fp->ts_tx_handler != NULL) + (*fp->ts_tx_handler) + (fp->ts_handler_arg); + else + ASSERT(!FD_ISSET(i, &ss->ts_tx_fds)); + --rv; + } + if (FD_ISSET(i, &ex_fds)) { + if (fp->ts_ex_handler != NULL) + (*fp->ts_ex_handler) + (fp->ts_handler_arg); + else + ASSERT(!FD_ISSET(i, &ss->ts_ex_fds)); + --rv; + } + } + if (ss->ts_callback != NULL) + (*ss->ts_callback)(); + } + return 0; +} + +void +sa_select_add_fd(int fd, + void (*rx_handler)(void *), + void (*tx_handler)(void *), + void (*ex_handler)(void *), + void *arg) +{ + struct sa_sel_state *ss = &sa_sel_state; + struct sa_sel_fd *fp; + + ASSERT_NOTIMPL(fd < NFC_NFDS); + ASSERT(rx_handler != NULL || tx_handler != NULL || ex_handler != NULL); + if (ss->ts_max_fd < fd) + ss->ts_max_fd = fd; + fp = &ss->ts_fd[fd]; + fp->ts_handler_arg = arg; + if (rx_handler != NULL) { + fp->ts_rx_handler = rx_handler; + FD_SET(fd, &ss->ts_rx_fds); + } + if (tx_handler != NULL) { + fp->ts_tx_handler = tx_handler; + FD_SET(fd, &ss->ts_tx_fds); + } + if (ex_handler != NULL) { + fp->ts_ex_handler = ex_handler; + FD_SET(fd, &ss->ts_ex_fds); + } +} + +void +sa_select_set_rx(int fd, void (*handler)(void *)) +{ + struct sa_sel_state *ss = &sa_sel_state; + + ASSERT(fd <= ss->ts_max_fd); + ss->ts_fd[fd].ts_rx_handler = handler; + if (handler != NULL) + FD_SET(fd, &ss->ts_rx_fds); + else + FD_CLR(fd, &ss->ts_rx_fds); +} + +void +sa_select_set_tx(int fd, void (*handler)(void *)) +{ + struct sa_sel_state *ss = &sa_sel_state; + + ASSERT(fd <= ss->ts_max_fd); + ss->ts_fd[fd].ts_tx_handler = handler; + if (handler != NULL) + FD_SET(fd, &ss->ts_tx_fds); + else + FD_CLR(fd, &ss->ts_tx_fds); +} + +void +sa_select_set_ex(int fd, void (*handler)(void *)) +{ + struct sa_sel_state *ss = &sa_sel_state; + + ASSERT(fd <= ss->ts_max_fd); + ss->ts_fd[fd].ts_ex_handler = handler; + if (handler != NULL) + FD_SET(fd, &ss->ts_ex_fds); + else + FD_CLR(fd, &ss->ts_ex_fds); +} + +void +sa_select_rem_fd(int fd) +{ + struct sa_sel_state *ss = &sa_sel_state; + struct sa_sel_fd *fp; + + ASSERT_NOTIMPL(fd < NFC_NFDS); + FD_CLR(fd, &ss->ts_rx_fds); + FD_CLR(fd, &ss->ts_tx_fds); + FD_CLR(fd, &ss->ts_ex_fds); + fp = &ss->ts_fd[fd]; + fp->ts_rx_handler = NULL; + fp->ts_tx_handler = NULL; + fp->ts_ex_handler = NULL; + fp->ts_handler_arg = NULL; +} + +/* + * Set callback for every time through the select loop. + */ +void +sa_select_set_callback(void (*cb)(void)) +{ + sa_sel_state.ts_callback = cb; +} + +/* + * Cause select loop to exit. + * This is invoked from a handler which wants the select loop to return + * after the handler is finished. For example, during receipt of a network + * packet, the program may decide to clean up and exit, but in order to do + * this cleanly, all lower-level protocol handlers should return first. + */ +void +sa_select_exit(void) +{ + sa_sel_state.ts_exit = 1; +} diff --git a/lib/sa_timer.c b/lib/sa_timer.c new file mode 100644 index 0000000..aee213f --- /dev/null +++ b/lib/sa_timer.c @@ -0,0 +1,226 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include "fcoemon_utils.h" +#include "net_types.h" +#include "fc_types.h" + +#define SA_TIMER_HZ (1000 * 1000 * 1000ULL) /* nanoseconds per second */ +#define SA_TIMER_FUZZ (500 * 1000ULL) /* 500 microseconds is close enough */ + +static struct sa_timer *sa_timer_head; /* queue of scheduled events */ +static u_int64_t sa_timer_nsec; /* nanoseconds since start */ + +/* + * Initialize a timer structure. Set handler. + */ +void +sa_timer_init(struct sa_timer *tm, void (*handler)(void *), void *arg) +{ + ASSERT(handler != NULL); + memset(tm, 0, sizeof(*tm)); + tm->tm_handler = handler; + tm->tm_handler_arg = arg; +} + +/* + * Allocate a timer structure. Set handler. + */ +struct sa_timer * +sa_timer_alloc(void (*handler)(void *arg), void *arg) +{ + struct sa_timer *tm; + + tm = malloc(sizeof(*tm)); + if (tm) + sa_timer_init(tm, handler, arg); + return tm; +} + +u_int64_t +sa_timer_get(void) +{ + u_int64_t nsec; +#ifndef _POSIX_TIMERS + struct timeval tv; + + gettimeofday(&tv, NULL); /* XXX want monotonic time, not TOD */ + nsec = tv.tv_sec * SA_TIMER_HZ + tv.tv_usec * 1000; +#else /* _POSIX_TIMERS */ + struct timespec ts; + int rc; + + rc = clock_gettime(CLOCK_MONOTONIC, &ts); + ASSERT_NOTIMPL(rc == 0); + nsec = ts.tv_sec * SA_TIMER_HZ + ts.tv_nsec; +#endif /* _POSIX_TIMERS */ + +#if 0 /* XXX */ + ASSERT(nsec >= sa_timer_nsec); /* really must be monotonic */ +#else + if (nsec < sa_timer_nsec) + sa_log("sa_timer_get: negative time lapse " + "old %qud new %qud diff %qd nsec\n", + (long long unsigned int) sa_timer_nsec, + (long long unsigned int) nsec, + (long long int) (nsec - sa_timer_nsec)); +#endif + sa_timer_nsec = nsec; + return nsec; +} + +/* + * Get monotonic time since some arbitrary time in the past. + * If _POSIX_MONOTONIC_CLOCK isn't available, we'll use time of day. + */ +u_int +sa_timer_get_secs(void) +{ + u_int sec; + +#ifndef _POSIX_TIMERS + struct timeval tv; + + gettimeofday(&tv, NULL); /* XXX want monotonic time, not TOD */ + sec = tv.tv_sec; +#else /* _POSIX_TIMERS */ + struct timespec ts; + int rc; + + rc = clock_gettime(CLOCK_MONOTONIC, &ts); + ASSERT_NOTIMPL(rc == 0); + sec = ts.tv_sec; +#endif /* _POSIX_TIMERS */ + return sec; +} + +/* + * Set timer to fire. Delta is in microseconds from now. + */ +void +sa_timer_set(struct sa_timer *tm, u_long delta_usec) +{ + struct sa_timer *cur; + struct sa_timer **prev; + + ASSERT(delta_usec != 0); + ASSERT(tm->tm_handler != NULL); + sa_timer_cancel(tm); + ASSERT(sa_timer_active(tm) == 0); + tm->tm_nsec = + sa_timer_get() + delta_usec * SA_TIMER_HZ / SA_TIMER_UNITS; + ASSERT(tm->tm_nsec != 0); + + /* + * Insert timer into sorted linked list. + * Find insertion point, before cur. + */ + for (prev = &sa_timer_head; + (cur = *prev) != NULL && cur->tm_nsec <= tm->tm_nsec; + prev = &cur->tm_next) + ; + *prev = tm; + tm->tm_next = cur; +} + +/* + * Cancel timer if it is active. + */ +void +sa_timer_cancel(struct sa_timer *tm) +{ + struct sa_timer *cur; + struct sa_timer **prev; + + if (sa_timer_active(tm)) { + for (prev = &sa_timer_head; (cur = *prev) != NULL; + prev = &cur->tm_next) + if (cur == tm) { + tm->tm_nsec = 0; + *prev = tm->tm_next; + break; + } + ASSERT(cur == tm); + } +} + +/* + * Free (and cancel) timer. + */ +void +sa_timer_free(struct sa_timer *tm) +{ + if (sa_timer_active(tm)) + sa_timer_cancel(tm); + free(tm); +} + +/* + * Handle timer checks. Called from select loop or other periodic function. + * + * The struct timeval is set before returning to the maximum amount of time + * that should elapse before the next call. + * + * Returns 1 if any timer functions were called, 0 otherwise. + */ +int +sa_timer_check(struct timeval *tv) +{ + u_int64_t now = 0; + u_int64_t next_due = 0; + struct sa_timer *tm; + int ret = 0; + + /* + * Remember, the list may change during the handler. + */ + for (;;) { + now = sa_timer_get(); + tm = sa_timer_head; + if (tm == NULL) { + next_due = now; + break; + } + + next_due = tm->tm_nsec; + if (next_due > now + SA_TIMER_FUZZ) + break; + + /* + * Remove this element from the list. + */ + sa_timer_head = tm->tm_next; + tm->tm_next = NULL; + + /* + * Mark cancelled and call handler. + */ + tm->tm_nsec = 0; + ASSERT(tm->tm_handler != NULL); + (*tm->tm_handler)(tm->tm_handler_arg); + ret = 1; + } + + ASSERT(next_due >= now); + next_due -= now; + tv->tv_sec = (time_t) (next_due / SA_TIMER_HZ); + tv->tv_usec = (long) (next_due % SA_TIMER_HZ) / 1000; + + return ret; +} diff --git a/log.c b/log.c deleted file mode 100644 index a99ab93..0000000 --- a/log.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright(c) 2009 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Maintained at www.Open-FCoE.org - */ - -#include <stdio.h> -#include <stdarg.h> -#include <syslog.h> -#include <string.h> - -static int log_syslog; -static int log_debug_level; -static char log_prefix[256]; - -void log_start(char *program, int daemon, int level) -{ - log_syslog = daemon; - log_debug_level = level; - strncpy(log_prefix, program, 256); - log_prefix[255] = '\0'; - - if (log_syslog) - openlog(log_prefix, 0, LOG_DAEMON); -} - -void log_stop() -{ - if (log_syslog) - closelog(); -} - -void do_log(int priority, const char *fmt, va_list ap) -{ - if (log_syslog) - vsyslog(priority, fmt, ap); - else { - printf("%s: ", log_prefix); - vprintf(fmt, ap); - printf("\n"); - } -} - -void log_debug(int level, char *fmt, ...) -{ - va_list ap; - if (log_debug_level >= level) { - va_start(ap, fmt); - do_log(LOG_DEBUG, fmt, ap); - va_end(ap); - } -} - -void log_warn(char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - do_log(LOG_WARNING, fmt, ap); - va_end(ap); -} - -void log_err(char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - do_log(LOG_ERR, fmt, ap); - va_end(ap); -} - -void _log_errno(const char *func, char *call, int errnum) -{ - log_err("%s %s: %s", func, call, strerror(errnum)); -} -
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor