diff options
| author | Sebastien Roy <Sebastien.Roy@Sun.COM> | 2009-09-22 22:04:45 -0400 |
|---|---|---|
| committer | Sebastien Roy <Sebastien.Roy@Sun.COM> | 2009-09-22 22:04:45 -0400 |
| commit | 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea (patch) | |
| tree | 72c0d7d4e1c44843a86bab6e3ed6f82cfa7356af /usr/src/cmd/dlmgmtd | |
| parent | 51fc88a818087605a0e5f11eddb8b66576f72c23 (diff) | |
| download | illumos-joyent-2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea.tar.gz | |
PSARC 2009/373 Clearview IP Tunneling
PSARC 2009/410 Datalink Administration from Non-Global Zones
6858533 Clearview IP Tunneling
4861777 *snoop* cannot snoop on tunnel interfaces
5010680 M_IOCTL interface between ip and tun is horribly wrong
5029727 tun prints bogus debug messages when receiving multicast packets on 6to4 tunnels
6835873 dlpi_walk() silently fails in an exclusive zone
4152864 must not allow two tunnels to have the same tsrc/tdst pair
6855902 link and flow kstats are too promiscuous
6218826 need to be able to tunnel into a zone
4505468 network interface names can confuse, lie, and deceive
4524756 tun_wproc() takes up too much stack
6417373 tun_wproc_mdata assertion failures
4627970 scalability problems with IP in IP tunnels
4674797 ifparse_ifspec() will not correctly parse ipv6 tunnels
6509231 dladm should show links in exclusive stack zone
4793233 tun driver should include addr in DL_PHYS_ADDR_ACK for non-zero lengths
6795831 ZONE_*_DATALINK syscalls should take datalink_id_t as argument
6791472 mac module doesn't allow MAC addresses < 6 bytes
6618091 Race condition trips ASSERT() in tun.c's SIOCSLIFNAME path
6837580 bogus mi_active check in mac_set_mtu()
6868083 libinetutil: ofmt_open()'s template argument should be const
6870313 libdladm: needless dladm_init_linkprop() in i_dladm_aggr_up()
6872221 panic in dls_devnet_close() if "mtu" property is being set
4289774 Change to the interface-id does not change IPv6 link-local address
6873561 unable to create links with 31 character link names
6874666 changing a link property can accidentally destroy it
6874682 removing a link attribute corrupts the attribute list
6875167 IPCL_ISV6 conn flag is set but never used
6881764 itp reference leak in ipsec_construct_inverse_acquire()
6881951 dladm delete-vlan can no longer delete persistent-only VLANs
--HG--
rename : usr/src/uts/common/inet/tun.h => usr/src/uts/common/inet/iptun.h
rename : usr/src/uts/common/inet/ip/tun.c => usr/src/uts/common/inet/iptun/iptun.c
rename : usr/src/uts/intel/tun/Makefile => usr/src/uts/intel/iptun/Makefile
rename : usr/src/uts/sparc/tun/Makefile => usr/src/uts/sparc/iptun/Makefile
Diffstat (limited to 'usr/src/cmd/dlmgmtd')
| -rw-r--r-- | usr/src/cmd/dlmgmtd/Makefile | 12 | ||||
| -rw-r--r-- | usr/src/cmd/dlmgmtd/dlmgmt_db.c | 991 | ||||
| -rw-r--r-- | usr/src/cmd/dlmgmtd/dlmgmt_door.c | 641 | ||||
| -rw-r--r-- | usr/src/cmd/dlmgmtd/dlmgmt_impl.h | 43 | ||||
| -rw-r--r-- | usr/src/cmd/dlmgmtd/dlmgmt_main.c | 359 | ||||
| -rw-r--r-- | usr/src/cmd/dlmgmtd/dlmgmt_util.c | 387 |
6 files changed, 1659 insertions, 774 deletions
diff --git a/usr/src/cmd/dlmgmtd/Makefile b/usr/src/cmd/dlmgmtd/Makefile index 2df2508f80..1eb82565c9 100644 --- a/usr/src/cmd/dlmgmtd/Makefile +++ b/usr/src/cmd/dlmgmtd/Makefile @@ -38,14 +38,22 @@ ROOTCFGFILES= $(CFGFILES:%=$(ROOTCFGDIR)/%) $(ROOTCFGDIR)/datalink.conf := FILEMODE= 644 -LDLIBS += -ldladm -ldlpi -lavl -lnvpair -lsysevent +LDLIBS += -ldladm -ldlpi -lavl -lnvpair -lsysevent -lcontract + +# +# Instrument dlmgmtd with CTF data to ease debugging. +# +CTFCONVERT_HOOK = && $(CTFCONVERT_O) +CTFMERGE_HOOK = && $(CTFMERGE) -L VERSION -o $@ $(OBJS) +$(OBJS) := CFLAGS += $(CTF_FLAGS) + .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) - $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK) $(POST_PROCESS) install: all $(ROOTSBINPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) $(ROOTCFGDIR) \ diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_db.c b/usr/src/cmd/dlmgmtd/dlmgmt_db.c index e65722613b..807b912d1e 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_db.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_db.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -30,11 +30,19 @@ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <strings.h> #include <syslog.h> +#include <zone.h> +#include <sys/types.h> #include <sys/stat.h> +#include <stropts.h> +#include <sys/conf.h> #include <pthread.h> #include <unistd.h> +#include <wait.h> +#include <libcontract.h> +#include <sys/contract/process.h> #include "dlmgmt_impl.h" typedef enum dlmgmt_db_op { @@ -46,7 +54,9 @@ typedef enum dlmgmt_db_op { typedef struct dlmgmt_db_req_s { struct dlmgmt_db_req_s *ls_next; dlmgmt_db_op_t ls_op; + char ls_link[MAXLINKNAMELEN]; datalink_id_t ls_linkid; + zoneid_t ls_zoneid; uint32_t ls_flags; /* Either DLMGMT_ACTIVE or */ /* DLMGMT_PERSIST, not both. */ } dlmgmt_db_req_t; @@ -57,19 +67,28 @@ typedef struct dlmgmt_db_req_s { static dlmgmt_db_req_t *dlmgmt_db_req_head = NULL; static dlmgmt_db_req_t *dlmgmt_db_req_tail = NULL; -static int dlmgmt_db_update(dlmgmt_db_op_t, datalink_id_t, - uint32_t); +/* + * rewrite_needed is set to B_TRUE by process_link_line() if it encounters a + * line with an old format. This will cause the file being read to be + * re-written with the current format. + */ +static boolean_t rewrite_needed; + +static int dlmgmt_db_update(dlmgmt_db_op_t, const char *, + dlmgmt_link_t *, uint32_t); static int dlmgmt_process_db_req(dlmgmt_db_req_t *); static int dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t); static void *dlmgmt_db_update_thread(void *); -static boolean_t process_link_line(char *, dlmgmt_link_t **); +static boolean_t process_link_line(char *, dlmgmt_link_t *); static int process_db_write(dlmgmt_db_req_t *, FILE *, FILE *); -static int process_db_read(dlmgmt_db_req_t *, FILE *, FILE *); +static int process_db_read(dlmgmt_db_req_t *, FILE *); static void generate_link_line(dlmgmt_link_t *, boolean_t, char *); #define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0) #define MAXLINELEN 1024 +typedef void db_walk_func_t(dlmgmt_link_t *); + /* * Translator functions to go from dladm_datatype_t to character strings. * Each function takes a pointer to a buffer, the size of the buffer, @@ -127,13 +146,262 @@ static size_t ntranslators = sizeof (translators) / sizeof (translator_t); /* * Name of the cache file to keep the active <link name, linkid> mapping */ -static char cachefile[MAXPATHLEN]; +char cachefile[MAXPATHLEN]; #define DLMGMT_PERSISTENT_DB_PATH "/etc/dladm/datalink.conf" #define DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent) \ (void) snprintf((buffer), MAXPATHLEN, "%s", \ (persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile); +typedef struct zopen_arg { + const char *zopen_modestr; + int *zopen_pipe; + int zopen_fd; +} zopen_arg_t; + +typedef struct zrename_arg { + const char *zrename_newname; +} zrename_arg_t; + +typedef union zfoparg { + zopen_arg_t zfop_openarg; + zrename_arg_t zfop_renamearg; +} zfoparg_t; + +typedef struct zfcbarg { + boolean_t zfarg_inglobalzone; /* is callback in global zone? */ + zoneid_t zfarg_finglobalzone; /* is file in global zone? */ + const char *zfarg_filename; + zfoparg_t *zfarg_oparg; +} zfarg_t; +#define zfarg_openarg zfarg_oparg->zfop_openarg +#define zfarg_renamearg zfarg_oparg->zfop_renamearg + +/* zone file callback */ +typedef int zfcb_t(zfarg_t *); + +/* + * Execute an operation on filename relative to zoneid's zone root. If the + * file is in the global zone, then the zfcb() callback will simply be called + * directly. If the file is in a non-global zone, then zfcb() will be called + * both from the global zone's context, and from the non-global zone's context + * (from a fork()'ed child that has entered the non-global zone). This is + * done to allow the callback to communicate with itself if needed (e.g. to + * pass back the file descriptor of an opened file). + */ +static int +dlmgmt_zfop(const char *filename, zoneid_t zoneid, zfcb_t *zfcb, + zfoparg_t *zfoparg) +{ + int ctfd; + int err; + pid_t childpid; + siginfo_t info; + zfarg_t zfarg; + + if (zoneid != GLOBAL_ZONEID) { + /* + * We need to access a file that isn't in the global zone. + * Accessing non-global zone files from the global zone is + * unsafe (due to symlink attacks), we'll need to fork a child + * that enters the zone in question and executes the callback + * that will operate on the file. + * + * Before we proceed with this zone tango, we need to create a + * new process contract for the child, as required by + * zone_enter(). + */ + errno = 0; + ctfd = open64("/system/contract/process/template", O_RDWR); + if (ctfd == -1) + return (errno); + if ((err = ct_tmpl_set_critical(ctfd, 0)) != 0 || + (err = ct_tmpl_set_informative(ctfd, 0)) != 0 || + (err = ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR)) != 0 || + (err = ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY)) != 0 || + (err = ct_tmpl_activate(ctfd)) != 0) { + return (err); + } + childpid = fork(); + (void) ct_tmpl_clear(ctfd); + (void) close(ctfd); + switch (childpid) { + case -1: + return (err); + case 0: + /* + * Elevate our privileges as zone_enter() requires all + * privileges. + */ + if ((err = dlmgmt_elevate_privileges()) != 0) + _exit(err); + if (zone_enter(zoneid) == -1) + _exit(errno); + if ((err = dlmgmt_drop_privileges()) != 0) + _exit(err); + break; + default: + if (waitid(P_PID, childpid, &info, WEXITED) == -1) + return (errno); + if (info.si_status != 0) + return (info.si_status); + } + } + + zfarg.zfarg_inglobalzone = (zoneid == GLOBAL_ZONEID || childpid != 0); + zfarg.zfarg_finglobalzone = (zoneid == GLOBAL_ZONEID); + zfarg.zfarg_filename = filename; + zfarg.zfarg_oparg = zfoparg; + err = zfcb(&zfarg); + if (!zfarg.zfarg_inglobalzone) + _exit(err); + return (err); +} + +static int +dlmgmt_zopen_cb(zfarg_t *zfarg) +{ + struct strrecvfd recvfd; + boolean_t newfile = B_FALSE; + boolean_t inglobalzone = zfarg->zfarg_inglobalzone; + zoneid_t finglobalzone = zfarg->zfarg_finglobalzone; + const char *filename = zfarg->zfarg_filename; + const char *modestr = zfarg->zfarg_openarg.zopen_modestr; + int *p = zfarg->zfarg_openarg.zopen_pipe; + struct stat statbuf; + int oflags; + mode_t mode; + int fd = -1; + int err; + + /* We only ever open a file for reading or writing, not both. */ + oflags = (modestr[0] == 'r') ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC; + mode = (modestr[0] == 'r') ? 0 : S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + + /* Open the file if we're in the same zone as the file. */ + if (inglobalzone == finglobalzone) { + /* + * First determine if we will be creating the file as part of + * opening it. If so, then we'll need to ensure that it has + * the proper ownership after having opened it. + */ + if (oflags & O_CREAT) { + if (stat(filename, &statbuf) == -1) { + if (errno == ENOENT) + newfile = B_TRUE; + else + return (errno); + } + } + if ((fd = open(filename, oflags, mode)) == -1) + return (errno); + if (newfile) { + if (chown(filename, UID_DLADM, GID_SYS) == -1) { + err = errno; + (void) close(fd); + return (err); + } + } + } + + /* + * If we're not in the global zone, send the file-descriptor back to + * our parent in the global zone. + */ + if (!inglobalzone) { + assert(!finglobalzone); + assert(fd != -1); + return (ioctl(p[1], I_SENDFD, fd) == -1 ? errno : 0); + } + + /* + * At this point, we know we're in the global zone. If the file was + * in a non-global zone, receive the file-descriptor from our child in + * the non-global zone. + */ + if (!finglobalzone) { + if (ioctl(p[0], I_RECVFD, &recvfd) == -1) + return (errno); + fd = recvfd.fd; + } + + zfarg->zfarg_openarg.zopen_fd = fd; + return (0); +} + +static int +dlmgmt_zunlink_cb(zfarg_t *zfarg) +{ + if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone) + return (0); + return (unlink(zfarg->zfarg_filename) == 0 ? 0 : errno); +} + +static int +dlmgmt_zrename_cb(zfarg_t *zfarg) +{ + if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone) + return (0); + return (rename(zfarg->zfarg_filename, + zfarg->zfarg_renamearg.zrename_newname) == 0 ? 0 : errno); +} + +/* + * Same as fopen(3C), except that it opens the file relative to zoneid's zone + * root. + */ +static FILE * +dlmgmt_zfopen(const char *filename, const char *modestr, zoneid_t zoneid, + int *err) +{ + int p[2]; + zfoparg_t zfoparg; + FILE *fp = NULL; + + if (zoneid != GLOBAL_ZONEID && pipe(p) == -1) { + *err = errno; + return (NULL); + } + + zfoparg.zfop_openarg.zopen_modestr = modestr; + zfoparg.zfop_openarg.zopen_pipe = p; + *err = dlmgmt_zfop(filename, zoneid, dlmgmt_zopen_cb, &zfoparg); + if (zoneid != GLOBAL_ZONEID) { + (void) close(p[0]); + (void) close(p[1]); + } + if (*err == 0) { + fp = fdopen(zfoparg.zfop_openarg.zopen_fd, modestr); + if (fp == NULL) { + *err = errno; + (void) close(zfoparg.zfop_openarg.zopen_fd); + } + } + return (fp); +} + +/* + * Same as rename(2), except that old and new are relative to zoneid's zone + * root. + */ +static int +dlmgmt_zrename(const char *old, const char *new, zoneid_t zoneid) +{ + zfoparg_t zfoparg; + + zfoparg.zfop_renamearg.zrename_newname = new; + return (dlmgmt_zfop(old, zoneid, dlmgmt_zrename_cb, &zfoparg)); +} + +/* + * Same as unlink(2), except that filename is relative to zoneid's zone root. + */ +static int +dlmgmt_zunlink(const char *filename, zoneid_t zoneid) +{ + return (dlmgmt_zfop(filename, zoneid, dlmgmt_zunlink_cb, NULL)); +} + static size_t write_str(char *buffer, size_t buffer_length, char *name, void *value) { @@ -237,24 +505,41 @@ read_int64(char *buffer, void **value) return (sizeof (int64_t)); } +static dlmgmt_db_req_t * +dlmgmt_db_req_alloc(dlmgmt_db_op_t op, const char *linkname, + datalink_id_t linkid, zoneid_t zoneid, uint32_t flags, int *err) +{ + dlmgmt_db_req_t *req; + + if ((req = calloc(1, sizeof (dlmgmt_db_req_t))) == NULL) { + *err = errno; + } else { + req->ls_op = op; + if (linkname != NULL) + (void) strlcpy(req->ls_link, linkname, MAXLINKNAMELEN); + req->ls_linkid = linkid; + req->ls_zoneid = zoneid; + req->ls_flags = flags; + } + return (req); +} + +/* + * Update the db entry with name "entryname" using information from "linkp". + */ static int -dlmgmt_db_update(dlmgmt_db_op_t op, datalink_id_t linkid, uint32_t flags) +dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp, + uint32_t flags) { dlmgmt_db_req_t *req; int err; - /* - * It is either a persistent request or an active request, not both. - */ + /* It is either a persistent request or an active request, not both. */ assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE)); - if ((req = malloc(sizeof (dlmgmt_db_req_t))) == NULL) - return (ENOMEM); - - req->ls_next = NULL; - req->ls_op = op; - req->ls_linkid = linkid; - req->ls_flags = flags; + if ((req = dlmgmt_db_req_alloc(op, entryname, linkp->ll_linkid, + linkp->ll_zoneid, flags, &err)) == NULL) + return (err); /* * If the return error is EINPROGRESS, this request is handled @@ -297,15 +582,11 @@ dlmgmt_process_db_req(dlmgmt_db_req_t *req) } err = dlmgmt_process_db_onereq(req, writeop); - if (err != EINPROGRESS && err != 0 && - (req->ls_flags != DLMGMT_ACTIVE || errno != ENOENT)) { - + if (err != EINPROGRESS && err != 0 && err != ENOENT) { /* - * Log the error unless the request processing: - * - is successful; - * - is still in progress; - * - has failed with ENOENT because the active configuration - * file is not created yet; + * Log the error unless the request processing is still in + * progress or if the configuration file hasn't been created + * yet (ENOENT). */ dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s " "operation on %s configuration failed: %s", @@ -331,79 +612,44 @@ dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop) FILE *fp, *nfp = NULL; char file[MAXPATHLEN]; char newfile[MAXPATHLEN]; - int nfd; DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST)); - if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) { - if (writeop && errno == EROFS) { - /* - * This can happen at boot when the file system is - * read-only. So add this request to the pending - * request list and start a retry thread. - */ - return (EINPROGRESS); - } else if (req->ls_flags == DLMGMT_ACTIVE && errno == ENOENT) { - /* - * It is fine if the file keeping active configuration - * does not exist. This happens during a new reboot. - */ - if (!writeop) - return (ENOENT); - /* - * If this is an update request for the active - * configuration, create the file. - */ - if ((fp = fopen(file, "w")) == NULL) - return (errno == EROFS ? EINPROGRESS : errno); - } else { - return (errno); - } - } + fp = dlmgmt_zfopen(file, "r", req->ls_zoneid, &err); + /* + * Note that it is not an error if the file doesn't exist. If we're + * reading, we treat this case the same way as an empty file. If + * we're writing, the file will be created when we open the file for + * writing below. + */ + if (fp == NULL && !writeop) + return (err); if (writeop) { (void) snprintf(newfile, MAXPATHLEN, "%s.new", file); - if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { - err = errno; - (void) fclose(fp); - return (err); - } - - if ((nfp = fdopen(nfd, "w")) == NULL) { - err = errno; - (void) close(nfd); - (void) fclose(fp); - (void) unlink(newfile); - return (err); + nfp = dlmgmt_zfopen(newfile, "w", req->ls_zoneid, &err); + if (nfp == NULL) { + /* + * EROFS can happen at boot when the file system is + * read-only. Return EINPROGRESS so that the caller + * can add this request to the pending request list + * and start a retry thread. + */ + err = (errno == EROFS ? EINPROGRESS : errno); + goto done; } } - if (writeop) - err = process_db_write(req, fp, nfp); - else - err = process_db_read(req, fp, nfp); - if (!writeop || err != 0) - goto done; - - if (fflush(nfp) == EOF) { - err = errno; - goto done; - } - (void) fclose(fp); - (void) fclose(nfp); - - if (rename(newfile, file) < 0) { - err = errno; - (void) unlink(newfile); - return (err); + if (writeop) { + if ((err = process_db_write(req, fp, nfp)) == 0) + err = dlmgmt_zrename(newfile, file, req->ls_zoneid); + } else { + err = process_db_read(req, fp); } - return (0); - done: if (nfp != NULL) { (void) fclose(nfp); if (err != 0) - (void) unlink(newfile); + (void) dlmgmt_zunlink(newfile, req->ls_zoneid); } (void) fclose(fp); return (err); @@ -414,15 +660,13 @@ static void * dlmgmt_db_update_thread(void *arg) { dlmgmt_db_req_t *req; - int err = 0; dlmgmt_table_lock(B_TRUE); assert(dlmgmt_db_req_head != NULL); while ((req = dlmgmt_db_req_head) != NULL) { assert(req->ls_flags == DLMGMT_PERSIST); - err = dlmgmt_process_db_onereq(req, B_TRUE); - if (err == EINPROGRESS) { + if (dlmgmt_process_db_onereq(req, B_TRUE) == EINPROGRESS) { /* * The filesystem is still read only. Go to sleep and * try again. @@ -494,7 +738,11 @@ parse_linkprops(char *buf, dlmgmt_link_t *linkp) if (c == '=') goto parse_fail; - if (strcmp(attr_name, "name") == 0) { + if (strcmp(attr_name, "linkid") == 0) { + (void) read_int64(curr, &attr_buf); + linkp->ll_linkid = + (datalink_class_t)*(int64_t *)attr_buf; + } else if (strcmp(attr_name, "name") == 0) { (void) read_str(curr, &attr_buf); (void) snprintf(linkp->ll_link, MAXLINKNAMELEN, "%s", attr_buf); @@ -553,12 +801,14 @@ parse_fail: } static boolean_t -process_link_line(char *buf, dlmgmt_link_t **linkpp) +process_link_line(char *buf, dlmgmt_link_t *linkp) { - dlmgmt_link_t *linkp; - int i, len, llen; - char *str, *lasts; - char tmpbuf[MAXLINELEN]; + int i, len, llen; + char *str, *lasts; + char tmpbuf[MAXLINELEN]; + + bzero(linkp, sizeof (*linkp)); + linkp->ll_linkid = DATALINK_INVALID_LINKID; /* * Use a copy of buf for parsing so that we can do whatever we want. @@ -573,24 +823,33 @@ process_link_line(char *buf, dlmgmt_link_t **linkpp) if (!isspace(tmpbuf[i])) break; } - if (i == len || tmpbuf[i] == '#') { - *linkpp = NULL; + if (i == len || tmpbuf[i] == '#') return (B_TRUE); - } - - linkp = calloc(1, sizeof (dlmgmt_link_t)); - if (linkp == NULL) - goto fail; str = tmpbuf + i; /* - * Find the link id and assign it to the link structure. + * Find the link name and assign it to the link structure. */ if (strtok_r(str, " \n\t", &lasts) == NULL) goto fail; llen = strlen(str); - linkp->ll_linkid = atoi(str); + /* + * Note that a previous version of the persistent datalink.conf file + * stored the linkid as the first field. In that case, the name will + * be obtained through parse_linkprops from a property with the format + * "name=<linkname>". If we encounter such a format, we set + * rewrite_needed so that dlmgmt_db_init() can rewrite the file with + * the new format after it's done reading in the data. + */ + if (isdigit(str[0])) { + linkp->ll_linkid = atoi(str); + rewrite_needed = B_TRUE; + } else { + if (strlcpy(linkp->ll_link, str, sizeof (linkp->ll_link)) >= + sizeof (linkp->ll_link)) + goto fail; + } str += llen + 1; if (str >= tmpbuf + len) @@ -605,12 +864,9 @@ process_link_line(char *buf, dlmgmt_link_t **linkpp) if (parse_linkprops(str, linkp) < 0) goto fail; - *linkpp = linkp; return (B_TRUE); fail: - link_destroy(linkp); - /* * Delete corrupted line. */ @@ -618,20 +874,82 @@ fail: return (B_FALSE); } +/* + * Find any properties in linkp that refer to "old", and rename to "new". + * Return B_TRUE if any renaming occurred. + */ +static int +dlmgmt_attr_rename(dlmgmt_link_t *linkp, const char *old, const char *new, + boolean_t *renamed) +{ + dlmgmt_linkattr_t *attrp; + char *newval = NULL, *pname; + char valcp[MAXLINKATTRVALLEN]; + size_t newsize; + + *renamed = B_FALSE; + + if ((attrp = linkattr_find(linkp->ll_head, "linkover")) != NULL || + (attrp = linkattr_find(linkp->ll_head, "simnetpeer")) != NULL) { + if (strcmp(old, (char *)attrp->lp_val) == 0) { + newsize = strlen(new) + 1; + if ((newval = malloc(newsize)) == NULL) + return (errno); + (void) strcpy(newval, new); + free(attrp->lp_val); + attrp->lp_val = newval; + attrp->lp_sz = newsize; + *renamed = B_TRUE; + } + return (0); + } + + if ((attrp = linkattr_find(linkp->ll_head, "portnames")) == NULL) + return (0); + + /* <linkname>:[<linkname>:]... */ + if ((newval = calloc(MAXLINKATTRVALLEN, sizeof (char))) == NULL) + return (errno); + + bcopy(attrp->lp_val, valcp, sizeof (valcp)); + pname = strtok(valcp, ":"); + while (pname != NULL) { + if (strcmp(pname, old) == 0) { + (void) strcat(newval, new); + *renamed = B_TRUE; + } else { + (void) strcat(newval, pname); + } + (void) strcat(newval, ":"); + pname = strtok(NULL, ":"); + } + if (*renamed) { + free(attrp->lp_val); + attrp->lp_val = newval; + attrp->lp_sz = strlen(newval) + 1; + } else { + free(newval); + } + return (0); +} + static int process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) { boolean_t done = B_FALSE; int err = 0; - dlmgmt_link_t *linkp, *link_in_file, link; + dlmgmt_link_t link_in_file, *linkp = NULL, *dblinkp; + boolean_t persist = (req->ls_flags == DLMGMT_PERSIST); + boolean_t writeall, rename, attr_renamed; char buf[MAXLINELEN]; - if (req->ls_op == DLMGMT_DB_OP_WRITE) { + writeall = (req->ls_linkid == DATALINK_ALL_LINKID); + + if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall) { /* * find the link in the avl tree with the given linkid. */ - link.ll_linkid = req->ls_linkid; - linkp = avl_find(&dlmgmt_id_avl, &link, NULL); + linkp = link_by_id(req->ls_linkid, req->ls_zoneid); if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) { /* * This link has already been changed. This could @@ -640,50 +958,77 @@ process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) */ return (0); } + /* + * In the case of a rename, linkp's name has been updated to + * the new name, and req->ls_link is the old link name. + */ + rename = (strcmp(req->ls_link, linkp->ll_link) != 0); } + /* + * fp can be NULL if the file didn't initially exist and we're + * creating it as part of this write operation. + */ + if (fp == NULL) + goto write; + while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL && process_link_line(buf, &link_in_file)) { - if (link_in_file == NULL || done) { + if (link_in_file.ll_link[0] == '\0' || done) { /* * this is a comment line or we are done updating the - * link of the given link, write the rest of lines out. + * line for the specified link, write the rest of + * lines out. */ if (fputs(buf, nfp) == EOF) err = errno; - if (link_in_file != NULL) - link_destroy(link_in_file); continue; } switch (req->ls_op) { case DLMGMT_DB_OP_WRITE: /* - * For write operations, if the linkid of the link - * read from the file does not match the id of what - * req->ll_linkid points to, write out the buffer. - * Otherwise, generate a new line. If we get to the - * end and have not seen what req->ll_linkid points - * to, write it out then. + * For write operations, we generate a new output line + * if we're either writing all links (writeall) or if + * the name of the link in the file matches the one + * we're looking for. Otherwise, we write out the + * buffer as-is. + * + * If we're doing a rename operation, ensure that any + * references to the link being renamed in link + * properties are also updated before we write + * anything. */ - if (linkp == NULL || - linkp->ll_linkid != link_in_file->ll_linkid) { - if (fputs(buf, nfp) == EOF) - err = errno; - } else { - generate_link_line(linkp, - req->ls_flags == DLMGMT_PERSIST, buf); - if (fputs(buf, nfp) == EOF) - err = errno; - done = B_TRUE; + if (writeall) { + linkp = link_by_name(link_in_file.ll_link, + req->ls_zoneid); } + if (writeall || strcmp(req->ls_link, + link_in_file.ll_link) == 0) { + generate_link_line(linkp, persist, buf); + if (!writeall && !rename) + done = B_TRUE; + } else if (rename && persist) { + dblinkp = link_by_name(link_in_file.ll_link, + req->ls_zoneid); + err = dlmgmt_attr_rename(dblinkp, req->ls_link, + linkp->ll_link, &attr_renamed); + if (err != 0) + break; + if (attr_renamed) { + generate_link_line(dblinkp, persist, + buf); + } + } + if (fputs(buf, nfp) == EOF) + err = errno; break; case DLMGMT_DB_OP_DELETE: /* * Delete is simple. If buf does not represent the * link we're deleting, write it out. */ - if (req->ls_linkid != link_in_file->ll_linkid) { + if (strcmp(req->ls_link, link_in_file.ll_link) != 0) { if (fputs(buf, nfp) == EOF) err = errno; } else { @@ -695,33 +1040,28 @@ process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) err = EINVAL; break; } - link_destroy(link_in_file); } +write: /* - * If we get to the end of the file and have not seen what - * req->ll_linkid points to, write it out then. + * If we get to the end of the file and have not seen what linkid + * points to, write it out then. */ - if (req->ls_op == DLMGMT_DB_OP_WRITE && !done) { - generate_link_line(linkp, req->ls_flags == DLMGMT_PERSIST, buf); + if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall && !rename && !done) { + generate_link_line(linkp, persist, buf); done = B_TRUE; if (fputs(buf, nfp) == EOF) err = errno; } - if (!done) - err = ENOENT; - return (err); } -/* ARGSUSED1 */ static int -process_db_read(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) +process_db_read(dlmgmt_db_req_t *req, FILE *fp) { avl_index_t name_where, id_where; - dlmgmt_link_t *link_in_file; - dlmgmt_link_t *linkp1, *linkp2; + dlmgmt_link_t link_in_file, *newlink, *link_in_db; char buf[MAXLINELEN]; int err = 0; @@ -737,40 +1077,71 @@ process_db_read(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) /* * Skip the comment line. */ - if (link_in_file == NULL) + if (link_in_file.ll_link[0] == '\0') + continue; + + if ((req->ls_flags & DLMGMT_ACTIVE) && + link_in_file.ll_linkid == DATALINK_INVALID_LINKID) continue; - linkp1 = avl_find(&dlmgmt_name_avl, link_in_file, &name_where); - linkp2 = avl_find(&dlmgmt_id_avl, link_in_file, &id_where); - if ((linkp1 != NULL) || (linkp2 != NULL)) { + link_in_file.ll_zoneid = req->ls_zoneid; + link_in_db = avl_find(&dlmgmt_name_avl, &link_in_file, + &name_where); + if (link_in_db != NULL) { /* - * If any of the following conditions are met, this is - * a duplicate entry: - * - * 1. link2 (with the given name) and link2 (with the - * given id) are not the same link; - * 2. This is a persistent req and find the link with - * the given name and id. Note that persistent db - * is read before the active one. - * 3. Found the link with the given name and id but - * the link is already active. + * If the link in the database already has the flag + * for this request set, then the entry is a + * duplicate. If it's not a duplicate, then simply + * turn on the appropriate flag on the existing link. */ - if ((linkp1 != linkp2) || - (req->ls_flags == DLMGMT_PERSIST) || - ((linkp1->ll_flags & DLMGMT_ACTIVE) != 0)) { - dlmgmt_log(LOG_WARNING, "Duplicate link " - "entries in repository: link name %s " - "link id %i", link_in_file->ll_link, - link_in_file->ll_linkid); + if (link_in_db->ll_flags & req->ls_flags) { + dlmgmt_log(LOG_WARNING, "Duplicate links " + "in the repository: %s", + link_in_file.ll_link); } else { - linkp1->ll_flags |= DLMGMT_ACTIVE; + if (req->ls_flags & DLMGMT_PERSIST) { + /* + * Save the newly read properties into + * the existing link. + */ + assert(link_in_db->ll_head == NULL); + link_in_db->ll_head = + link_in_file.ll_head; + } + link_in_db->ll_flags |= req->ls_flags; } - link_destroy(link_in_file); } else { - avl_insert(&dlmgmt_name_avl, link_in_file, name_where); - avl_insert(&dlmgmt_id_avl, link_in_file, id_where); - dlmgmt_advance(link_in_file); - link_in_file->ll_flags |= req->ls_flags; + /* + * This is a new link. Allocate a new dlmgmt_link_t + * and add it to the trees. + */ + newlink = calloc(1, sizeof (*newlink)); + if (newlink == NULL) { + dlmgmt_log(LOG_WARNING, "Unable to allocate " + "memory to create new link %s", + link_in_file.ll_link); + continue; + } + bcopy(&link_in_file, newlink, sizeof (*newlink)); + + if (newlink->ll_linkid == DATALINK_INVALID_LINKID) + newlink->ll_linkid = dlmgmt_nextlinkid; + if (avl_find(&dlmgmt_id_avl, newlink, &id_where) != + NULL) { + link_destroy(newlink); + continue; + } + if ((req->ls_flags & DLMGMT_ACTIVE) && + link_activate(newlink) != 0) { + dlmgmt_log(LOG_WARNING, "Unable to activate %s", + newlink->ll_link); + link_destroy(newlink); + continue; + } + avl_insert(&dlmgmt_name_avl, newlink, name_where); + avl_insert(&dlmgmt_id_avl, newlink, id_where); + dlmgmt_advance(newlink); + newlink->ll_flags |= req->ls_flags; } } @@ -780,70 +1151,72 @@ process_db_read(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) /* * Generate an entry in the link database. * Each entry has this format: - * <link id> <prop0>=<type>,<val>;...;<propn>=<type>,<val>; + * <link name> <prop0>=<type>,<val>;...;<propn>=<type>,<val>; */ static void generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf) { char tmpbuf[MAXLINELEN]; - char *ptr; + char *ptr = tmpbuf; char *lim = tmpbuf + MAXLINELEN; - char *name_to_write = NULL; - datalink_id_t id_to_write; dlmgmt_linkattr_t *cur_p = NULL; uint64_t u64; - ptr = tmpbuf; - id_to_write = linkp->ll_linkid; - ptr += snprintf(ptr, BUFLEN(lim, ptr), "%d\t", id_to_write); - name_to_write = linkp->ll_link; - ptr += write_str(ptr, BUFLEN(lim, ptr), "name", name_to_write); + ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link); + if (!persist) { + /* + * We store the linkid in the active database so that dlmgmtd + * can recover in the event that it is restarted. + */ + u64 = linkp->ll_linkid; + ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64); + } u64 = linkp->ll_class; ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64); u64 = linkp->ll_media; ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64); /* - * The daemon does not keep any active link attribute. If this request - * is for active configuration, we are done. + * The daemon does not keep any active link attribute. Only store the + * attributes if this request is for persistent configuration, */ - if (!persist) - goto done; - - for (cur_p = linkp->ll_head; cur_p != NULL; cur_p = cur_p->lp_next) { - ptr += translators[cur_p->lp_type].write_func(ptr, - BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val); + if (persist) { + for (cur_p = linkp->ll_head; cur_p != NULL; + cur_p = cur_p->lp_next) { + ptr += translators[cur_p->lp_type].write_func(ptr, + BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val); + } } -done: - if (ptr > lim) - return; - (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf); + + if (ptr <= lim) + (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf); } int -dlmgmt_delete_db_entry(datalink_id_t linkid, uint32_t flags) +dlmgmt_delete_db_entry(dlmgmt_link_t *linkp, uint32_t flags) { - return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkid, flags)); + return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkp->ll_link, linkp, + flags)); } int -dlmgmt_write_db_entry(datalink_id_t linkid, uint32_t flags) +dlmgmt_write_db_entry(const char *entryname, dlmgmt_link_t *linkp, + uint32_t flags) { - int err; + int err; if (flags & DLMGMT_PERSIST) { - if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, - linkid, DLMGMT_PERSIST)) != 0) { + if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname, + linkp, DLMGMT_PERSIST)) != 0) { return (err); } } if (flags & DLMGMT_ACTIVE) { - if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, - linkid, DLMGMT_ACTIVE)) != 0) && - (flags & DLMGMT_PERSIST)) { - (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, - linkid, DLMGMT_PERSIST); + if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname, + linkp, DLMGMT_ACTIVE)) != 0) && (flags & DLMGMT_PERSIST)) { + (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, entryname, + linkp, DLMGMT_PERSIST); return (err); } } @@ -852,87 +1225,177 @@ dlmgmt_write_db_entry(datalink_id_t linkid, uint32_t flags) } /* + * Upgrade properties that have link IDs as values to link names. Because '.' + * is a valid linkname character, the port separater for link aggregations + * must be changed to ':'.4 + */ +static void +linkattr_upgrade(dlmgmt_linkattr_t *attrp) +{ + datalink_id_t linkid; + char *portidstr; + char portname[MAXLINKNAMELEN + 1]; + dlmgmt_link_t *linkp; + char *new_attr_val; + size_t new_attr_sz; + boolean_t upgraded = B_FALSE; + + if (strcmp(attrp->lp_name, "linkover") == 0 || + strcmp(attrp->lp_name, "simnetpeer") == 0) { + if (attrp->lp_type == DLADM_TYPE_UINT64) { + linkid = *(datalink_id_t *)attrp->lp_val; + if ((linkp = link_by_id(linkid, GLOBAL_ZONEID)) == NULL) + return; + new_attr_sz = strlen(linkp->ll_link) + 1; + if ((new_attr_val = malloc(new_attr_sz)) == NULL) + return; + (void) strcpy(new_attr_val, linkp->ll_link); + upgraded = B_TRUE; + } + } else if (strcmp(attrp->lp_name, "portnames") == 0) { + /* + * The old format for "portnames" was + * "<linkid>.[<linkid>.]...". The new format is + * "<linkname>:[<linkname>:]...". + */ + if (!isdigit(((char *)attrp->lp_val)[0])) + return; + new_attr_val = calloc(MAXLINKATTRVALLEN, sizeof (char)); + if (new_attr_val == NULL) + return; + portidstr = (char *)attrp->lp_val; + while (*portidstr != '\0') { + errno = 0; + linkid = strtol(portidstr, &portidstr, 10); + if (linkid == 0 || *portidstr != '.' || + (linkp = link_by_id(linkid, GLOBAL_ZONEID)) == + NULL) { + free(new_attr_val); + return; + } + (void) snprintf(portname, sizeof (portname), "%s:", + linkp->ll_link); + if (strlcat(new_attr_val, portname, + MAXLINKATTRVALLEN) >= MAXLINKATTRVALLEN) { + free(new_attr_val); + return; + } + /* skip the '.' delimiter */ + portidstr++; + } + new_attr_sz = strlen(new_attr_val) + 1; + upgraded = B_TRUE; + } + + if (upgraded) { + attrp->lp_type = DLADM_TYPE_STR; + attrp->lp_sz = new_attr_sz; + free(attrp->lp_val); + attrp->lp_val = new_attr_val; + } +} + +static void +dlmgmt_db_upgrade(dlmgmt_link_t *linkp) +{ + dlmgmt_linkattr_t *attrp; + + for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) + linkattr_upgrade(attrp); +} + +static void +dlmgmt_db_phys_activate(dlmgmt_link_t *linkp) +{ + linkp->ll_flags |= DLMGMT_ACTIVE; + (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_ACTIVE); +} + +static void +dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func) +{ + dlmgmt_link_t *linkp; + + for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL; + linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { + if (linkp->ll_zoneid == zoneid && (linkp->ll_class & class)) + func(linkp); + } +} + +/* * Initialize the datalink <link name, linkid> mapping and the link's * attributes list based on the configuration file /etc/dladm/datalink.conf * and the active configuration cache file * /etc/svc/volatile/dladm/datalink-management:default.cache. - * - * This function is called when the datalink-management service is started - * during reboot, and when the dlmgmtd daemon is restarted. */ int -dlmgmt_db_init() +dlmgmt_db_init(zoneid_t zoneid) { - char filename[MAXPATHLEN]; - dlmgmt_db_req_t req; + dlmgmt_db_req_t *req; int err; - dlmgmt_link_t *linkp; - char *fmri, *c; + boolean_t boot = B_FALSE; - /* - * First derive the name of the cache file from the FMRI name. This - * cache name is used to keep active datalink configuration. - */ - if (debug) { - (void) snprintf(cachefile, MAXPATHLEN, "%s/%s%s", - DLMGMT_TMPFS_DIR, progname, ".debug.cache"); - } else { - if ((fmri = getenv("SMF_FMRI")) == NULL) { - dlmgmt_log(LOG_WARNING, "dlmgmtd is an smf(5) managed " - "service and should not be run from the command " - "line."); - return (EINVAL); - } + if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL, + DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL) + return (err); + if ((err = dlmgmt_process_db_req(req)) != 0) { /* - * The FMRI name is in the form of - * svc:/service/service:instance. We need to remove the - * prefix "svc:/" and replace '/' with '-'. The cache file - * name is in the form of "service:instance.cache". + * If we get back ENOENT, that means that the active + * configuration file doesn't exist yet, and is not an error. + * We'll create it down below after we've loaded the + * persistent configuration. */ - if ((c = strchr(fmri, '/')) != NULL) - c++; - else - c = fmri; - (void) snprintf(filename, MAXPATHLEN, "%s.cache", c); - for (c = filename; *c != '\0'; c++) { - if (*c == '/') - *c = '-'; - } - - (void) snprintf(cachefile, MAXPATHLEN, "%s/%s", - DLMGMT_TMPFS_DIR, filename); + if (err != ENOENT) + goto done; + boot = B_TRUE; } - dlmgmt_table_lock(B_TRUE); - - req.ls_next = NULL; - req.ls_op = DLMGMT_DB_OP_READ; - req.ls_linkid = DATALINK_INVALID_LINKID; - req.ls_flags = DLMGMT_PERSIST; - - if ((err = dlmgmt_process_db_req(&req)) != 0) + req->ls_flags = DLMGMT_PERSIST; + err = dlmgmt_process_db_req(req); + if (err != 0 && err != ENOENT) goto done; - - req.ls_flags = DLMGMT_ACTIVE; - err = dlmgmt_process_db_req(&req); - if (err == ENOENT) { + err = 0; + if (rewrite_needed) { /* - * The temporary datalink.conf does not exist. This is - * the first boot. Mark all the physical links active. + * First update links in memory, then dump the entire db to + * disk. */ - for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL; - linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { - if (linkp->ll_class == DATALINK_CLASS_PHYS) { - linkp->ll_flags |= DLMGMT_ACTIVE; - (void) dlmgmt_write_db_entry( - linkp->ll_linkid, DLMGMT_ACTIVE); - } - } - err = 0; + dlmgmt_db_walk(zoneid, DATALINK_CLASS_ALL, dlmgmt_db_upgrade); + req->ls_op = DLMGMT_DB_OP_WRITE; + req->ls_linkid = DATALINK_ALL_LINKID; + if ((err = dlmgmt_process_db_req(req)) != 0 && + err != EINPROGRESS) + goto done; + } + if (boot) { + dlmgmt_db_walk(zoneid, DATALINK_CLASS_PHYS, + dlmgmt_db_phys_activate); } done: - dlmgmt_table_unlock(); + if (err == EINPROGRESS) + err = 0; + else + free(req); return (err); } + +/* + * Remove all links in the given zoneid. + */ +void +dlmgmt_db_fini(zoneid_t zoneid) +{ + dlmgmt_link_t *linkp = avl_first(&dlmgmt_name_avl), *next_linkp; + + while (linkp != NULL) { + next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp); + if (linkp->ll_zoneid == zoneid) { + (void) dlmgmt_destroy_common(linkp, + DLMGMT_ACTIVE | DLMGMT_PERSIST); + } + linkp = next_linkp; + } +} diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_door.c b/usr/src/cmd/dlmgmtd/dlmgmt_door.c index e73f4841b6..ad59cfe190 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_door.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_door.c @@ -28,6 +28,23 @@ * Main door handler functions used by dlmgmtd to process the different door * call requests. Door call requests can come from the user-land applications, * or from the kernel. + * + * Note on zones handling: + * + * There are two zoneid's associated with a link. One is the zoneid of the + * zone in which the link was created (ll_zoneid in the dlmgmt_link_t), and + * the other is the zoneid of the zone where the link is currently assigned + * (the "zone" link property). The two can be different if a datalink is + * created in the global zone and subsequently assigned to a non-global zone + * via zonecfg or via explicitly setting the "zone" link property. + * + * Door clients can see links that were created in their zone, and links that + * are currently assigned to their zone. Door clients in a zone can only + * modify links that were created in their zone. + * + * The datalink ID space is global, while each zone has its own datalink name + * space. This allows each zone to have complete freedom over the names that + * they assign to links created within the zone. */ #include <assert.h> @@ -38,29 +55,47 @@ #include <strings.h> #include <syslog.h> #include <sys/sysevent/eventdefs.h> +#include <zone.h> #include <libsysevent.h> #include <libdlmgmt.h> #include <librcm.h> #include "dlmgmt_impl.h" -typedef void dlmgmt_door_handler_t(void *, void *); +typedef void dlmgmt_door_handler_t(void *, void *, zoneid_t, ucred_t *); typedef struct dlmgmt_door_info_s { uint_t di_cmd; - boolean_t di_set; size_t di_reqsz; size_t di_acksz; dlmgmt_door_handler_t *di_handler; } dlmgmt_door_info_t; +/* + * Check if the caller has the required privileges to operate on a link of the + * given class. + */ +static int +dlmgmt_checkprivs(datalink_class_t class, ucred_t *cred) +{ + const priv_set_t *eset; + + eset = ucred_getprivset(cred, PRIV_EFFECTIVE); + if (eset != NULL && ((class == DATALINK_CLASS_IPTUN && + priv_ismember(eset, PRIV_SYS_IPTUN_CONFIG)) || + priv_ismember(eset, PRIV_SYS_DL_CONFIG) || + priv_ismember(eset, PRIV_SYS_NET_CONFIG))) + return (0); + return (EACCES); +} static dlmgmt_link_t * -dlmgmt_getlink_by_dev(char *devname) +dlmgmt_getlink_by_dev(char *devname, zoneid_t zoneid) { dlmgmt_link_t *linkp = avl_first(&dlmgmt_id_avl); for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { - if ((linkp->ll_class == DATALINK_CLASS_PHYS) && + if (link_is_visible(linkp, zoneid) && + (linkp->ll_class == DATALINK_CLASS_PHYS) && linkattr_equal(&(linkp->ll_head), FDEVNAME, devname, strlen(devname) + 1)) { return (linkp); @@ -102,7 +137,7 @@ done: } static void -dlmgmt_upcall_create(void *argp, void *retp) +dlmgmt_upcall_create(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_upcall_arg_create_t *create = argp; dlmgmt_create_retval_t *retvalp = retp; @@ -129,13 +164,15 @@ dlmgmt_upcall_create(void *argp, void *retp) */ dlmgmt_table_lock(B_TRUE); + if ((err = dlmgmt_checkprivs(class, cred)) != 0) + goto done; + /* * Check to see whether this is the reattachment of an existing * physical link. If so, return its linkid. */ - if ((class == DATALINK_CLASS_PHYS) && - (linkp = dlmgmt_getlink_by_dev(create->ld_devname)) != NULL) { - + if ((class == DATALINK_CLASS_PHYS) && (linkp = + dlmgmt_getlink_by_dev(create->ld_devname, zoneid)) != NULL) { if (linkattr_equal(&(linkp->ll_head), FPHYMAJ, &create->ld_phymaj, sizeof (uint64_t)) && linkattr_equal(&(linkp->ll_head), FPHYINST, @@ -163,6 +200,8 @@ dlmgmt_upcall_create(void *argp, void *retp) if ((linkp->ll_flags & DLMGMT_ACTIVE) == 0) reconfigured = B_TRUE; + if ((err = link_activate(linkp)) != 0) + goto done; linkp->ll_flags |= flags; linkp->ll_gen++; @@ -170,7 +209,7 @@ dlmgmt_upcall_create(void *argp, void *retp) } if ((err = dlmgmt_create_common(create->ld_devname, class, media, - flags, &linkp)) == EEXIST) { + zoneid, flags, &linkp)) == EEXIST) { /* * The link name already exists. Return error if this is a * non-physical link (in that case, the link name must be @@ -183,11 +222,12 @@ dlmgmt_upcall_create(void *argp, void *retp) * The physical link's name already exists, request * a suggested link name: net<nextppa> */ - err = dlmgmt_generate_name("net", link, MAXLINKNAMELEN); + err = dlmgmt_generate_name("net", link, MAXLINKNAMELEN, zoneid); if (err != 0) goto done; - err = dlmgmt_create_common(link, class, media, flags, &linkp); + err = dlmgmt_create_common(link, class, media, zoneid, flags, + &linkp); } if (err != 0) @@ -210,7 +250,7 @@ dlmgmt_upcall_create(void *argp, void *retp) } done: - if ((err == 0) && ((err = dlmgmt_write_db_entry(linkp->ll_linkid, + if ((err == 0) && ((err = dlmgmt_write_db_entry(linkp->ll_link, linkp, linkp->ll_flags)) != 0) && created) { (void) dlmgmt_destroy_common(linkp, flags); } @@ -235,7 +275,7 @@ noupdate: } static void -dlmgmt_upcall_update(void *argp, void *retp) +dlmgmt_upcall_update(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_upcall_arg_update_t *update = argp; dlmgmt_update_retval_t *retvalp = retp; @@ -252,11 +292,15 @@ dlmgmt_upcall_update(void *argp, void *retp) * Check to see whether this is the reattachment of an existing * physical link. If so, return its linkid. */ - if ((linkp = dlmgmt_getlink_by_dev(update->ld_devname)) == NULL) { + if ((linkp = dlmgmt_getlink_by_dev(update->ld_devname, zoneid)) == + NULL) { err = ENOENT; goto done; } + if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) + goto done; + retvalp->lr_linkid = linkp->ll_linkid; retvalp->lr_media = media; if (linkp->ll_media != media && linkp->ll_media != DL_OTHER) { @@ -295,7 +339,8 @@ dlmgmt_upcall_update(void *argp, void *retp) if (linkp->ll_media != media) { linkp->ll_media = media; linkp->ll_gen++; - (void) dlmgmt_write_db_entry(linkp->ll_linkid, linkp->ll_flags); + (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, + linkp->ll_flags); } done: @@ -304,7 +349,7 @@ done: } static void -dlmgmt_upcall_destroy(void *argp, void *retp) +dlmgmt_upcall_destroy(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_upcall_arg_destroy_t *destroy = argp; dlmgmt_destroy_retval_t *retvalp = retp; @@ -320,21 +365,22 @@ dlmgmt_upcall_destroy(void *argp, void *retp) */ dlmgmt_table_lock(B_TRUE); - if ((linkp = link_by_id(linkid)) == NULL) { + if ((linkp = link_by_id(linkid, zoneid)) == NULL) { err = ENOENT; goto done; } + if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) + goto done; + if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) != 0) { - err = dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE); - if (err != 0) + if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE)) != 0) goto done; dflags |= DLMGMT_ACTIVE; } if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) != 0) { - err = dlmgmt_delete_db_entry(linkid, DLMGMT_PERSIST); - if (err != 0) + if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST)) != 0) goto done; dflags |= DLMGMT_PERSIST; } @@ -342,14 +388,15 @@ dlmgmt_upcall_destroy(void *argp, void *retp) err = dlmgmt_destroy_common(linkp, flags); done: if (err != 0 && dflags != 0) - (void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags); + (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, dflags); dlmgmt_table_unlock(); retvalp->lr_err = err; } +/* ARGSUSED */ static void -dlmgmt_getname(void *argp, void *retp) +dlmgmt_getname(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_getname_t *getname = argp; dlmgmt_getname_retval_t *retvalp = retp; @@ -360,30 +407,24 @@ dlmgmt_getname(void *argp, void *retp) * Hold the reader lock to access the link */ dlmgmt_table_lock(B_FALSE); - if ((linkp = link_by_id(getname->ld_linkid)) == NULL) { - /* - * The link does not exists. - */ + if ((linkp = link_by_id(getname->ld_linkid, zoneid)) == NULL) { err = ENOENT; - goto done; - } - - if (strlcpy(retvalp->lr_link, linkp->ll_link, MAXLINKNAMELEN) >= + } else if (strlcpy(retvalp->lr_link, linkp->ll_link, MAXLINKNAMELEN) >= MAXLINKNAMELEN) { err = ENOSPC; - goto done; + } else { + retvalp->lr_flags = linkp->ll_flags; + retvalp->lr_class = linkp->ll_class; + retvalp->lr_media = linkp->ll_media; } - retvalp->lr_flags = linkp->ll_flags; - retvalp->lr_class = linkp->ll_class; - retvalp->lr_media = linkp->ll_media; -done: dlmgmt_table_unlock(); retvalp->lr_err = err; } +/* ARGSUSED */ static void -dlmgmt_getlinkid(void *argp, void *retp) +dlmgmt_getlinkid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_getlinkid_t *getlinkid = argp; dlmgmt_getlinkid_retval_t *retvalp = retp; @@ -394,9 +435,10 @@ dlmgmt_getlinkid(void *argp, void *retp) * Hold the reader lock to access the link */ dlmgmt_table_lock(B_FALSE); - if ((linkp = link_by_name(getlinkid->ld_link)) == NULL) { + + if ((linkp = link_by_name(getlinkid->ld_link, zoneid)) == NULL) { /* - * The link does not exists. + * The link does not exist in this zone. */ err = ENOENT; goto done; @@ -412,13 +454,13 @@ done: retvalp->lr_err = err; } +/* ARGSUSED */ static void -dlmgmt_getnext(void *argp, void *retp) +dlmgmt_getnext(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_getnext_t *getnext = argp; dlmgmt_getnext_retval_t *retvalp = retp; dlmgmt_link_t link, *linkp; - datalink_id_t linkid = getnext->ld_linkid; avl_index_t where; int err = 0; @@ -427,12 +469,13 @@ dlmgmt_getnext(void *argp, void *retp) */ dlmgmt_table_lock(B_FALSE); - link.ll_linkid = (linkid + 1); - linkp = avl_find(&dlmgmt_id_avl, &link, &where); - if (linkp == NULL) + link.ll_linkid = (getnext->ld_linkid + 1); + if ((linkp = avl_find(&dlmgmt_id_avl, &link, &where)) == NULL) linkp = avl_nearest(&dlmgmt_id_avl, where, AVL_AFTER); for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { + if (!link_is_visible(linkp, zoneid)) + continue; if ((linkp->ll_class & getnext->ld_class) && (linkp->ll_flags & getnext->ld_flags) && DATALINK_MEDIA_ACCEPTED(getnext->ld_dmedia, @@ -453,8 +496,9 @@ dlmgmt_getnext(void *argp, void *retp) retvalp->lr_err = err; } +/* ARGSUSED */ static void -dlmgmt_upcall_getattr(void *argp, void *retp) +dlmgmt_upcall_getattr(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_upcall_arg_getattr_t *getattr = argp; dlmgmt_getattr_retval_t *retvalp = retp; @@ -464,22 +508,17 @@ dlmgmt_upcall_getattr(void *argp, void *retp) * Hold the reader lock to access the link */ dlmgmt_table_lock(B_FALSE); - if ((linkp = link_by_id(getattr->ld_linkid)) == NULL) { - /* - * The link does not exist. - */ + if ((linkp = link_by_id(getattr->ld_linkid, zoneid)) == NULL) { retvalp->lr_err = ENOENT; - goto done; + } else { + retvalp->lr_err = dlmgmt_getattr_common(&linkp->ll_head, + getattr->ld_attr, retvalp); } - - dlmgmt_getattr_common(&linkp->ll_head, getattr->ld_attr, retvalp); - -done: dlmgmt_table_unlock(); } static void -dlmgmt_createid(void *argp, void *retp) +dlmgmt_createid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_createid_t *createid = argp; dlmgmt_createid_retval_t *retvalp = retp; @@ -493,18 +532,21 @@ dlmgmt_createid(void *argp, void *retp) */ dlmgmt_table_lock(B_TRUE); + if ((err = dlmgmt_checkprivs(createid->ld_class, cred)) != 0) + goto done; + if (createid->ld_prefix) { err = dlmgmt_generate_name(createid->ld_link, link, - MAXLINKNAMELEN); + MAXLINKNAMELEN, zoneid); if (err != 0) goto done; err = dlmgmt_create_common(link, createid->ld_class, - createid->ld_media, createid->ld_flags, &linkp); + createid->ld_media, zoneid, createid->ld_flags, &linkp); } else { err = dlmgmt_create_common(createid->ld_link, - createid->ld_class, createid->ld_media, createid->ld_flags, - &linkp); + createid->ld_class, createid->ld_media, zoneid, + createid->ld_flags, &linkp); } if (err == 0) { @@ -512,8 +554,10 @@ dlmgmt_createid(void *argp, void *retp) * Keep the active mapping. */ linkid = linkp->ll_linkid; - if (createid->ld_flags & DLMGMT_ACTIVE) - (void) dlmgmt_write_db_entry(linkid, DLMGMT_ACTIVE); + if (createid->ld_flags & DLMGMT_ACTIVE) { + (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, + DLMGMT_ACTIVE); + } } done: @@ -523,7 +567,7 @@ done: } static void -dlmgmt_destroyid(void *argp, void *retp) +dlmgmt_destroyid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_destroyid_t *destroyid = argp; dlmgmt_destroyid_retval_t *retvalp = retp; @@ -536,20 +580,21 @@ dlmgmt_destroyid(void *argp, void *retp) * Hold the writer lock to update the link table. */ dlmgmt_table_lock(B_TRUE); - if ((linkp = link_by_id(linkid)) == NULL) { + if ((linkp = link_by_id(linkid, zoneid)) == NULL) { err = ENOENT; goto done; } - if ((err = dlmgmt_destroy_common(linkp, flags)) != 0) + if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) goto done; /* * Delete the active mapping. */ if (flags & DLMGMT_ACTIVE) - (void) dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE); - + err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE); + if (err == 0) + err = dlmgmt_destroy_common(linkp, flags); done: dlmgmt_table_unlock(); retvalp->lr_err = err; @@ -561,13 +606,13 @@ done: * the given link name. */ static void -dlmgmt_remapid(void *argp, void *retp) +dlmgmt_remapid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_remapid_t *remapid = argp; dlmgmt_remapid_retval_t *retvalp = retp; - datalink_id_t linkid1 = remapid->ld_linkid; - dlmgmt_link_t link, *linkp1, *tmp; - avl_index_t where; + dlmgmt_link_t *linkp; + char oldname[MAXLINKNAMELEN]; + boolean_t renamed = B_FALSE; int err = 0; if (!dladm_valid_linkname(remapid->ld_link)) { @@ -579,36 +624,55 @@ dlmgmt_remapid(void *argp, void *retp) * Hold the writer lock to update the link table. */ dlmgmt_table_lock(B_TRUE); - if ((linkp1 = link_by_id(linkid1)) == NULL) { + if ((linkp = link_by_id(remapid->ld_linkid, zoneid)) == NULL) { err = ENOENT; goto done; } - if (link_by_name(remapid->ld_link) != NULL) { + if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) + goto done; + + if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) { err = EEXIST; goto done; } - avl_remove(&dlmgmt_name_avl, linkp1); - (void) strlcpy(link.ll_link, remapid->ld_link, MAXLINKNAMELEN); - tmp = avl_find(&dlmgmt_name_avl, &link, &where); - assert(tmp == NULL); - (void) strlcpy(linkp1->ll_link, remapid->ld_link, MAXLINKNAMELEN); - avl_insert(&dlmgmt_name_avl, linkp1, where); - dlmgmt_advance(linkp1); + (void) strlcpy(oldname, linkp->ll_link, MAXLINKNAMELEN); + avl_remove(&dlmgmt_name_avl, linkp); + (void) strlcpy(linkp->ll_link, remapid->ld_link, MAXLINKNAMELEN); + avl_add(&dlmgmt_name_avl, linkp); + renamed = B_TRUE; - /* - * If we renamed a temporary link, update the temporary repository. - */ - if (linkp1->ll_flags & DLMGMT_ACTIVE) - (void) dlmgmt_write_db_entry(linkid1, DLMGMT_ACTIVE); + if (linkp->ll_flags & DLMGMT_ACTIVE) { + err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_ACTIVE); + if (err != 0) + goto done; + } + if (linkp->ll_flags & DLMGMT_PERSIST) { + err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_PERSIST); + if (err != 0) { + if (linkp->ll_flags & DLMGMT_ACTIVE) { + (void) dlmgmt_write_db_entry(remapid->ld_link, + linkp, DLMGMT_ACTIVE); + } + goto done; + } + } + + dlmgmt_advance(linkp); + linkp->ll_gen++; done: + if (err != 0 && renamed) { + avl_remove(&dlmgmt_name_avl, linkp); + (void) strlcpy(linkp->ll_link, oldname, MAXLINKNAMELEN); + avl_add(&dlmgmt_name_avl, linkp); + } dlmgmt_table_unlock(); retvalp->lr_err = err; } static void -dlmgmt_upid(void *argp, void *retp) +dlmgmt_upid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_upid_t *upid = argp; dlmgmt_upid_retval_t *retvalp = retp; @@ -619,30 +683,34 @@ dlmgmt_upid(void *argp, void *retp) * Hold the writer lock to update the link table. */ dlmgmt_table_lock(B_TRUE); - if ((linkp = link_by_id(upid->ld_linkid)) == NULL) { + if ((linkp = link_by_id(upid->ld_linkid, zoneid)) == NULL) { err = ENOENT; goto done; } + if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) + goto done; + if (linkp->ll_flags & DLMGMT_ACTIVE) { err = EINVAL; goto done; } - linkp->ll_flags |= DLMGMT_ACTIVE; - (void) dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_ACTIVE); + if ((err = link_activate(linkp)) == 0) { + (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, + DLMGMT_ACTIVE); + } done: dlmgmt_table_unlock(); retvalp->lr_err = err; } static void -dlmgmt_createconf(void *argp, void *retp) +dlmgmt_createconf(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_createconf_t *createconf = argp; dlmgmt_createconf_retval_t *retvalp = retp; - dlmgmt_dlconf_t dlconf, *dlconfp, *tmp; - avl_index_t where; + dlmgmt_dlconf_t *dlconfp; int err; /* @@ -650,25 +718,23 @@ dlmgmt_createconf(void *argp, void *retp) */ dlmgmt_dlconf_table_lock(B_TRUE); - if ((err = dlconf_create(createconf->ld_link, createconf->ld_linkid, - createconf->ld_class, createconf->ld_media, &dlconfp)) != 0) { + if ((err = dlmgmt_checkprivs(createconf->ld_class, cred)) != 0) goto done; - } - - dlconf.ld_id = dlconfp->ld_id; - tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where); - assert(tmp == NULL); - avl_insert(&dlmgmt_dlconf_avl, dlconfp, where); - dlmgmt_advance_dlconfid(dlconfp); - retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; + err = dlconf_create(createconf->ld_link, createconf->ld_linkid, + createconf->ld_class, createconf->ld_media, zoneid, &dlconfp); + if (err == 0) { + avl_add(&dlmgmt_dlconf_avl, dlconfp); + dlmgmt_advance_dlconfid(dlconfp); + retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; + } done: dlmgmt_dlconf_table_unlock(); retvalp->lr_err = err; } static void -dlmgmt_setattr(void *argp, void *retp) +dlmgmt_setattr(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_setattr_t *setattr = argp; dlmgmt_setattr_retval_t *retvalp = retp; @@ -682,11 +748,14 @@ dlmgmt_setattr(void *argp, void *retp) dlconf.ld_id = (int)setattr->ld_conf; dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); - if (dlconfp == NULL) { + if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) { err = ENOENT; goto done; } + if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0) + goto done; + err = linkattr_set(&(dlconfp->ld_head), setattr->ld_attr, &setattr->ld_attrval, setattr->ld_attrsz, setattr->ld_type); @@ -696,7 +765,7 @@ done: } static void -dlmgmt_unsetconfattr(void *argp, void *retp) +dlmgmt_unsetconfattr(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_unsetattr_t *unsetattr = argp; dlmgmt_unsetattr_retval_t *retvalp = retp; @@ -710,12 +779,15 @@ dlmgmt_unsetconfattr(void *argp, void *retp) dlconf.ld_id = (int)unsetattr->ld_conf; dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); - if (dlconfp == NULL) { + if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) { err = ENOENT; goto done; } - err = linkattr_unset(&(dlconfp->ld_head), unsetattr->ld_attr); + if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0) + goto done; + + linkattr_unset(&(dlconfp->ld_head), unsetattr->ld_attr); done: dlmgmt_dlconf_table_unlock(); @@ -735,7 +807,7 @@ done: * across the pair of dladm_read_conf() and dladm_write_conf() calls. */ static void -dlmgmt_writeconf(void *argp, void *retp) +dlmgmt_writeconf(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_writeconf_t *writeconf = argp; dlmgmt_writeconf_retval_t *retvalp = retp; @@ -751,16 +823,19 @@ dlmgmt_writeconf(void *argp, void *retp) dlconf.ld_id = (int)writeconf->ld_conf; dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); - if (dlconfp == NULL) { + if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) { err = ENOENT; goto done; } + if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0) + goto done; + /* * Hold the writer lock to update the link table. */ dlmgmt_table_lock(B_TRUE); - linkp = link_by_id(dlconfp->ld_linkid); + linkp = link_by_id(dlconfp->ld_linkid, zoneid); if ((linkp == NULL) || (linkp->ll_class != dlconfp->ld_class) || (linkp->ll_media != dlconfp->ld_media) || (strcmp(linkp->ll_link, dlconfp->ld_link) != 0)) { @@ -803,7 +878,7 @@ dlmgmt_writeconf(void *argp, void *retp) } linkp->ll_gen++; - err = dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_PERSIST); + err = dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_PERSIST); dlmgmt_table_unlock(); done: dlmgmt_dlconf_table_unlock(); @@ -811,20 +886,38 @@ done: } static void -dlmgmt_removeconf(void *argp, void *retp) +dlmgmt_removeconf(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_removeconf_t *removeconf = argp; dlmgmt_removeconf_retval_t *retvalp = retp; + dlmgmt_link_t *linkp; int err; dlmgmt_table_lock(B_TRUE); - err = dlmgmt_delete_db_entry(removeconf->ld_linkid, DLMGMT_PERSIST); + if ((linkp = link_by_id(removeconf->ld_linkid, zoneid)) == NULL) { + err = ENOENT; + goto done; + } + if (zoneid != GLOBAL_ZONEID && linkp->ll_onloan) { + /* + * A non-global zone cannot remove the persistent + * configuration of a link that is on loan from the global + * zone. + */ + err = EACCES; + goto done; + } + if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) + goto done; + + err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST); +done: dlmgmt_table_unlock(); retvalp->lr_err = err; } static void -dlmgmt_destroyconf(void *argp, void *retp) +dlmgmt_destroyconf(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_destroyconf_t *destroyconf = argp; dlmgmt_destroyconf_retval_t *retvalp = retp; @@ -838,11 +931,14 @@ dlmgmt_destroyconf(void *argp, void *retp) dlconf.ld_id = (int)destroyconf->ld_conf; dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); - if (dlconfp == NULL) { + if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) { err = ENOENT; goto done; } + if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0) + goto done; + avl_remove(&dlmgmt_dlconf_avl, dlconfp); dlconf_destroy(dlconfp); @@ -855,16 +951,16 @@ done: * See the comments above dladm_write_conf() to see how ld_gen is used to * ensure atomicity across the {dlmgmt_readconf(), dlmgmt_writeconf()} pair. */ +/* ARGSUSED */ static void -dlmgmt_readconf(void *argp, void *retp) +dlmgmt_readconf(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_readconf_t *readconf = argp; dlmgmt_readconf_retval_t *retvalp = retp; dlmgmt_link_t *linkp; datalink_id_t linkid = readconf->ld_linkid; - dlmgmt_dlconf_t *dlconfp, *tmp, dlconf; + dlmgmt_dlconf_t *dlconfp; dlmgmt_linkattr_t *attrp; - avl_index_t where; int err = 0; /* @@ -876,19 +972,24 @@ dlmgmt_readconf(void *argp, void *retp) * Hold the reader lock to access the link */ dlmgmt_table_lock(B_FALSE); - linkp = link_by_id(linkid); + linkp = link_by_id(linkid, zoneid); if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) { + /* The persistent link configuration does not exist. */ + err = ENOENT; + goto done; + } + if (linkp->ll_onloan && zoneid != GLOBAL_ZONEID) { /* - * The persistent link configuration does not exists. + * The caller is in a non-global zone and the persistent + * configuration belongs to the global zone. */ - err = ENOENT; + err = EACCES; goto done; } if ((err = dlconf_create(linkp->ll_link, linkp->ll_linkid, - linkp->ll_class, linkp->ll_media, &dlconfp)) != 0) { + linkp->ll_class, linkp->ll_media, zoneid, &dlconfp)) != 0) goto done; - } for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) { if ((err = linkattr_set(&(dlconfp->ld_head), attrp->lp_name, @@ -898,11 +999,7 @@ dlmgmt_readconf(void *argp, void *retp) } } dlconfp->ld_gen = linkp->ll_gen; - - dlconf.ld_id = dlconfp->ld_id; - tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where); - assert(tmp == NULL); - avl_insert(&dlmgmt_dlconf_avl, dlconfp, where); + avl_add(&dlmgmt_dlconf_avl, dlconfp); dlmgmt_advance_dlconfid(dlconfp); retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; @@ -915,8 +1012,9 @@ done: /* * Note: the caller must free *retvalpp in case of success. */ +/* ARGSUSED */ static void -dlmgmt_getattr(void *argp, void *retp) +dlmgmt_getattr(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_getattr_t *getattr = argp; dlmgmt_getattr_retval_t *retvalp = retp; @@ -928,49 +1026,45 @@ dlmgmt_getattr(void *argp, void *retp) dlmgmt_dlconf_table_lock(B_FALSE); dlconf.ld_id = (int)getattr->ld_conf; - dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); - if (dlconfp == NULL) { + if ((dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL)) == NULL || + zoneid != dlconfp->ld_zoneid) { retvalp->lr_err = ENOENT; - goto done; + } else { + retvalp->lr_err = dlmgmt_getattr_common(&dlconfp->ld_head, + getattr->ld_attr, retvalp); } - dlmgmt_getattr_common(&dlconfp->ld_head, getattr->ld_attr, retvalp); - -done: dlmgmt_dlconf_table_unlock(); } static void -dlmgmt_upcall_linkprop_init(void *argp, void *retp) +dlmgmt_upcall_linkprop_init(void *argp, void *retp, zoneid_t zoneid, + ucred_t *cred) { dlmgmt_door_linkprop_init_t *lip = argp; dlmgmt_linkprop_init_retval_t *retvalp = retp; - boolean_t do_linkprop = B_FALSE; + dlmgmt_link_t *linkp; + int err; - /* - * Ignore wifi links until wifi property ioctls are converted - * to generic property ioctls. This avoids deadlocks due to - * wifi property ioctls using their own /dev/net device, - * not the DLD control device. - */ dlmgmt_table_lock(B_FALSE); - if (link_by_id(lip->ld_linkid) == NULL) - retvalp->lr_err = ENOENT; + if ((linkp = link_by_id(lip->ld_linkid, zoneid)) == NULL) + err = ENOENT; else - do_linkprop = B_TRUE; + err = dlmgmt_checkprivs(linkp->ll_class, cred); dlmgmt_table_unlock(); - if (do_linkprop) - retvalp->lr_err = dladm_init_linkprop(dld_handle, - lip->ld_linkid, B_TRUE); + if (err == 0) + err = dladm_init_linkprop(dld_handle, lip->ld_linkid, B_TRUE); + retvalp->lr_err = err; } /* * Get the link property that follows ld_last_attr. * If ld_last_attr is empty, return the first property. */ +/* ARGSUSED */ static void -dlmgmt_linkprop_getnext(void *argp, void *retp) +dlmgmt_linkprop_getnext(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) { dlmgmt_door_linkprop_getnext_t *getnext = argp; dlmgmt_linkprop_getnext_retval_t *retvalp = retp; @@ -1013,100 +1107,225 @@ done: retvalp->lr_err = err; } +static void +dlmgmt_setzoneid(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) +{ + dlmgmt_door_setzoneid_t *setzoneid = argp; + dlmgmt_setzoneid_retval_t *retvalp = retp; + dlmgmt_link_t *linkp; + datalink_id_t linkid = setzoneid->ld_linkid; + zoneid_t oldzoneid, newzoneid; + int err = 0; + + dlmgmt_table_lock(B_TRUE); + + /* We currently only allow changing zoneid's from the global zone. */ + if (zoneid != GLOBAL_ZONEID) { + err = EACCES; + goto done; + } + + if ((linkp = link_by_id(linkid, zoneid)) == NULL) { + err = ENOENT; + goto done; + } + + if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) + goto done; + + /* We can only assign an active link to a zone. */ + if (!(linkp->ll_flags & DLMGMT_ACTIVE)) { + err = EINVAL; + goto done; + } + + oldzoneid = linkp->ll_zoneid; + newzoneid = setzoneid->ld_zoneid; + + if (oldzoneid == newzoneid) + goto done; + + /* + * Before we remove the link from its current zone, make sure that + * there isn't a link with the same name in the destination zone. + */ + if (zoneid != GLOBAL_ZONEID && + link_by_name(linkp->ll_link, newzoneid) != NULL) { + err = EEXIST; + goto done; + } + + if (oldzoneid != GLOBAL_ZONEID) { + if (zone_remove_datalink(oldzoneid, linkid) != 0) { + err = errno; + dlmgmt_log(LOG_WARNING, "unable to remove link %d from " + "zone %d: %s", linkid, oldzoneid, strerror(err)); + goto done; + } + avl_remove(&dlmgmt_loan_avl, linkp); + linkp->ll_onloan = B_FALSE; + } + if (newzoneid != GLOBAL_ZONEID) { + if (zone_add_datalink(newzoneid, linkid) != 0) { + err = errno; + dlmgmt_log(LOG_WARNING, "unable to add link %d to zone " + "%d: %s", linkid, newzoneid, strerror(err)); + (void) zone_add_datalink(oldzoneid, linkid); + goto done; + } + avl_add(&dlmgmt_loan_avl, linkp); + linkp->ll_onloan = B_TRUE; + } + + avl_remove(&dlmgmt_name_avl, linkp); + linkp->ll_zoneid = newzoneid; + avl_add(&dlmgmt_name_avl, linkp); + +done: + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_zoneboot(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) +{ + int err; + dlmgmt_door_zoneboot_t *zoneboot = argp; + dlmgmt_zoneboot_retval_t *retvalp = retp; + + dlmgmt_table_lock(B_TRUE); + + if ((err = dlmgmt_checkprivs(0, cred)) != 0) + goto done; + + if (zoneid != GLOBAL_ZONEID) { + err = EACCES; + goto done; + } + if (zoneboot->ld_zoneid == GLOBAL_ZONEID) { + err = EINVAL; + goto done; + } + + if ((err = dlmgmt_elevate_privileges()) == 0) { + err = dlmgmt_zone_init(zoneboot->ld_zoneid); + (void) dlmgmt_drop_privileges(); + } +done: + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_zonehalt(void *argp, void *retp, zoneid_t zoneid, ucred_t *cred) +{ + int err = 0; + dlmgmt_door_zonehalt_t *zonehalt = argp; + dlmgmt_zonehalt_retval_t *retvalp = retp; + + if ((err = dlmgmt_checkprivs(0, cred)) == 0) { + if (zoneid != GLOBAL_ZONEID) { + err = EACCES; + } else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) { + err = EINVAL; + } else { + dlmgmt_table_lock(B_TRUE); + dlmgmt_db_fini(zonehalt->ld_zoneid); + dlmgmt_table_unlock(); + } + } + retvalp->lr_err = err; +} + static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = { - { DLMGMT_CMD_DLS_CREATE, B_TRUE, sizeof (dlmgmt_upcall_arg_create_t), + { DLMGMT_CMD_DLS_CREATE, sizeof (dlmgmt_upcall_arg_create_t), sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create }, - { DLMGMT_CMD_DLS_GETATTR, B_FALSE, sizeof (dlmgmt_upcall_arg_getattr_t), + { DLMGMT_CMD_DLS_GETATTR, sizeof (dlmgmt_upcall_arg_getattr_t), sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr }, - { DLMGMT_CMD_DLS_DESTROY, B_TRUE, sizeof (dlmgmt_upcall_arg_destroy_t), + { DLMGMT_CMD_DLS_DESTROY, sizeof (dlmgmt_upcall_arg_destroy_t), sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy }, - { DLMGMT_CMD_GETNAME, B_FALSE, sizeof (dlmgmt_door_getname_t), + { DLMGMT_CMD_GETNAME, sizeof (dlmgmt_door_getname_t), sizeof (dlmgmt_getname_retval_t), dlmgmt_getname }, - { DLMGMT_CMD_GETLINKID, B_FALSE, sizeof (dlmgmt_door_getlinkid_t), + { DLMGMT_CMD_GETLINKID, sizeof (dlmgmt_door_getlinkid_t), sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid }, - { DLMGMT_CMD_GETNEXT, B_FALSE, sizeof (dlmgmt_door_getnext_t), + { DLMGMT_CMD_GETNEXT, sizeof (dlmgmt_door_getnext_t), sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext }, - { DLMGMT_CMD_DLS_UPDATE, B_TRUE, sizeof (dlmgmt_upcall_arg_update_t), + { DLMGMT_CMD_DLS_UPDATE, sizeof (dlmgmt_upcall_arg_update_t), sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update }, - { DLMGMT_CMD_CREATE_LINKID, B_TRUE, sizeof (dlmgmt_door_createid_t), + { DLMGMT_CMD_CREATE_LINKID, sizeof (dlmgmt_door_createid_t), sizeof (dlmgmt_createid_retval_t), dlmgmt_createid }, - { DLMGMT_CMD_DESTROY_LINKID, B_TRUE, sizeof (dlmgmt_door_destroyid_t), + { DLMGMT_CMD_DESTROY_LINKID, sizeof (dlmgmt_door_destroyid_t), sizeof (dlmgmt_destroyid_retval_t), dlmgmt_destroyid }, - { DLMGMT_CMD_REMAP_LINKID, B_TRUE, sizeof (dlmgmt_door_remapid_t), + { DLMGMT_CMD_REMAP_LINKID, sizeof (dlmgmt_door_remapid_t), sizeof (dlmgmt_remapid_retval_t), dlmgmt_remapid }, - { DLMGMT_CMD_CREATECONF, B_TRUE, sizeof (dlmgmt_door_createconf_t), + { DLMGMT_CMD_CREATECONF, sizeof (dlmgmt_door_createconf_t), sizeof (dlmgmt_createconf_retval_t), dlmgmt_createconf }, - { DLMGMT_CMD_READCONF, B_FALSE, sizeof (dlmgmt_door_readconf_t), + { DLMGMT_CMD_READCONF, sizeof (dlmgmt_door_readconf_t), sizeof (dlmgmt_readconf_retval_t), dlmgmt_readconf }, - { DLMGMT_CMD_WRITECONF, B_TRUE, sizeof (dlmgmt_door_writeconf_t), + { DLMGMT_CMD_WRITECONF, sizeof (dlmgmt_door_writeconf_t), sizeof (dlmgmt_writeconf_retval_t), dlmgmt_writeconf }, - { DLMGMT_CMD_UP_LINKID, B_TRUE, sizeof (dlmgmt_door_upid_t), + { DLMGMT_CMD_UP_LINKID, sizeof (dlmgmt_door_upid_t), sizeof (dlmgmt_upid_retval_t), dlmgmt_upid }, - { DLMGMT_CMD_SETATTR, B_TRUE, sizeof (dlmgmt_door_setattr_t), + { DLMGMT_CMD_SETATTR, sizeof (dlmgmt_door_setattr_t), sizeof (dlmgmt_setattr_retval_t), dlmgmt_setattr }, - { DLMGMT_CMD_UNSETATTR, B_TRUE, sizeof (dlmgmt_door_unsetattr_t), + { DLMGMT_CMD_UNSETATTR, sizeof (dlmgmt_door_unsetattr_t), sizeof (dlmgmt_unsetattr_retval_t), dlmgmt_unsetconfattr }, - { DLMGMT_CMD_REMOVECONF, B_TRUE, sizeof (dlmgmt_door_removeconf_t), + { DLMGMT_CMD_REMOVECONF, sizeof (dlmgmt_door_removeconf_t), sizeof (dlmgmt_removeconf_retval_t), dlmgmt_removeconf }, - { DLMGMT_CMD_DESTROYCONF, B_TRUE, sizeof (dlmgmt_door_destroyconf_t), + { DLMGMT_CMD_DESTROYCONF, sizeof (dlmgmt_door_destroyconf_t), sizeof (dlmgmt_destroyconf_retval_t), dlmgmt_destroyconf }, - { DLMGMT_CMD_GETATTR, B_FALSE, sizeof (dlmgmt_door_getattr_t), + { DLMGMT_CMD_GETATTR, sizeof (dlmgmt_door_getattr_t), sizeof (dlmgmt_getattr_retval_t), dlmgmt_getattr }, - { DLMGMT_CMD_LINKPROP_INIT, B_TRUE, - sizeof (dlmgmt_door_linkprop_init_t), + { DLMGMT_CMD_LINKPROP_INIT, sizeof (dlmgmt_door_linkprop_init_t), sizeof (dlmgmt_linkprop_init_retval_t), dlmgmt_upcall_linkprop_init }, - { DLMGMT_CMD_LINKPROP_GETNEXT, B_FALSE, - sizeof (dlmgmt_door_linkprop_getnext_t), + { DLMGMT_CMD_LINKPROP_GETNEXT, sizeof (dlmgmt_door_linkprop_getnext_t), sizeof (dlmgmt_linkprop_getnext_retval_t), - dlmgmt_linkprop_getnext } + dlmgmt_linkprop_getnext }, + { DLMGMT_CMD_SETZONEID, sizeof (dlmgmt_door_setzoneid_t), + sizeof (dlmgmt_setzoneid_retval_t), dlmgmt_setzoneid }, + { DLMGMT_CMD_ZONEBOOT, sizeof (dlmgmt_door_zoneboot_t), + sizeof (dlmgmt_zoneboot_retval_t), dlmgmt_zoneboot }, + { DLMGMT_CMD_ZONEHALT, sizeof (dlmgmt_door_zonehalt_t), + sizeof (dlmgmt_zonehalt_retval_t), dlmgmt_zonehalt }, + { 0, 0, 0, NULL } }; -#define DLMGMT_INFO_TABLE_SIZE (sizeof (i_dlmgmt_door_info_tbl) / \ - sizeof (i_dlmgmt_door_info_tbl[0])) +static dlmgmt_door_info_t * +dlmgmt_getcmdinfo(int cmd) +{ + dlmgmt_door_info_t *infop = i_dlmgmt_door_info_tbl; + + while (infop->di_handler != NULL) { + if (infop->di_cmd == cmd) + break; + infop++; + } + return (infop); +} /* ARGSUSED */ void dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, uint_t n_desc) { + dlmgmt_door_arg_t *door_arg = (dlmgmt_door_arg_t *)(void *)argp; dlmgmt_door_info_t *infop = NULL; dlmgmt_retval_t retval; + ucred_t *cred = NULL; + zoneid_t zoneid; void *retvalp; int err = 0; - int i; - - for (i = 0; i < DLMGMT_INFO_TABLE_SIZE; i++) { - if (i_dlmgmt_door_info_tbl[i].di_cmd == - ((dlmgmt_door_arg_t *)(void *)argp)->ld_cmd) { - infop = i_dlmgmt_door_info_tbl + i; - break; - } - } + infop = dlmgmt_getcmdinfo(door_arg->ld_cmd); if (infop == NULL || argsz != infop->di_reqsz) { err = EINVAL; - goto fail; + goto done; } - if (infop->di_set) { - ucred_t *cred = NULL; - const priv_set_t *eset; - - if (door_ucred(&cred) != 0) { - err = errno; - goto fail; - } - - eset = ucred_getprivset(cred, PRIV_EFFECTIVE); - if ((eset == NULL) || - (!priv_ismember(eset, PRIV_SYS_DL_CONFIG) && - !priv_ismember(eset, PRIV_SYS_NET_CONFIG))) { - err = EACCES; - } - ucred_free(cred); - if (err != 0) - goto fail; + if (door_ucred(&cred) != 0 || (zoneid = ucred_getzoneid(cred)) == -1) { + err = errno; + goto done; } /* @@ -1114,11 +1333,15 @@ dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, * memory allocated by malloc() would get leaked. Use alloca() instead. */ retvalp = alloca(infop->di_acksz); - infop->di_handler(argp, retvalp); - (void) door_return(retvalp, infop->di_acksz, NULL, 0); - return; + infop->di_handler(argp, retvalp, zoneid, cred); -fail: - retval.lr_err = err; - (void) door_return((char *)&retval, sizeof (retval), NULL, 0); +done: + if (cred != NULL) + ucred_free(cred); + if (err == 0) { + (void) door_return(retvalp, infop->di_acksz, NULL, 0); + } else { + retval.lr_err = err; + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + } } diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_impl.h b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h index 237910ede6..eb7f6410b2 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_impl.h +++ b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h @@ -61,8 +61,11 @@ typedef struct dlmgmt_link_s { datalink_class_t ll_class; uint32_t ll_media; datalink_id_t ll_linkid; - avl_node_t ll_node_by_name; - avl_node_t ll_node_by_id; + zoneid_t ll_zoneid; + boolean_t ll_onloan; + avl_node_t ll_name_node; + avl_node_t ll_id_node; + avl_node_t ll_loan_node; uint32_t ll_flags; uint32_t ll_gen; /* generation number */ } dlmgmt_link_t; @@ -77,21 +80,25 @@ typedef struct dlmgmt_dlconf_s { datalink_class_t ld_class; uint32_t ld_media; int ld_id; + zoneid_t ld_zoneid; uint32_t ld_gen; avl_node_t ld_node; } dlmgmt_dlconf_t; extern boolean_t debug; extern const char *progname; +extern char cachefile[]; extern dladm_handle_t dld_handle; - +extern datalink_id_t dlmgmt_nextlinkid; extern avl_tree_t dlmgmt_name_avl; extern avl_tree_t dlmgmt_id_avl; +extern avl_tree_t dlmgmt_loan_avl; extern avl_tree_t dlmgmt_dlconf_avl; boolean_t linkattr_equal(dlmgmt_linkattr_t **, const char *, void *, size_t); -int linkattr_unset(dlmgmt_linkattr_t **, const char *); +dlmgmt_linkattr_t *linkattr_find(dlmgmt_linkattr_t *, const char *); +void linkattr_unset(dlmgmt_linkattr_t **, const char *); int linkattr_set(dlmgmt_linkattr_t **, const char *, void *, size_t, dladm_datatype_t); int linkattr_get(dlmgmt_linkattr_t **, const char *, void **, @@ -100,12 +107,14 @@ int linkprop_getnext(dlmgmt_linkattr_t **, const char *, char **, void **, size_t *, dladm_datatype_t *); void link_destroy(dlmgmt_link_t *); -dlmgmt_link_t *link_by_id(datalink_id_t); -dlmgmt_link_t *link_by_name(const char *); +int link_activate(dlmgmt_link_t *); +boolean_t link_is_visible(dlmgmt_link_t *, zoneid_t); +dlmgmt_link_t *link_by_id(datalink_id_t, zoneid_t); +dlmgmt_link_t *link_by_name(const char *, zoneid_t); int dlmgmt_create_common(const char *, datalink_class_t, - uint32_t, uint32_t, dlmgmt_link_t **); + uint32_t, zoneid_t, uint32_t, dlmgmt_link_t **); int dlmgmt_destroy_common(dlmgmt_link_t *, uint32_t); -void dlmgmt_getattr_common(dlmgmt_linkattr_t **, const char *, +int dlmgmt_getattr_common(dlmgmt_linkattr_t **, const char *, dlmgmt_getattr_retval_t *); void dlmgmt_advance(dlmgmt_link_t *); @@ -113,24 +122,26 @@ void dlmgmt_table_lock(boolean_t); void dlmgmt_table_unlock(); int dlconf_create(const char *, datalink_id_t, datalink_class_t, - uint32_t, dlmgmt_dlconf_t **); + uint32_t, zoneid_t, dlmgmt_dlconf_t **); void dlconf_destroy(dlmgmt_dlconf_t *); void dlmgmt_advance_dlconfid(dlmgmt_dlconf_t *); void dlmgmt_dlconf_table_lock(boolean_t); void dlmgmt_dlconf_table_unlock(void); -int dlmgmt_generate_name(const char *, char *, size_t); +int dlmgmt_generate_name(const char *, char *, size_t, zoneid_t); -int dlmgmt_linktable_init(void); +void dlmgmt_linktable_init(void); void dlmgmt_linktable_fini(void); +int dlmgmt_zone_init(zoneid_t); +int dlmgmt_elevate_privileges(void); +int dlmgmt_drop_privileges(); void dlmgmt_handler(void *, char *, size_t, door_desc_t *, uint_t); void dlmgmt_log(int, const char *, ...); -int dlmgmt_write_db_entry(datalink_id_t, uint32_t); -int dlmgmt_delete_db_entry(datalink_id_t, uint32_t); -int dlmgmt_db_init(void); - -#define DLMGMT_TMPFS_DIR "/etc/svc/volatile/dladm" +int dlmgmt_write_db_entry(const char *, dlmgmt_link_t *, uint32_t); +int dlmgmt_delete_db_entry(dlmgmt_link_t *, uint32_t); +int dlmgmt_db_init(zoneid_t); +void dlmgmt_db_fini(zoneid_t); #ifdef __cplusplus } diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_main.c b/usr/src/cmd/dlmgmtd/dlmgmt_main.c index 5a27fdc2c2..7b97664792 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_main.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_main.c @@ -41,12 +41,13 @@ #include <assert.h> #include <errno.h> #include <fcntl.h> -#include <priv_utils.h> +#include <priv.h> #include <signal.h> #include <stdlib.h> #include <stdio.h> #include <strings.h> #include <syslog.h> +#include <zone.h> #include <sys/dld.h> #include <sys/dld_ioc.h> #include <sys/param.h> @@ -66,19 +67,18 @@ static int pfds[2]; static int dlmgmt_door_fd = -1; /* - * This libdladm handle is global so that dlmgmt_upcall_linkprop_init() - * can pass to libdladm. The handle is opened during dlmgmt_init_privileges() - * with "ALL" privileges. It is not able to open DLMGMT_DOOR at that time as - * it hasn't been created yet. This door in the handle is opened in the first - * call to dladm_door_fd(). + * This libdladm handle is global so that dlmgmt_upcall_linkprop_init() can + * pass to libdladm. The handle is opened with "ALL" privileges, before + * privileges are dropped in dlmgmt_drop_privileges(). It is not able to open + * DLMGMT_DOOR at that time as it hasn't been created yet. This door in the + * handle is opened in the first call to dladm_door_fd(). */ dladm_handle_t dld_handle = NULL; static void dlmgmtd_exit(int); static int dlmgmt_init(); static void dlmgmt_fini(); -static int dlmgmt_init_privileges(); -static void dlmgmt_fini_privileges(); +static int dlmgmt_set_privileges(); static int dlmgmt_set_doorfd(boolean_t start) @@ -97,73 +97,162 @@ dlmgmt_set_doorfd(boolean_t start) } static int -dlmgmt_door_init() +dlmgmt_door_init(void) { - int fd; - int err; + int err = 0; - /* - * Create the door file for dlmgmtd. - */ - if ((fd = open(DLMGMT_DOOR, O_CREAT|O_RDONLY, 0644)) == -1) { + if ((dlmgmt_door_fd = door_create(dlmgmt_handler, NULL, + DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { err = errno; - dlmgmt_log(LOG_ERR, "open(%s) failed: %s", - DLMGMT_DOOR, strerror(err)); + dlmgmt_log(LOG_ERR, "door_create() failed: %s", + strerror(err)); return (err); } - (void) close(fd); + return (err); +} - if ((dlmgmt_door_fd = door_create(dlmgmt_handler, NULL, - DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { +static void +dlmgmt_door_fini(void) +{ + if (dlmgmt_door_fd == -1) + return; + + if (door_revoke(dlmgmt_door_fd) == -1) { + dlmgmt_log(LOG_WARNING, "door_revoke(%s) failed: %s", + DLMGMT_DOOR, strerror(errno)); + } + (void) dlmgmt_set_doorfd(B_FALSE); + dlmgmt_door_fd = -1; +} + +int +dlmgmt_door_attach(zoneid_t zoneid, char *rootdir) +{ + int fd; + int err = 0; + char doorpath[MAXPATHLEN]; + + (void) snprintf(doorpath, sizeof (doorpath), "%s%s", rootdir, + DLMGMT_DOOR); + + /* + * Create the door file for dlmgmtd. + */ + if ((fd = open(doorpath, O_CREAT|O_RDONLY, 0644)) == -1) { err = errno; - dlmgmt_log(LOG_ERR, "door_create() failed: %s", + dlmgmt_log(LOG_ERR, "open(%s) failed: %s", doorpath, strerror(err)); return (err); } + (void) close(fd); + if (chown(doorpath, UID_DLADM, GID_SYS) == -1) + return (errno); + /* * fdetach first in case a previous daemon instance exited * ungracefully. */ - (void) fdetach(DLMGMT_DOOR); - if (fattach(dlmgmt_door_fd, DLMGMT_DOOR) != 0) { + (void) fdetach(doorpath); + if (fattach(dlmgmt_door_fd, doorpath) != 0) { err = errno; - dlmgmt_log(LOG_ERR, "fattach(%s) failed: %s", - DLMGMT_DOOR, strerror(err)); - goto fail; - } - if ((err = dlmgmt_set_doorfd(B_TRUE)) != 0) { - dlmgmt_log(LOG_ERR, "cannot set kernel doorfd: %s", + dlmgmt_log(LOG_ERR, "fattach(%s) failed: %s", doorpath, strerror(err)); - (void) fdetach(DLMGMT_DOOR); - goto fail; + } else if (zoneid == GLOBAL_ZONEID) { + if ((err = dlmgmt_set_doorfd(B_TRUE)) != 0) { + dlmgmt_log(LOG_ERR, "cannot set kernel doorfd: %s", + strerror(err)); + } } - return (0); -fail: - (void) door_revoke(dlmgmt_door_fd); - dlmgmt_door_fd = -1; return (err); } -static void -dlmgmt_door_fini() +/* + * Create the /etc/svc/volatile/dladm/ directory if it doesn't exist, load the + * datalink.conf data for this zone, and create/attach the door rendezvous + * file. + */ +int +dlmgmt_zone_init(zoneid_t zoneid) { - if (dlmgmt_door_fd == -1) - return; + char rootdir[MAXPATHLEN], tmpfsdir[MAXPATHLEN]; + int err; + struct stat statbuf; + + if (zoneid == GLOBAL_ZONEID) { + rootdir[0] = '\0'; + } else if (zone_getattr(zoneid, ZONE_ATTR_ROOT, rootdir, + sizeof (rootdir)) < 0) { + return (errno); + } - if (door_revoke(dlmgmt_door_fd) == -1) { - dlmgmt_log(LOG_WARNING, "door_revoke(%s) failed: %s", - DLMGMT_DOOR, strerror(errno)); + /* + * Create the DLMGMT_TMPFS_DIR directory. + */ + (void) snprintf(tmpfsdir, sizeof (tmpfsdir), "%s%s", rootdir, + DLMGMT_TMPFS_DIR); + if (stat(tmpfsdir, &statbuf) < 0) { + if (mkdir(tmpfsdir, (mode_t)0755) < 0) + return (errno); + } else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + return (ENOTDIR); } - (void) fdetach(DLMGMT_DOOR); - (void) dlmgmt_set_doorfd(B_FALSE); + if ((chmod(tmpfsdir, 0755) < 0) || + (chown(tmpfsdir, UID_DLADM, GID_SYS) < 0)) { + return (EPERM); + } + + if ((err = dlmgmt_db_init(zoneid)) != 0) + return (err); + return (dlmgmt_door_attach(zoneid, rootdir)); } +/* + * Initialize each running zone. + */ static int -dlmgmt_init() +dlmgmt_allzones_init(void) { - int err; + int err, i; + zoneid_t *zids = NULL; + uint_t nzids, nzids_saved; + + if (zone_list(NULL, &nzids) != 0) + return (errno); +again: + nzids *= 2; + if ((zids = malloc(nzids * sizeof (zoneid_t))) == NULL) + return (errno); + nzids_saved = nzids; + if (zone_list(zids, &nzids) != 0) { + free(zids); + return (errno); + } + if (nzids > nzids_saved) { + free(zids); + goto again; + } + + for (i = 0; i < nzids; i++) { + if ((err = dlmgmt_zone_init(zids[i])) != 0) + break; + } + free(zids); + return (err); +} + +static int +dlmgmt_init(void) +{ + int err; + char *fmri, *c; + char filename[MAXPATHLEN]; + + if (dladm_open(&dld_handle) != DLADM_STATUS_OK) { + dlmgmt_log(LOG_ERR, "dladm_open() failed"); + return (EPERM); + } if (signal(SIGTERM, dlmgmtd_exit) == SIG_ERR || signal(SIGINT, dlmgmtd_exit) == SIG_ERR) { @@ -173,20 +262,66 @@ dlmgmt_init() return (err); } - if ((err = dlmgmt_linktable_init()) != 0) - return (err); + /* + * First derive the name of the cache file from the FMRI name. This + * cache name is used to keep active datalink configuration. + */ + if (debug) { + (void) snprintf(cachefile, MAXPATHLEN, "%s/%s%s", + DLMGMT_TMPFS_DIR, progname, ".debug.cache"); + } else { + if ((fmri = getenv("SMF_FMRI")) == NULL) { + dlmgmt_log(LOG_ERR, "dlmgmtd is an smf(5) managed " + "service and should not be run from the command " + "line."); + return (EINVAL); + } - if ((err = dlmgmt_db_init()) != 0 || (err = dlmgmt_door_init()) != 0) - dlmgmt_linktable_fini(); + /* + * The FMRI name is in the form of + * svc:/service/service:instance. We need to remove the + * prefix "svc:/" and replace '/' with '-'. The cache file + * name is in the form of "service:instance.cache". + */ + if ((c = strchr(fmri, '/')) != NULL) + c++; + else + c = fmri; + (void) snprintf(filename, MAXPATHLEN, "%s.cache", c); + c = filename; + while ((c = strchr(c, '/')) != NULL) + *c = '-'; + + (void) snprintf(cachefile, MAXPATHLEN, "%s/%s", + DLMGMT_TMPFS_DIR, filename); + } + + dlmgmt_linktable_init(); + if ((err = dlmgmt_door_init()) != 0) + goto done; + + /* + * Load datalink configuration and create dlmgmtd door files for all + * currently running zones. + */ + if ((err = dlmgmt_allzones_init()) != 0) + dlmgmt_door_fini(); +done: + if (err != 0) + dlmgmt_linktable_fini(); return (err); } static void -dlmgmt_fini() +dlmgmt_fini(void) { dlmgmt_door_fini(); dlmgmt_linktable_fini(); + if (dld_handle != NULL) { + dladm_close(dld_handle); + dld_handle = NULL; + } } /* @@ -214,7 +349,6 @@ dlmgmtd_exit(int signo) { (void) close(pfds[1]); dlmgmt_fini(); - dlmgmt_fini_privileges(); exit(EXIT_FAILURE); } @@ -226,65 +360,76 @@ usage(void) } /* - * Set the uid of this daemon to the "dladm" user. Finish the following - * operations before setuid() because they need root privileges: - * - * - create the /etc/svc/volatile/dladm directory; - * - change its uid/gid to "dladm"/"sys"; - * - open the dld control node + * Restrict privileges to only those needed. */ -static int -dlmgmt_init_privileges() +int +dlmgmt_drop_privileges(void) { - struct stat statbuf; - - /* - * Create the DLMGMT_TMPFS_DIR directory. - */ - if (stat(DLMGMT_TMPFS_DIR, &statbuf) < 0) { - if (mkdir(DLMGMT_TMPFS_DIR, (mode_t)0755) < 0) - return (errno); - } else { - if ((statbuf.st_mode & S_IFMT) != S_IFDIR) - return (ENOTDIR); - } - - if ((chmod(DLMGMT_TMPFS_DIR, 0755) < 0) || - (chown(DLMGMT_TMPFS_DIR, UID_DLADM, GID_SYS) < 0)) { - return (EPERM); - } + priv_set_t *pset; + priv_ptype_t ptype; + zoneid_t zoneid = getzoneid(); + int err = 0; - /* - * When dlmgmtd is started at boot, "ALL" privilege is required - * to open the dld control node. The door isn't created yet. - */ - if (dladm_open(&dld_handle) != DLADM_STATUS_OK) { - dlmgmt_log(LOG_ERR, "dladm_open() failed"); - return (EPERM); - } + if ((pset = priv_allocset()) == NULL) + return (errno); /* - * We need PRIV_SYS_DL_CONFIG for the DLDIOC_DOORSERVER ioctl, - * and PRIV_SYS_CONFIG to post sysevents. + * The global zone needs PRIV_PROC_FORK so that it can fork() when it + * issues db ops in non-global zones, PRIV_SYS_CONFIG to post + * sysevents, and PRIV_SYS_DL_CONFIG to initialize link properties in + * dlmgmt_upcall_linkprop_init(). + * + * We remove all privileges from the permitted (and thus effective) + * set in the non-global zone. When executing in a non-global zone, + * dlmgmtd only needs to read and write to files that it already owns. */ - if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, UID_DLADM, - GID_SYS, PRIV_SYS_DL_CONFIG, PRIV_SYS_CONFIG, NULL) == -1) { - dladm_close(dld_handle); - dld_handle = NULL; - return (EPERM); + priv_emptyset(pset); + if (zoneid == GLOBAL_ZONEID) { + ptype = PRIV_EFFECTIVE; + if (priv_addset(pset, PRIV_PROC_FORK) == -1 || + priv_addset(pset, PRIV_SYS_CONFIG) == -1 || + priv_addset(pset, PRIV_SYS_DL_CONFIG) == -1) + err = errno; + } else { + ptype = PRIV_PERMITTED; } + if (err == 0 && setppriv(PRIV_SET, ptype, pset) == -1) + err = errno; +done: + priv_freeset(pset); + return (err); +} +int +dlmgmt_elevate_privileges(void) +{ + priv_set_t *privset; + int err = 0; - return (0); + if ((privset = priv_str_to_set("zone", ",", NULL)) == NULL) + return (errno); + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, privset) == -1) + err = errno; + priv_freeset(privset); + return (err); } -static void -dlmgmt_fini_privileges() +/* + * Set the uid of this daemon to the "dladm" user and drop privileges to only + * those needed. + */ +static int +dlmgmt_set_privileges(void) { - if (dld_handle != NULL) { - dladm_close(dld_handle); - dld_handle = NULL; - } + int err; + + (void) setgroups(0, NULL); + if (setegid(GID_SYS) == -1 || seteuid(UID_DLADM) == -1) + err = errno; + else + err = dlmgmt_drop_privileges(); +done: + return (err); } /* @@ -347,7 +492,7 @@ dlmgmt_daemonize(void) int main(int argc, char *argv[]) { - int opt; + int opt, err; progname = strrchr(argv[0], '/'); if (progname != NULL) @@ -371,14 +516,14 @@ main(int argc, char *argv[]) if (!debug && !dlmgmt_daemonize()) return (EXIT_FAILURE); - if ((errno = dlmgmt_init_privileges()) != 0) { - dlmgmt_log(LOG_ERR, "dlmgmt_init_privileges() failed: %s", - strerror(errno)); + if ((err = dlmgmt_init()) != 0) { + dlmgmt_log(LOG_ERR, "unable to initialize daemon: %s", + strerror(err)); goto child_out; - } - - if (dlmgmt_init() != 0) { - dlmgmt_fini_privileges(); + } else if ((err = dlmgmt_set_privileges()) != 0) { + dlmgmt_log(LOG_ERR, "unable to set daemon privileges: %s", + strerror(err)); + dlmgmt_fini(); goto child_out; } diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_util.c b/usr/src/cmd/dlmgmtd/dlmgmt_util.c index 87d2ed394f..58e44b9182 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_util.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_util.c @@ -33,20 +33,26 @@ #include <stddef.h> #include <stdlib.h> #include <stdio.h> +#include <errno.h> #include <strings.h> +#include <string.h> #include <syslog.h> #include <stdarg.h> +#include <zone.h> #include <errno.h> #include <libdlpi.h> #include "dlmgmt_impl.h" /* - * There are two datalink AVL tables. One table (dlmgmt_name_avl) is keyed by - * the link name, and the other (dlmgmt_id_avl) is keyed by the link id. - * Each link will be present in both tables. + * There are three datalink AVL tables. The dlmgmt_name_avl tree contains all + * datalinks and is keyed by zoneid and link name. The dlmgmt_id_avl also + * contains all datalinks, and it is keyed by link ID. The dlmgmt_loan_avl is + * keyed by link name, and contains the set of global-zone links that are + * currently on loan to non-global zones. */ avl_tree_t dlmgmt_name_avl; avl_tree_t dlmgmt_id_avl; +avl_tree_t dlmgmt_loan_avl; avl_tree_t dlmgmt_dlconf_avl; @@ -58,20 +64,14 @@ static pthread_rwlock_t dlmgmt_dlconf_lock = PTHREAD_RWLOCK_INITIALIZER; typedef struct dlmgmt_prefix { struct dlmgmt_prefix *lp_next; char lp_prefix[MAXLINKNAMELEN]; + zoneid_t lp_zoneid; uint_t lp_nextppa; } dlmgmt_prefix_t; -static dlmgmt_prefix_t *dlmgmt_prefixlist; +static dlmgmt_prefix_t dlmgmt_prefixlist; -static datalink_id_t dlmgmt_nextlinkid; +datalink_id_t dlmgmt_nextlinkid; static datalink_id_t dlmgmt_nextconfid = 1; -static int linkattr_add(dlmgmt_linkattr_t **, - dlmgmt_linkattr_t *); -static int linkattr_rm(dlmgmt_linkattr_t **, - dlmgmt_linkattr_t *); -static int link_create(const char *, datalink_class_t, uint32_t, - uint32_t, dlmgmt_link_t **); - static void dlmgmt_advance_linkid(dlmgmt_link_t *); static void dlmgmt_advance_ppa(dlmgmt_link_t *); @@ -101,6 +101,24 @@ cmp_link_by_name(const void *v1, const void *v2) return ((cmp == 0) ? 0 : ((cmp < 0) ? -1 : 1)); } +/* + * Note that the zoneid associated with a link is effectively part of its + * name. This is essentially what results in having each zone have disjoint + * datalink namespaces. + */ +static int +cmp_link_by_zname(const void *v1, const void *v2) +{ + const dlmgmt_link_t *link1 = v1; + const dlmgmt_link_t *link2 = v2; + + if (link1->ll_zoneid < link2->ll_zoneid) + return (-1); + if (link1->ll_zoneid > link2->ll_zoneid) + return (1); + return (cmp_link_by_name(link1, link2)); +} + static int cmp_link_by_id(const void *v1, const void *v2) { @@ -129,49 +147,46 @@ cmp_dlconf_by_id(const void *v1, const void *v2) return (1); } -int -dlmgmt_linktable_init() +void +dlmgmt_linktable_init(void) { /* - * Initialize the prefix list. First add the "net" prefix to the list. + * Initialize the prefix list. First add the "net" prefix for the + * global zone to the list. */ - dlmgmt_prefixlist = malloc(sizeof (dlmgmt_prefix_t)); - if (dlmgmt_prefixlist == NULL) { - dlmgmt_log(LOG_WARNING, "dlmgmt_linktable_init() failed: %s", - strerror(ENOMEM)); - return (ENOMEM); - } - - dlmgmt_prefixlist->lp_next = NULL; - dlmgmt_prefixlist->lp_nextppa = 0; - (void) strlcpy(dlmgmt_prefixlist->lp_prefix, "net", MAXLINKNAMELEN); + dlmgmt_prefixlist.lp_next = NULL; + dlmgmt_prefixlist.lp_zoneid = GLOBAL_ZONEID; + dlmgmt_prefixlist.lp_nextppa = 0; + (void) strlcpy(dlmgmt_prefixlist.lp_prefix, "net", MAXLINKNAMELEN); - avl_create(&dlmgmt_name_avl, cmp_link_by_name, sizeof (dlmgmt_link_t), - offsetof(dlmgmt_link_t, ll_node_by_name)); + avl_create(&dlmgmt_name_avl, cmp_link_by_zname, sizeof (dlmgmt_link_t), + offsetof(dlmgmt_link_t, ll_name_node)); avl_create(&dlmgmt_id_avl, cmp_link_by_id, sizeof (dlmgmt_link_t), - offsetof(dlmgmt_link_t, ll_node_by_id)); + offsetof(dlmgmt_link_t, ll_id_node)); + avl_create(&dlmgmt_loan_avl, cmp_link_by_name, sizeof (dlmgmt_link_t), + offsetof(dlmgmt_link_t, ll_loan_node)); avl_create(&dlmgmt_dlconf_avl, cmp_dlconf_by_id, sizeof (dlmgmt_dlconf_t), offsetof(dlmgmt_dlconf_t, ld_node)); dlmgmt_nextlinkid = 1; - return (0); } void -dlmgmt_linktable_fini() +dlmgmt_linktable_fini(void) { - dlmgmt_prefix_t *lpp, *next; + dlmgmt_prefix_t *lpp, *next; - for (lpp = dlmgmt_prefixlist; lpp != NULL; lpp = next) { + for (lpp = dlmgmt_prefixlist.lp_next; lpp != NULL; lpp = next) { next = lpp->lp_next; free(lpp); } avl_destroy(&dlmgmt_dlconf_avl); avl_destroy(&dlmgmt_name_avl); + avl_destroy(&dlmgmt_loan_avl); avl_destroy(&dlmgmt_id_avl); } -static int +static void linkattr_add(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp) { if (*headp == NULL) { @@ -181,10 +196,9 @@ linkattr_add(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp) attrp->lp_next = *headp; *headp = attrp; } - return (0); } -static int +static void linkattr_rm(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp) { dlmgmt_linkattr_t *next, *prev; @@ -197,8 +211,18 @@ linkattr_rm(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp) prev->lp_next = next; else *headp = next; +} - return (0); +dlmgmt_linkattr_t * +linkattr_find(dlmgmt_linkattr_t *headp, const char *attr) +{ + dlmgmt_linkattr_t *attrp; + + for (attrp = headp; attrp != NULL; attrp = attrp->lp_next) { + if (strcmp(attrp->lp_name, attr) == 0) + break; + } + return (attrp); } int @@ -206,24 +230,17 @@ linkattr_set(dlmgmt_linkattr_t **headp, const char *attr, void *attrval, size_t attrsz, dladm_datatype_t type) { dlmgmt_linkattr_t *attrp; - int err; - - /* - * See whether the attr is already set. - */ - for (attrp = *headp; attrp != NULL; attrp = attrp->lp_next) { - if (strcmp(attrp->lp_name, attr) == 0) - break; - } + void *newval; + boolean_t new; + attrp = linkattr_find(*headp, attr); if (attrp != NULL) { /* * It is already set. If the value changed, update it. */ if (linkattr_equal(headp, attr, attrval, attrsz)) return (0); - - free(attrp->lp_val); + new = B_FALSE; } else { /* * It is not set yet, allocate the linkattr and prepend to the @@ -232,73 +249,43 @@ linkattr_set(dlmgmt_linkattr_t **headp, const char *attr, void *attrval, if ((attrp = calloc(1, sizeof (dlmgmt_linkattr_t))) == NULL) return (ENOMEM); - if ((err = linkattr_add(headp, attrp)) != 0) { - free(attrp); - return (err); - } (void) strlcpy(attrp->lp_name, attr, MAXLINKATTRLEN); + new = B_TRUE; } - if ((attrp->lp_val = calloc(1, attrsz)) == NULL) { - (void) linkattr_rm(headp, attrp); - free(attrp); + if ((newval = calloc(1, attrsz)) == NULL) { + if (new) + free(attrp); return (ENOMEM); } + if (!new) + free(attrp->lp_val); + attrp->lp_val = newval; bcopy(attrval, attrp->lp_val, attrsz); attrp->lp_sz = attrsz; attrp->lp_type = type; attrp->lp_linkprop = dladm_attr_is_linkprop(attr); + if (new) + linkattr_add(headp, attrp); return (0); } -int +void linkattr_unset(dlmgmt_linkattr_t **headp, const char *attr) { - dlmgmt_linkattr_t *attrp, *prev; - - /* - * See whether the attr exists. - */ - for (prev = NULL, attrp = *headp; attrp != NULL; - prev = attrp, attrp = attrp->lp_next) { - if (strcmp(attrp->lp_name, attr) == 0) - break; - } - - /* - * This attribute is not set in the first place. Return success. - */ - if (attrp == NULL) - return (0); - - /* - * Remove this attr from the list. - */ - if (prev == NULL) - *headp = attrp->lp_next; - else - prev->lp_next = attrp->lp_next; + dlmgmt_linkattr_t *attrp; - free(attrp->lp_val); - free(attrp); - return (0); + if ((attrp = linkattr_find(*headp, attr)) != NULL) + linkattr_rm(headp, attrp); } int linkattr_get(dlmgmt_linkattr_t **headp, const char *attr, void **attrvalp, size_t *attrszp, dladm_datatype_t *typep) { - dlmgmt_linkattr_t *attrp = *headp; + dlmgmt_linkattr_t *attrp; - /* - * find the specific attr. - */ - for (attrp = *headp; attrp != NULL; attrp = attrp->lp_next) { - if (strcmp(attrp->lp_name, attr) == 0) - break; - } - - if (attrp == NULL) + if ((attrp = linkattr_find(*headp, attr)) == NULL) return (ENOENT); *attrvalp = attrp->lp_val; @@ -369,7 +356,7 @@ dlmgmt_table_lock(boolean_t write) } void -dlmgmt_table_unlock() +dlmgmt_table_unlock(void) { (void) pthread_rwlock_unlock(&dlmgmt_avl_lock); (void) pthread_mutex_lock(&dlmgmt_avl_mutex); @@ -377,34 +364,6 @@ dlmgmt_table_unlock() (void) pthread_mutex_unlock(&dlmgmt_avl_mutex); } -static int -link_create(const char *name, datalink_class_t class, uint32_t media, - uint32_t flags, dlmgmt_link_t **linkpp) -{ - dlmgmt_link_t *linkp = NULL; - int err = 0; - - if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID) { - err = ENOSPC; - goto done; - } - - if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) { - err = ENOMEM; - goto done; - } - - (void) strlcpy(linkp->ll_link, name, MAXLINKNAMELEN); - linkp->ll_class = class; - linkp->ll_media = media; - linkp->ll_linkid = dlmgmt_nextlinkid; - linkp->ll_flags = flags; - linkp->ll_gen = 0; -done: - *linkpp = linkp; - return (err); -} - void link_destroy(dlmgmt_link_t *linkp) { @@ -418,56 +377,129 @@ link_destroy(dlmgmt_link_t *linkp) free(linkp); } +/* + * Set the DLMGMT_ACTIVE flag on the link to note that it is active. When a + * link becomes active and it belongs to a non-global zone, it is also added + * to that zone. + */ +int +link_activate(dlmgmt_link_t *linkp) +{ + int err = 0; + zoneid_t zoneid; + + if (zone_check_datalink(&zoneid, linkp->ll_linkid) == 0) { + /* + * This link was already added to a non-global zone. This can + * happen if dlmgmtd is restarted. + */ + if (zoneid != linkp->ll_zoneid) { + if (link_by_name(linkp->ll_link, zoneid) != NULL) { + err = EEXIST; + goto done; + } + avl_remove(&dlmgmt_name_avl, linkp); + linkp->ll_zoneid = zoneid; + avl_add(&dlmgmt_name_avl, linkp); + avl_add(&dlmgmt_loan_avl, linkp); + linkp->ll_onloan = B_TRUE; + } + } else if (linkp->ll_zoneid != GLOBAL_ZONEID) { + err = zone_add_datalink(linkp->ll_zoneid, linkp->ll_linkid); + } +done: + if (err == 0) + linkp->ll_flags |= DLMGMT_ACTIVE; + return (err); +} + +/* + * Is linkp visible from the caller's zoneid? It is if the link is in the + * same zone as the caller, or if the caller is in the global zone and the + * link is on loan to a non-global zone. + */ +boolean_t +link_is_visible(dlmgmt_link_t *linkp, zoneid_t zoneid) +{ + return (linkp->ll_zoneid == zoneid || + (zoneid == GLOBAL_ZONEID && linkp->ll_onloan)); +} + dlmgmt_link_t * -link_by_id(datalink_id_t linkid) +link_by_id(datalink_id_t linkid, zoneid_t zoneid) { - dlmgmt_link_t link; + dlmgmt_link_t link, *linkp; link.ll_linkid = linkid; - return (avl_find(&dlmgmt_id_avl, &link, NULL)); + linkp = avl_find(&dlmgmt_id_avl, &link, NULL); + if (zoneid != GLOBAL_ZONEID && linkp->ll_zoneid != zoneid) + linkp = NULL; + return (linkp); } dlmgmt_link_t * -link_by_name(const char *name) +link_by_name(const char *name, zoneid_t zoneid) { - dlmgmt_link_t link; + dlmgmt_link_t link, *linkp; (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN); - return (avl_find(&dlmgmt_name_avl, &link, NULL)); + link.ll_zoneid = zoneid; + linkp = avl_find(&dlmgmt_name_avl, &link, NULL); + if (linkp == NULL && zoneid == GLOBAL_ZONEID) { + /* The link could be on loan to a non-global zone? */ + linkp = avl_find(&dlmgmt_loan_avl, &link, NULL); + } + return (linkp); } int dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media, - uint32_t flags, dlmgmt_link_t **linkpp) + zoneid_t zoneid, uint32_t flags, dlmgmt_link_t **linkpp) { - dlmgmt_link_t link, *linkp, *tmp; + dlmgmt_link_t *linkp = NULL; avl_index_t name_where, id_where; - int err; + int err = 0; - /* - * Validate the link. - */ if (!dladm_valid_linkname(name)) return (EINVAL); + if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID) + return (ENOSPC); - /* - * Check to see whether this is an existing link name. - */ - (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN); - if ((linkp = avl_find(&dlmgmt_name_avl, &link, &name_where)) != NULL) - return (EEXIST); + if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) { + err = ENOMEM; + goto done; + } - if ((err = link_create(name, class, media, flags, &linkp)) != 0) - return (err); + (void) strlcpy(linkp->ll_link, name, MAXLINKNAMELEN); + linkp->ll_class = class; + linkp->ll_media = media; + linkp->ll_linkid = dlmgmt_nextlinkid; + linkp->ll_zoneid = zoneid; + linkp->ll_gen = 0; + + if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL || + avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) { + err = EEXIST; + goto done; + } - link.ll_linkid = linkp->ll_linkid; - tmp = avl_find(&dlmgmt_id_avl, &link, &id_where); - assert(tmp == NULL); avl_insert(&dlmgmt_name_avl, linkp, name_where); avl_insert(&dlmgmt_id_avl, linkp, id_where); + + if ((flags & DLMGMT_ACTIVE) && (err = link_activate(linkp)) != 0) { + avl_remove(&dlmgmt_name_avl, linkp); + avl_remove(&dlmgmt_id_avl, linkp); + goto done; + } + + linkp->ll_flags = flags; dlmgmt_advance(linkp); *linkpp = linkp; - return (0); + +done: + if (err != 0) + free(linkp); + return (err); } int @@ -479,8 +511,9 @@ dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags) */ return (ENOENT); } + linkp->ll_flags &= ~flags; - if (!(linkp->ll_flags & DLMGMT_PERSIST)) { + if (flags & DLMGMT_PERSIST) { dlmgmt_linkattr_t *next, *attrp; for (attrp = linkp->ll_head; attrp != NULL; attrp = next) { @@ -491,6 +524,12 @@ dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags) linkp->ll_head = NULL; } + if ((flags & DLMGMT_ACTIVE) && linkp->ll_zoneid != GLOBAL_ZONEID) { + (void) zone_remove_datalink(linkp->ll_zoneid, linkp->ll_linkid); + if (linkp->ll_onloan) + avl_remove(&dlmgmt_loan_avl, linkp); + } + if (linkp->ll_flags == 0) { avl_remove(&dlmgmt_id_avl, linkp); avl_remove(&dlmgmt_name_avl, linkp); @@ -500,7 +539,7 @@ dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags) return (0); } -void +int dlmgmt_getattr_common(dlmgmt_linkattr_t **headp, const char *attr, dlmgmt_getattr_retval_t *retvalp) { @@ -511,19 +550,16 @@ dlmgmt_getattr_common(dlmgmt_linkattr_t **headp, const char *attr, err = linkattr_get(headp, attr, &attrval, &attrsz, &attrtype); if (err != 0) - goto done; + return (err); assert(attrsz > 0); - if (attrsz > MAXLINKATTRVALLEN) { - err = EINVAL; - goto done; - } + if (attrsz > MAXLINKATTRVALLEN) + return (EINVAL); retvalp->lr_type = attrtype; retvalp->lr_attrsz = attrsz; bcopy(attrval, retvalp->lr_attrval, attrsz); -done: - retvalp->lr_err = err; + return (0); } void @@ -536,14 +572,14 @@ dlmgmt_dlconf_table_lock(boolean_t write) } void -dlmgmt_dlconf_table_unlock() +dlmgmt_dlconf_table_unlock(void) { (void) pthread_rwlock_unlock(&dlmgmt_dlconf_lock); } int dlconf_create(const char *name, datalink_id_t linkid, datalink_class_t class, - uint32_t media, dlmgmt_dlconf_t **dlconfpp) + uint32_t media, zoneid_t zoneid, dlmgmt_dlconf_t **dlconfpp) { dlmgmt_dlconf_t *dlconfp = NULL; int err = 0; @@ -563,6 +599,7 @@ dlconf_create(const char *name, datalink_id_t linkid, datalink_class_t class, dlconfp->ld_class = class; dlconfp->ld_media = media; dlconfp->ld_id = dlmgmt_nextconfid; + dlconfp->ld_zoneid = zoneid; done: *dlconfpp = dlconfp; @@ -583,16 +620,19 @@ dlconf_destroy(dlmgmt_dlconf_t *dlconfp) } int -dlmgmt_generate_name(const char *prefix, char *name, size_t size) +dlmgmt_generate_name(const char *prefix, char *name, size_t size, + zoneid_t zoneid) { dlmgmt_prefix_t *lpp, *prev = NULL; + dlmgmt_link_t link, *linkp; /* * See whether the requested prefix is already in the list. */ - for (lpp = dlmgmt_prefixlist; lpp != NULL; prev = lpp, - lpp = lpp->lp_next) { - if (strcmp(prefix, lpp->lp_prefix) == 0) + for (lpp = &dlmgmt_prefixlist; lpp != NULL; + prev = lpp, lpp = lpp->lp_next) { + if (lpp->lp_zoneid == zoneid && + strcmp(prefix, lpp->lp_prefix) == 0) break; } @@ -600,8 +640,6 @@ dlmgmt_generate_name(const char *prefix, char *name, size_t size) * Not found. */ if (lpp == NULL) { - dlmgmt_link_t *linkp, link; - assert(prev != NULL); /* @@ -612,6 +650,7 @@ dlmgmt_generate_name(const char *prefix, char *name, size_t size) prev->lp_next = lpp; lpp->lp_next = NULL; + lpp->lp_zoneid = zoneid; lpp->lp_nextppa = 0; (void) strlcpy(lpp->lp_prefix, prefix, MAXLINKNAMELEN); @@ -619,9 +658,9 @@ dlmgmt_generate_name(const char *prefix, char *name, size_t size) * Now determine this prefix's nextppa. */ (void) snprintf(link.ll_link, MAXLINKNAMELEN, "%s%d", - prefix, lpp->lp_nextppa); - linkp = avl_find(&dlmgmt_name_avl, &link, NULL); - if (linkp != NULL) + prefix, 0); + link.ll_zoneid = zoneid; + if ((linkp = avl_find(&dlmgmt_name_avl, &link, NULL)) != NULL) dlmgmt_advance_ppa(linkp); } @@ -641,6 +680,7 @@ dlmgmt_advance_ppa(dlmgmt_link_t *linkp) { dlmgmt_prefix_t *lpp; char prefix[MAXLINKNAMELEN]; + char linkname[MAXLINKNAMELEN]; uint_t start, ppa; (void) dlpi_parselink(linkp->ll_link, prefix, &ppa); @@ -648,8 +688,9 @@ dlmgmt_advance_ppa(dlmgmt_link_t *linkp) /* * See whether the requested prefix is already in the list. */ - for (lpp = dlmgmt_prefixlist; lpp != NULL; lpp = lpp->lp_next) { - if (strcmp(prefix, lpp->lp_prefix) == 0) + for (lpp = &dlmgmt_prefixlist; lpp != NULL; lpp = lpp->lp_next) { + if (lpp->lp_zoneid == linkp->ll_zoneid && + strcmp(prefix, lpp->lp_prefix) == 0) break; } @@ -664,15 +705,13 @@ dlmgmt_advance_ppa(dlmgmt_link_t *linkp) linkp = AVL_NEXT(&dlmgmt_name_avl, linkp); while (lpp->lp_nextppa != start) { if (lpp->lp_nextppa == (uint_t)-1) { - dlmgmt_link_t link; - /* * wrapped around. search from <prefix>1. */ lpp->lp_nextppa = 0; - (void) snprintf(link.ll_link, MAXLINKNAMELEN, + (void) snprintf(linkname, MAXLINKNAMELEN, "%s%d", lpp->lp_prefix, lpp->lp_nextppa); - linkp = avl_find(&dlmgmt_name_avl, &link, NULL); + linkp = link_by_name(linkname, lpp->lp_zoneid); if (linkp == NULL) return; } else { @@ -706,15 +745,11 @@ dlmgmt_advance_linkid(dlmgmt_link_t *linkp) do { if (dlmgmt_nextlinkid == DATALINK_MAX_LINKID) { - dlmgmt_link_t link; - /* * wrapped around. search from 1. */ dlmgmt_nextlinkid = 1; - link.ll_linkid = 1; - linkp = avl_find(&dlmgmt_id_avl, &link, NULL); - if (linkp == NULL) + if ((linkp = link_by_id(1, GLOBAL_ZONEID)) == NULL) return; } else { dlmgmt_nextlinkid++; |
