xref: /illumos-gate/usr/src/lib/varpd/libvarpd/common/libvarpd_client.c (revision dd72704bd9e794056c558153663c739e2012d721)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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