This patch introduces the 'fipfcf' application. It will send FIP encapsulated FCF discovery solicitations and will print a list of any found devices.
This application is intended to be used with the fabric selection feature. The user can use 'fipfcf' to discover FCFs or fabrics and then can use that information to set the SELECTION variable in the cfg-ethX files. Signed-off-by: Robert Love <robert.w.l...@intel.com> --- Makefile.am | 6 fipfcf.c | 778 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/fip.h | 44 +++ lib/fip.c | 71 +++++ 4 files changed, 890 insertions(+), 9 deletions(-) create mode 100644 fipfcf.c diff --git a/Makefile.am b/Makefile.am index 1fcd8b7..4a324d3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ ## target programs, to be built and installed in $(prefix)/sbin -sbin_PROGRAMS = fcoeadm fcoemon fcping fipvlan fcnsq fcrls +sbin_PROGRAMS = fcoeadm fcoemon fcping fipvlan fipfcf fcnsq fcrls ## all targets should look for headers in the include directory AM_CPPFLAGS = -I${srcdir}/include -I${builddir}/include @@ -29,6 +29,10 @@ fcping_LDFLAGS = $(AM_LDFLAGS) $(HBAAPI_LIBS) -lrt fipvlan_SOURCES = fipvlan.c fipvlan_LDADD = lib/libutil.a +## rules for building fipfcf +fipfcf_SOURCES = fipfcf.c +fipfcf_LDADD = lib/libutil.a + AUTOMAKE_OPTIONS=subdir-objects noinst_LIBRARIES = lib/libutil.a libopenfcoe.a lib_libutil_a_SOURCES = lib/fcoe_utils.c lib/sa_log.c lib/sa_select.c \ diff --git a/fipfcf.c b/fipfcf.c new file mode 100644 index 0000000..c2d416b --- /dev/null +++ b/fipfcf.c @@ -0,0 +1,778 @@ +/* + * Copyright(c) 2012 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 <arpa/inet.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/if_packet.h> +#include <linux/capability.h> +#include <sys/syscall.h> + +#include <sys/stat.h> +#include <fcntl.h> + +#include "fcoe_utils_version.h" +#include "fip.h" +#include "fcoemon_utils.h" +#include "fcoe_utils.h" +#include "rtnetlink.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 MAX_FCF_RETRIES 50 + +/* global configuration */ + +struct { + char **namev; + int namec; + bool automode; + bool debug; + int link_retry; +} config = { + .namev = NULL, + .namec = 0, + .debug = false, + .link_retry = 20, +}; + +char *exe; + +static struct pollfd *pfd = NULL; +static int pfd_len = 0; + +void pfd_add(int fd) +{ + struct pollfd *npfd; + int i; + + for (i = 0; i < pfd_len; i++) + if (pfd[i].fd == fd) + return; + + npfd = realloc(pfd, (pfd_len + 1) * sizeof(struct pollfd)); + if (!npfd) { + perror("realloc fail"); + return; + } + pfd = npfd; + pfd[pfd_len].fd = fd; + pfd[pfd_len].events = POLLIN; + pfd_len++; +} + +void pfd_remove(int fd) +{ + struct pollfd *npfd; + int i; + + for (i = 0; i < pfd_len; i++) { + if (pfd[i].fd == fd) + break; + } + if (i == pfd_len) + return; + memmove(&pfd[i], &pfd[i+1], (--pfd_len - i) * sizeof(struct pollfd)); + npfd = realloc(pfd, pfd_len * sizeof(struct pollfd)); + if (npfd) + pfd = npfd; +} + +TAILQ_HEAD(iff_list_head, iff); + +struct iff { + int ps; /* packet socket file descriptor */ + int ifindex; + int iflink; + char ifname[IFNAMSIZ]; + unsigned char mac_addr[ETHER_ADDR_LEN]; + bool running; + bool is_vlan; + short int vid; + bool linkup_sent; + bool req_sent; + bool resp_recv; + bool fip_ready; + TAILQ_ENTRY(iff) list_node; + struct iff_list_head vlans; +}; + +struct iff_list_head interfaces = TAILQ_HEAD_INITIALIZER(interfaces); + +TAILQ_HEAD(fcf_list_head, fcf); + +struct fcf { + int ifindex; + uint16_t vlan; + unsigned char mac_addr[ETHER_ADDR_LEN]; + uint8_t priority; + uint64_t wwn; + uint64_t fabric_name; + uint32_t fka; + + TAILQ_ENTRY(fcf) list_node; +}; + +struct fcf_list_head fcfs = TAILQ_HEAD_INITIALIZER(fcfs); + +struct fcf *lookup_fcf(int ifindex, unsigned char *mac) +{ + struct fcf *fcf; + + TAILQ_FOREACH(fcf, &fcfs, list_node) + if ((ifindex == fcf->ifindex) && + (memcmp(mac, fcf->mac_addr, ETHER_ADDR_LEN) == 0)) + return fcf; + return NULL; +} + +struct iff *lookup_iff(int ifindex, char *ifname) +{ + struct iff *iff; + struct iff *vlan; + + if (!ifindex && !ifname) + return NULL; + + TAILQ_FOREACH(iff, &interfaces, list_node) { + if ((!ifindex || ifindex == iff->ifindex) && + (!ifname || strcmp(ifname, iff->ifname) == 0)) + return iff; + + TAILQ_FOREACH(vlan, &iff->vlans, list_node) + if ((!ifindex || ifindex == vlan->ifindex) && + (!ifname || strcmp(ifname, vlan->ifname) == 0)) + return vlan; + } + return NULL; +} + +struct iff *lookup_vlan(int ifindex, short int vid) +{ + 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; +} + +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 { + struct fip_tlv_mac_addr *mac; + struct fip_tlv_priority *prio; + struct fip_tlv_name_id *name; + struct fip_tlv_fabric_name *fab; + struct fip_tlv_fka *fka; +}; + +#define SET_BIT(b, n) ((b) |= (1 << (n))) + +#define TLV_LEN_CHECK(t, l) ({ \ + int _tlc = ((t)->tlv_len != (l)) ? 1 : 0; \ + if (_tlc) \ + FIP_LOG("bad length for TLV of type %d, tlv_len is %d", (t)->tlv_type, (t)->tlv_len); \ + _tlc; \ + }) + +/** + * fip_parse_tlvs - parse type/length/value encoded FIP descriptors + * @ptr: pointer to beginning of FIP TLV payload, the first descriptor + * @len: total length of all TLVs, in double words + * @tlv_ptrs: pointers to type specific structures to fill out + */ +unsigned int fip_parse_tlvs(void *ptr, int len, struct fip_tlv_ptrs *tlv_ptrs) +{ + struct fip_tlv_hdr *tlv = ptr; + unsigned int bitmap = 0; + + while (len > 0) { + switch (tlv->tlv_type) { + case FIP_TLV_PRIORITY: + if (TLV_LEN_CHECK(tlv, 1)) + break; + SET_BIT(bitmap, FIP_TLV_PRIORITY); + tlv_ptrs->prio = (struct fip_tlv_priority *) tlv; + break; + case FIP_TLV_MAC_ADDR: + if (TLV_LEN_CHECK(tlv, 2)) + break; + SET_BIT(bitmap, FIP_TLV_MAC_ADDR); + tlv_ptrs->mac = (struct fip_tlv_mac_addr *) tlv; + break; + case FIP_TLV_FABRIC_NAME: + if (TLV_LEN_CHECK(tlv, 4)) + break; + SET_BIT(bitmap, FIP_TLV_FABRIC_NAME); + tlv_ptrs->fab = (struct fip_tlv_fabric_name *) tlv; + break; + case FIP_TLV_NAME_IDENTIFIER: + if (TLV_LEN_CHECK(tlv, 3)) + break; + SET_BIT(bitmap, FIP_TLV_NAME_IDENTIFIER); + tlv_ptrs->name = (struct fip_tlv_name_id *) tlv; + break; + case FIP_TLV_FKA: + if (TLV_LEN_CHECK(tlv, 2)) + break; + SET_BIT(bitmap, FIP_TLV_FKA); + tlv_ptrs->fka = (struct fip_tlv_fka *) tlv; + break; + default: + /* unexpected or unrecognized descriptor */ + FIP_LOG("unrecognized TLV type %d", tlv->tlv_type); + break; + } + len -= tlv->tlv_len; + tlv = ((void *) tlv) + (tlv->tlv_len << 2); + }; + return bitmap; +} + +/** + * fip_recv_disc_adv - parse a FIP FCF Discovery Advertisement + * @fh: FIP header, the beginning of the received FIP frame + * @ifindex: index of interface this was received on + */ +int fip_recv_disc_adv(struct fiphdr *fh, int ifindex) +{ + struct fip_tlv_ptrs tlvs; + struct fcf *fcf; + struct iff *iff; + unsigned int bitmap, required_tlvs; + int len; + + FIP_LOG_DBG("received FIP discovery notification"); + + len = ntohs(fh->fip_desc_len); + + required_tlvs = (1 << FIP_TLV_PRIORITY) | + (1 << FIP_TLV_MAC_ADDR) | + (1 << FIP_TLV_NAME_IDENTIFIER) | + (1 << FIP_TLV_FABRIC_NAME) | + (1 << FIP_TLV_FKA); + + bitmap = fip_parse_tlvs((fh + 1), len, &tlvs); + if ((bitmap & required_tlvs) != required_tlvs) + return -1; + + iff = lookup_iff(ifindex, NULL); + if (iff) + iff->resp_recv = true; + + if (lookup_fcf(ifindex, tlvs.mac->mac_addr)) + return -1; + + fcf = malloc(sizeof(*fcf)); + if (!fcf) { + FIP_LOG_ERRNO("malloc failed"); + return -1; + } + memset(fcf, 0, sizeof(*fcf)); + fcf->ifindex = ifindex; + fcf->priority = tlvs.prio->priority; + memcpy(fcf->mac_addr, tlvs.mac->mac_addr, ETHER_ADDR_LEN); + fcf->wwn = be64toh(tlvs.name->wwn); + fcf->fabric_name = be64toh(tlvs.fab->wwn); + fcf->fka = be32toh(tlvs.fka->fka_peroid); + TAILQ_INSERT_TAIL(&fcfs, fcf, list_node); + + return 0; +} + +int fip_subcode_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg) +{ + int rc = -1; + + /* We only care about DISC Notifications */ + if (ntohs(fh->fip_proto) != FIP_PROTO_DISC) { + FIP_LOG_DBG("ignoring FIP packet, protocol %d", + ntohs(fh->fip_proto)); + return -1; + } + + switch (fh->fip_subcode) { + case FIP_DISC_ADV: + rc = fip_recv_disc_adv(fh, sa->sll_ifindex); + break; + default: + FIP_LOG_DBG("ignored FIP Discovery packet with subcode %d", + fh->fip_subcode); + break; + } + return rc; +} + +/** + * rtnl_recv_newlink - parse response to RTM_GETLINK, or an RTM_NEWLINK event + * @nh: netlink message header, beginning of received netlink frame + */ +void rtnl_recv_newlink(struct nlmsghdr *nh) +{ + 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; + bool running; + + FIP_LOG_DBG("RTM_NEWLINK: ifindex %d, type %d, flags %x", + ifm->ifi_index, ifm->ifi_type, ifm->ifi_flags); + + /* We only deal with Ethernet interfaces */ + if (ifm->ifi_type != ARPHRD_ETHER) + return; + + /* not on bond master, but rather allow FIP on the slaves below */ + if (ifm->ifi_flags & IFF_MASTER) + return; + + running = !!(ifm->ifi_flags & (IFF_RUNNING | IFF_SLAVE)); + iff = lookup_iff(ifm->ifi_index, NULL); + if (iff) { + /* already tracking, update operstate and return */ + iff->running = running; + if (iff->running) + pfd_add(iff->ps); + else + pfd_remove(iff->ps); + return; + } + + iff = malloc(sizeof(*iff)); + if (!iff) { + FIP_LOG_ERRNO("malloc failed"); + return; + } + memset(iff, 0, sizeof(*iff)); + TAILQ_INIT(&iff->vlans); + + parse_ifinfo(ifla, nh); + + iff->ifindex = ifm->ifi_index; + iff->running = running; + iff->fip_ready = false; + 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]); + /* Track VLAN devices separately */ + 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) + TAILQ_INSERT_TAIL(&real_dev->vlans, + iff, list_node); + else + free(iff); + return; + } + /* ignore bonding interfaces */ + if (linkinfo[IFLA_INFO_KIND] && + !strcmp(RTA_DATA(linkinfo[IFLA_INFO_KIND]), "bond")) { + free(iff); + return; + } + } + TAILQ_INSERT_TAIL(&interfaces, iff, list_node); +} + +/* command line arguments */ + +#define GETOPT_STR "adhv" + +static const struct option long_options[] = { + { "all", no_argument, NULL, 'a' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } +}; + +static void help(int status) +{ + printf( + "Usage: %s [ options ] [ network interfaces ]\n" + "Options:\n" + " -a, --all Discover on all interfaces\n" + " -d, --debug Enable debugging output\n" + " -h, --help Display this help and exit\n" + " -v, --version Display version information and exit\n", + exe); + + exit(status); +} + +void parse_cmdline(int argc, char **argv) +{ + char c; + + while (1) { + c = getopt_long(argc, argv, GETOPT_STR, long_options, NULL); + if (c < 0) + break; + switch (c) { + case 'a': + config.automode = true; + break; + case 'd': + config.debug = true; + break; + case 'h': + help(0); + break; + case 'v': + printf("%s version %s\n", exe, FCOE_UTILS_VERSION); + exit(0); + break; + default: + fprintf(stderr, "Try '%s --help' " + "for more information\n", exe); + exit(1); + } + } + + if ((optind == argc) && (!config.automode)) + help(1); + + config.namev = &argv[optind]; + config.namec = argc - optind; +} + +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 print_results() +{ + struct iff *iff; + struct fcf *fcf; + + if (TAILQ_EMPTY(&fcfs)) { + printf("No Fibre Channel Forwarders Found\n"); + return ENODEV; + } + + printf("Fibre Channel Forwarders Discovered\n"); + printf("%-17.17s| %-4.44s| %-18s| %-18s| %-4s\n", + "FCF MAC", "prio", "name", "fabric name", "FKA (s)"); + printf("-----------------------------------------------------------------------------\n"); + TAILQ_FOREACH(fcf, &fcfs, list_node) { + iff = lookup_iff(fcf->ifindex, NULL); + printf("%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x| %-4u| 0x%-16.16lx| 0x%-16.16lx| %-4u\n", + fcf->mac_addr[0], fcf->mac_addr[1], fcf->mac_addr[2], + fcf->mac_addr[3], fcf->mac_addr[4], fcf->mac_addr[5], + fcf->priority, fcf->wwn, fcf->fabric_name, fcf->fka); + } + printf("\n"); + + return 0; +} + +void recv_loop(int timeout) +{ + int i; + int rc; + + while (1) { + rc = poll(pfd, pfd_len, timeout); + FIP_LOG_DBG("return from poll %d", rc); + if (rc == 0) /* timeout */ + break; + if (rc == -1) { + FIP_LOG_ERRNO("poll error"); + break; + } + /* pfd[0] must be the netlink socket */ + if (pfd[0].revents & POLLIN) + rtnl_recv(pfd[0].fd, rtnl_listener_handler, NULL); + /* everything else should be FIP packet sockets */ + for (i = 1; i < pfd_len; i++) { + if (pfd[i].revents & POLLIN) + fip_recv(pfd[i].fd, fip_subcode_handler, NULL); + } + } +} + +void find_interfaces(int ns) +{ + send_getlink_dump(ns); + rtnl_recv(ns, rtnl_listener_handler, NULL); +} + +int send_disc_solicitations(void) +{ + struct iff *iff; + int i; + int skipped = 0; + int origdev = 1; + + if (config.automode) { + TAILQ_FOREACH(iff, &interfaces, list_node) { + if (iff->resp_recv) + continue; + if (!iff->running) { + if (iff->linkup_sent) { + FIP_LOG_DBG("if %d not running, " + "waiting for link up", + iff->ifindex); + } else { + FIP_LOG_DBG("if %d not running, " + "starting", + iff->ifindex); + rtnl_set_iff_up(iff->ifindex, NULL); + iff->linkup_sent = true; + } + skipped++; + iff->req_sent = false; + continue; + } + if (iff->req_sent) + continue; + + if (!iff->fip_ready) { + iff->ps = fip_socket(iff->ifindex); + setsockopt(iff->ps, SOL_PACKET, PACKET_ORIGDEV, + &origdev, sizeof(origdev)); + pfd_add(iff->ps); + iff->fip_ready = true; + } + + fip_send_disc_sol(iff->ps, + iff->ifindex, + iff->mac_addr); + iff->req_sent = true; + } + } else { + for (i = 0; i < config.namec; i++) { + iff = lookup_iff(0, config.namev[i]); + if (!iff) { + skipped++; + continue; + } + if (iff->resp_recv) + continue; + if (!iff->running) { + if (iff->linkup_sent) { + FIP_LOG_DBG("if %d not running, " + "waiting for link up", + iff->ifindex); + } else { + FIP_LOG_DBG("if %d not running, " + "starting", + iff->ifindex); + rtnl_set_iff_up(iff->ifindex, NULL); + iff->linkup_sent = true; + } + + skipped++; + iff->req_sent = false; + continue; + } + + if (!iff->fip_ready) { + iff->ps = fip_socket(iff->ifindex); + setsockopt(iff->ps, SOL_PACKET, PACKET_ORIGDEV, + &origdev, sizeof(origdev)); + pfd_add(iff->ps); + iff->fip_ready = true; + } + + fip_send_disc_sol(iff->ps, + iff->ifindex, + iff->mac_addr); + iff->req_sent = true; + } + } + return skipped; +} + +void do_fcf_discovery(void) +{ + struct iff *iff; + int retry_count = 0; + int skip_retry_count = 0; + int skipped = 0; +retry: + skipped += send_disc_solicitations(); + if (skipped && skip_retry_count++ < config.link_retry) { + FIP_LOG_DBG("waiting for IFF_RUNNING [%d]\n", skip_retry_count); + recv_loop(500); + skipped = 0; + retry_count = 0; + goto retry; + } + recv_loop(200); + TAILQ_FOREACH(iff, &interfaces, list_node) + /* if we did not receive a response, retry */ + if (iff->req_sent && !iff->resp_recv && + retry_count++ < MAX_FCF_RETRIES) { + FIP_LOG_DBG("FCF Discovery RETRY [%d]", retry_count); + goto retry; + } +} + +void cleanup_interfaces(void) +{ + struct iff *iff; + int i; + int skipped = 0; + + if (config.automode) { + TAILQ_FOREACH(iff, &interfaces, list_node) { + if (iff->linkup_sent && + (!iff->running || !iff->req_sent || !iff->resp_recv)) { + FIP_LOG_DBG("shutdown if %d", + iff->ifindex); + rtnl_set_iff_down(iff->ifindex, NULL); + iff->linkup_sent = false; + } + } + } else { + for (i = 0; i < config.namec; i++) { + iff = lookup_iff(0, config.namev[i]); + if (!iff) { + skipped++; + continue; + } + if (iff->linkup_sent && + (!iff->running || !iff->req_sent || !iff->resp_recv)) { + FIP_LOG_DBG("shutdown if %d", + iff->ifindex); + rtnl_set_iff_down(iff->ifindex, NULL); + iff->linkup_sent = false; + } + } + } + +} +/* this is to not require headers from libcap */ +static inline int capget(cap_user_header_t hdrp, cap_user_data_t datap) +{ + return syscall(__NR_capget, hdrp, datap); +} + +int checkcaps() +{ + struct __user_cap_header_struct caphdr = { + .version = _LINUX_CAPABILITY_VERSION_3, + .pid = 0, + }; + struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3]; + + capget(&caphdr, caps); + return !(caps[CAP_TO_INDEX(CAP_NET_RAW)].effective & + CAP_TO_MASK(CAP_NET_RAW)); +} + +int main(int argc, char **argv) +{ + int ns; + int rc = 0; + int find_cnt = 0; + + exe = strrchr(argv[0], '/'); + if (exe) + exe++; + else + exe = argv[0]; + + parse_cmdline(argc, argv); + sa_log_prefix = exe; + sa_log_flags = 0; + enable_debug_log(config.debug); + + if (checkcaps()) { + FIP_LOG("must run as root or with the NET_RAW capability"); + exit(1); + } + + ns = rtnl_socket(); + if (ns < 0) { + rc = ns; + goto ns_err; + } + pfd_add(ns); + + find_interfaces(ns); + while ((TAILQ_EMPTY(&interfaces)) && ++find_cnt < 5) { + FIP_LOG_DBG("no interfaces found, trying again"); + find_interfaces(ns); + } + + if (TAILQ_EMPTY(&interfaces)) { + FIP_LOG_ERR(ENODEV, "no interfaces to perform discovery on"); + exit(1); + } + + do_fcf_discovery(); + + rc = print_results(); + + cleanup_interfaces(); + + close(ns); +ns_err: + exit(rc); +} + diff --git a/include/fip.h b/include/fip.h index 226301e..4b843c0 100644 --- a/include/fip.h +++ b/include/fip.h @@ -72,7 +72,7 @@ struct fiphdr { struct fip_tlv_hdr { uint8_t tlv_type; uint8_t tlv_len; /* length in quad-words of entire TLV */ -}; +} __attribute__((packed)); #define FIP_TLV_PRIORITY 1 #define FIP_TLV_MAC_ADDR 2 @@ -85,6 +85,8 @@ struct fip_tlv_hdr { #define FIP_TLV_LOGO 9 #define FIP_TLV_ELP 10 +#define FIP_TLV_FKA 12 + #define FIP_TLV_VLAN 14 #define DEFAULT_FIP_PRIORITY 128 @@ -94,39 +96,55 @@ struct fip_tlv_priority { struct fip_tlv_hdr hdr; unsigned char __resvd; uint8_t priority; -}; +} __attribute__((packed)); /* MAC Address Descriptor */ struct fip_tlv_mac_addr { struct fip_tlv_hdr hdr; unsigned char mac_addr[ETHER_ADDR_LEN]; -}; +} __attribute__((packed)); /* FC-MAP Descriptor */ struct fip_tlv_fc_map { struct fip_tlv_hdr hdr; unsigned char __resvd[3]; uint8_t map[3]; -}; +} __attribute__((packed)); /* Name Identifier Descriptor (also used for Fabric Name Descriptor) */ struct fip_tlv_name_id { struct fip_tlv_hdr hdr; unsigned char __resvd[2]; - unsigned char wwn[8]; -}; + uint64_t wwn; +} __attribute__((packed)); + +/* Fabric Name */ +struct fip_tlv_fabric_name { + struct fip_tlv_hdr hdr; + uint16_t vfid; + unsigned char __resvd[1]; + uint8_t map[3]; + uint64_t wwn; +} __attribute__((packed)); /* Max Receive Size Descriptor */ struct fip_tlv_max_recv_size { struct fip_tlv_hdr hdr; uint16_t mtu; -}; +} __attribute__((packed)); + +/* FIP Keep Alive (FKA) */ +struct fip_tlv_fka { + struct fip_tlv_hdr hdr; + unsigned char __resvd[2]; /* Ignore Dbit */ + uint32_t fka_peroid; +} __attribute__((packed)); /* VLAN */ struct fip_tlv_vlan { struct fip_tlv_hdr hdr; uint16_t vlan; /* only lower 12 bits matter */ -}; +} __attribute__((packed)); /* libutil / fip.c functionality */ @@ -152,4 +170,14 @@ int fip_recv(int s, fip_handler *fn, void *arg); */ ssize_t fip_send_vlan_request(int s, int ifindex, unsigned char *mac); +/** + * fip_send_disc_sol() - send a FIP FCF solicitation + * @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_disc_sol(int s, int ifindex, unsigned char *mac); + #endif /* FIP_H */ diff --git a/lib/fip.c b/lib/fip.c index cf730ee..f1438d1 100644 --- a/lib/fip.c +++ b/lib/fip.c @@ -190,6 +190,77 @@ int fip_socket(int ifindex) return s; } +/** + * fip_send_disc_sol() - send a FIP FCF solicitation + * @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_disc_sol(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_DISC), + .fip_subcode = FIP_DISC_SOL, + .fip_desc_len = htons(2), + .fip_flags = 0, + }; + struct { + struct fip_tlv_mac_addr mac; + struct fip_tlv_name_id name; + struct fip_tlv_max_recv_size size; + } tlvs = { + .mac = { + .hdr.tlv_type = FIP_TLV_MAC_ADDR, + .hdr.tlv_len = sizeof(struct fip_tlv_mac_addr) / 4, + .mac_addr = { 0x00, 0x1B, 0x21, 0x3C, 0x9E, 0x10 }, + }, + .name = { + .hdr.tlv_type = FIP_TLV_NAME_IDENTIFIER, + .hdr.tlv_len = sizeof(struct fip_tlv_name_id) / 4, + .wwn = 0x1000001B213C9E12, + }, + .size = { + .hdr.tlv_type = FIP_TLV_MAX_RECV_SIZE, + .hdr.tlv_len = sizeof(struct fip_tlv_max_recv_size) / 4, + .mtu = 2112, + }, + }; + struct ethhdr eh; + struct iovec iov[] = { + { .iov_base = &eh, .iov_len = sizeof(eh), }, + { .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; + + fip_ethhdr(ifindex, mac, &eh); + memcpy(tlvs.mac.mac_addr, eh.h_source, ETHER_ADDR_LEN); + FIP_LOG_DBG("Sending FIP Discovery Solicitation"); + rc = sendmsg(s, &msg, 0); + if (rc < 0) { + rc = -errno; + FIP_LOG_ERRNO("sendmsg error"); + } + return rc; +} /** * fip_send_vlan_request - send a FIP VLAN request _______________________________________________ devel mailing list devel@open-fcoe.org https://lists.open-fcoe.org/mailman/listinfo/devel