| /* |
| * Copyright (c) 2014 Intel Corporation. All rights reserved. |
| * |
| * This software is available to you under the OpenFabrics.org BSD license |
| * below: |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials |
| * provided with the distribution. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AWV |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <inttypes.h> |
| #include <net/if_arp.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <arpa/inet.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <errno.h> |
| #include <netlink/route/addr.h> |
| #include <netlink/route/link.h> |
| #include <netlink/socket.h> |
| |
| #include <infiniband/acm.h> |
| #include "acm_mad.h" |
| #include "acm_util.h" |
| |
| int acm_if_get_pkey(char *ifname, uint16_t *pkey) |
| { |
| char buf[128], *end; |
| FILE *f; |
| int ret; |
| |
| snprintf(buf, sizeof buf, "//sys//class//net//%s//pkey", ifname); |
| f = fopen(buf, "r"); |
| if (!f) { |
| acm_log(0, "failed to open %s\n", buf); |
| return -1; |
| } |
| |
| if (fgets(buf, sizeof buf, f)) { |
| *pkey = strtol(buf, &end, 16); |
| ret = 0; |
| } else { |
| acm_log(0, "failed to read pkey\n"); |
| ret = -1; |
| } |
| |
| fclose(f); |
| return ret; |
| } |
| |
| int acm_if_get_sgid(char *ifname, union ibv_gid *sgid) |
| { |
| char buf[128], *end; |
| FILE *f; |
| int i, p, ret; |
| |
| snprintf(buf, sizeof buf, "//sys//class//net//%s//address", ifname); |
| f = fopen(buf, "r"); |
| if (!f) { |
| acm_log(0, "failed to open %s\n", buf); |
| return -1; |
| } |
| |
| if (fgets(buf, sizeof buf, f)) { |
| for (i = 0, p = 12; i < 16; i++, p += 3) { |
| buf[p + 2] = '\0'; |
| sgid->raw[i] = (uint8_t) strtol(buf + p, &end, 16); |
| } |
| ret = 0; |
| } else { |
| acm_log(0, "failed to read sgid\n"); |
| ret = -1; |
| } |
| |
| fclose(f); |
| return ret; |
| } |
| |
| static struct nl_sock *sk; |
| static struct nl_cache *link_cache; |
| static struct nl_cache *addr_cache; |
| |
| int acm_init_if_iter_sys(void) |
| { |
| int sts; |
| |
| sk = nl_socket_alloc(); |
| if (!sk) { |
| acm_log(0, "nl_socket_alloc"); |
| return -1; |
| } |
| |
| sts = nl_connect(sk, NETLINK_ROUTE); |
| if (sts) { |
| acm_log(0, "nl_connect failed"); |
| goto out_connect; |
| } |
| |
| sts = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); |
| if (sts) { |
| acm_log(0, "rtnl_link_alloc_cache failed"); |
| goto out_connect; |
| } |
| |
| sts = rtnl_addr_alloc_cache(sk, &addr_cache); |
| if (sts) { |
| acm_log(0, "rtnl_addr_alloc_cache"); |
| goto out_addr; |
| } |
| |
| return 0; |
| |
| out_addr: |
| nl_cache_free(link_cache); |
| |
| out_connect: |
| nl_close(sk); |
| return sts; |
| } |
| |
| void acm_fini_if_iter_sys(void) |
| { |
| nl_cache_free(link_cache); |
| nl_cache_free(addr_cache); |
| nl_close(sk); |
| } |
| |
| static inline int af2acm_addr_type(int af) |
| { |
| switch (af) { |
| case AF_INET: |
| return ACM_ADDRESS_IP; |
| |
| case AF_INET6: |
| return ACM_ADDRESS_IP6; |
| } |
| |
| acm_log(0, "Unnkown address family\n"); |
| return ACM_ADDRESS_INVALID; |
| } |
| |
| struct ctx_and_cb { |
| void *ctx; |
| acm_if_iter_cb cb; |
| }; |
| |
| static void acm_if_iter(struct nl_object *obj, void *_ctx_and_cb) |
| { |
| struct ctx_and_cb *ctx_cb = (struct ctx_and_cb *)_ctx_and_cb; |
| struct rtnl_addr *addr = (struct rtnl_addr *)obj; |
| struct nl_addr *a = rtnl_addr_get_local(addr); |
| uint8_t bin_addr[ACM_MAX_ADDRESS] = {}; |
| int addr_len = nl_addr_get_len(a); |
| char ip_str[INET6_ADDRSTRLEN]; |
| struct nl_addr *link_addr; |
| struct rtnl_link *link; |
| char flags_str[128]; |
| union ibv_gid sgid; |
| uint16_t pkey; |
| char *label; |
| int af; |
| |
| link = rtnl_link_get(link_cache, rtnl_addr_get_ifindex(addr)); |
| |
| if (rtnl_link_get_arptype(link) != ARPHRD_INFINIBAND) |
| return; |
| |
| if (!a) |
| return; |
| |
| if (addr_len > ACM_MAX_ADDRESS) { |
| acm_log(0, "address too long (%d)\n", addr_len); |
| return; |
| } |
| |
| af = nl_addr_get_family(a); |
| if (af != AF_INET && af != AF_INET6) |
| return; |
| |
| label = rtnl_addr_get_label(addr); |
| |
| link_addr = rtnl_link_get_addr(link); |
| /* gid has a 4 byte offset into the link address */ |
| memcpy(sgid.raw, nl_addr_get_binary_addr(link_addr) + 4, sizeof(sgid)); |
| |
| if (acm_if_get_pkey(rtnl_link_get_name(link), &pkey)) |
| return; |
| |
| acm_log(2, "name: %5s label: %9s index: %2d flags: %s addr: %s pkey: 0x%04x guid: 0x%" PRIx64 "\n", |
| rtnl_link_get_name(link), label, |
| rtnl_addr_get_ifindex(addr), |
| rtnl_link_flags2str(rtnl_link_get_flags(link), flags_str, sizeof(flags_str)), |
| nl_addr2str(a, ip_str, sizeof(ip_str)), pkey, |
| be64toh(sgid.global.interface_id)); |
| |
| memcpy(&bin_addr, nl_addr_get_binary_addr(a), addr_len); |
| ctx_cb->cb(label ? label : rtnl_link_get_name(link), |
| &sgid, pkey, af2acm_addr_type(af), bin_addr, ip_str, ctx_cb->ctx); |
| } |
| |
| |
| int acm_if_iter_sys(acm_if_iter_cb cb, void *ctx) |
| { |
| struct ctx_and_cb ctx_cb; |
| int sts; |
| |
| sts = nl_cache_refill(sk, link_cache); |
| if (sts) { |
| acm_log(0, "nl_cache_refill link_cache"); |
| return sts; |
| } |
| |
| sts = nl_cache_refill(sk, addr_cache); |
| if (sts) { |
| acm_log(0, "nl_cache_refill addr_cache"); |
| return sts; |
| } |
| |
| ctx_cb.ctx = ctx; |
| ctx_cb.cb = cb; |
| nl_cache_foreach(addr_cache, acm_if_iter, (void *)&ctx_cb); |
| |
| return 0; |
| } |