#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <stdlib.h>
#include <err.h>
#include <libdladm.h>
#include <libdllink.h>
#include <string.h>
#include <stdbool.h>
#include <libdlpi.h>
typedef struct {
const struct ifaddrs *ic_ifa;
int ic_ret;
uint32_t ic_nmatch;
} ifaddr_cb_t;
typedef struct {
bool mc_found;
bool *mc_pass;
const char *mc_name;
const struct ifaddrs *mc_ifa;
} mac_cb_t;
static boolean_t
dladm_walk_mac_cb(void *arg, dladm_macaddr_attr_t *attr)
{
mac_cb_t *cb = arg;
const struct ifaddrs *ifa = cb->mc_ifa;
const struct sockaddr_dl *dl = (struct sockaddr_dl *)ifa->ifa_addr;
const struct if_data *if_data = ifa->ifa_data;
if (attr->ma_addrlen != dl->sdl_alen)
return (B_TRUE);
if (memcmp(LLADDR(dl), attr->ma_addr, attr->ma_addrlen) != 0) {
return (B_TRUE);
}
cb->mc_found = true;
if (if_data != NULL && if_data->ifi_addrlen != attr->ma_addrlen) {
warnx("TEST FAILED: link %s: found if_data address length "
"0x%x, but expected 0x%x", cb->mc_name,
if_data->ifi_addrlen, attr->ma_addrlen);
*cb->mc_pass = false;
}
return (B_TRUE);
}
static int
dladm_walk_cb(dladm_handle_t hdl, datalink_id_t id, void *arg)
{
dladm_status_t dlret;
ifaddr_cb_t *cb = arg;
char name[MAXLINKNAMELEN];
char dlerr[DLADM_STRSIZE];
uint32_t media;
char buf[DLADM_PROP_VAL_MAX];
char *valptr[1];
uint_t valcnt;
dlret = dladm_datalink_id2info(hdl, id, NULL, NULL, &media, name,
sizeof (name));
if (dlret != DLADM_STATUS_OK) {
cb->ic_ret = EXIT_FAILURE;
warnx("INTERNAL TEST FAILURE: failed to get datalink "
"information for link 0x%x: %s", id, dladm_status2str(dlret,
dlerr));
return (DLADM_WALK_CONTINUE);
}
valptr[0] = buf;
valcnt = 1;
if (dladm_get_linkprop(hdl, id, DLADM_PROP_VAL_CURRENT, "state", valptr,
&valcnt) != DLADM_STATUS_OK) {
warnx("skipping datalink %s as we could not get \"state\" link "
"link property", name);
return (DLADM_WALK_CONTINUE);
}
if (strcmp(buf, "up") != 0 && strcmp(buf, "down") != 0) {
return (DLADM_WALK_CONTINUE);
}
for (const struct ifaddrs *i = cb->ic_ifa; i != NULL; i = i->ifa_next) {
bool pass = true;
if (strcmp(name, i->ifa_name) != 0)
continue;
if (i->ifa_addr->sa_family != AF_LINK)
continue;
struct sockaddr_dl *dl = (struct sockaddr_dl *)i->ifa_addr;
uint_t ift_type = dlpi_iftype(media);
if (ift_type != dl->sdl_type) {
pass = false;
warnx("TEST FAILED: link %s: found type 0x%x, but "
"expected type 0x%x (dlpi 0x%x)", name,
dl->sdl_type, ift_type, media);
}
size_t nlen = strlen(name);
if (nlen != dl->sdl_nlen) {
pass = false;
warnx("TEST FAILED: link %s: name length mismatch: "
"found %u, expected %zu", name, dl->sdl_nlen, nlen);
}
if (dl->sdl_nlen > 0) {
if (strncmp(name, &dl->sdl_data[0], dl->sdl_nlen) !=
0) {
pass = false;
warnx("TEST FAILED: link %s: sockaddr_dl does "
"not match name", name);
}
}
mac_cb_t mcb = { false, &pass, name, i };
(void) dladm_walk_macaddr(hdl, id, &mcb, dladm_walk_mac_cb);
if (!mcb.mc_found) {
pass = false;
warnx("TEST FAILED: link %s: failed to find matching "
"mac address", name);
}
if (i->ifa_data != NULL) {
struct if_data *data = i->ifa_data;
if (data->ifi_type != ift_type) {
pass = false;
warnx("TEST FAILED: link %s: found if_data "
"ifi_type 0x%x, but expected type 0x%x "
"(dlpi 0x%x)", name, data->ifi_type,
ift_type, media);
}
valptr[0] = buf;
valcnt = 1;
if (dladm_get_linkprop(hdl, id, DLADM_PROP_VAL_CURRENT,
"mtu", valptr, &valcnt) == DLADM_STATUS_OK) {
ulong_t val = strtoul(buf, NULL, 10);
if (val != data->ifi_mtu) {
pass = false;
warnx("TEST FAILED: link %s: found "
"MTU %u, expected %lu", name,
data->ifi_mtu, val);
}
}
} else {
pass = false;
warnx("TEST FAILED: link %s: found NULL ifa_data "
"pointer", name);
}
if (pass) {
(void) printf("TEST PASSED: %s AF_LINK entry looks "
"right\n", name);
} else {
cb->ic_ret = EXIT_FAILURE;
}
cb->ic_nmatch++;
return (DLADM_WALK_CONTINUE);
}
warnx("TEST FAILED: failed to find matching ifaddrs entry for datalink "
"%s (0x%x)", name, id);
cb->ic_ret = EXIT_FAILURE;
return (DLADM_WALK_CONTINUE);
}
int
main(void)
{
struct ifaddrs *ifa;
dladm_status_t dlret;
dladm_handle_t dladm;
char dlerr[DLADM_STRSIZE];
ifaddr_cb_t cb;
if (getifaddrs(&ifa) != 0) {
err(EXIT_FAILURE, "INTERNAL TEST FAILURE: getifaddrs() failed: "
"test cannot proceed");
}
dlret = dladm_open(&dladm);
if (dlret != DLADM_STATUS_OK) {
errx(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to "
"initialize libdladm handle: %s", dladm_status2str(dlret,
dlerr));
}
cb.ic_ifa = ifa;
cb.ic_ret = EXIT_SUCCESS;
cb.ic_nmatch = 0;
(void) dladm_walk_datalink_id(dladm_walk_cb, dladm, &cb,
DATALINK_CLASS_PHYS | DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE,
DLADM_OPT_ACTIVE);
dladm_close(dladm);
freeifaddrs(ifa);
if (cb.ic_nmatch == 0) {
cb.ic_ret = EXIT_FAILURE;
warnx("no AF_LINK entries found");
}
if (cb.ic_ret == EXIT_SUCCESS) {
(void) printf("All tests passed successfully\n");
}
return (cb.ic_ret);
}