blob: 1c6b9690430ce68baaee16d55f5e7fbc865366d1 [file] [log] [blame]
/*
* 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;
}