#include <inet/ip.h>
#include <inet/ip_impl.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <sys/ktest.h>
#include <sys/mac_client.h>
#include <sys/mac_provider.h>
#include <sys/pattr.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#define PADDING_MAX 32
#define SPLITS_MAX 8
typedef struct emul_test_params {
mblk_t *etp_mp;
uchar_t *etp_raw;
uint_t etp_raw_sz;
uchar_t *etp_outputs;
uint_t etp_outputs_sz;
boolean_t etp_do_partial;
boolean_t etp_do_full;
boolean_t etp_do_ipv4;
boolean_t etp_do_lso;
uint_t etp_mss;
uint_t etp_splits[SPLITS_MAX];
} emul_test_params_t;
static void
etp_free(const emul_test_params_t *etp)
{
if (etp->etp_mp != NULL) {
freemsgchain(etp->etp_mp);
}
if (etp->etp_raw != NULL) {
kmem_free(etp->etp_raw, etp->etp_raw_sz);
}
if (etp->etp_outputs != NULL) {
kmem_free(etp->etp_outputs, etp->etp_outputs_sz);
}
}
static mblk_t *
cksum_alloc_pkt(const emul_test_params_t *etp, uint32_t padding)
{
uint32_t remain = etp->etp_raw_sz;
uint_t split_idx = 0;
const uint8_t *pkt_bytes = etp->etp_raw;
mblk_t *head = NULL, *tail = NULL;
while (remain > 0) {
const boolean_t has_split = etp->etp_splits[split_idx] != 0;
const uint32_t to_copy = has_split ?
MIN(remain, etp->etp_splits[split_idx]) : remain;
const uint32_t to_alloc = padding + to_copy;
mblk_t *mp = allocb(to_alloc, 0);
if (mp == NULL) {
freemsg(head);
return (NULL);
}
if (head == NULL) {
head = mp;
}
if (tail != NULL) {
tail->b_cont = mp;
}
tail = mp;
if (padding != 0) {
bzero(mp->b_rptr, padding);
mp->b_rptr += padding;
mp->b_wptr += padding;
padding = 0;
}
bcopy(pkt_bytes, mp->b_rptr, to_copy);
mp->b_wptr += to_copy;
pkt_bytes += to_copy;
remain -= to_copy;
if (has_split) {
split_idx++;
}
}
return (head);
}
static boolean_t
emul_test_parse_input(ktest_ctx_hdl_t *ctx, emul_test_params_t *etp)
{
uchar_t *bytes;
size_t num_bytes = 0;
ktest_get_input(ctx, &bytes, &num_bytes);
bzero(etp, sizeof (*etp));
nvlist_t *params = NULL;
if (nvlist_unpack((char *)bytes, num_bytes, ¶ms, KM_SLEEP) != 0) {
KT_ERROR(ctx, "Invalid nvlist input");
return (B_FALSE);
}
uchar_t *pkt_bytes, *out_pkt_bytes;
uint_t pkt_sz, out_pkt_sz;
if (nvlist_lookup_byte_array(params, "pkt_bytes", &pkt_bytes,
&pkt_sz) != 0) {
KT_ERROR(ctx, "Input missing pkt_bytes field");
goto bail;
}
if (pkt_sz == 0) {
KT_ERROR(ctx, "Packet must not be 0-length");
goto bail;
}
if (nvlist_lookup_byte_array(params, "out_pkt_bytes", &out_pkt_bytes,
&out_pkt_sz) == 0) {
if (out_pkt_sz < sizeof (uint32_t)) {
KT_ERROR(ctx, "Serialized packets need a u32 length");
goto bail;
}
etp->etp_outputs = kmem_alloc(out_pkt_sz, KM_SLEEP);
bcopy(out_pkt_bytes, etp->etp_outputs, out_pkt_sz);
etp->etp_outputs_sz = out_pkt_sz;
}
(void) nvlist_lookup_uint32(params, "mss", &etp->etp_mss);
uint32_t padding = 0;
(void) nvlist_lookup_uint32(params, "padding", &padding);
if (padding & 1) {
KT_ERROR(ctx, "padding must be even");
goto bail;
} else if (padding > PADDING_MAX) {
KT_ERROR(ctx, "padding greater than max of %u", PADDING_MAX);
goto bail;
}
etp->etp_do_ipv4 = fnvlist_lookup_boolean(params, "cksum_ipv4");
etp->etp_do_partial = fnvlist_lookup_boolean(params, "cksum_partial");
etp->etp_do_full = fnvlist_lookup_boolean(params, "cksum_full");
uint32_t *splits;
uint_t nsplits;
if (nvlist_lookup_uint32_array(params, "cksum_splits", &splits,
&nsplits) == 0) {
if (nsplits > SPLITS_MAX) {
KT_ERROR(ctx, "Too many splits requested");
goto bail;
}
for (uint_t i = 0; i < nsplits; i++) {
if (splits[i] == 0) {
KT_ERROR(ctx, "Splits should not be 0");
goto bail;
} else if (splits[i] & 1) {
KT_ERROR(ctx, "Splits must be 2-byte aligned");
goto bail;
}
etp->etp_splits[i] = splits[i];
}
}
if (etp->etp_do_partial && etp->etp_do_full) {
KT_ERROR(ctx, "Cannot request full and partial cksum");
goto bail;
}
etp->etp_raw = kmem_alloc(pkt_sz, KM_SLEEP);
bcopy(pkt_bytes, etp->etp_raw, pkt_sz);
etp->etp_raw_sz = pkt_sz;
etp->etp_mp = cksum_alloc_pkt(etp, padding);
if (etp->etp_mp == NULL) {
KT_ERROR(ctx, "Could not allocate mblk");
goto bail;
}
nvlist_free(params);
return (B_TRUE);
bail:
etp_free(etp);
if (params != NULL) {
nvlist_free(params);
}
return (B_FALSE);
}
static uint16_t
cksum_calc_pseudo(ktest_ctx_hdl_t *ctx, const uint8_t *pkt_data,
const mac_ether_offload_info_t *meoi, boolean_t exclude_len)
{
if ((meoi->meoi_flags & MEOI_L4INFO_SET) == 0) {
KT_ERROR(ctx, "MEOI lacks L4 info");
return (0);
}
const uint16_t *iphs = (const uint16_t *)(pkt_data + meoi->meoi_l2hlen);
uint32_t cksum = 0;
if (meoi->meoi_l3proto == ETHERTYPE_IP) {
cksum += iphs[6] + iphs[7] + iphs[8] + iphs[9];
} else if (meoi->meoi_l3proto == ETHERTYPE_IPV6) {
cksum += iphs[4] + iphs[5] + iphs[6] + iphs[7] +
iphs[8] + iphs[9] + iphs[10] + iphs[11] +
iphs[12] + iphs[13] + iphs[14] + iphs[15] +
iphs[16] + iphs[17] + iphs[18] + iphs[19];
} else {
KT_ERROR(ctx, "unexpected proto %u", meoi->meoi_l3proto);
return (0);
}
switch (meoi->meoi_l4proto) {
case IPPROTO_TCP:
cksum += IP_TCP_CSUM_COMP;
break;
case IPPROTO_UDP:
cksum += IP_UDP_CSUM_COMP;
break;
case IPPROTO_ICMPV6:
cksum += IP_ICMPV6_CSUM_COMP;
break;
default:
KT_ERROR(ctx, "unexpected L4 proto %u", meoi->meoi_l4proto);
return (0);
}
uint16_t ulp_len =
meoi->meoi_len - ((uint16_t)meoi->meoi_l2hlen + meoi->meoi_l3hlen);
if (meoi->meoi_l3proto == ETHERTYPE_IP) {
const ipha_t *ipha =
(const ipha_t *)(pkt_data + meoi->meoi_l2hlen);
ulp_len = ntohs(ipha->ipha_length) - meoi->meoi_l3hlen;
}
if (!exclude_len) {
cksum += htons(ulp_len);
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum = (cksum >> 16) + (cksum & 0xffff);
return (cksum);
}
static void
mblk_write16(mblk_t *mp, uint_t off, uint16_t val)
{
VERIFY(mp != NULL);
VERIFY3U(off & 1, ==, 0);
VERIFY3U(off + 2, <=, msgdsize(mp));
while (off >= MBLKL(mp)) {
off -= MBLKL(mp);
mp = mp->b_cont;
VERIFY(mp != NULL);
}
uint16_t *datap = (uint16_t *)(mp->b_rptr + off);
*datap = val;
}
static boolean_t
pkt_compare(ktest_ctx_hdl_t *ctx, const uchar_t *buf, const uint_t len,
mblk_t *mp)
{
if (msgdsize(mp) != len) {
KT_FAIL(ctx, "mp size %u != %u", msgdsize(mp), len);
return (B_FALSE);
}
uint32_t fail_val = 0, good_val = 0;
uint_t mp_off = 0, fail_len = 0, i;
for (i = 0; i < len; i++) {
if (mp->b_rptr[mp_off] != buf[i] || fail_len != 0) {
fail_val |= mp->b_rptr[mp_off] << (fail_len * 8);
good_val |= buf[i] << (fail_len * 8);
fail_len++;
if (fail_len == 4) {
break;
}
}
mp_off++;
if (mp_off == MBLKL(mp)) {
mp = mp->b_cont;
mp_off = 0;
}
}
if (fail_len != 0) {
KT_FAIL(ctx, "mp[%02X] %08X != %08X", (i - fail_len),
fail_val, good_val);
return (B_FALSE);
}
return (B_TRUE);
}
static boolean_t
pkt_result_compare_chain(ktest_ctx_hdl_t *ctx, const emul_test_params_t *etp,
mblk_t *mp)
{
uint_t remaining = etp->etp_outputs_sz;
const uchar_t *raw_cur = etp->etp_outputs;
uint_t idx = 0;
while (remaining != 0 && mp != NULL) {
uint32_t inner_pkt_len;
if (remaining < sizeof (inner_pkt_len)) {
KT_ERROR(ctx, "insufficient bytes to read packet len");
return (B_FALSE);
}
bcopy(raw_cur, &inner_pkt_len, sizeof (inner_pkt_len));
remaining -= sizeof (inner_pkt_len);
raw_cur += sizeof (inner_pkt_len);
if (remaining < inner_pkt_len) {
KT_ERROR(ctx, "wanted %u bytes to read packet, had %u",
inner_pkt_len, remaining);
return (B_FALSE);
}
if (!pkt_compare(ctx, raw_cur, inner_pkt_len, mp)) {
ktest_msg_prepend(ctx, "packet %u: ", idx);
return (B_FALSE);
}
remaining -= inner_pkt_len;
raw_cur += inner_pkt_len;
idx++;
mp = mp->b_next;
}
if (remaining != 0) {
KT_FAIL(ctx, "fewer packets returned than expected");
return (B_FALSE);
}
if (mp != NULL) {
KT_FAIL(ctx, "more packets returned than expected");
return (B_FALSE);
}
return (B_TRUE);
}
static void
mac_hw_emul_test(ktest_ctx_hdl_t *ctx, emul_test_params_t *etp)
{
mblk_t *mp = etp->etp_mp;
mac_ether_offload_info_t meoi;
mac_ether_offload_info(mp, &meoi);
if ((meoi.meoi_flags & MEOI_L3INFO_SET) == 0 ||
(meoi.meoi_l3proto != ETHERTYPE_IP &&
meoi.meoi_l3proto != ETHERTYPE_IPV6)) {
KT_SKIP(ctx, "l3 protocol not recognized/supported");
return;
}
mac_emul_t emul_flags = 0;
uint_t hck_flags = 0, hck_start = 0, hck_stuff = 0, hck_end = 0;
if (etp->etp_do_lso) {
emul_flags |= MAC_LSO_EMUL;
hck_flags |= HW_LSO;
if (etp->etp_mss == 0) {
KT_ERROR(ctx, "invalid MSS for LSO");
return;
}
}
if (meoi.meoi_l3proto == ETHERTYPE_IP && etp->etp_do_ipv4) {
mblk_write16(mp,
meoi.meoi_l2hlen + offsetof(ipha_t, ipha_hdr_checksum), 0);
emul_flags |= MAC_IPCKSUM_EMUL;
hck_flags |= HCK_IPV4_HDRCKSUM;
}
const boolean_t do_l4 = etp->etp_do_partial || etp->etp_do_full;
if ((meoi.meoi_flags & MEOI_L4INFO_SET) != 0 && do_l4) {
boolean_t skip_pseudo = B_FALSE;
hck_start = meoi.meoi_l2hlen + meoi.meoi_l3hlen;
hck_stuff = hck_start;
hck_end = meoi.meoi_len;
switch (meoi.meoi_l4proto) {
case IPPROTO_TCP:
hck_stuff += TCP_CHECKSUM_OFFSET;
break;
case IPPROTO_UDP:
hck_stuff += UDP_CHECKSUM_OFFSET;
break;
case IPPROTO_ICMP:
hck_stuff += ICMP_CHECKSUM_OFFSET;
skip_pseudo = B_TRUE;
break;
case IPPROTO_ICMPV6:
hck_stuff += ICMPV6_CHECKSUM_OFFSET;
break;
case IPPROTO_SCTP:
hck_stuff += SCTP_CHECKSUM_OFFSET;
if (etp->etp_do_full) {
mblk_write16(mp, hck_stuff, 0);
mblk_write16(mp, hck_stuff + 2, 0);
} else {
KT_SKIP(ctx,
"Partial L4 cksum not supported for SCTP");
return;
}
break;
default:
KT_SKIP(ctx,
"Partial L4 cksum not supported for proto");
return;
}
emul_flags |= MAC_HWCKSUM_EMUL;
if (etp->etp_do_partial) {
hck_flags |= HCK_PARTIALCKSUM;
if (!skip_pseudo) {
const uint16_t pcksum = cksum_calc_pseudo(ctx,
etp->etp_raw, &meoi, etp->etp_do_lso);
mblk_write16(mp, hck_stuff, pcksum);
} else {
mblk_write16(mp, hck_stuff, 0);
}
} else {
hck_flags |= HCK_FULLCKSUM;
mblk_write16(mp, hck_stuff, 0);
}
}
if (do_l4 && (hck_flags & (HCK_FULLCKSUM|HCK_PARTIALCKSUM)) == 0) {
KT_SKIP(ctx, "L4 checksum not supported for packet");
return;
}
if (emul_flags != 0) {
if ((hck_flags & HCK_PARTIALCKSUM) == 0) {
hck_start = hck_stuff = hck_end = 0;
} else {
hck_start -= meoi.meoi_l2hlen;
hck_stuff -= meoi.meoi_l2hlen;
hck_end -= meoi.meoi_l2hlen;
}
for (mblk_t *cmp = mp; cmp != NULL; cmp = cmp->b_cont) {
mac_hcksum_set(cmp, hck_start, hck_stuff, hck_end, 0,
hck_flags & HCK_FLAGS);
lso_info_set(cmp, etp->etp_mss,
hck_flags & HW_LSO_FLAGS);
}
mac_hw_emul(&mp, NULL, NULL, emul_flags);
KT_ASSERT3P(mp, !=, NULL, ctx);
etp->etp_mp = mp;
for (mblk_t *curr = mp; curr != NULL; curr = curr->b_next) {
KT_ASSERT(OK_32PTR(curr->b_rptr + meoi.meoi_l2hlen),
ctx);
}
boolean_t success = (etp->etp_outputs == NULL) ?
pkt_compare(ctx, etp->etp_raw, etp->etp_raw_sz, mp) :
pkt_result_compare_chain(ctx, etp, mp);
if (!success) {
return;
}
} else {
KT_SKIP(ctx, "offloads unsupported for packet");
return;
}
KT_PASS(ctx);
}
static void
mac_sw_cksum_test(ktest_ctx_hdl_t *ctx)
{
emul_test_params_t etp;
if (!emul_test_parse_input(ctx, &etp)) {
goto cleanup;
}
mac_hw_emul_test(ctx, &etp);
cleanup:
etp_free(&etp);
}
static void
mac_sw_lso_test(ktest_ctx_hdl_t *ctx)
{
emul_test_params_t etp;
if (!emul_test_parse_input(ctx, &etp)) {
goto cleanup;
}
if (etp.etp_mss == 0) {
KT_ERROR(ctx, "invalid MSS for LSO");
goto cleanup;
}
if (etp.etp_outputs == NULL) {
KT_ERROR(ctx, "LSO tests require explicit packet list");
goto cleanup;
}
etp.etp_do_lso = B_TRUE;
mac_hw_emul_test(ctx, &etp);
cleanup:
etp_free(&etp);
}
typedef struct meoi_test_params {
mblk_t *mtp_mp;
mac_ether_offload_info_t mtp_partial;
mac_ether_offload_info_t mtp_results;
uint_t mtp_offset;
} meoi_test_params_t;
static void
nvlist_to_meoi(nvlist_t *results, mac_ether_offload_info_t *meoi)
{
uint64_t u64_val;
int int_val;
uint16_t u16_val;
uint8_t u8_val;
bzero(meoi, sizeof (*meoi));
if (nvlist_lookup_int32(results, "meoi_flags", &int_val) == 0) {
meoi->meoi_flags = int_val;
}
if (nvlist_lookup_uint64(results, "meoi_len", &u64_val) == 0) {
meoi->meoi_len = u64_val;
}
if (nvlist_lookup_uint8(results, "meoi_l2hlen", &u8_val) == 0) {
meoi->meoi_l2hlen = u8_val;
}
if (nvlist_lookup_uint16(results, "meoi_l3proto", &u16_val) == 0) {
meoi->meoi_l3proto = u16_val;
}
if (nvlist_lookup_uint16(results, "meoi_l3hlen", &u16_val) == 0) {
meoi->meoi_l3hlen = u16_val;
}
if (nvlist_lookup_uint8(results, "meoi_l4proto", &u8_val) == 0) {
meoi->meoi_l4proto = u8_val;
}
if (nvlist_lookup_uint8(results, "meoi_l4hlen", &u8_val) == 0) {
meoi->meoi_l4hlen = u8_val;
}
}
static mblk_t *
alloc_split_pkt(ktest_ctx_hdl_t *ctx, nvlist_t *nvl, const char *pkt_field)
{
uchar_t *pkt_bytes;
uint_t pkt_sz;
if (nvlist_lookup_byte_array(nvl, pkt_field, &pkt_bytes,
&pkt_sz) != 0) {
KT_ERROR(ctx, "Input missing %s field", pkt_field);
return (NULL);
}
const uint32_t *splits = NULL;
uint_t num_splits = 0;
(void) nvlist_lookup_uint32_array(nvl, "splits", (uint32_t **)&splits,
&num_splits);
uint_t split_idx = 0;
mblk_t *result = NULL, *tail = NULL;
do {
uint_t block_sz = pkt_sz;
if (split_idx < num_splits) {
block_sz = MIN(block_sz, splits[split_idx]);
}
mblk_t *mp = allocb(block_sz, 0);
if (mp == NULL) {
KT_ERROR(ctx, "mblk alloc failure");
freemsg(result);
return (NULL);
}
if (result == NULL) {
result = mp;
} else {
tail->b_cont = mp;
}
tail = mp;
if (block_sz != 0) {
bcopy(pkt_bytes, mp->b_wptr, block_sz);
mp->b_wptr += block_sz;
}
pkt_sz -= block_sz;
pkt_bytes += block_sz;
split_idx++;
} while (pkt_sz > 0);
return (result);
}
static boolean_t
meoi_test_parse_input(ktest_ctx_hdl_t *ctx, meoi_test_params_t *mtp,
boolean_t test_partial)
{
uchar_t *bytes;
size_t num_bytes = 0;
ktest_get_input(ctx, &bytes, &num_bytes);
bzero(mtp, sizeof (*mtp));
nvlist_t *params = NULL;
if (nvlist_unpack((char *)bytes, num_bytes, ¶ms, KM_SLEEP) != 0) {
KT_ERROR(ctx, "Invalid nvlist input");
return (B_FALSE);
}
nvlist_t *results;
if (nvlist_lookup_nvlist(params, "results", &results) != 0) {
KT_ERROR(ctx, "Input missing results field");
nvlist_free(params);
return (B_FALSE);
}
if (test_partial) {
nvlist_t *partial;
if (nvlist_lookup_nvlist(params, "partial", &partial) != 0) {
KT_ERROR(ctx, "Input missing partial field");
nvlist_free(params);
return (B_FALSE);
} else {
nvlist_to_meoi(partial, &mtp->mtp_partial);
}
(void) nvlist_lookup_uint32(params, "offset", &mtp->mtp_offset);
}
mtp->mtp_mp = alloc_split_pkt(ctx, params, "pkt_bytes");
if (mtp->mtp_mp == NULL) {
nvlist_free(params);
return (B_FALSE);
}
nvlist_to_meoi(results, &mtp->mtp_results);
nvlist_free(params);
return (B_TRUE);
}
void
mac_ether_offload_info_test(ktest_ctx_hdl_t *ctx)
{
meoi_test_params_t mtp = { 0 };
if (!meoi_test_parse_input(ctx, &mtp, B_FALSE)) {
return;
}
mac_ether_offload_info_t result;
mac_ether_offload_info(mtp.mtp_mp, &result);
const mac_ether_offload_info_t *expect = &mtp.mtp_results;
KT_ASSERT3UG(result.meoi_flags, ==, expect->meoi_flags, ctx, done);
KT_ASSERT3UG(result.meoi_l2hlen, ==, expect->meoi_l2hlen, ctx, done);
KT_ASSERT3UG(result.meoi_l3proto, ==, expect->meoi_l3proto, ctx, done);
KT_ASSERT3UG(result.meoi_l3hlen, ==, expect->meoi_l3hlen, ctx, done);
KT_ASSERT3UG(result.meoi_l4proto, ==, expect->meoi_l4proto, ctx, done);
KT_ASSERT3UG(result.meoi_l4hlen, ==, expect->meoi_l4hlen, ctx, done);
KT_PASS(ctx);
done:
freemsg(mtp.mtp_mp);
}
void
mac_partial_offload_info_test(ktest_ctx_hdl_t *ctx)
{
meoi_test_params_t mtp = { 0 };
if (!meoi_test_parse_input(ctx, &mtp, B_TRUE)) {
return;
}
mac_ether_offload_info_t *result = &mtp.mtp_partial;
mac_partial_offload_info(mtp.mtp_mp, mtp.mtp_offset, result);
const mac_ether_offload_info_t *expect = &mtp.mtp_results;
KT_ASSERT3UG(result->meoi_flags, ==, expect->meoi_flags, ctx, done);
KT_ASSERT3UG(result->meoi_l2hlen, ==, expect->meoi_l2hlen, ctx, done);
KT_ASSERT3UG(result->meoi_l3proto, ==, expect->meoi_l3proto, ctx, done);
KT_ASSERT3UG(result->meoi_l3hlen, ==, expect->meoi_l3hlen, ctx, done);
KT_ASSERT3UG(result->meoi_l4proto, ==, expect->meoi_l4proto, ctx, done);
KT_ASSERT3UG(result->meoi_l4hlen, ==, expect->meoi_l4hlen, ctx, done);
KT_PASS(ctx);
done:
freemsg(mtp.mtp_mp);
}
typedef struct ether_test_params {
mblk_t *etp_mp;
uint32_t etp_tci;
uint8_t etp_dstaddr[ETHERADDRL];
boolean_t etp_is_err;
} ether_test_params_t;
static boolean_t
ether_parse_input(ktest_ctx_hdl_t *ctx, ether_test_params_t *etp)
{
uchar_t *bytes;
size_t num_bytes = 0;
ktest_get_input(ctx, &bytes, &num_bytes);
bzero(etp, sizeof (*etp));
nvlist_t *params = NULL;
if (nvlist_unpack((char *)bytes, num_bytes, ¶ms, KM_SLEEP) != 0) {
KT_ERROR(ctx, "Invalid nvlist input");
return (B_FALSE);
}
etp->etp_mp = alloc_split_pkt(ctx, params, "pkt_bytes");
if (etp->etp_mp == NULL) {
nvlist_free(params);
return (B_FALSE);
}
if (nvlist_lookup_uint32(params, "tci", &etp->etp_tci) != 0) {
KT_ERROR(ctx, "Input missing tci field");
nvlist_free(params);
return (B_FALSE);
}
uchar_t *dstaddr;
uint_t dstaddr_sz;
if (nvlist_lookup_byte_array(params, "dstaddr", &dstaddr,
&dstaddr_sz) != 0) {
KT_ERROR(ctx, "Input missing dstaddr field");
nvlist_free(params);
return (B_FALSE);
} else if (dstaddr_sz != ETHERADDRL) {
KT_ERROR(ctx, "bad dstaddr size %u != %u", dstaddr_sz,
ETHERADDRL);
nvlist_free(params);
return (B_FALSE);
}
bcopy(dstaddr, &etp->etp_dstaddr, ETHERADDRL);
etp->etp_is_err = nvlist_lookup_boolean(params, "is_err") == 0;
nvlist_free(params);
return (B_TRUE);
}
void
mac_ether_l2_info_test(ktest_ctx_hdl_t *ctx)
{
ether_test_params_t etp = { 0 };
if (!ether_parse_input(ctx, &etp)) {
return;
}
uint8_t dstaddr[ETHERADDRL];
uint32_t vlan_tci = 0;
const boolean_t is_err =
!mac_ether_l2_info(etp.etp_mp, dstaddr, &vlan_tci);
KT_ASSERTG(is_err == etp.etp_is_err, ctx, done);
KT_ASSERTG(bcmp(dstaddr, etp.etp_dstaddr, ETHERADDRL) == 0, ctx,
done);
KT_ASSERT3UG(vlan_tci, ==, etp.etp_tci, ctx, done);
KT_PASS(ctx);
done:
freemsg(etp.etp_mp);
}
static struct modlmisc mac_ktest_modlmisc = {
.misc_modops = &mod_miscops,
.misc_linkinfo = "mac ktest module"
};
static struct modlinkage mac_ktest_modlinkage = {
.ml_rev = MODREV_1,
.ml_linkage = { &mac_ktest_modlmisc, NULL }
};
int
_init()
{
int ret;
ktest_module_hdl_t *km = NULL;
ktest_suite_hdl_t *ks = NULL;
VERIFY0(ktest_create_module("mac", &km));
VERIFY0(ktest_add_suite(km, "checksum", &ks));
VERIFY0(ktest_add_test(ks, "mac_sw_cksum_test",
mac_sw_cksum_test, KTEST_FLAG_INPUT));
ks = NULL;
VERIFY0(ktest_add_suite(km, "lso", &ks));
VERIFY0(ktest_add_test(ks, "mac_sw_lso_test",
mac_sw_lso_test, KTEST_FLAG_INPUT));
ks = NULL;
VERIFY0(ktest_add_suite(km, "parsing", &ks));
VERIFY0(ktest_add_test(ks, "mac_ether_offload_info_test",
mac_ether_offload_info_test, KTEST_FLAG_INPUT));
VERIFY0(ktest_add_test(ks, "mac_partial_offload_info_test",
mac_partial_offload_info_test, KTEST_FLAG_INPUT));
VERIFY0(ktest_add_test(ks, "mac_ether_l2_info_test",
mac_ether_l2_info_test, KTEST_FLAG_INPUT));
if ((ret = ktest_register_module(km)) != 0) {
ktest_free_module(km);
return (ret);
}
if ((ret = mod_install(&mac_ktest_modlinkage)) != 0) {
ktest_unregister_module("mac");
return (ret);
}
return (0);
}
int
_fini(void)
{
ktest_unregister_module("mac");
return (mod_remove(&mac_ktest_modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&mac_ktest_modlinkage, modinfop));
}