xref: /illumos-gate/usr/src/uts/common/io/mac/mac_test.c (revision 08855964b9970604433f7b19dcd71cf5af5e5f14)
1*08855964SRyan Zezeski /*
2*08855964SRyan Zezeski  * This file and its contents are supplied under the terms of the
3*08855964SRyan Zezeski  * Common Development and Distribution License ("CDDL"), version 1.0.
4*08855964SRyan Zezeski  * You may only use this file in accordance with the terms of version
5*08855964SRyan Zezeski  * 1.0 of the CDDL.
6*08855964SRyan Zezeski  *
7*08855964SRyan Zezeski  * A full copy of the text of the CDDL should have accompanied this
8*08855964SRyan Zezeski  * source.  A copy of the CDDL is also available via the Internet at
9*08855964SRyan Zezeski  * http://www.illumos.org/license/CDDL.
10*08855964SRyan Zezeski  */
11*08855964SRyan Zezeski 
12*08855964SRyan Zezeski /*
13*08855964SRyan Zezeski  * Copyright 2023 Oxide Computer Company
14*08855964SRyan Zezeski  * Copyright 2024 Ryan Zezeski
15*08855964SRyan Zezeski  */
16*08855964SRyan Zezeski 
17*08855964SRyan Zezeski /*
18*08855964SRyan Zezeski  * A test module for various mac routines.
19*08855964SRyan Zezeski  */
20*08855964SRyan Zezeski #include <inet/ip.h>
21*08855964SRyan Zezeski #include <inet/ip_impl.h>
22*08855964SRyan Zezeski #include <inet/tcp.h>
23*08855964SRyan Zezeski #include <sys/dlpi.h>
24*08855964SRyan Zezeski #include <sys/ethernet.h>
25*08855964SRyan Zezeski #include <sys/ktest.h>
26*08855964SRyan Zezeski 
27*08855964SRyan Zezeski static uint32_t
mt_pseudo_sum(const uint8_t proto,ipha_t * ip)28*08855964SRyan Zezeski mt_pseudo_sum(const uint8_t proto, ipha_t *ip)
29*08855964SRyan Zezeski {
30*08855964SRyan Zezeski 	const uint32_t ip_hdr_sz = IPH_HDR_LENGTH(ip);
31*08855964SRyan Zezeski 	const ipaddr_t src = ip->ipha_src;
32*08855964SRyan Zezeski 	const ipaddr_t dst = ip->ipha_dst;
33*08855964SRyan Zezeski 	uint16_t len;
34*08855964SRyan Zezeski 	uint32_t sum = 0;
35*08855964SRyan Zezeski 
36*08855964SRyan Zezeski 	switch (proto) {
37*08855964SRyan Zezeski 	case IPPROTO_TCP:
38*08855964SRyan Zezeski 		sum = IP_TCP_CSUM_COMP;
39*08855964SRyan Zezeski 		break;
40*08855964SRyan Zezeski 
41*08855964SRyan Zezeski 	case IPPROTO_UDP:
42*08855964SRyan Zezeski 		sum = IP_UDP_CSUM_COMP;
43*08855964SRyan Zezeski 		break;
44*08855964SRyan Zezeski 	}
45*08855964SRyan Zezeski 
46*08855964SRyan Zezeski 	len = ntohs(ip->ipha_length) - ip_hdr_sz;
47*08855964SRyan Zezeski 	sum += (dst >> 16) + (dst & 0xFFFF) + (src >> 16) + (src & 0xFFFF);
48*08855964SRyan Zezeski 	sum += htons(len);
49*08855964SRyan Zezeski 	return (sum);
50*08855964SRyan Zezeski }
51*08855964SRyan Zezeski 
52*08855964SRyan Zezeski /*
53*08855964SRyan Zezeski  * An implementation of the internet checksum inspired by RFC 1071.
54*08855964SRyan Zezeski  * This implementation is as naive as possible. It serves as the
55*08855964SRyan Zezeski  * reference point for testing the optimized versions in the rest of
56*08855964SRyan Zezeski  * our stack. This is no place for optimization or cleverness.
57*08855964SRyan Zezeski  *
58*08855964SRyan Zezeski  * Arguments
59*08855964SRyan Zezeski  *
60*08855964SRyan Zezeski  *     initial: The initial sum value.
61*08855964SRyan Zezeski  *
62*08855964SRyan Zezeski  *     addr: Pointer to the beginning of the byte stream to sum.
63*08855964SRyan Zezeski  *
64*08855964SRyan Zezeski  *     len: The number of bytes to sum.
65*08855964SRyan Zezeski  *
66*08855964SRyan Zezeski  * Return
67*08855964SRyan Zezeski  *
68*08855964SRyan Zezeski  *     The resulting internet checksum.
69*08855964SRyan Zezeski  */
70*08855964SRyan Zezeski static uint32_t
mt_rfc1071_sum(uint32_t initial,uint16_t * addr,size_t len)71*08855964SRyan Zezeski mt_rfc1071_sum(uint32_t initial, uint16_t *addr, size_t len)
72*08855964SRyan Zezeski {
73*08855964SRyan Zezeski 	uint32_t sum = initial;
74*08855964SRyan Zezeski 
75*08855964SRyan Zezeski 	while (len > 1) {
76*08855964SRyan Zezeski 		sum += *addr;
77*08855964SRyan Zezeski 		addr++;
78*08855964SRyan Zezeski 		len -= 2;
79*08855964SRyan Zezeski 	}
80*08855964SRyan Zezeski 
81*08855964SRyan Zezeski 	if (len == 1) {
82*08855964SRyan Zezeski 		sum += *((uint8_t *)addr);
83*08855964SRyan Zezeski 	}
84*08855964SRyan Zezeski 
85*08855964SRyan Zezeski 	while ((sum >> 16) != 0) {
86*08855964SRyan Zezeski 		sum = (sum >> 16) + (sum & 0xFFFF);
87*08855964SRyan Zezeski 	}
88*08855964SRyan Zezeski 
89*08855964SRyan Zezeski 	return (~sum & 0xFFFF);
90*08855964SRyan Zezeski }
91*08855964SRyan Zezeski 
92*08855964SRyan Zezeski typedef boolean_t (*mac_sw_cksum_ipv4_t)(mblk_t *, uint32_t, ipha_t *,
93*08855964SRyan Zezeski     const char **);
94*08855964SRyan Zezeski 
95*08855964SRyan Zezeski /*
96*08855964SRyan Zezeski  * Fill out a basic TCP header in the given mblk at the given offset.
97*08855964SRyan Zezeski  * A TCP header should never straddle an mblk boundary.
98*08855964SRyan Zezeski  */
99*08855964SRyan Zezeski static tcpha_t *
mt_tcp_basic_hdr(mblk_t * mp,uint16_t offset,uint16_t lport,uint16_t fport,uint32_t seq,uint32_t ack,uint8_t flags,uint16_t win)100*08855964SRyan Zezeski mt_tcp_basic_hdr(mblk_t *mp, uint16_t offset, uint16_t lport, uint16_t fport,
101*08855964SRyan Zezeski     uint32_t seq, uint32_t ack, uint8_t flags, uint16_t win)
102*08855964SRyan Zezeski {
103*08855964SRyan Zezeski 	tcpha_t *tcp = (tcpha_t *)(mp->b_rptr + offset);
104*08855964SRyan Zezeski 
105*08855964SRyan Zezeski 	VERIFY3U((uintptr_t)tcp + sizeof (*tcp), <=, mp->b_wptr);
106*08855964SRyan Zezeski 	tcp->tha_lport = htons(lport);
107*08855964SRyan Zezeski 	tcp->tha_fport = htons(fport);
108*08855964SRyan Zezeski 	tcp->tha_seq = htonl(seq);
109*08855964SRyan Zezeski 	tcp->tha_ack = htonl(ack);
110*08855964SRyan Zezeski 	tcp->tha_offset_and_reserved = 0x5 << 4;
111*08855964SRyan Zezeski 	tcp->tha_flags = flags;
112*08855964SRyan Zezeski 	tcp->tha_win = htons(win);
113*08855964SRyan Zezeski 	tcp->tha_sum = 0x0;
114*08855964SRyan Zezeski 	tcp->tha_urp = 0x0;
115*08855964SRyan Zezeski 
116*08855964SRyan Zezeski 	return (tcp);
117*08855964SRyan Zezeski }
118*08855964SRyan Zezeski 
119*08855964SRyan Zezeski static ipha_t *
mt_ipv4_simple_hdr(mblk_t * mp,uint16_t offset,uint16_t datum_length,uint16_t ident,uint8_t proto,char * src,char * dst)120*08855964SRyan Zezeski mt_ipv4_simple_hdr(mblk_t *mp, uint16_t offset, uint16_t datum_length,
121*08855964SRyan Zezeski     uint16_t ident, uint8_t proto, char *src, char *dst)
122*08855964SRyan Zezeski {
123*08855964SRyan Zezeski 	uint32_t srcaddr, dstaddr;
124*08855964SRyan Zezeski 	ipha_t *ip = (ipha_t *)(mp->b_rptr + offset);
125*08855964SRyan Zezeski 
126*08855964SRyan Zezeski 	VERIFY3U((uintptr_t)ip + sizeof (*ip), <=, mp->b_wptr);
127*08855964SRyan Zezeski 
128*08855964SRyan Zezeski 	VERIFY(inet_pton(AF_INET, src, &srcaddr));
129*08855964SRyan Zezeski 	VERIFY(inet_pton(AF_INET, dst, &dstaddr));
130*08855964SRyan Zezeski 	ip->ipha_version_and_hdr_length = IP_SIMPLE_HDR_VERSION;
131*08855964SRyan Zezeski 	ip->ipha_type_of_service = 0x0;
132*08855964SRyan Zezeski 	ip->ipha_length = htons(sizeof (*ip) + datum_length);
133*08855964SRyan Zezeski 	ip->ipha_ident = htons(ident);
134*08855964SRyan Zezeski 	ip->ipha_fragment_offset_and_flags = IPH_DF_HTONS;
135*08855964SRyan Zezeski 	ip->ipha_ttl = 255;
136*08855964SRyan Zezeski 	ip->ipha_protocol = proto;
137*08855964SRyan Zezeski 	ip->ipha_hdr_checksum = 0x0;
138*08855964SRyan Zezeski 	ip->ipha_src = srcaddr;
139*08855964SRyan Zezeski 	ip->ipha_dst = dstaddr;
140*08855964SRyan Zezeski 
141*08855964SRyan Zezeski 	return (ip);
142*08855964SRyan Zezeski }
143*08855964SRyan Zezeski 
144*08855964SRyan Zezeski static struct ether_header *
mt_ether_hdr(mblk_t * mp,uint16_t offset,char * dst,char * src,uint16_t etype)145*08855964SRyan Zezeski mt_ether_hdr(mblk_t *mp, uint16_t offset, char *dst, char *src, uint16_t etype)
146*08855964SRyan Zezeski {
147*08855964SRyan Zezeski 	char *byte = dst;
148*08855964SRyan Zezeski 	unsigned long tmp;
149*08855964SRyan Zezeski 	struct ether_header *eh = (struct ether_header *)(mp->b_rptr + offset);
150*08855964SRyan Zezeski 
151*08855964SRyan Zezeski 	VERIFY3U((uintptr_t)eh + sizeof (*eh), <=, mp->b_wptr);
152*08855964SRyan Zezeski 
153*08855964SRyan Zezeski 	/* No strtok in these here parts. */
154*08855964SRyan Zezeski 	for (uint_t i = 0; i < 6; i++) {
155*08855964SRyan Zezeski 		char *end = strchr(dst, ':');
156*08855964SRyan Zezeski 		VERIFY3P(end, !=, NULL);
157*08855964SRyan Zezeski 		VERIFY0(ddi_strtoul(byte, NULL, 16, &tmp));
158*08855964SRyan Zezeski 		VERIFY3U(tmp, <=, 255);
159*08855964SRyan Zezeski 		eh->ether_dhost.ether_addr_octet[i] = tmp;
160*08855964SRyan Zezeski 		byte = end + 1;
161*08855964SRyan Zezeski 	}
162*08855964SRyan Zezeski 
163*08855964SRyan Zezeski 	byte = src;
164*08855964SRyan Zezeski 	for (uint_t i = 0; i < 6; i++) {
165*08855964SRyan Zezeski 		char *end = strchr(dst, ':');
166*08855964SRyan Zezeski 		VERIFY3P(end, !=, NULL);
167*08855964SRyan Zezeski 		VERIFY0(ddi_strtoul(byte, NULL, 16, &tmp));
168*08855964SRyan Zezeski 		VERIFY3U(tmp, <=, 255);
169*08855964SRyan Zezeski 		eh->ether_shost.ether_addr_octet[i] = tmp;
170*08855964SRyan Zezeski 		byte = end + 1;
171*08855964SRyan Zezeski 	}
172*08855964SRyan Zezeski 
173*08855964SRyan Zezeski 	eh->ether_type = etype;
174*08855964SRyan Zezeski 	return (eh);
175*08855964SRyan Zezeski }
176*08855964SRyan Zezeski 
177*08855964SRyan Zezeski void
mac_sw_cksum_ipv4_tcp_test(ktest_ctx_hdl_t * ctx)178*08855964SRyan Zezeski mac_sw_cksum_ipv4_tcp_test(ktest_ctx_hdl_t *ctx)
179*08855964SRyan Zezeski {
180*08855964SRyan Zezeski 	ddi_modhandle_t hdl = NULL;
181*08855964SRyan Zezeski 	mac_sw_cksum_ipv4_t mac_sw_cksum_ipv4 = NULL;
182*08855964SRyan Zezeski 	tcpha_t *tcp;
183*08855964SRyan Zezeski 	ipha_t *ip;
184*08855964SRyan Zezeski 	struct ether_header *eh;
185*08855964SRyan Zezeski 	mblk_t *mp = NULL;
186*08855964SRyan Zezeski 	char *msg = "...when it's not your turn";
187*08855964SRyan Zezeski 	size_t msglen = strlen(msg) + 1;
188*08855964SRyan Zezeski 	size_t mplen;
189*08855964SRyan Zezeski 	const char *err = "";
190*08855964SRyan Zezeski 	uint32_t sum;
191*08855964SRyan Zezeski 	size_t ehsz = sizeof (*eh);
192*08855964SRyan Zezeski 	size_t ipsz = sizeof (*ip);
193*08855964SRyan Zezeski 	size_t tcpsz = sizeof (*tcp);
194*08855964SRyan Zezeski 
195*08855964SRyan Zezeski 	if (ktest_hold_mod("mac", &hdl) != 0) {
196*08855964SRyan Zezeski 		KT_ERROR(ctx, "failed to hold 'mac' module");
197*08855964SRyan Zezeski 		return;
198*08855964SRyan Zezeski 	}
199*08855964SRyan Zezeski 
200*08855964SRyan Zezeski 	if (ktest_get_fn(hdl, "mac_sw_cksum_ipv4",
201*08855964SRyan Zezeski 	    (void **)&mac_sw_cksum_ipv4) != 0) {
202*08855964SRyan Zezeski 		KT_ERROR(ctx, "failed to resolve symbol %s`%s", "mac",
203*08855964SRyan Zezeski 		    "mac_sw_cksum_ipv4");
204*08855964SRyan Zezeski 		goto cleanup;
205*08855964SRyan Zezeski 	}
206*08855964SRyan Zezeski 
207*08855964SRyan Zezeski 	mplen = ehsz + ipsz + tcpsz + msglen;
208*08855964SRyan Zezeski 	mp = allocb(mplen, 0);
209*08855964SRyan Zezeski 	KT_EASSERT3P(mp, !=, NULL, ctx);
210*08855964SRyan Zezeski 	mp->b_wptr = mp->b_rptr + mplen;
211*08855964SRyan Zezeski 	tcp = mt_tcp_basic_hdr(mp, ehsz + ipsz, 2002, 2008, 1, 166, 0, 32000);
212*08855964SRyan Zezeski 	ip = mt_ipv4_simple_hdr(mp, ehsz, tcpsz + msglen, 410, IPPROTO_TCP,
213*08855964SRyan Zezeski 	    "192.168.2.4", "192.168.2.5");
214*08855964SRyan Zezeski 	eh = mt_ether_hdr(mp, 0, "f2:35:c2:72:26:57", "92:ce:5a:29:46:9d",
215*08855964SRyan Zezeski 	    ETHERTYPE_IP);
216*08855964SRyan Zezeski 
217*08855964SRyan Zezeski 	bcopy(msg, mp->b_rptr + ehsz + ipsz + tcpsz, msglen);
218*08855964SRyan Zezeski 
219*08855964SRyan Zezeski 	/*
220*08855964SRyan Zezeski 	 * It's important that we calculate the reference checksum
221*08855964SRyan Zezeski 	 * first, because mac_sw_cksum_ipv4() populates the checksum
222*08855964SRyan Zezeski 	 * field.
223*08855964SRyan Zezeski 	 */
224*08855964SRyan Zezeski 	sum = mt_pseudo_sum(IPPROTO_TCP, ip);
225*08855964SRyan Zezeski 	sum = mt_rfc1071_sum(sum, (uint16_t *)(mp->b_rptr + ehsz + ipsz),
226*08855964SRyan Zezeski 	    tcpsz + msglen);
227*08855964SRyan Zezeski 
228*08855964SRyan Zezeski 	/*
229*08855964SRyan Zezeski 	 * The internet checksum can never be 0xFFFF, as that would
230*08855964SRyan Zezeski 	 * indicate an input of all zeros.
231*08855964SRyan Zezeski 	 */
232*08855964SRyan Zezeski 	KT_ASSERT3UG(sum, !=, 0xFFFF, ctx, cleanup);
233*08855964SRyan Zezeski 	KT_ASSERTG(mac_sw_cksum_ipv4(mp, ehsz, ip, &err), ctx, cleanup);
234*08855964SRyan Zezeski 	KT_ASSERT3UG(tcp->tha_sum, !=, 0xFFFF, ctx, cleanup);
235*08855964SRyan Zezeski 	KT_ASSERT3UG(sum, ==, tcp->tha_sum, ctx, cleanup);
236*08855964SRyan Zezeski 	KT_PASS(ctx);
237*08855964SRyan Zezeski 
238*08855964SRyan Zezeski cleanup:
239*08855964SRyan Zezeski 	if (hdl != NULL) {
240*08855964SRyan Zezeski 		ktest_release_mod(hdl);
241*08855964SRyan Zezeski 	}
242*08855964SRyan Zezeski 
243*08855964SRyan Zezeski 	if (mp != NULL) {
244*08855964SRyan Zezeski 		freeb(mp);
245*08855964SRyan Zezeski 	}
246*08855964SRyan Zezeski }
247*08855964SRyan Zezeski 
248*08855964SRyan Zezeski /*
249*08855964SRyan Zezeski  * Verify that an unexpected IP protocol results in the expect
250*08855964SRyan Zezeski  * failure.
251*08855964SRyan Zezeski  */
252*08855964SRyan Zezeski void
mac_sw_cksum_ipv4_bad_proto_test(ktest_ctx_hdl_t * ctx)253*08855964SRyan Zezeski mac_sw_cksum_ipv4_bad_proto_test(ktest_ctx_hdl_t *ctx)
254*08855964SRyan Zezeski {
255*08855964SRyan Zezeski 	ddi_modhandle_t hdl = NULL;
256*08855964SRyan Zezeski 	mac_sw_cksum_ipv4_t mac_sw_cksum_ipv4 = NULL;
257*08855964SRyan Zezeski 	tcpha_t *tcp;
258*08855964SRyan Zezeski 	ipha_t *ip;
259*08855964SRyan Zezeski 	struct ether_header *eh;
260*08855964SRyan Zezeski 	mblk_t *mp = NULL;
261*08855964SRyan Zezeski 	char *msg = "...when it's not your turn";
262*08855964SRyan Zezeski 	size_t msglen = strlen(msg) + 1;
263*08855964SRyan Zezeski 	size_t mplen;
264*08855964SRyan Zezeski 	const char *err = "";
265*08855964SRyan Zezeski 	size_t ehsz = sizeof (*eh);
266*08855964SRyan Zezeski 	size_t ipsz = sizeof (*ip);
267*08855964SRyan Zezeski 	size_t tcpsz = sizeof (*tcp);
268*08855964SRyan Zezeski 
269*08855964SRyan Zezeski 	if (ktest_hold_mod("mac", &hdl) != 0) {
270*08855964SRyan Zezeski 		KT_ERROR(ctx, "failed to hold 'mac' module");
271*08855964SRyan Zezeski 		return;
272*08855964SRyan Zezeski 	}
273*08855964SRyan Zezeski 
274*08855964SRyan Zezeski 	if (ktest_get_fn(hdl, "mac_sw_cksum_ipv4",
275*08855964SRyan Zezeski 	    (void **)&mac_sw_cksum_ipv4) != 0) {
276*08855964SRyan Zezeski 		KT_ERROR(ctx, "failed to resolve symbol mac`mac_sw_cksum_ipv4");
277*08855964SRyan Zezeski 		goto cleanup;
278*08855964SRyan Zezeski 	}
279*08855964SRyan Zezeski 
280*08855964SRyan Zezeski 	mplen = ehsz + ipsz + tcpsz + msglen;
281*08855964SRyan Zezeski 	mp = allocb(mplen, 0);
282*08855964SRyan Zezeski 	KT_EASSERT3P(mp, !=, NULL, ctx);
283*08855964SRyan Zezeski 	mp->b_wptr = mp->b_rptr + mplen;
284*08855964SRyan Zezeski 	tcp = mt_tcp_basic_hdr(mp, ehsz + ipsz, 2002, 2008, 1, 166, 0, 32000);
285*08855964SRyan Zezeski 	ip = mt_ipv4_simple_hdr(mp, ehsz, tcpsz + msglen, 410, IPPROTO_ENCAP,
286*08855964SRyan Zezeski 	    "192.168.2.4", "192.168.2.5");
287*08855964SRyan Zezeski 	eh = mt_ether_hdr(mp, 0, "f2:35:c2:72:26:57", "92:ce:5a:29:46:9d",
288*08855964SRyan Zezeski 	    ETHERTYPE_IP);
289*08855964SRyan Zezeski 	bcopy(msg, mp->b_rptr + ehsz + ipsz + tcpsz, msglen);
290*08855964SRyan Zezeski 	KT_ASSERT0G(mac_sw_cksum_ipv4(mp, ehsz, ip, &err), ctx, cleanup);
291*08855964SRyan Zezeski 	KT_PASS(ctx);
292*08855964SRyan Zezeski 
293*08855964SRyan Zezeski cleanup:
294*08855964SRyan Zezeski 	if (hdl != NULL) {
295*08855964SRyan Zezeski 		ktest_release_mod(hdl);
296*08855964SRyan Zezeski 	}
297*08855964SRyan Zezeski 
298*08855964SRyan Zezeski 	if (mp != NULL) {
299*08855964SRyan Zezeski 		freeb(mp);
300*08855964SRyan Zezeski 	}
301*08855964SRyan Zezeski }
302*08855964SRyan Zezeski 
303*08855964SRyan Zezeski typedef struct snoop_pkt_record_hdr {
304*08855964SRyan Zezeski 	uint32_t	spr_orig_len;
305*08855964SRyan Zezeski 	uint32_t	spr_include_len;
306*08855964SRyan Zezeski 	uint32_t	spr_record_len;
307*08855964SRyan Zezeski 	uint32_t	spr_cumulative_drops;
308*08855964SRyan Zezeski 	uint32_t	spr_ts_secs;
309*08855964SRyan Zezeski 	uint32_t	spr_ts_micros;
310*08855964SRyan Zezeski } snoop_pkt_record_hdr_t;
311*08855964SRyan Zezeski 
312*08855964SRyan Zezeski typedef struct snoop_pkt {
313*08855964SRyan Zezeski 	uchar_t *sp_bytes;
314*08855964SRyan Zezeski 	uint16_t sp_len;
315*08855964SRyan Zezeski } snoop_pkt_t;
316*08855964SRyan Zezeski 
317*08855964SRyan Zezeski typedef struct snoop_iter {
318*08855964SRyan Zezeski 	uchar_t *sic_input;	/* beginning of stream */
319*08855964SRyan Zezeski 	uintptr_t sic_end;	/* end of stream */
320*08855964SRyan Zezeski 	uchar_t *sic_pos;	/* current position in stream */
321*08855964SRyan Zezeski 	uint_t sic_pkt_num;	/* current packet number, 1-based */
322*08855964SRyan Zezeski 	snoop_pkt_record_hdr_t *sic_pkt_hdr; /* current packet record header */
323*08855964SRyan Zezeski } snoop_iter_t;
324*08855964SRyan Zezeski 
325*08855964SRyan Zezeski #define	PAST_END(itr, len)	\
326*08855964SRyan Zezeski 	(((uintptr_t)(itr)->sic_pos + len) > itr->sic_end)
327*08855964SRyan Zezeski 
328*08855964SRyan Zezeski /*
329*08855964SRyan Zezeski  * Get the next packet in the snoop stream iterator returned by
330*08855964SRyan Zezeski  * mt_snoop_iter_get(). A copy of the packet is returned via the pkt
331*08855964SRyan Zezeski  * pointer. The caller provides the snoop_pkt_t, and this function
332*08855964SRyan Zezeski  * allocates a new buffer inside it to hold a copy of the packet's
333*08855964SRyan Zezeski  * bytes. It is the responsibility of the caller to free the copy. It
334*08855964SRyan Zezeski  * is recommended the caller make use of desballoc(9F) along with the
335*08855964SRyan Zezeski  * snoop_pkt_free() callback. When all the packets in the stream have
336*08855964SRyan Zezeski  * been read all subsequent calls to this function will set sp_bytes
337*08855964SRyan Zezeski  * to NULL and sp_len to 0.
338*08855964SRyan Zezeski  *
339*08855964SRyan Zezeski  * The caller may optionally specify an rhdr argument in order to
340*08855964SRyan Zezeski  * receive a pointer to the packet record header (unlike the packet
341*08855964SRyan Zezeski  * bytes this is a pointer into the stream, not a copy).
342*08855964SRyan Zezeski  */
343*08855964SRyan Zezeski static int
mt_snoop_iter_next(ktest_ctx_hdl_t * ctx,snoop_iter_t * itr,snoop_pkt_t * pkt,snoop_pkt_record_hdr_t ** rhdr)344*08855964SRyan Zezeski mt_snoop_iter_next(ktest_ctx_hdl_t *ctx, snoop_iter_t *itr, snoop_pkt_t *pkt,
345*08855964SRyan Zezeski     snoop_pkt_record_hdr_t **rhdr)
346*08855964SRyan Zezeski {
347*08855964SRyan Zezeski 	uchar_t *pkt_start;
348*08855964SRyan Zezeski 
349*08855964SRyan Zezeski 	/*
350*08855964SRyan Zezeski 	 * We've read exactly the number of bytes expected, this is
351*08855964SRyan Zezeski 	 * the end.
352*08855964SRyan Zezeski 	 */
353*08855964SRyan Zezeski 	if ((uintptr_t)(itr->sic_pos) == itr->sic_end) {
354*08855964SRyan Zezeski 		pkt->sp_bytes = NULL;
355*08855964SRyan Zezeski 		pkt->sp_len = 0;
356*08855964SRyan Zezeski 
357*08855964SRyan Zezeski 		if (rhdr != NULL)
358*08855964SRyan Zezeski 			*rhdr = NULL;
359*08855964SRyan Zezeski 
360*08855964SRyan Zezeski 		return (0);
361*08855964SRyan Zezeski 	}
362*08855964SRyan Zezeski 
363*08855964SRyan Zezeski 	/*
364*08855964SRyan Zezeski 	 * A corrupted record or truncated stream could point us past
365*08855964SRyan Zezeski 	 * the end of the stream.
366*08855964SRyan Zezeski 	 */
367*08855964SRyan Zezeski 	if (PAST_END(itr, sizeof (snoop_pkt_record_hdr_t))) {
368*08855964SRyan Zezeski 		KT_ERROR(ctx, "record corrupted or stream truncated, read past "
369*08855964SRyan Zezeski 		    "end of stream for record header #%d: 0x%p + %u > 0x%p",
370*08855964SRyan Zezeski 		    itr->sic_pkt_num, itr->sic_pos,
371*08855964SRyan Zezeski 		    sizeof (snoop_pkt_record_hdr_t), itr->sic_end);
372*08855964SRyan Zezeski 		return (EIO);
373*08855964SRyan Zezeski 	}
374*08855964SRyan Zezeski 
375*08855964SRyan Zezeski 	itr->sic_pkt_hdr = (snoop_pkt_record_hdr_t *)itr->sic_pos;
376*08855964SRyan Zezeski 	pkt_start = itr->sic_pos + sizeof (snoop_pkt_record_hdr_t);
377*08855964SRyan Zezeski 
378*08855964SRyan Zezeski 	/*
379*08855964SRyan Zezeski 	 * A corrupted record or truncated stream could point us past
380*08855964SRyan Zezeski 	 * the end of the stream.
381*08855964SRyan Zezeski 	 */
382*08855964SRyan Zezeski 	if (PAST_END(itr, ntohl(itr->sic_pkt_hdr->spr_record_len))) {
383*08855964SRyan Zezeski 		KT_ERROR(ctx, "record corrupted or stream truncated, read past "
384*08855964SRyan Zezeski 		    "end of stream for record #%d: 0x%p + %u > 0x%p",
385*08855964SRyan Zezeski 		    itr->sic_pkt_num, itr->sic_pos,
386*08855964SRyan Zezeski 		    ntohl(itr->sic_pkt_hdr->spr_record_len), itr->sic_end);
387*08855964SRyan Zezeski 		return (EIO);
388*08855964SRyan Zezeski 	}
389*08855964SRyan Zezeski 
390*08855964SRyan Zezeski 	pkt->sp_len = ntohl(itr->sic_pkt_hdr->spr_include_len);
391*08855964SRyan Zezeski 	pkt->sp_bytes = kmem_zalloc(pkt->sp_len, KM_SLEEP);
392*08855964SRyan Zezeski 	bcopy(pkt_start, pkt->sp_bytes, pkt->sp_len);
393*08855964SRyan Zezeski 	itr->sic_pos += ntohl(itr->sic_pkt_hdr->spr_record_len);
394*08855964SRyan Zezeski 	itr->sic_pkt_num++;
395*08855964SRyan Zezeski 
396*08855964SRyan Zezeski 	if (rhdr != NULL) {
397*08855964SRyan Zezeski 		*rhdr = itr->sic_pkt_hdr;
398*08855964SRyan Zezeski 	}
399*08855964SRyan Zezeski 
400*08855964SRyan Zezeski 	return (0);
401*08855964SRyan Zezeski }
402*08855964SRyan Zezeski 
403*08855964SRyan Zezeski /*
404*08855964SRyan Zezeski  * Parse a snoop data stream (RFC 1761) provided by input and return
405*08855964SRyan Zezeski  * a packet iterator to be used by mt_snoop_iter_next().
406*08855964SRyan Zezeski  */
407*08855964SRyan Zezeski static int
mt_snoop_iter_get(ktest_ctx_hdl_t * ctx,uchar_t * input,const uint_t input_len,snoop_iter_t ** itr_out)408*08855964SRyan Zezeski mt_snoop_iter_get(ktest_ctx_hdl_t *ctx, uchar_t *input, const uint_t input_len,
409*08855964SRyan Zezeski     snoop_iter_t **itr_out)
410*08855964SRyan Zezeski {
411*08855964SRyan Zezeski 	const uchar_t id[8] = { 's', 'n', 'o', 'o', 'p', '\0', '\0', '\0' };
412*08855964SRyan Zezeski 	uint32_t version;
413*08855964SRyan Zezeski 	uint32_t datalink;
414*08855964SRyan Zezeski 	snoop_iter_t *itr;
415*08855964SRyan Zezeski 
416*08855964SRyan Zezeski 	*itr_out = NULL;
417*08855964SRyan Zezeski 
418*08855964SRyan Zezeski 	if (input_len < 16) {
419*08855964SRyan Zezeski 		KT_ERROR(ctx, "snoop stream truncated at file header: %u < %u ",
420*08855964SRyan Zezeski 		    input_len, 16);
421*08855964SRyan Zezeski 		return (ENOBUFS);
422*08855964SRyan Zezeski 	}
423*08855964SRyan Zezeski 
424*08855964SRyan Zezeski 	if (memcmp(input, &id, sizeof (id)) != 0) {
425*08855964SRyan Zezeski 		KT_ERROR(ctx, "snoop stream malformed identification: %x %x %x "
426*08855964SRyan Zezeski 		    "%x %x %x %x %x", input[0], input[1], input[2], input[3],
427*08855964SRyan Zezeski 		    input[4], input[5], input[6], input[7]);
428*08855964SRyan Zezeski 		return (EINVAL);
429*08855964SRyan Zezeski 	}
430*08855964SRyan Zezeski 
431*08855964SRyan Zezeski 	itr = kmem_zalloc(sizeof (*itr), KM_SLEEP);
432*08855964SRyan Zezeski 	itr->sic_input = input;
433*08855964SRyan Zezeski 	itr->sic_end = (uintptr_t)input + input_len;
434*08855964SRyan Zezeski 	itr->sic_pos = input + sizeof (id);
435*08855964SRyan Zezeski 	itr->sic_pkt_num = 1;
436*08855964SRyan Zezeski 	itr->sic_pkt_hdr = NULL;
437*08855964SRyan Zezeski 	version = ntohl(*(uint32_t *)itr->sic_pos);
438*08855964SRyan Zezeski 
439*08855964SRyan Zezeski 	if (version != 2) {
440*08855964SRyan Zezeski 		KT_ERROR(ctx, "snoop stream bad version: %u != %u", version, 2);
441*08855964SRyan Zezeski 		return (EINVAL);
442*08855964SRyan Zezeski 	}
443*08855964SRyan Zezeski 
444*08855964SRyan Zezeski 	itr->sic_pos += sizeof (version);
445*08855964SRyan Zezeski 	datalink = ntohl(*(uint32_t *)itr->sic_pos);
446*08855964SRyan Zezeski 
447*08855964SRyan Zezeski 	/* We expect only Ethernet. */
448*08855964SRyan Zezeski 	if (datalink != DL_ETHER) {
449*08855964SRyan Zezeski 		KT_ERROR(ctx, "snoop stream bad datalink type: %u != %u",
450*08855964SRyan Zezeski 		    datalink, DL_ETHER);
451*08855964SRyan Zezeski 		kmem_free(itr, sizeof (*itr));
452*08855964SRyan Zezeski 		return (EINVAL);
453*08855964SRyan Zezeski 	}
454*08855964SRyan Zezeski 
455*08855964SRyan Zezeski 	itr->sic_pos += sizeof (datalink);
456*08855964SRyan Zezeski 	*itr_out = itr;
457*08855964SRyan Zezeski 	return (0);
458*08855964SRyan Zezeski }
459*08855964SRyan Zezeski 
460*08855964SRyan Zezeski static void
snoop_pkt_free(snoop_pkt_t * pkt)461*08855964SRyan Zezeski snoop_pkt_free(snoop_pkt_t *pkt)
462*08855964SRyan Zezeski {
463*08855964SRyan Zezeski 	kmem_free(pkt->sp_bytes, pkt->sp_len);
464*08855964SRyan Zezeski }
465*08855964SRyan Zezeski 
466*08855964SRyan Zezeski /*
467*08855964SRyan Zezeski  * Verify mac_sw_cksum_ipv4() against an arbitrary TCP stream read
468*08855964SRyan Zezeski  * from the snoop capture given as input. In order to verify the
469*08855964SRyan Zezeski  * checksum all TCP/IPv4 packets must be captured in full. The snoop
470*08855964SRyan Zezeski  * capture may contain non-TCP/IPv4 packets, which will be skipped
471*08855964SRyan Zezeski  * over. If not a single TCP/IPv4 packet is found, the test will
472*08855964SRyan Zezeski  * report an error.
473*08855964SRyan Zezeski  */
474*08855964SRyan Zezeski void
mac_sw_cksum_ipv4_snoop_test(ktest_ctx_hdl_t * ctx)475*08855964SRyan Zezeski mac_sw_cksum_ipv4_snoop_test(ktest_ctx_hdl_t *ctx)
476*08855964SRyan Zezeski {
477*08855964SRyan Zezeski 	ddi_modhandle_t hdl = NULL;
478*08855964SRyan Zezeski 	mac_sw_cksum_ipv4_t mac_sw_cksum_ipv4 = NULL;
479*08855964SRyan Zezeski 	uchar_t *bytes;
480*08855964SRyan Zezeski 	size_t num_bytes = 0;
481*08855964SRyan Zezeski 	uint_t pkt_num = 0;
482*08855964SRyan Zezeski 	tcpha_t *tcp;
483*08855964SRyan Zezeski 	ipha_t *ip;
484*08855964SRyan Zezeski 	struct ether_header *eh;
485*08855964SRyan Zezeski 	mblk_t *mp = NULL;
486*08855964SRyan Zezeski 	const char *err = "";
487*08855964SRyan Zezeski 	uint32_t csum;
488*08855964SRyan Zezeski 	size_t ehsz, ipsz, tcpsz, msglen;
489*08855964SRyan Zezeski 	snoop_iter_t *itr = NULL;
490*08855964SRyan Zezeski 	snoop_pkt_record_hdr_t *hdr = NULL;
491*08855964SRyan Zezeski 	boolean_t at_least_one = B_FALSE;
492*08855964SRyan Zezeski 	snoop_pkt_t pkt;
493*08855964SRyan Zezeski 	int ret;
494*08855964SRyan Zezeski 
495*08855964SRyan Zezeski 	if (ktest_hold_mod("mac", &hdl) != 0) {
496*08855964SRyan Zezeski 		KT_ERROR(ctx, "failed to hold 'mac' module");
497*08855964SRyan Zezeski 		return;
498*08855964SRyan Zezeski 	}
499*08855964SRyan Zezeski 
500*08855964SRyan Zezeski 	if (ktest_get_fn(hdl, "mac_sw_cksum_ipv4",
501*08855964SRyan Zezeski 	    (void **)&mac_sw_cksum_ipv4) != 0) {
502*08855964SRyan Zezeski 		KT_ERROR(ctx, "failed to resolve symbol mac`mac_sw_cksum_ipv4");
503*08855964SRyan Zezeski 		return;
504*08855964SRyan Zezeski 	}
505*08855964SRyan Zezeski 
506*08855964SRyan Zezeski 	ktest_get_input(ctx, &bytes, &num_bytes);
507*08855964SRyan Zezeski 	ret = mt_snoop_iter_get(ctx, bytes, num_bytes, &itr);
508*08855964SRyan Zezeski 	if (ret != 0) {
509*08855964SRyan Zezeski 		/* mt_snoop_iter_get() already set error context. */
510*08855964SRyan Zezeski 		goto cleanup;
511*08855964SRyan Zezeski 	}
512*08855964SRyan Zezeski 
513*08855964SRyan Zezeski 	bzero(&pkt, sizeof (pkt));
514*08855964SRyan Zezeski 
515*08855964SRyan Zezeski 	while ((ret = mt_snoop_iter_next(ctx, itr, &pkt, &hdr)) == 0) {
516*08855964SRyan Zezeski 		frtn_t frtn;
517*08855964SRyan Zezeski 
518*08855964SRyan Zezeski 		if (pkt.sp_len == 0) {
519*08855964SRyan Zezeski 			break;
520*08855964SRyan Zezeski 		}
521*08855964SRyan Zezeski 
522*08855964SRyan Zezeski 		pkt_num++;
523*08855964SRyan Zezeski 
524*08855964SRyan Zezeski 		/*
525*08855964SRyan Zezeski 		 * Prepend the packet record number to any
526*08855964SRyan Zezeski 		 * fail/skip/error message so the user knows which
527*08855964SRyan Zezeski 		 * record in the snoop stream to inspect.
528*08855964SRyan Zezeski 		 */
529*08855964SRyan Zezeski 		ktest_msg_prepend(ctx, "pkt #%u: ", pkt_num);
530*08855964SRyan Zezeski 
531*08855964SRyan Zezeski 		/* IPv4 only */
532*08855964SRyan Zezeski 		if (hdr->spr_include_len < (sizeof (*eh) + sizeof (*ip))) {
533*08855964SRyan Zezeski 			continue;
534*08855964SRyan Zezeski 		}
535*08855964SRyan Zezeski 
536*08855964SRyan Zezeski 		/* fully recorded packets only */
537*08855964SRyan Zezeski 		if (hdr->spr_include_len != hdr->spr_orig_len) {
538*08855964SRyan Zezeski 			continue;
539*08855964SRyan Zezeski 		}
540*08855964SRyan Zezeski 
541*08855964SRyan Zezeski 		frtn.free_func = snoop_pkt_free;
542*08855964SRyan Zezeski 		frtn.free_arg = (caddr_t)&pkt;
543*08855964SRyan Zezeski 		mp = desballoc(pkt.sp_bytes, pkt.sp_len, 0, &frtn);
544*08855964SRyan Zezeski 		KT_EASSERT3PG(mp, !=, NULL, ctx, cleanup);
545*08855964SRyan Zezeski 		mp->b_wptr += pkt.sp_len;
546*08855964SRyan Zezeski 		eh = (struct ether_header *)mp->b_rptr;
547*08855964SRyan Zezeski 		ehsz = sizeof (*eh);
548*08855964SRyan Zezeski 
549*08855964SRyan Zezeski 		/* IPv4 only */
550*08855964SRyan Zezeski 		if (ntohs(eh->ether_type) != ETHERTYPE_IP) {
551*08855964SRyan Zezeski 			freeb(mp);
552*08855964SRyan Zezeski 			mp = NULL;
553*08855964SRyan Zezeski 			continue;
554*08855964SRyan Zezeski 		}
555*08855964SRyan Zezeski 
556*08855964SRyan Zezeski 		ip = (ipha_t *)(mp->b_rptr + ehsz);
557*08855964SRyan Zezeski 		ipsz = sizeof (*ip);
558*08855964SRyan Zezeski 
559*08855964SRyan Zezeski 		if (ip->ipha_protocol == IPPROTO_TCP) {
560*08855964SRyan Zezeski 			tcp = (tcpha_t *)(mp->b_rptr + sizeof (*eh) +
561*08855964SRyan Zezeski 			    sizeof (*ip));
562*08855964SRyan Zezeski 			tcpsz = TCP_HDR_LENGTH(tcp);
563*08855964SRyan Zezeski 			msglen = ntohs(ip->ipha_length) - (ipsz + tcpsz);
564*08855964SRyan Zezeski 
565*08855964SRyan Zezeski 			/* Let's make sure we don't run off into space. */
566*08855964SRyan Zezeski 			if ((tcpsz + msglen) > (pkt.sp_len - (ehsz + ipsz))) {
567*08855964SRyan Zezeski 				KT_ERROR(ctx, "(tcpsz=%lu + msglen=%lu) > "
568*08855964SRyan Zezeski 				    "(pkt_len=%lu - (ehsz=%lu + ipsz=%lu))",
569*08855964SRyan Zezeski 				    tcpsz, msglen, pkt.sp_len, ehsz, ipsz);
570*08855964SRyan Zezeski 				goto cleanup;
571*08855964SRyan Zezeski 			}
572*08855964SRyan Zezeski 
573*08855964SRyan Zezeski 			/*
574*08855964SRyan Zezeski 			 * As we are reading a snoop input stream we
575*08855964SRyan Zezeski 			 * need to make sure to zero out any existing
576*08855964SRyan Zezeski 			 * checksum.
577*08855964SRyan Zezeski 			 */
578*08855964SRyan Zezeski 			tcp->tha_sum = 0;
579*08855964SRyan Zezeski 			csum = mt_pseudo_sum(IPPROTO_TCP, ip);
580*08855964SRyan Zezeski 			csum = mt_rfc1071_sum(csum,
581*08855964SRyan Zezeski 			    (uint16_t *)(mp->b_rptr + ehsz + ipsz),
582*08855964SRyan Zezeski 			    tcpsz + msglen);
583*08855964SRyan Zezeski 		} else {
584*08855964SRyan Zezeski 			freeb(mp);
585*08855964SRyan Zezeski 			mp = NULL;
586*08855964SRyan Zezeski 			continue;
587*08855964SRyan Zezeski 		}
588*08855964SRyan Zezeski 
589*08855964SRyan Zezeski 		/*
590*08855964SRyan Zezeski 		 * The internet checksum can never be 0xFFFF, as that
591*08855964SRyan Zezeski 		 * would indicate an input of all zeros.
592*08855964SRyan Zezeski 		 */
593*08855964SRyan Zezeski 		KT_ASSERT3UG(csum, !=, 0xFFFF, ctx, cleanup);
594*08855964SRyan Zezeski 		KT_ASSERTG(mac_sw_cksum_ipv4(mp, ehsz, ip, &err), ctx, cleanup);
595*08855964SRyan Zezeski 		KT_ASSERT3UG(tcp->tha_sum, !=, 0xFFFF, ctx, cleanup);
596*08855964SRyan Zezeski 		KT_ASSERT3UG(tcp->tha_sum, ==, csum, ctx, cleanup);
597*08855964SRyan Zezeski 		at_least_one = B_TRUE;
598*08855964SRyan Zezeski 		freeb(mp);
599*08855964SRyan Zezeski 		mp = NULL;
600*08855964SRyan Zezeski 
601*08855964SRyan Zezeski 		/*
602*08855964SRyan Zezeski 		 * Clear the prepended message for the iterator call
603*08855964SRyan Zezeski 		 * as it already includes the current record number
604*08855964SRyan Zezeski 		 * (and pkt_num is not incremented, thus incorrect,
605*08855964SRyan Zezeski 		 * until after a successful call).
606*08855964SRyan Zezeski 		 */
607*08855964SRyan Zezeski 		ktest_msg_clear(ctx);
608*08855964SRyan Zezeski 	}
609*08855964SRyan Zezeski 
610*08855964SRyan Zezeski 	if (ret != 0) {
611*08855964SRyan Zezeski 		/* mt_snoop_next() already set error context. */
612*08855964SRyan Zezeski 		goto cleanup;
613*08855964SRyan Zezeski 	}
614*08855964SRyan Zezeski 
615*08855964SRyan Zezeski 	if (at_least_one) {
616*08855964SRyan Zezeski 		KT_PASS(ctx);
617*08855964SRyan Zezeski 	} else {
618*08855964SRyan Zezeski 		ktest_msg_clear(ctx);
619*08855964SRyan Zezeski 		KT_ERROR(ctx, "at least one TCP/IPv4 packet expected");
620*08855964SRyan Zezeski 	}
621*08855964SRyan Zezeski 
622*08855964SRyan Zezeski cleanup:
623*08855964SRyan Zezeski 	if (hdl != NULL) {
624*08855964SRyan Zezeski 		ktest_release_mod(hdl);
625*08855964SRyan Zezeski 	}
626*08855964SRyan Zezeski 
627*08855964SRyan Zezeski 	if (mp != NULL) {
628*08855964SRyan Zezeski 		freeb(mp);
629*08855964SRyan Zezeski 	}
630*08855964SRyan Zezeski 
631*08855964SRyan Zezeski 	if (itr != NULL) {
632*08855964SRyan Zezeski 		kmem_free(itr, sizeof (*itr));
633*08855964SRyan Zezeski 	}
634*08855964SRyan Zezeski }
635*08855964SRyan Zezeski 
636*08855964SRyan Zezeski static struct modlmisc mac_test_modlmisc = {
637*08855964SRyan Zezeski 	.misc_modops = &mod_miscops,
638*08855964SRyan Zezeski 	.misc_linkinfo = "mac ktest module"
639*08855964SRyan Zezeski };
640*08855964SRyan Zezeski 
641*08855964SRyan Zezeski static struct modlinkage mac_test_modlinkage = {
642*08855964SRyan Zezeski 	.ml_rev = MODREV_1,
643*08855964SRyan Zezeski 	.ml_linkage = { &mac_test_modlmisc, NULL }
644*08855964SRyan Zezeski };
645*08855964SRyan Zezeski 
646*08855964SRyan Zezeski int
_init()647*08855964SRyan Zezeski _init()
648*08855964SRyan Zezeski {
649*08855964SRyan Zezeski 	int ret;
650*08855964SRyan Zezeski 	ktest_module_hdl_t *km = NULL;
651*08855964SRyan Zezeski 	ktest_suite_hdl_t *ks = NULL;
652*08855964SRyan Zezeski 
653*08855964SRyan Zezeski 	VERIFY0(ktest_create_module("mac", &km));
654*08855964SRyan Zezeski 	VERIFY0(ktest_add_suite(km, "checksum", &ks));
655*08855964SRyan Zezeski 	VERIFY0(ktest_add_test(ks, "mac_sw_cksum_ipv4_tcp_test",
656*08855964SRyan Zezeski 	    mac_sw_cksum_ipv4_tcp_test, KTEST_FLAG_NONE));
657*08855964SRyan Zezeski 	VERIFY0(ktest_add_test(ks, "mac_sw_cksum_ipv4_bad_proto_test",
658*08855964SRyan Zezeski 	    mac_sw_cksum_ipv4_bad_proto_test, KTEST_FLAG_NONE));
659*08855964SRyan Zezeski 	VERIFY0(ktest_add_test(ks, "mac_sw_cksum_ipv4_snoop_test",
660*08855964SRyan Zezeski 	    mac_sw_cksum_ipv4_snoop_test, KTEST_FLAG_INPUT));
661*08855964SRyan Zezeski 
662*08855964SRyan Zezeski 	if ((ret = ktest_register_module(km)) != 0) {
663*08855964SRyan Zezeski 		ktest_free_module(km);
664*08855964SRyan Zezeski 		return (ret);
665*08855964SRyan Zezeski 	}
666*08855964SRyan Zezeski 
667*08855964SRyan Zezeski 	if ((ret = mod_install(&mac_test_modlinkage)) != 0) {
668*08855964SRyan Zezeski 		ktest_unregister_module("mac");
669*08855964SRyan Zezeski 		return (ret);
670*08855964SRyan Zezeski 	}
671*08855964SRyan Zezeski 
672*08855964SRyan Zezeski 	return (0);
673*08855964SRyan Zezeski }
674*08855964SRyan Zezeski 
675*08855964SRyan Zezeski int
_fini(void)676*08855964SRyan Zezeski _fini(void)
677*08855964SRyan Zezeski {
678*08855964SRyan Zezeski 	ktest_unregister_module("mac");
679*08855964SRyan Zezeski 	return (mod_remove(&mac_test_modlinkage));
680*08855964SRyan Zezeski }
681*08855964SRyan Zezeski 
682*08855964SRyan Zezeski int
_info(struct modinfo * modinfop)683*08855964SRyan Zezeski _info(struct modinfo *modinfop)
684*08855964SRyan Zezeski {
685*08855964SRyan Zezeski 	return (mod_info(&mac_test_modlinkage, modinfop));
686*08855964SRyan Zezeski }
687