xref: /onnv-gate/usr/src/lib/libdhcpsvc/private/private.c (revision 0:68f95e015346)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This module contains the private layer API.
31  */
32 
33 #include <stdio.h>
34 #include <assert.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <sys/param.h>
39 #include <libelf.h>
40 #include <gelf.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <dlfcn.h>
46 #include <glob.h>
47 #include <fcntl.h>
48 #include <libinetutil.h>
49 #include <dhcp_svc_public.h>
50 #include <dhcp_svc_private.h>
51 
52 /*
53  * Threading notes for private layer consumers:
54  *
55  * The handles returned from open_dd() may be shared across multiple
56  * threads with no adverse side effects.  However, it's up to that consumer
57  * to ensure that all threads have finished using an instance before
58  * closing the instance or removing the container it's referencing.
59  * Phrased differently:
60  *
61  *	* Consumers must ensure all threads sharing a handle are
62  *	  finished before calling close_dd().
63  *
64  *	* Consumers must ensure all threads referencing a container are
65  *	  closed before calling remove_dd().
66  */
67 
68 static boolean_t validate_dd_entry(dsvc_handle_t, const void *, boolean_t);
69 static int  synch_init(dsvc_handle_t, const char *, uint_t);
70 static void synch_fini(dsvc_handle_t);
71 
72 /*
73  * Order here should match the function array in <dhcp_svc_private.h>
74  */
75 static char	*funcnames[] = {
76 	"status",	"version",	"mklocation",
77 	"list_dt",	"open_dt",	"close_dt",	"remove_dt",
78 	"lookup_dt",	"add_dt",	"modify_dt",	"delete_dt",
79 	"list_dn",	"open_dn",	"close_dn",	"remove_dn",
80 	"lookup_dn",	"add_dn",	"modify_dn",	"delete_dn"
81 };
82 
83 extern dsvc_synch_ops_t dsvcd_synch_ops;
84 
85 /*
86  * Retrieve the current version associated with the datastore named by
87  * `resource' and store in `converp'.  One might think we could do this via
88  * a simple readlink(2), but on internal release builds $(ROOTLINKS)
89  * installs using hardlinks, not symlinks.  For this reason and to make it
90  * harder for us to be fooled, we'll dredge up the actual soname through
91  * ELF.  Close your eyes, it's gonna get ugly.
92  */
93 static int
get_conver(const char * resource,int * converp)94 get_conver(const char *resource, int *converp)
95 {
96 	int		elf_fd;
97 	int		i;
98 	GElf_Shdr	gelf_shdr;
99 	GElf_Dyn	gelf_dyn;
100 	Elf_Scn		*elf_scn = NULL;
101 	Elf		*elf_file;
102 	Elf_Data	*elf_data;
103 	char		*soname = NULL;
104 	char		path[MAXPATHLEN];
105 
106 	(void) snprintf(path, sizeof (path), "%s%s/%s_%s.so", DHCP_CONFOPT_ROOT,
107 	    DSVC_MODULE_DIR, DSVC_PUBLIC_PREFIX, resource);
108 
109 	elf_fd = open(path, O_RDONLY);
110 	if (elf_fd == -1)
111 		return (DSVC_MODULE_ERR);
112 
113 	if (elf_version(EV_CURRENT) == EV_NONE) {
114 		(void) close(elf_fd);
115 		return (DSVC_INTERNAL);
116 	}
117 
118 	elf_file = elf_begin(elf_fd, ELF_C_READ, NULL);
119 	if (elf_file == NULL || elf_kind(elf_file) != ELF_K_ELF) {
120 		(void) close(elf_fd);
121 		return (DSVC_INTERNAL);
122 	}
123 
124 	while ((elf_scn = elf_nextscn(elf_file, elf_scn)) != NULL) {
125 		if (gelf_getshdr(elf_scn, &gelf_shdr) == 0)
126 			continue;
127 
128 		if (gelf_shdr.sh_type != SHT_DYNAMIC)
129 			continue;
130 
131 		elf_data = elf_getdata(elf_scn, NULL);
132 		if (elf_data == NULL)
133 			continue;
134 
135 		i = 0;
136 		do {
137 			(void) gelf_getdyn(elf_data, i++, &gelf_dyn);
138 			if (gelf_dyn.d_tag == DT_SONAME)
139 				soname = elf_strptr(elf_file, gelf_shdr.sh_link,
140 				    gelf_dyn.d_un.d_ptr);
141 		} while (gelf_dyn.d_tag != DT_NULL && soname == NULL);
142 	}
143 	if (soname == NULL || sscanf(soname, "%*[^.].so.%d", converp) != 1) {
144 		(void) elf_end(elf_file);
145 		(void) close(elf_fd);
146 		return (DSVC_MODULE_ERR);
147 	}
148 	(void) elf_end(elf_file);
149 	(void) close(elf_fd);
150 
151 	return (DSVC_SUCCESS);
152 }
153 
154 /*
155  * Unload a public datastore module.
156  */
157 static int
unload_public_module(void ** instance,dsvc_splapi_t * api)158 unload_public_module(void **instance, dsvc_splapi_t *api)
159 {
160 	static dsvc_splapi_t	null_api;
161 
162 	if (dlclose(*instance) != 0)
163 		return (DSVC_MODULE_UNLOAD_ERR);
164 
165 	*instance = NULL;
166 	*api = null_api;
167 
168 	return (DSVC_SUCCESS);
169 }
170 
171 /*
172  * Load public datastore module.  Validates version of module.  Returns
173  * instance of opened module, and populates the api argument with the
174  * function addresses exporting the API.
175  */
176 static int
load_public_module(dsvc_datastore_t * ddp,void ** instance,dsvc_splapi_t * api)177 load_public_module(dsvc_datastore_t *ddp, void **instance, dsvc_splapi_t *api)
178 {
179 	int		i, v;
180 	dsvc_splfuncp_t	configure;
181 	char		path[MAXPATHLEN];
182 
183 	(void) snprintf(path, sizeof (path), "%s%s/%s_%s.so", DHCP_CONFOPT_ROOT,
184 	    DSVC_MODULE_DIR, DSVC_PUBLIC_PREFIX, ddp->d_resource);
185 
186 	if (ddp->d_conver != DSVC_CUR_CONVER)
187 		(void) snprintf(path, sizeof (path), "%s.%d", path,
188 		    ddp->d_conver);
189 
190 	*instance = dlopen(path, RTLD_LAZY|RTLD_GROUP|RTLD_WORLD);
191 	if (*instance == NULL)
192 		return (DSVC_MODULE_LOAD_ERR);
193 
194 	/*
195 	 * No requirement to duplicate the names - we can always reference
196 	 * the same set.
197 	 */
198 	api->version = (dsvc_splfuncp_t)dlsym(*instance, "version");
199 	if (api->version == NULL || api->version(&v) != DSVC_SUCCESS ||
200 	    v != DSVC_PUBLIC_VERSION) {
201 		(void) unload_public_module(instance, api);
202 		return (DSVC_MODULE_VERSION);
203 	}
204 
205 	configure = (dsvc_splfuncp_t)dlsym(*instance, "configure");
206 	if (configure != NULL) {
207 		if (configure(ddp->d_config) != DSVC_SUCCESS) {
208 			(void) unload_public_module(instance, api);
209 			return (DSVC_MODULE_CFG_ERR);
210 		}
211 	}
212 
213 	for (i = 0; i < DSVC_NSPLFUNCS; i++) {
214 		if ((((dsvc_splfuncp_t *)api)[i] =
215 		    (dsvc_splfuncp_t)dlsym(*instance, funcnames[i])) == NULL) {
216 			(void) unload_public_module(instance, api);
217 			return (DSVC_MODULE_ERR);
218 		}
219 	}
220 
221 	/*
222 	 * Caller requested the current version; fill in what that current
223 	 * version is.
224 	 */
225 	if (ddp->d_conver == DSVC_CUR_CONVER) {
226 		int	error;
227 		error = get_conver(ddp->d_resource, &ddp->d_conver);
228 		if (error != DSVC_SUCCESS) {
229 			(void) unload_public_module(instance, api);
230 			return (error);
231 		}
232 	}
233 
234 	return (DSVC_SUCCESS);
235 }
236 
237 /*
238  * Return a dynamically-allocated null-terminated list of the available
239  * modules stored in the module directory.  A count of the available
240  * modules is stored in the num argument.  Caller is responsible for
241  * freeing the list.
242  */
243 int
enumerate_dd(char *** modules,int * nump)244 enumerate_dd(char ***modules, int *nump)
245 {
246 	int	i, retval;
247 	char	*ptr;
248 	glob_t	globbuf;
249 	char	globpat[MAXPATHLEN];
250 
251 	if (modules == NULL || nump == NULL)
252 		return (DSVC_INVAL);
253 
254 	(void) snprintf(globpat, sizeof (globpat), "%s%s/%s_*\\.so",
255 	    DHCP_CONFOPT_ROOT, DSVC_MODULE_DIR, DSVC_PUBLIC_PREFIX);
256 
257 	retval = glob(globpat, GLOB_NOSORT, NULL, &globbuf);
258 	if (retval != 0) {
259 		globfree(&globbuf);
260 		switch (retval) {
261 		case GLOB_NOMATCH:
262 			*nump = 0;
263 			*modules = NULL;
264 			return (DSVC_SUCCESS);
265 		case GLOB_NOSPACE:
266 			return (DSVC_NO_MEMORY);
267 		default:
268 			return (DSVC_INTERNAL);
269 		}
270 	}
271 
272 	*modules = calloc(globbuf.gl_pathc, sizeof (char **));
273 	if (*modules == NULL) {
274 		globfree(&globbuf);
275 		return (DSVC_NO_MEMORY);
276 	}
277 
278 	for (i = 0; i < globbuf.gl_pathc; i++) {
279 		ptr = strrchr(globbuf.gl_pathv[i], '/');
280 		if (ptr == NULL)
281 			ptr = globbuf.gl_pathv[i];
282 		else
283 			ptr++;
284 		(*modules)[i] = malloc(strlen(ptr) + 1);
285 		if ((*modules)[i] == NULL) {
286 			while (i--)
287 				free((*modules)[i]);
288 			free(modules);
289 			globfree(&globbuf);
290 			return (DSVC_NO_MEMORY);
291 		}
292 
293 		(void) sscanf(ptr, "%*[^_]_%[^.]", (*modules)[i]);
294 	}
295 
296 	globfree(&globbuf);
297 	*nump = i;
298 	return (DSVC_SUCCESS);
299 }
300 
301 /*
302  * Check the status of the underlying service supporting the data store.
303  * Caller is responsible for freeing any dynamically allocated arguments.
304  */
305 int
status_dd(dsvc_datastore_t * ddp)306 status_dd(dsvc_datastore_t *ddp)
307 {
308 	void		*instance;
309 	dsvc_splapi_t	api;
310 	int		error;
311 
312 	error = load_public_module(ddp, &instance, &api);
313 	if (error != DSVC_SUCCESS)
314 		return (error);
315 
316 	error = api.status(ddp->d_location);
317 
318 	(void) unload_public_module(&instance, &api);
319 
320 	return (error);
321 }
322 
323 /*
324  * Create within the data store the "location" where containers will be
325  * stored.
326  */
327 int
mklocation_dd(dsvc_datastore_t * ddp)328 mklocation_dd(dsvc_datastore_t *ddp)
329 {
330 	void		*instance;
331 	dsvc_splapi_t	api;
332 	int		error;
333 
334 	error = load_public_module(ddp, &instance, &api);
335 	if (error != DSVC_SUCCESS)
336 		return (error);
337 
338 	error = api.mklocation(ddp->d_location);
339 
340 	(void) unload_public_module(&instance, &api);
341 
342 	return (error);
343 }
344 
345 /*
346  * Return a list of the current container objects of type 'type' located at
347  * 'location' in listppp.  Return the number of list elements in 'count'.
348  */
349 int
list_dd(dsvc_datastore_t * ddp,dsvc_contype_t type,char *** listppp,uint_t * count)350 list_dd(dsvc_datastore_t *ddp, dsvc_contype_t type, char ***listppp,
351     uint_t *count)
352 {
353 	void		*instance;
354 	dsvc_splapi_t	api;
355 	int		error;
356 
357 	error = load_public_module(ddp, &instance, &api);
358 	if (error != DSVC_SUCCESS)
359 		return (error);
360 
361 	if (type == DSVC_DHCPTAB)
362 		error = api.list_dt(ddp->d_location, listppp, count);
363 	else
364 		error = api.list_dn(ddp->d_location, listppp, count);
365 
366 	(void) unload_public_module(&instance, &api);
367 
368 	return (error);
369 }
370 
371 /*
372  * Creates or opens the DHCP container of type called name within the
373  * specific datastore referenced by ddp, and returns a handle to this
374  * container in the handp argument.  New containers are created with
375  * the identity of the caller.  Caller is responsible for freeing any
376  * dynamically allocated arguments.  The returned handle instance must
377  * be released by calling close_dd().
378  */
379 int
open_dd(dsvc_handle_t * handp,dsvc_datastore_t * ddp,dsvc_contype_t type,const char * name,uint_t flags)380 open_dd(dsvc_handle_t *handp, dsvc_datastore_t *ddp, dsvc_contype_t type,
381     const char *name, uint_t flags)
382 {
383 	int			error;
384 	dsvc_handle_t		hp;
385 
386 	*handp = NULL;
387 
388 	if (type == DSVC_DHCPNETWORK && name == NULL)
389 		return (DSVC_INVAL);
390 
391 	if (flags & DSVC_CREATE && (flags & DSVC_WRITE) == 0)
392 		return (DSVC_INVAL);
393 
394 	if ((hp = calloc(1, sizeof (struct dsvc_handle))) == NULL)
395 		return (DSVC_NO_MEMORY);
396 
397 	if (type == DSVC_DHCPNETWORK) {
398 		hp->d_conid.c_net.s_addr = ntohl(inet_addr(name));
399 		if (hp->d_conid.c_net.s_addr == INADDR_BROADCAST) {
400 			free(hp);
401 			return (DSVC_INVAL);
402 		}
403 		get_netmask4(&hp->d_conid.c_net, &hp->d_conid.c_mask);
404 	}
405 
406 	error = load_public_module(ddp, &hp->d_instance, &hp->d_api);
407 	if (error != DSVC_SUCCESS) {
408 		free(hp);
409 		return (error);
410 	}
411 
412 	hp->d_type = type;
413 	hp->d_desc.d_conver = ddp->d_conver;
414 	hp->d_desc.d_resource = strdup(ddp->d_resource);
415 	hp->d_desc.d_location = strdup(ddp->d_location);
416 	if (hp->d_desc.d_resource == NULL || hp->d_desc.d_location == NULL) {
417 		error = DSVC_NO_MEMORY;
418 		goto error;
419 	}
420 
421 	/*
422 	 * Initialize the synchronization strategy (may not be any).
423 	 */
424 	error = synch_init(hp, name, flags);
425 	if (error != DSVC_SUCCESS)
426 		goto error;
427 
428 	if (type == DSVC_DHCPTAB)
429 		error = hp->d_api.open_dt(&hp->d_hand, ddp->d_location, flags);
430 	else
431 		error = hp->d_api.open_dn(&hp->d_hand, ddp->d_location, flags,
432 		    &hp->d_conid.c_net, &hp->d_conid.c_mask);
433 
434 	if (error != DSVC_SUCCESS) {
435 		if (hp->d_synch != NULL)
436 			synch_fini(hp);
437 		goto error;
438 	}
439 
440 	*handp = hp;
441 	return (DSVC_SUCCESS);
442 error:
443 	(void) unload_public_module(&hp->d_instance, &hp->d_api);
444 	free(hp->d_desc.d_resource);
445 	free(hp->d_desc.d_location);
446 	free(hp);
447 	return (error);
448 }
449 
450 /*
451  * Remove DHCP container called name of type within the specific datastore
452  * referenced by ddp.  Caller is responsible for freeing any dynamically
453  * allocated arguments.
454  */
455 int
remove_dd(dsvc_datastore_t * ddp,dsvc_contype_t type,const char * name)456 remove_dd(dsvc_datastore_t *ddp, dsvc_contype_t type, const char *name)
457 {
458 	void		*instance;
459 	int		error;
460 	dsvc_splapi_t	api;
461 	struct in_addr	ip, mask;
462 
463 	if (type != DSVC_DHCPTAB) {
464 		if ((ip.s_addr = inet_addr(name)) == INADDR_BROADCAST)
465 			return (DSVC_INVAL);
466 		ip.s_addr = ntohl(ip.s_addr);
467 		get_netmask4(&ip, &mask);
468 	}
469 
470 	error = load_public_module(ddp, &instance, &api);
471 	if (error != DSVC_SUCCESS)
472 		return (error);
473 
474 	/* remove the DHCP container */
475 	if (type == DSVC_DHCPTAB)
476 		error = api.remove_dt(ddp->d_location);
477 	else
478 		error = api.remove_dn(ddp->d_location, &ip, &mask);
479 
480 	(void) unload_public_module(&instance, &api);
481 
482 	return (error);
483 }
484 
485 /*
486  * Delete the handle instance referenced by hand. Frees hand if the close
487  * operation was successful.  NOTE: Caller is responsible for synchronizing
488  * multiple threads such that close_dd() is called when all consuming
489  * threads have exited.
490  */
491 int
close_dd(dsvc_handle_t * handp)492 close_dd(dsvc_handle_t *handp)
493 {
494 	int	error;
495 
496 	if (handp == NULL || DSVC_HANDLE_INVAL(*handp))
497 		return (DSVC_INVAL);
498 
499 	if ((*handp)->d_type == DSVC_DHCPTAB)
500 		error = (*handp)->d_api.close_dt(&((*handp)->d_hand));
501 	else
502 		error = (*handp)->d_api.close_dn(&((*handp)->d_hand));
503 
504 	if (error == DSVC_SUCCESS) {
505 		error = unload_public_module(&(*handp)->d_instance,
506 		    &(*handp)->d_api);
507 		if ((*handp)->d_synch != NULL)
508 			synch_fini(*handp);
509 		free((*handp)->d_desc.d_resource);
510 		free((*handp)->d_desc.d_location);
511 		free(*handp);
512 		*handp = NULL;
513 	}
514 
515 	return (error);
516 }
517 
518 /*
519  * Searches hand container for records that match the query described by
520  * the combination of query and targetp. If the partial field is true, then
521  * lookup operations that have located some records but are unable to
522  * complete entirely are allowed.  The query argument consists of 2 fields,
523  * each 16 bits long. The lower 16 bits selects which fields in the targetp
524  * record are to be considered in the query. The upper 16 bits identifies
525  * whether a particular field value must match (bit set) or not match (bit
526  * clear). Unused bits in both 16 bit fields must be 0. The count argument
527  * specifies the maximum number of matching records to return. A count
528  * value of -1 requests that all matching records be returned. recordsp is
529  * set to point to the resulting list of records; if recordsp is NULL then
530  * no records are actually returned. Note that these records are
531  * dynamically allocated, thus the caller is responsible for freeing them.
532  * The number of records found is returned in nrecordsp; a value of 0 means
533  * that no records matched the query.
534  */
535 int
lookup_dd(dsvc_handle_t hand,boolean_t partial,uint_t query,int count,const void * targetp,void ** recordsp,uint_t * nrecordsp)536 lookup_dd(dsvc_handle_t hand, boolean_t partial, uint_t query,
537     int count, const void *targetp, void **recordsp, uint_t *nrecordsp)
538 {
539 	uint_t	mask = 0;
540 	int	error;
541 	void	*unlock_cookie;
542 	int	(*lookup)();
543 
544 	if (targetp == NULL || nrecordsp == NULL || DSVC_HANDLE_INVAL(hand))
545 		return (DSVC_INVAL);
546 
547 	if (hand->d_type == DSVC_DHCPTAB) {
548 		mask = (uint_t)~DT_QALL;
549 		lookup = hand->d_api.lookup_dt;
550 	} else {
551 		mask = (uint_t)~DN_QALL;
552 		lookup = hand->d_api.lookup_dn;
553 	}
554 
555 	/* validate query */
556 	if (((query & 0xffff) & mask) || ((query >> 16) & mask))
557 		return (DSVC_INVAL);
558 
559 	/*
560 	 * XXX: need to validate the `targetp' -- what a mess cuz only the
561 	 *	fields lit in `query' need to be valid.
562 	 */
563 
564 	if (hand->d_synch != NULL) {
565 		error = DSVC_SYNCH_RDLOCK(hand->d_synch, &unlock_cookie);
566 		if (error != DSVC_SUCCESS)
567 			return (error);
568 	}
569 
570 	error = lookup(hand->d_hand, partial, query, count, targetp, recordsp,
571 	    nrecordsp);
572 
573 	if (hand->d_synch != NULL)
574 		(void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
575 
576 	return (error);
577 }
578 
579 /*
580  * Frees the record pointed to by entryp.
581  */
582 void
free_dd(dsvc_handle_t hand,void * entryp)583 free_dd(dsvc_handle_t hand, void *entryp)
584 {
585 	if (DSVC_HANDLE_INVAL(hand) || entryp == NULL)
586 		return;
587 
588 	if (hand->d_type == DSVC_DHCPTAB)
589 		free_dtrec((dt_rec_t *)entryp);
590 	else
591 		free_dnrec((dn_rec_t *)entryp);
592 }
593 
594 /*
595  * Frees the list of records pointed to by listp.
596  */
597 void
free_dd_list(dsvc_handle_t hand,void * listp)598 free_dd_list(dsvc_handle_t hand, void *listp)
599 {
600 	if (DSVC_HANDLE_INVAL(hand) || listp == NULL)
601 		return;
602 
603 	if (hand->d_type == DSVC_DHCPTAB)
604 		free_dtrec_list((dt_rec_list_t *)listp);
605 	else
606 		free_dnrec_list((dn_rec_list_t *)listp);
607 }
608 
609 /*
610  * Add the record newp to the DHCP container hand. newp's update signature
611  * will be updated by the public layer module doing the update. Caller is
612  * responsible for freeing newp if it was dynamically allocated.
613  */
614 int
add_dd_entry(dsvc_handle_t hand,void * newp)615 add_dd_entry(dsvc_handle_t hand, void *newp)
616 {
617 	int	error;
618 	void	*unlock_cookie;
619 
620 	if (DSVC_HANDLE_INVAL(hand))
621 		return (DSVC_INVAL);
622 
623 	if (!validate_dd_entry(hand, newp, B_FALSE))
624 		return (DSVC_INVAL);
625 
626 	if (hand->d_synch != NULL) {
627 		error = DSVC_SYNCH_WRLOCK(hand->d_synch, &unlock_cookie);
628 		if (error != DSVC_SUCCESS)
629 			return (error);
630 	}
631 
632 	if (hand->d_type == DSVC_DHCPTAB)
633 		error = hand->d_api.add_dt(hand->d_hand, newp);
634 	else
635 		error = hand->d_api.add_dn(hand->d_hand, newp);
636 
637 	if (hand->d_synch != NULL)
638 		(void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
639 
640 	return (error);
641 }
642 
643 /*
644  * Modify the record origp with the record newp in the DHCP container hand.
645  * newp's update signature will be updated by the public layer module doing
646  * the update. Caller is responsible for freeing origp and/or newp if they
647  * were dynamically allocated.
648  */
649 int
modify_dd_entry(dsvc_handle_t hand,const void * origp,void * newp)650 modify_dd_entry(dsvc_handle_t hand, const void *origp, void *newp)
651 {
652 	int	error;
653 	void 	*unlock_cookie;
654 
655 	if (DSVC_HANDLE_INVAL(hand))
656 		return (DSVC_INVAL);
657 
658 	if (!validate_dd_entry(hand, origp, B_TRUE))
659 		return (DSVC_INVAL);
660 
661 	if (!validate_dd_entry(hand, newp, B_FALSE))
662 		return (DSVC_INVAL);
663 
664 	if (hand->d_synch != NULL) {
665 		error = DSVC_SYNCH_WRLOCK(hand->d_synch, &unlock_cookie);
666 		if (error != DSVC_SUCCESS)
667 			return (error);
668 	}
669 
670 	if (hand->d_type == DSVC_DHCPTAB)
671 		error = hand->d_api.modify_dt(hand->d_hand, origp, newp);
672 	else
673 		error = hand->d_api.modify_dn(hand->d_hand, origp, newp);
674 
675 	if (hand->d_synch != NULL)
676 		(void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
677 
678 	return (error);
679 }
680 
681 /*
682  * Deletes the record referred to by entryp from the DHCP container hand.
683  * Caller is responsible for freeing entryp if it was dynamically
684  * allocated.
685  */
686 int
delete_dd_entry(dsvc_handle_t hand,void * entryp)687 delete_dd_entry(dsvc_handle_t hand, void *entryp)
688 {
689 	int	error;
690 	void	*unlock_cookie;
691 
692 	if (DSVC_HANDLE_INVAL(hand))
693 		return (DSVC_INVAL);
694 
695 	if (!validate_dd_entry(hand, entryp, B_TRUE))
696 		return (DSVC_INVAL);
697 
698 	if (hand->d_synch != NULL) {
699 		error = DSVC_SYNCH_WRLOCK(hand->d_synch, &unlock_cookie);
700 		if (error != DSVC_SUCCESS)
701 			return (error);
702 	}
703 
704 	if (hand->d_type == DSVC_DHCPTAB)
705 		error = hand->d_api.delete_dt(hand->d_hand, entryp);
706 	else
707 		error = hand->d_api.delete_dn(hand->d_hand, entryp);
708 
709 	if (hand->d_synch != NULL)
710 		(void) DSVC_SYNCH_UNLOCK(hand->d_synch, unlock_cookie);
711 
712 	return (error);
713 }
714 
715 /*
716  * Validate that the DHCP network record `dn' is correctly formed; returns
717  * B_TRUE if it is, B_FALSE if it's not.  If `justkey' is set, then only
718  * validate the key.
719  */
720 static boolean_t
validate_dnrec(dsvc_handle_t hand,const dn_rec_t * dn,boolean_t justkey)721 validate_dnrec(dsvc_handle_t hand, const dn_rec_t *dn, boolean_t justkey)
722 {
723 	/* CIP must be on container's network */
724 	if (hand->d_conid.c_net.s_addr !=
725 	    (dn->dn_cip.s_addr & hand->d_conid.c_mask.s_addr))
726 		return (B_FALSE);
727 
728 	if (justkey)
729 		return (B_TRUE);
730 
731 	if (dn->dn_cid_len < 1 || dn->dn_cid_len > DN_MAX_CID_LEN)
732 		return (B_FALSE);
733 
734 	if ((dn->dn_flags & ~DN_FALL) != 0)
735 		return (B_FALSE);
736 
737 	return (B_TRUE);
738 }
739 
740 /*
741  * Validate that the dhcptab record `dt' is correctly formed; returns
742  * B_TRUE if it is, B_FALSE if it's not.  If `justkey' is set, then only
743  * validate the key.
744  */
745 /* ARGSUSED */
746 static boolean_t
validate_dtrec(dsvc_handle_t hand,const dt_rec_t * dt,boolean_t justkey)747 validate_dtrec(dsvc_handle_t hand, const dt_rec_t *dt, boolean_t justkey)
748 {
749 	return (dt->dt_type == DT_SYMBOL || dt->dt_type == DT_MACRO);
750 }
751 
752 /*
753  * Validate that a DHCP record of type `hand->d_type' is correctly formed;
754  * returns B_TRUE if it is, B_FALSE if it's not.  If `justkey' is set, then
755  * only validate the key.
756  */
757 static boolean_t
validate_dd_entry(dsvc_handle_t hand,const void * entryp,boolean_t justkey)758 validate_dd_entry(dsvc_handle_t hand, const void *entryp, boolean_t justkey)
759 {
760 	if (entryp == NULL)
761 		return (B_FALSE);
762 
763 	if (hand->d_type == DSVC_DHCPTAB)
764 		return (validate_dtrec(hand, (dt_rec_t *)entryp, justkey));
765 	else if (hand->d_type == DSVC_DHCPNETWORK)
766 		return (validate_dnrec(hand, (dn_rec_t *)entryp, justkey));
767 
768 	return (B_FALSE);
769 }
770 
771 /*
772  * Get the type of synchronization needed for this module and store in
773  * `synchtypep'.  Returns a DSVC_* code.  This function is exported so that
774  * dsvclockd(1M) can use it.
775  */
776 int
module_synchtype(dsvc_datastore_t * ddp,dsvc_synchtype_t * synchtypep)777 module_synchtype(dsvc_datastore_t *ddp, dsvc_synchtype_t *synchtypep)
778 {
779 	void			*instance;
780 	dsvc_splapi_t		api;
781 	dsvc_synchtype_t	*dsvc_synchtypep;
782 
783 	if (load_public_module(ddp, &instance, &api) != DSVC_SUCCESS)
784 		return (DSVC_INTERNAL);
785 
786 	dsvc_synchtypep = dlsym(instance, "dsvc_synchtype");
787 	if (dsvc_synchtypep != NULL)
788 		*synchtypep = *dsvc_synchtypep;
789 	else
790 		*synchtypep = DSVC_SYNCH_NONE;
791 
792 	(void) unload_public_module(&instance, &api);
793 
794 	return (DSVC_SUCCESS);
795 }
796 
797 /*
798  * Initialize private-layer synchronization on handle `hand' for container
799  * `conname'; `flags' is the same flags passed into open_dd().  If there's
800  * no synchronization needed, always succeeds.  Returns a DSVC_* code.
801  */
802 int
synch_init(dsvc_handle_t hand,const char * conname,uint_t flags)803 synch_init(dsvc_handle_t hand, const char *conname, uint_t flags)
804 {
805 	dsvc_synchtype_t	synchtype;
806 	dsvc_synch_t		*sp;
807 	int 			error;
808 	int			(*mkloctoken)(const char *, char *, size_t);
809 
810 	error = module_synchtype(&hand->d_desc, &synchtype);
811 	if (error != DSVC_SUCCESS)
812 		return (error);
813 
814 	if (synchtype == DSVC_SYNCH_NONE)
815 		return (DSVC_SUCCESS);
816 
817 	sp = malloc(sizeof (dsvc_synch_t));
818 	if (sp == NULL)
819 		return (DSVC_NO_MEMORY);
820 
821 	sp->s_conname = strdup(conname);
822 	if (sp->s_conname == NULL) {
823 		free(sp);
824 		return (DSVC_NO_MEMORY);
825 	}
826 	sp->s_nonblock	= flags & DSVC_NONBLOCK;
827 	sp->s_datastore = &hand->d_desc;
828 
829 	mkloctoken = (int (*)())dlsym(hand->d_instance, "mkloctoken");
830 	if (mkloctoken == NULL) {
831 		(void) strlcpy(sp->s_loctoken, sp->s_datastore->d_location,
832 		    sizeof (sp->s_loctoken));
833 	} else {
834 		error = mkloctoken(sp->s_datastore->d_location, sp->s_loctoken,
835 		    sizeof (sp->s_loctoken));
836 		if (error != DSVC_SUCCESS) {
837 			free(sp->s_conname);
838 			free(sp);
839 			return (error);
840 		}
841 	}
842 
843 	/*
844 	 * The only synchtype supported is DSVC_SYNCH_DSVCD; if this
845 	 * changes, we'll need to enhance this.
846 	 */
847 	assert((synchtype & DSVC_SYNCH_STRATMASK) == DSVC_SYNCH_DSVCD);
848 	sp->s_ops = &dsvcd_synch_ops;
849 
850 	error = DSVC_SYNCH_INIT(sp, synchtype & DSVC_SYNCH_FLAGMASK);
851 	if (error != DSVC_SUCCESS) {
852 		free(sp->s_conname);
853 		free(sp);
854 		return (error);
855 	}
856 
857 	hand->d_synch = sp;
858 	return (DSVC_SUCCESS);
859 }
860 
861 /*
862  * Finish using private-layer synchronization on handle `hand'.
863  */
864 void
synch_fini(dsvc_handle_t hand)865 synch_fini(dsvc_handle_t hand)
866 {
867 	DSVC_SYNCH_FINI(hand->d_synch);
868 	free(hand->d_synch->s_conname);
869 	free(hand->d_synch);
870 	hand->d_synch = NULL;
871 }
872