1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2019 Joyent, Inc.
14 */
15
16 /*
17 * varpd client interfaces
18 */
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <umem.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <strings.h>
28 #include <door.h>
29
30 #include <libvarpd_impl.h>
31
32 typedef struct varpd_client {
33 int vcl_doorfd;
34 } varpd_client_t;
35
36 typedef struct varpd_client_prop_info {
37 varpd_client_t *vcprop_client;
38 uint64_t vcprop_instance;
39 uint_t vcprop_propid;
40 uint_t vcprop_type;
41 uint_t vcprop_prot;
42 uint32_t vcprop_defsize;
43 uint32_t vcprop_psize;
44 char vcprop_name[LIBVARPD_PROP_NAMELEN];
45 uint8_t vcprop_default[LIBVARPD_PROP_SIZEMAX];
46 uint8_t vcprop_poss[LIBVARPD_PROP_SIZEMAX];
47 } varpd_client_prop_info_t;
48
49 static int
libvarpd_c_door_call(varpd_client_t * client,varpd_client_arg_t * argp,size_t altsize)50 libvarpd_c_door_call(varpd_client_t *client, varpd_client_arg_t *argp,
51 size_t altsize)
52 {
53 int ret;
54 door_arg_t darg;
55
56 darg.data_ptr = (char *)argp;
57 darg.desc_ptr = NULL;
58 darg.desc_num = 0;
59 darg.rbuf = (char *)argp;
60 if (altsize != 0) {
61 darg.data_size = altsize;
62 darg.rsize = altsize;
63 } else {
64 darg.data_size = sizeof (varpd_client_arg_t);
65 darg.rsize = sizeof (varpd_client_arg_t);
66 }
67
68 do {
69 ret = door_call(client->vcl_doorfd, &darg);
70 } while (ret != 0 && errno == EINTR);
71 if (ret != 0) {
72 switch (errno) {
73 case E2BIG:
74 case EFAULT:
75 case EINVAL:
76 case ENOTSUP:
77 case EOVERFLOW:
78 case ENFILE:
79 libvarpd_panic("unhandleable errno from door_call: %d",
80 errno);
81 }
82 ret = errno;
83 }
84
85 return (ret);
86 }
87
88 int
libvarpd_c_create(varpd_client_handle_t ** chpp,const char * doorname)89 libvarpd_c_create(varpd_client_handle_t **chpp, const char *doorname)
90 {
91 varpd_client_t *client;
92
93 client = umem_alloc(sizeof (varpd_client_t), UMEM_DEFAULT);
94 if (client == NULL)
95 return (ENOMEM);
96
97 client->vcl_doorfd = open(doorname, O_RDWR);
98 if (client->vcl_doorfd < 0) {
99 int ret = errno;
100 umem_free(client, sizeof (varpd_client_t));
101 return (ret);
102 }
103
104 *chpp = (varpd_client_handle_t *)client;
105 return (0);
106 }
107
108 void
libvarpd_c_destroy(varpd_client_handle_t * chp)109 libvarpd_c_destroy(varpd_client_handle_t *chp)
110 {
111 varpd_client_t *client = (varpd_client_t *)chp;
112 if (close(client->vcl_doorfd) != 0)
113 libvarpd_panic("failed to close door fd %d: %d",
114 client->vcl_doorfd, errno);
115
116 umem_free(chp, sizeof (varpd_client_t));
117 }
118
119 int
libvarpd_c_instance_create(varpd_client_handle_t * chp,datalink_id_t linkid,const char * search,uint64_t * cidp)120 libvarpd_c_instance_create(varpd_client_handle_t *chp, datalink_id_t linkid,
121 const char *search, uint64_t *cidp)
122 {
123 int ret;
124 varpd_client_t *client = (varpd_client_t *)chp;
125 varpd_client_arg_t carg;
126 varpd_client_create_arg_t *cap = &carg.vca_un.vca_create;
127
128 if (strlen(search) >= LIBVARPD_PROP_NAMELEN)
129 return (EINVAL);
130 carg.vca_command = VARPD_CLIENT_CREATE;
131 carg.vca_errno = 0;
132 cap->vcca_linkid = linkid;
133 (void) strlcpy(cap->vcca_plugin, search, LIBVARPD_PROP_NAMELEN);
134
135 ret = libvarpd_c_door_call(client, &carg, 0);
136 if (ret != 0)
137 return (ret);
138
139 if (carg.vca_errno != 0)
140 return (carg.vca_errno);
141
142 *cidp = cap->vcca_id;
143
144 return (0);
145 }
146
147 int
libvarpd_c_instance_activate(varpd_client_handle_t * chp,uint64_t cid)148 libvarpd_c_instance_activate(varpd_client_handle_t *chp, uint64_t cid)
149 {
150 int ret;
151 varpd_client_t *client = (varpd_client_t *)chp;
152 varpd_client_arg_t carg;
153 varpd_client_instance_arg_t *vciap = &carg.vca_un.vca_instance;
154
155 carg.vca_command = VARPD_CLIENT_ACTIVATE;
156 carg.vca_errno = 0;
157 vciap->vcia_id = cid;
158
159 ret = libvarpd_c_door_call(client, &carg, 0);
160 if (ret != 0)
161 return (ret);
162
163 if (carg.vca_errno != 0)
164 return (carg.vca_errno);
165
166 return (0);
167 }
168
169 int
libvarpd_c_instance_destroy(varpd_client_handle_t * chp,uint64_t cid)170 libvarpd_c_instance_destroy(varpd_client_handle_t *chp, uint64_t cid)
171 {
172 int ret;
173 varpd_client_t *client = (varpd_client_t *)chp;
174 varpd_client_arg_t carg;
175 varpd_client_instance_arg_t *vciap = &carg.vca_un.vca_instance;
176
177 carg.vca_command = VARPD_CLIENT_DESTROY;
178 carg.vca_errno = 0;
179 vciap->vcia_id = cid;
180
181 ret = libvarpd_c_door_call(client, &carg, 0);
182 if (ret != 0)
183 return (ret);
184
185 if (carg.vca_errno != 0)
186 return (carg.vca_errno);
187
188 return (0);
189 }
190
191 int
libvarpd_c_prop_nprops(varpd_client_handle_t * chp,uint64_t cid,uint_t * nprops)192 libvarpd_c_prop_nprops(varpd_client_handle_t *chp, uint64_t cid, uint_t *nprops)
193 {
194 int ret;
195 varpd_client_t *client = (varpd_client_t *)chp;
196 varpd_client_arg_t carg;
197 varpd_client_nprops_arg_t *vcnap = &carg.vca_un.vca_nprops;
198
199 carg.vca_command = VARPD_CLIENT_NPROPS;
200 carg.vca_errno = 0;
201 vcnap->vcna_id = cid;
202 vcnap->vcna_nprops = 0;
203
204 ret = libvarpd_c_door_call(client, &carg, 0);
205 if (ret != 0)
206 return (ret);
207
208 if (carg.vca_errno != 0)
209 return (carg.vca_errno);
210 *nprops = vcnap->vcna_nprops;
211 return (0);
212 }
213
214 int
libvarpd_c_prop_handle_alloc(varpd_client_handle_t * chp,uint64_t cid,varpd_client_prop_handle_t ** phdlp)215 libvarpd_c_prop_handle_alloc(varpd_client_handle_t *chp, uint64_t cid,
216 varpd_client_prop_handle_t **phdlp)
217 {
218 varpd_client_prop_info_t *infop;
219
220 infop = umem_alloc(sizeof (varpd_client_prop_info_t), UMEM_DEFAULT);
221 if (infop == NULL)
222 return (ENOMEM);
223
224 bzero(infop, sizeof (varpd_client_prop_info_t));
225 infop->vcprop_client = (varpd_client_t *)chp;
226 infop->vcprop_instance = cid;
227 infop->vcprop_propid = UINT_MAX;
228 *phdlp = (varpd_client_prop_handle_t *)infop;
229 return (0);
230 }
231
232 void
libvarpd_c_prop_handle_free(varpd_client_prop_handle_t * phdl)233 libvarpd_c_prop_handle_free(varpd_client_prop_handle_t *phdl)
234 {
235 umem_free(phdl, sizeof (varpd_client_prop_info_t));
236 phdl = NULL;
237 }
238
239 static void
libvarpd_c_prop_info_from_door(varpd_client_prop_info_t * infop,const varpd_client_propinfo_arg_t * vcfap)240 libvarpd_c_prop_info_from_door(varpd_client_prop_info_t *infop,
241 const varpd_client_propinfo_arg_t *vcfap)
242 {
243 infop->vcprop_propid = vcfap->vcfa_propid;
244 infop->vcprop_type = vcfap->vcfa_type;
245 infop->vcprop_prot = vcfap->vcfa_prot;
246 infop->vcprop_defsize = vcfap->vcfa_defsize;
247 infop->vcprop_psize = vcfap->vcfa_psize;
248 bcopy(vcfap->vcfa_name, infop->vcprop_name, LIBVARPD_PROP_NAMELEN);
249 bcopy(vcfap->vcfa_default, infop->vcprop_default,
250 LIBVARPD_PROP_SIZEMAX);
251 bcopy(vcfap->vcfa_poss, infop->vcprop_poss, LIBVARPD_PROP_SIZEMAX);
252 }
253
254 int
libvarpd_c_prop_info_fill_by_name(varpd_client_prop_handle_t * phdl,const char * name)255 libvarpd_c_prop_info_fill_by_name(varpd_client_prop_handle_t *phdl,
256 const char *name)
257 {
258 int ret;
259 varpd_client_arg_t carg;
260 varpd_client_propinfo_arg_t *vcfap = &carg.vca_un.vca_info;
261 varpd_client_prop_info_t *infop = (varpd_client_prop_info_t *)phdl;
262
263 if (strlen(name) >= LIBVARPD_PROP_NAMELEN)
264 return (EINVAL);
265 bzero(&carg, sizeof (varpd_client_arg_t));
266 carg.vca_command = VARPD_CLIENT_PROPINFO;
267 carg.vca_errno = 0;
268 vcfap->vcfa_id = infop->vcprop_instance;
269 vcfap->vcfa_propid = UINT_MAX;
270 (void) strlcpy(vcfap->vcfa_name, name, LIBVARPD_PROP_NAMELEN);
271
272 ret = libvarpd_c_door_call(infop->vcprop_client, &carg, 0);
273 if (ret != 0)
274 return (ret);
275
276 if (carg.vca_errno != 0)
277 return (carg.vca_errno);
278
279 libvarpd_c_prop_info_from_door(infop, vcfap);
280 return (0);
281 }
282
283 int
libvarpd_c_prop_info_fill(varpd_client_prop_handle_t * phdl,uint_t propid)284 libvarpd_c_prop_info_fill(varpd_client_prop_handle_t *phdl, uint_t propid)
285 {
286 int ret;
287 varpd_client_arg_t carg;
288 varpd_client_propinfo_arg_t *vcfap = &carg.vca_un.vca_info;
289 varpd_client_prop_info_t *infop = (varpd_client_prop_info_t *)phdl;
290
291 bzero(&carg, sizeof (varpd_client_arg_t));
292 carg.vca_command = VARPD_CLIENT_PROPINFO;
293 carg.vca_errno = 0;
294 vcfap->vcfa_id = infop->vcprop_instance;
295 vcfap->vcfa_propid = propid;
296
297 ret = libvarpd_c_door_call(infop->vcprop_client, &carg, 0);
298 if (ret != 0)
299 return (ret);
300
301 if (carg.vca_errno != 0)
302 return (carg.vca_errno);
303
304 libvarpd_c_prop_info_from_door(infop, vcfap);
305 return (0);
306 }
307
308 int
libvarpd_c_prop_info(varpd_client_prop_handle_t * phdl,const char ** namep,uint_t * typep,uint_t * protp,const void ** defp,uint32_t * defsizep,const mac_propval_range_t ** possp)309 libvarpd_c_prop_info(varpd_client_prop_handle_t *phdl, const char **namep,
310 uint_t *typep, uint_t *protp, const void **defp, uint32_t *defsizep,
311 const mac_propval_range_t **possp)
312 {
313 varpd_client_prop_info_t *infop = (varpd_client_prop_info_t *)phdl;
314 if (infop->vcprop_propid == UINT_MAX)
315 return (EINVAL);
316
317 if (namep != NULL)
318 *namep = infop->vcprop_name;
319 if (typep != NULL)
320 *typep = infop->vcprop_type;
321 if (protp != NULL)
322 *protp = infop->vcprop_prot;
323 if (defp != NULL)
324 *defp = infop->vcprop_default;
325 if (defsizep != NULL)
326 *defsizep = infop->vcprop_defsize;
327 if (possp != NULL)
328 *possp = (const mac_propval_range_t *)infop->vcprop_poss;
329 return (0);
330 }
331
332 int
libvarpd_c_prop_get(varpd_client_prop_handle_t * phdl,void * buf,uint32_t * len)333 libvarpd_c_prop_get(varpd_client_prop_handle_t *phdl, void *buf, uint32_t *len)
334 {
335 int ret;
336 varpd_client_arg_t carg;
337 varpd_client_prop_arg_t *vcpap = &carg.vca_un.vca_prop;
338 varpd_client_prop_info_t *infop = (varpd_client_prop_info_t *)phdl;
339
340 if (len == NULL || buf == NULL || infop->vcprop_propid == UINT_MAX)
341 return (EINVAL);
342 if (*len < LIBVARPD_PROP_SIZEMAX)
343 return (EOVERFLOW);
344
345 bzero(&carg, sizeof (varpd_client_arg_t));
346 carg.vca_command = VARPD_CLIENT_GETPROP;
347 carg.vca_errno = 0;
348 vcpap->vcpa_id = infop->vcprop_instance;
349 vcpap->vcpa_propid = infop->vcprop_propid;
350
351 ret = libvarpd_c_door_call(infop->vcprop_client, &carg, 0);
352 if (ret != 0)
353 return (ret);
354
355 if (carg.vca_errno != 0)
356 return (carg.vca_errno);
357
358 /*
359 * If the buffer size is too large then something odd has certainly
360 * happened here, it means that varpd has gone rogue. In such a case we
361 * return a rather odd errror, though we don't believe that this should
362 * generally happen.
363 */
364 if (vcpap->vcpa_bufsize > LIBVARPD_PROP_SIZEMAX)
365 return (E2BIG);
366
367 bcopy(vcpap->vcpa_buf, buf, vcpap->vcpa_bufsize);
368 *len = vcpap->vcpa_bufsize;
369 return (0);
370 }
371
372 int
libvarpd_c_prop_set(varpd_client_prop_handle_t * phdl,const void * buf,uint32_t len)373 libvarpd_c_prop_set(varpd_client_prop_handle_t *phdl, const void *buf,
374 uint32_t len)
375 {
376 int ret;
377 varpd_client_arg_t carg;
378 varpd_client_prop_arg_t *vcpap = &carg.vca_un.vca_prop;
379 varpd_client_prop_info_t *infop = (varpd_client_prop_info_t *)phdl;
380
381 if (len == 0 || buf == NULL || infop->vcprop_propid == UINT_MAX)
382 return (EINVAL);
383 if (len > LIBVARPD_PROP_SIZEMAX)
384 return (EOVERFLOW);
385
386 carg.vca_command = VARPD_CLIENT_SETPROP;
387 carg.vca_errno = 0;
388 vcpap->vcpa_id = infop->vcprop_instance;
389 vcpap->vcpa_propid = infop->vcprop_propid;
390 vcpap->vcpa_bufsize = len;
391 bcopy(buf, vcpap->vcpa_buf, len);
392
393 ret = libvarpd_c_door_call(infop->vcprop_client, &carg, 0);
394 if (ret != 0)
395 return (ret);
396
397 if (carg.vca_errno != 0)
398 return (carg.vca_errno);
399
400 return (0);
401 }
402
403 int
libvarpd_c_instance_lookup(varpd_client_handle_t * chp,datalink_id_t linkid,uint64_t * instp)404 libvarpd_c_instance_lookup(varpd_client_handle_t *chp, datalink_id_t linkid,
405 uint64_t *instp)
406 {
407 int ret;
408 varpd_client_arg_t carg;
409 varpd_client_lookup_arg_t *vclap = &carg.vca_un.vca_lookup;
410 varpd_client_t *client = (varpd_client_t *)chp;
411
412 carg.vca_command = VARPD_CLIENT_LOOKUP;
413 carg.vca_errno = 0;
414 vclap->vcla_linkid = linkid;
415 ret = libvarpd_c_door_call(client, &carg, 0);
416 if (ret != 0)
417 return (ret);
418
419 if (carg.vca_errno != 0)
420 return (carg.vca_errno);
421 if (instp != NULL)
422 *instp = vclap->vcla_id;
423
424 return (0);
425 }
426
427 int
libvarpd_c_instance_target_mode(varpd_client_handle_t * chp,uint64_t cid,uint_t * dtype,uint_t * mtype)428 libvarpd_c_instance_target_mode(varpd_client_handle_t *chp, uint64_t cid,
429 uint_t *dtype, uint_t *mtype)
430 {
431 int ret;
432 varpd_client_arg_t carg;
433 varpd_client_target_mode_arg_t *vctmap = &carg.vca_un.vca_mode;
434 varpd_client_t *client = (varpd_client_t *)chp;
435
436 carg.vca_command = VARPD_CLIENT_TARGET_MODE;
437 carg.vca_errno = 0;
438 vctmap->vtma_id = cid;
439 ret = libvarpd_c_door_call(client, &carg, 0);
440 if (ret != 0)
441 return (ret);
442
443 if (carg.vca_errno != 0)
444 return (carg.vca_errno);
445 if (ret == 0) {
446 if (mtype != NULL)
447 *mtype = vctmap->vtma_mode;
448 if (dtype != NULL)
449 *dtype = vctmap->vtma_dest;
450 }
451
452 return (ret);
453 }
454
455 int
libvarpd_c_instance_cache_flush(varpd_client_handle_t * chp,uint64_t cid)456 libvarpd_c_instance_cache_flush(varpd_client_handle_t *chp, uint64_t cid)
457 {
458 int ret;
459 varpd_client_arg_t carg;
460 varpd_client_target_cache_arg_t *vctcap = &carg.vca_un.vca_cache;
461 varpd_client_t *client = (varpd_client_t *)chp;
462
463 carg.vca_command = VARPD_CLIENT_CACHE_FLUSH;
464 carg.vca_errno = 0;
465
466 vctcap->vtca_id = cid;
467 ret = libvarpd_c_door_call(client, &carg, 0);
468 if (ret != 0)
469 return (ret);
470
471 if (carg.vca_errno != 0)
472 return (carg.vca_errno);
473
474 return (0);
475 }
476
477 int
libvarpd_c_instance_cache_delete(varpd_client_handle_t * chp,uint64_t cid,const struct ether_addr * key)478 libvarpd_c_instance_cache_delete(varpd_client_handle_t *chp, uint64_t cid,
479 const struct ether_addr *key)
480 {
481 int ret;
482 varpd_client_arg_t carg;
483 varpd_client_target_cache_arg_t *vctcap = &carg.vca_un.vca_cache;
484 varpd_client_t *client = (varpd_client_t *)chp;
485
486 if (key == NULL)
487 return (EINVAL);
488
489 carg.vca_command = VARPD_CLIENT_CACHE_DELETE;
490 carg.vca_errno = 0;
491 vctcap->vtca_id = cid;
492 bcopy(key, vctcap->vtca_key, ETHERADDRL);
493
494 ret = libvarpd_c_door_call(client, &carg, 0);
495 if (ret != 0)
496 return (ret);
497
498 if (carg.vca_errno != 0)
499 return (carg.vca_errno);
500
501 return (0);
502 }
503
504 int
libvarpd_c_instance_cache_get(varpd_client_handle_t * chp,uint64_t cid,const struct ether_addr * key,varpd_client_cache_entry_t * entry)505 libvarpd_c_instance_cache_get(varpd_client_handle_t *chp, uint64_t cid,
506 const struct ether_addr *key, varpd_client_cache_entry_t *entry)
507 {
508 int ret;
509 varpd_client_arg_t carg;
510 varpd_client_target_cache_arg_t *vctcap = &carg.vca_un.vca_cache;
511 varpd_client_t *client = (varpd_client_t *)chp;
512
513 if (key == NULL || entry == NULL)
514 return (EINVAL);
515
516 carg.vca_command = VARPD_CLIENT_CACHE_GET;
517 carg.vca_errno = 0;
518 vctcap->vtca_id = cid;
519 bcopy(key, vctcap->vtca_key, ETHERADDRL);
520 bzero(&vctcap->vtca_entry, sizeof (varpd_client_cache_entry_t));
521
522 ret = libvarpd_c_door_call(client, &carg, 0);
523 if (ret != 0)
524 return (ret);
525
526 if (carg.vca_errno != 0)
527 return (carg.vca_errno);
528
529 bcopy(&vctcap->vtca_entry, entry, sizeof (varpd_client_cache_entry_t));
530 return (0);
531 }
532
533 int
libvarpd_c_instance_cache_set(varpd_client_handle_t * chp,uint64_t cid,const struct ether_addr * key,const varpd_client_cache_entry_t * entry)534 libvarpd_c_instance_cache_set(varpd_client_handle_t *chp, uint64_t cid,
535 const struct ether_addr *key, const varpd_client_cache_entry_t *entry)
536 {
537 int ret;
538 varpd_client_arg_t carg;
539 varpd_client_target_cache_arg_t *vctcap = &carg.vca_un.vca_cache;
540 varpd_client_t *client = (varpd_client_t *)chp;
541
542 if (key == NULL || entry == NULL)
543 return (EINVAL);
544
545 carg.vca_command = VARPD_CLIENT_CACHE_SET;
546 carg.vca_errno = 0;
547 vctcap->vtca_id = cid;
548 bcopy(key, vctcap->vtca_key, ETHERADDRL);
549 bcopy(entry, &vctcap->vtca_entry, sizeof (varpd_client_cache_entry_t));
550
551 ret = libvarpd_c_door_call(client, &carg, 0);
552 if (ret != 0)
553 return (ret);
554
555 if (carg.vca_errno != 0)
556 return (carg.vca_errno);
557
558 return (0);
559 }
560
561 int
libvarpd_c_instance_cache_walk(varpd_client_handle_t * chp,uint64_t cid,varpd_client_cache_f func,void * arg)562 libvarpd_c_instance_cache_walk(varpd_client_handle_t *chp, uint64_t cid,
563 varpd_client_cache_f func, void *arg)
564 {
565 int ret = 0;
566 size_t bufsize = sizeof (varpd_client_arg_t) +
567 100 * sizeof (varpd_client_cache_entry_t);
568 varpd_client_t *client = (varpd_client_t *)chp;
569 varpd_client_arg_t *cargp;
570 varpd_client_target_walk_arg_t *vctwap;
571
572 /*
573 * Because the number of entries involved in a walk may be large, we
574 * dynamically allocate a number of queries to make at a single time.
575 * This also means that the average door request doesn't inflate by the
576 * number of entries we want. For now, let's always grab 100 entries in
577 * a request.
578 */
579 cargp = umem_zalloc(bufsize, UMEM_DEFAULT);
580 if (cargp == NULL)
581 return (errno);
582 vctwap = &cargp->vca_un.vca_walk;
583 for (;;) {
584 int i;
585
586 cargp->vca_command = VARPD_CLIENT_CACHE_WALK;
587 cargp->vca_errno = 0;
588 vctwap->vtcw_id = cid;
589 vctwap->vtcw_count = 100;
590
591 ret = libvarpd_c_door_call(client, cargp, bufsize);
592 if (ret != 0)
593 break;
594
595 if (cargp->vca_errno != 0) {
596 ret = cargp->vca_errno;
597 break;
598 }
599
600 if (vctwap->vtcw_count == 0) {
601 ret = 0;
602 break;
603 }
604
605 for (i = 0; i < vctwap->vtcw_count; i++) {
606 varpd_client_cache_entry_t ent;
607
608 ent.vcp_flags = vctwap->vtcw_ents[i].otce_flags;
609 bcopy(vctwap->vtcw_ents[i].otce_dest.otp_mac,
610 &ent.vcp_mac, ETHERADDRL);
611 ent.vcp_ip = vctwap->vtcw_ents[i].otce_dest.otp_ip;
612 ent.vcp_port = vctwap->vtcw_ents[i].otce_dest.otp_port;
613 ret = func(chp, cid,
614 (struct ether_addr *)vctwap->vtcw_ents[i].otce_mac,
615 &ent, arg);
616 if (ret != 0) {
617 ret = 0;
618 goto done;
619 }
620 }
621 }
622
623 done:
624 umem_free(cargp, bufsize);
625 return (ret);
626 }
627