1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/ethernet.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <sys/stat.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <stropts.h>
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <strings.h>
40 #include <libintl.h>
41 #include <netdb.h>
42 #include <net/if_types.h>
43 #include <net/if_dl.h>
44 #include <inet/ip.h>
45 #include <inet/ip6.h>
46 #include <libdlflow.h>
47 #include <libdlflow_impl.h>
48 #include <libdladm_impl.h>
49 
50 /* minimum buffer size for DLDIOCWALKFLOW */
51 #define	MIN_INFO_SIZE	(4 * 1024)
52 
53 #define	DLADM_FLOW_DB		"/etc/dladm/flowadm.conf"
54 #define	DLADM_FLOW_DB_TMP	"/etc/dladm/flowadm.conf.new"
55 #define	DLADM_FLOW_DB_LOCK	"/tmp/flowadm.conf.lock"
56 
57 #define	DLADM_FLOW_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
58 #define	DLADM_FLOW_DB_OWNER	UID_DLADM
59 #define	DLADM_FLOW_DB_GROUP	GID_SYS
60 
61 #define	BLANK_LINE(s)	((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
62 #define	MAXLINELEN	1024
63 #define	MAXPATHLEN	1024
64 
65 #define	V4_PART_OF_V6(v6)	((v6)._S6_un._S6_u32[3])
66 
67 /* database file parameters */
68 static const char *BW_LIMIT = "bw_limit";
69 static const char *PRIORITY = "priority";
70 static const char *LOCAL_IP_ADDR = "local_ip";
71 static const char *REMOTE_IP_ADDR = "remote_ip";
72 static const char *TRANSPORT = "transport";
73 static const char *LOCAL_PORT = "local_port";
74 static const char *DSFIELD = "dsfield";
75 
76 /*
77  * Open and lock the flowadm configuration file lock. The lock is
78  * acquired as a reader (F_RDLCK) or writer (F_WRLCK).
79  */
80 static int
81 i_dladm_flow_lock_db(short type)
82 {
83 	int lock_fd;
84 	struct flock lock;
85 
86 	if ((lock_fd = open(DLADM_FLOW_DB_LOCK, O_RDWR | O_CREAT | O_TRUNC,
87 	    DLADM_FLOW_DB_PERMS)) < 0)
88 		return (-1);
89 
90 	lock.l_type = type;
91 	lock.l_whence = SEEK_SET;
92 	lock.l_start = 0;
93 	lock.l_len = 0;
94 
95 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
96 		(void) close(lock_fd);
97 		(void) unlink(DLADM_FLOW_DB_LOCK);
98 		return (-1);
99 	}
100 	return (lock_fd);
101 }
102 
103 /*
104  * Unlock and close the specified file.
105  */
106 static void
107 i_dladm_flow_unlock_db(int fd)
108 {
109 	struct flock lock;
110 
111 	if (fd < 0)
112 		return;
113 
114 	lock.l_type = F_UNLCK;
115 	lock.l_whence = SEEK_SET;
116 	lock.l_start = 0;
117 	lock.l_len = 0;
118 
119 	(void) fcntl(fd, F_SETLKW, &lock);
120 	(void) close(fd);
121 	(void) unlink(DLADM_FLOW_DB_LOCK);
122 }
123 
124 /*
125  * Parse one line of the link flowadm DB
126  * Returns -1 on failure, 0 on success.
127  */
128 dladm_status_t
129 dladm_flow_parse_db(char *line, dld_flowinfo_t *attr)
130 {
131 	char		*token;
132 	char		*value, *name = NULL;
133 	char		*endp = NULL;
134 	char		*lasts = NULL;
135 	dladm_status_t	status = DLADM_STATUS_FLOW_DB_PARSE_ERR;
136 
137 	bzero(attr, sizeof (*attr));
138 
139 	/* flow name */
140 	if ((token = strtok_r(line, " \t", &lasts)) == NULL)
141 		goto done;
142 
143 	if (strlcpy(attr->fi_flowname, token, MAXNAMELEN) >= MAXNAMELEN)
144 		goto done;
145 
146 	/* resource control and flow descriptor parameters */
147 	while ((token = strtok_r(NULL, " \t", &lasts)) != NULL) {
148 		if ((name = strdup(token)) == NULL)
149 			goto done;
150 
151 		(void) strtok(name, "=");
152 		value = strtok(NULL, "=");
153 		if (value == NULL)
154 			goto done;
155 
156 		if (strcmp(name, "linkid") == 0) {
157 			if ((attr->fi_linkid =
158 			    (uint32_t)strtol(value, &endp, 10)) ==
159 			    DATALINK_INVALID_LINKID)
160 				goto done;
161 
162 		} else if (strcmp(name, BW_LIMIT) == 0) {
163 			attr->fi_resource_props.mrp_mask |=
164 			    MRP_MAXBW;
165 			attr->fi_resource_props.mrp_maxbw =
166 			    (uint64_t)strtol(value, &endp, 0);
167 
168 		} else if (strcmp(name, PRIORITY) == 0) {
169 			attr->fi_resource_props.mrp_mask |= MRP_PRIORITY;
170 			status = dladm_str2pri(value,
171 			    &attr->fi_resource_props.mrp_priority);
172 			if (status != DLADM_STATUS_OK)
173 				goto done;
174 
175 		} else if (strcmp(name, DSFIELD) == 0) {
176 			status = do_check_dsfield(value,
177 			    &attr->fi_flow_desc);
178 			if (status != DLADM_STATUS_OK)
179 				goto done;
180 
181 		} else if (strcmp(name, LOCAL_IP_ADDR) == 0) {
182 			status = do_check_ip_addr(value, B_TRUE,
183 			    &attr->fi_flow_desc);
184 			if (status != DLADM_STATUS_OK)
185 				goto done;
186 
187 		} else if (strcmp(name, REMOTE_IP_ADDR) == 0) {
188 			status = do_check_ip_addr(value, B_FALSE,
189 			    &attr->fi_flow_desc);
190 			if (status != DLADM_STATUS_OK)
191 				goto done;
192 
193 		} else if (strcmp(name, TRANSPORT) == 0) {
194 			attr->fi_flow_desc.fd_mask |= FLOW_IP_PROTOCOL;
195 			attr->fi_flow_desc.fd_protocol =
196 			    (uint8_t)strtol(value, &endp, 0);
197 
198 		} else if (strcmp(name, LOCAL_PORT) == 0) {
199 			attr->fi_flow_desc.fd_mask |= FLOW_ULP_PORT_LOCAL;
200 			attr->fi_flow_desc.fd_local_port =
201 			    (uint16_t)strtol(value, &endp, 10);
202 			attr->fi_flow_desc.fd_local_port =
203 			    htons(attr->fi_flow_desc.fd_local_port);
204 		}
205 		free(name);
206 		name = NULL;
207 	}
208 	if (attr->fi_linkid != DATALINK_INVALID_LINKID)
209 		status = DLADM_STATUS_OK;
210 done:
211 	free(name);
212 	return (status);
213 }
214 
215 #define	FPRINTF_ERR(fcall) if ((fcall) < 0) return (-1);
216 
217 /*
218  * Write the attribute of a group to the specified file. Returns 0 on
219  * success, -1 on failure.
220  */
221 static int
222 i_dladm_flow_fput_grp(FILE *fp, dld_flowinfo_t *attr)
223 {
224 
225 	FPRINTF_ERR(fprintf(fp, "%s\tlinkid=%d\t",
226 	    attr->fi_flowname, attr->fi_linkid));
227 
228 	/* flow policy */
229 	if (attr->fi_resource_props.mrp_mask & MRP_MAXBW)
230 		FPRINTF_ERR(fprintf(fp, "%s=%" PRIu64 "\t", BW_LIMIT,
231 		    attr->fi_resource_props.mrp_maxbw));
232 
233 	if (attr->fi_resource_props.mrp_mask & MRP_PRIORITY)
234 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", PRIORITY,
235 		    attr->fi_resource_props.mrp_priority));
236 
237 	/* flow descriptor */
238 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_DSFIELD)
239 		FPRINTF_ERR(fprintf(fp, "%s=%x:%x\t", DSFIELD,
240 		    attr->fi_flow_desc.fd_dsfield,
241 		    attr->fi_flow_desc.fd_dsfield_mask));
242 
243 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_LOCAL) {
244 		char abuf[INET6_ADDRSTRLEN], *ap;
245 		struct in_addr ipaddr;
246 		int prefix_len, prefix_max;
247 
248 		if (attr->fi_flow_desc.fd_ipversion != 6) {
249 			ipaddr.s_addr =
250 			    attr->fi_flow_desc.
251 			    fd_local_addr._S6_un._S6_u32[3];
252 
253 			ap = inet_ntoa(ipaddr);
254 			prefix_max = IP_ABITS;
255 		} else {
256 			(void) inet_ntop(AF_INET6,
257 			    &attr->fi_flow_desc.fd_local_addr,
258 			    abuf, INET6_ADDRSTRLEN);
259 
260 			ap = abuf;
261 			prefix_max = IPV6_ABITS;
262 		}
263 		(void) dladm_mask2prefixlen(
264 		    &attr->fi_flow_desc.fd_local_netmask, prefix_max,
265 		    &prefix_len);
266 
267 		FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", LOCAL_IP_ADDR,
268 		    ap, prefix_len));
269 	}
270 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_REMOTE) {
271 		char abuf[INET6_ADDRSTRLEN], *ap;
272 		struct in_addr ipaddr;
273 		int prefix_len, prefix_max;
274 
275 		if (attr->fi_flow_desc.fd_ipversion != 6) {
276 			ipaddr.s_addr =
277 			    attr->fi_flow_desc.
278 			    fd_remote_addr._S6_un._S6_u32[3];
279 
280 			ap = inet_ntoa(ipaddr);
281 			prefix_max = IP_ABITS;
282 		} else {
283 			(void) inet_ntop(AF_INET6,
284 			    &(attr->fi_flow_desc.fd_remote_addr),
285 			    abuf, INET6_ADDRSTRLEN);
286 
287 			ap = abuf;
288 			prefix_max = IPV6_ABITS;
289 		}
290 		(void) dladm_mask2prefixlen(
291 		    &attr->fi_flow_desc.fd_remote_netmask, prefix_max,
292 		    &prefix_len);
293 
294 		FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", REMOTE_IP_ADDR,
295 		    ap, prefix_len));
296 	}
297 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_PROTOCOL)
298 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", TRANSPORT,
299 		    attr->fi_flow_desc.fd_protocol));
300 
301 	if (attr->fi_flow_desc.fd_mask & FLOW_ULP_PORT_LOCAL)
302 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", LOCAL_PORT,
303 		    ntohs(attr->fi_flow_desc.fd_local_port)));
304 
305 	FPRINTF_ERR(fprintf(fp, "\n"));
306 
307 	return (0);
308 
309 }
310 
311 static dladm_status_t
312 i_dladm_flow_walk_rw_db(int (*fn)(void *, dld_flowinfo_t *),
313     void *arg,
314     const char *root)
315 {
316 	FILE *fp, *nfp;
317 	int nfd, fn_rc, lock_fd;
318 	char line[MAXLINELEN];
319 	dld_flowinfo_t attr;
320 	char *db_file, *tmp_db_file;
321 	char db_file_buf[MAXPATHLEN];
322 	char tmp_db_file_buf[MAXPATHLEN];
323 	dladm_status_t	status = DLADM_STATUS_FLOW_DB_ERR;
324 
325 	if (root == NULL) {
326 		db_file = DLADM_FLOW_DB;
327 		tmp_db_file = DLADM_FLOW_DB_TMP;
328 	} else {
329 		(void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
330 		    DLADM_FLOW_DB);
331 		(void) snprintf(tmp_db_file_buf, MAXPATHLEN, "%s%s", root,
332 		    DLADM_FLOW_DB_TMP);
333 		db_file = db_file_buf;
334 		tmp_db_file = tmp_db_file_buf;
335 	}
336 
337 	if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0)
338 		return (DLADM_STATUS_FLOW_DB_ERR);
339 
340 	if ((fp = fopen(db_file, "r")) == NULL) {
341 		i_dladm_flow_unlock_db(lock_fd);
342 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
343 	}
344 
345 	if ((nfd = open(tmp_db_file, O_WRONLY|O_CREAT|O_TRUNC,
346 	    DLADM_FLOW_DB_PERMS)) == -1) {
347 		(void) fclose(fp);
348 		i_dladm_flow_unlock_db(lock_fd);
349 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
350 	}
351 
352 	if ((nfp = fdopen(nfd, "w")) == NULL) {
353 		(void) close(nfd);
354 		(void) fclose(fp);
355 		(void) unlink(tmp_db_file);
356 		i_dladm_flow_unlock_db(lock_fd);
357 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
358 	}
359 
360 	while (fgets(line, MAXLINELEN, fp) != NULL) {
361 
362 		/* skip comments */
363 		if (BLANK_LINE(line)) {
364 			if (fputs(line, nfp) == EOF)
365 				goto failed;
366 			continue;
367 		}
368 		(void) strtok(line, " \n");
369 
370 		if ((status = dladm_flow_parse_db(line, &attr)) !=
371 		    DLADM_STATUS_OK)
372 			goto failed;
373 
374 		fn_rc = fn(arg, &attr);
375 
376 		switch (fn_rc) {
377 		case -1:
378 			/* failure, stop walking */
379 			goto failed;
380 		case 0:
381 			/*
382 			 * Success, write group attributes, which could
383 			 * have been modified by fn().
384 			 */
385 			if (i_dladm_flow_fput_grp(nfp, &attr) != 0)
386 				goto failed;
387 			break;
388 		case 1:
389 			/* skip current group */
390 			break;
391 		}
392 	}
393 	if (fchmod(nfd, DLADM_FLOW_DB_PERMS) == -1)
394 		goto failed;
395 
396 	if (fchown(nfd, DLADM_FLOW_DB_OWNER, DLADM_FLOW_DB_GROUP) == -1)
397 		goto failed;
398 
399 	if (fflush(nfp) == EOF)
400 		goto failed;
401 
402 	(void) fclose(fp);
403 	(void) fclose(nfp);
404 
405 	if (rename(tmp_db_file, db_file) == -1) {
406 		(void) unlink(tmp_db_file);
407 		i_dladm_flow_unlock_db(lock_fd);
408 		return (DLADM_STATUS_FLOW_DB_ERR);
409 	}
410 	i_dladm_flow_unlock_db(lock_fd);
411 	return (DLADM_STATUS_OK);
412 
413 failed:
414 	(void) fclose(fp);
415 	(void) fclose(nfp);
416 	(void) unlink(tmp_db_file);
417 	i_dladm_flow_unlock_db(lock_fd);
418 
419 	return (status);
420 }
421 
422 /*
423  * Remove existing flow from DB.
424  */
425 
426 typedef struct remove_db_state {
427 	dld_flowinfo_t	rs_newattr;
428 	dld_flowinfo_t	rs_oldattr;
429 	boolean_t	rs_found;
430 } remove_db_state_t;
431 
432 static int
433 i_dladm_flow_remove_db_fn(void *arg, dld_flowinfo_t *grp)
434 {
435 	remove_db_state_t *state = (remove_db_state_t *)arg;
436 	dld_flowinfo_t *attr = &state->rs_newattr;
437 
438 	if ((strcmp(grp->fi_flowname, attr->fi_flowname)) != 0)
439 		return (0);
440 	else {
441 		bcopy(grp, &state->rs_oldattr,
442 		    sizeof (dld_flowinfo_t));
443 		state->rs_found = B_TRUE;
444 		return (1);
445 	}
446 }
447 
448 /* ARGSUSED */
449 static int
450 i_dladm_flow_remove_db(remove_db_state_t *state, const char *root)
451 {
452 	if (i_dladm_flow_walk_rw_db(i_dladm_flow_remove_db_fn, state, root)
453 	    != 0)
454 		return (-1);
455 
456 	if (!state->rs_found) {
457 		errno = ENOENT;
458 		return (-1);
459 	}
460 
461 	return (0);
462 }
463 
464 /*
465  * Create a flow in the DB.
466  */
467 
468 typedef struct modify_db_state {
469 	dld_flowinfo_t	ms_newattr;
470 	dld_flowinfo_t	ms_oldattr;
471 	boolean_t	ms_found;
472 } modify_db_state_t;
473 
474 static dladm_status_t
475 i_dladm_flow_create_db(dld_flowinfo_t *attr, const char *root)
476 {
477 	FILE 	*fp;
478 	char 	line[MAXLINELEN];
479 	char	*db_file;
480 	char	db_file_buf[MAXPATHLEN];
481 	int	lock_fd;
482 	dladm_status_t	status = DLADM_STATUS_OK;
483 
484 	if (root == NULL) {
485 		db_file = DLADM_FLOW_DB;
486 	} else {
487 		(void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
488 		    DLADM_FLOW_DB);
489 		db_file = db_file_buf;
490 	}
491 
492 	if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0)
493 		return (DLADM_STATUS_FLOW_DB_ERR);
494 
495 	if ((fp = fopen(db_file, "r+")) == NULL &&
496 	    (fp = fopen(db_file, "w")) == NULL) {
497 		i_dladm_flow_unlock_db(lock_fd);
498 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
499 	}
500 
501 	/* look for existing group with same flowname */
502 	while (fgets(line, MAXLINELEN, fp) != NULL) {
503 		char *holder, *lasts;
504 
505 		/* skip comments */
506 		if (BLANK_LINE(line))
507 			continue;
508 
509 		/* ignore corrupted lines */
510 		holder = strtok_r(line, " \t", &lasts);
511 		if (holder == NULL)
512 			continue;
513 
514 		/* flow id */
515 		if (strcmp(holder, attr->fi_flowname) == 0) {
516 			/* group with flow id already exists */
517 			status = DLADM_STATUS_PERSIST_FLOW_EXISTS;
518 			goto failed;
519 		}
520 	}
521 	/*
522 	 * If we get here, we've verified that no existing group with
523 	 * the same flow id already exists. Its now time to add the new
524 	 * group to the DB.
525 	 */
526 	if (i_dladm_flow_fput_grp(fp, attr) != 0)
527 		status = DLADM_STATUS_FLOW_DB_PARSE_ERR;
528 
529 failed:
530 	(void) fclose(fp);
531 	i_dladm_flow_unlock_db(lock_fd);
532 	return (status);
533 }
534 
535 static dladm_status_t
536 i_dladm_flow_add(char *flowname, datalink_id_t linkid, flow_desc_t *flowdesc,
537     mac_resource_props_t *mrp)
538 {
539 	dld_ioc_addflow_t	attr;
540 	int			fd;
541 
542 	/* create flow */
543 	bzero(&attr, sizeof (attr));
544 	bcopy(flowdesc, &attr.af_flow_desc, sizeof (flow_desc_t));
545 	if (mrp != NULL) {
546 		bcopy(mrp, &attr.af_resource_props,
547 		    sizeof (mac_resource_props_t));
548 	}
549 
550 	(void) strlcpy(attr.af_name, flowname, sizeof (attr.af_name));
551 	attr.af_linkid = linkid;
552 
553 	fd = open(DLD_CONTROL_DEV, O_RDWR);
554 	if (fd < 0)
555 		return (dladm_errno2status(errno));
556 
557 	if (ioctl(fd, DLDIOC_ADDFLOW, &attr) < 0) {
558 		(void) close(fd);
559 		return (dladm_errno2status(errno));
560 	}
561 
562 	(void) close(fd);
563 
564 	return (DLADM_STATUS_OK);
565 }
566 
567 static dladm_status_t
568 i_dladm_flow_remove(char *flowname)
569 {
570 	dld_ioc_removeflow_t	attr;
571 	int			fd;
572 	dladm_status_t		status = DLADM_STATUS_OK;
573 
574 	(void) strlcpy(attr.rf_name, flowname,
575 	    sizeof (attr.rf_name));
576 
577 	fd = open(DLD_CONTROL_DEV, O_RDWR);
578 	if (fd < 0)
579 		return (dladm_errno2status(errno));
580 
581 	if (ioctl(fd, DLDIOC_REMOVEFLOW, &attr) < 0)
582 		status = dladm_errno2status(errno);
583 
584 	(void) close(fd);
585 
586 	return (status);
587 }
588 
589 
590 /* ARGSUSED */
591 dladm_status_t
592 dladm_flow_add(datalink_id_t linkid, dladm_arg_list_t *attrlist,
593     dladm_arg_list_t *proplist, char *flowname, boolean_t tempop,
594     const char *root)
595 {
596 	dld_flowinfo_t		db_attr;
597 	flow_desc_t		flowdesc;
598 	mac_resource_props_t	mrp;
599 	dladm_status_t		status;
600 
601 	/* Extract flow attributes from attrlist */
602 	bzero(&flowdesc, sizeof (flow_desc_t));
603 	if (attrlist != NULL && (status = dladm_flow_attrlist_extract(attrlist,
604 	    &flowdesc)) != DLADM_STATUS_OK) {
605 		return (status);
606 	}
607 
608 	/* Extract resource_ctl and cpu_list from proplist */
609 	bzero(&mrp, sizeof (mac_resource_props_t));
610 	if (proplist != NULL && (status = dladm_flow_proplist_extract(proplist,
611 	    &mrp)) != DLADM_STATUS_OK) {
612 		return (status);
613 	}
614 
615 	/* Add flow in kernel */
616 	status = i_dladm_flow_add(flowname, linkid, &flowdesc, &mrp);
617 	if (status != DLADM_STATUS_OK)
618 		return (status);
619 
620 	/* Add flow to DB */
621 	if (!tempop) {
622 		bzero(&db_attr, sizeof (db_attr));
623 		bcopy(&flowdesc, &db_attr.fi_flow_desc, sizeof (flow_desc_t));
624 		(void) strlcpy(db_attr.fi_flowname, flowname,
625 		    sizeof (db_attr.fi_flowname));
626 		db_attr.fi_linkid = linkid;
627 
628 		if ((status = i_dladm_flow_create_db(&db_attr, root)) !=
629 		    DLADM_STATUS_OK) {
630 			(void) i_dladm_flow_remove(flowname);
631 			return (status);
632 		}
633 		/* set flow properties */
634 		if (proplist != NULL) {
635 			status = i_dladm_set_flow_proplist_db(flowname,
636 			    proplist);
637 			if (status != DLADM_STATUS_OK) {
638 				(void) i_dladm_flow_remove(flowname);
639 				return (status);
640 			}
641 		}
642 	}
643 	return (status);
644 }
645 
646 /*
647  * Remove a flow.
648  */
649 /* ARGSUSED */
650 dladm_status_t
651 dladm_flow_remove(char *flowname, boolean_t tempop,
652     const char *root)
653 {
654 	remove_db_state_t		state;
655 	dladm_status_t			status = DLADM_STATUS_OK;
656 	dladm_status_t			s = DLADM_STATUS_OK;
657 
658 	/* remove flow */
659 	status = i_dladm_flow_remove(flowname);
660 	if ((status != DLADM_STATUS_OK) &&
661 	    (tempop || status != DLADM_STATUS_NOTFOUND))
662 		goto done;
663 
664 	/* remove flow from DB */
665 	if (!tempop) {
666 		bzero(&state, sizeof (state));
667 		(void) strlcpy(state.rs_newattr.fi_flowname, flowname,
668 		    sizeof (state.rs_newattr.fi_flowname));
669 		state.rs_found = B_FALSE;
670 
671 		/* flow DB */
672 		if (i_dladm_flow_remove_db(&state, root) < 0) {
673 			s = dladm_errno2status(errno);
674 			goto done;
675 		}
676 
677 		/* flow prop DB */
678 		s = dladm_set_flowprop(flowname, NULL, NULL, 0,
679 		    DLADM_OPT_PERSIST, NULL);
680 	}
681 
682 done:
683 	if (!tempop) {
684 		if (s == DLADM_STATUS_OK) {
685 			if (status == DLADM_STATUS_NOTFOUND)
686 				status = s;
687 		} else {
688 			if (s != DLADM_STATUS_NOTFOUND)
689 				status = s;
690 		}
691 	}
692 	return (status);
693 }
694 
695 /*
696  * Get an existing flow in the DB.
697  */
698 
699 typedef struct get_db_state {
700 	int		(*gs_fn)(dladm_flow_attr_t *, void *);
701 	void		*gs_arg;
702 	datalink_id_t	gs_linkid;
703 } get_db_state_t;
704 
705 /*
706  * For each flow which matches the linkid, copy all flow information
707  * to a new dladm_flow_attr_t structure and call the provided
708  * function.  This is used to display perisistent flows from
709  * the database.
710  */
711 
712 static int
713 i_dladm_flow_get_db_fn(void *arg, dld_flowinfo_t *grp)
714 {
715 	get_db_state_t		*state = (get_db_state_t *)arg;
716 	dladm_flow_attr_t	attr;
717 
718 	if (grp->fi_linkid == state->gs_linkid) {
719 		attr.fa_linkid = state->gs_linkid;
720 		bcopy(grp->fi_flowname, &attr.fa_flowname,
721 		    sizeof (attr.fa_flowname));
722 		bcopy(&grp->fi_flow_desc, &attr.fa_flow_desc,
723 		    sizeof (attr.fa_flow_desc));
724 		bcopy(&grp->fi_resource_props, &attr.fa_resource_props,
725 		    sizeof (attr.fa_resource_props));
726 		(void) state->gs_fn(&attr, state->gs_arg);
727 	}
728 	return (0);
729 }
730 
731 /*
732  * Walk through the flows defined on the system and for each flow
733  * invoke <fn>(<arg>, <flow>);
734  * Currently used for show-flow.
735  */
736 /* ARGSUSED */
737 dladm_status_t
738 dladm_walk_flow(int (*fn)(dladm_flow_attr_t *, void *),
739     datalink_id_t linkid, void *arg, boolean_t persist)
740 {
741 	dld_flowinfo_t		*flow;
742 	int			i, bufsize, fd;
743 	dld_ioc_walkflow_t	*ioc = NULL;
744 	dladm_flow_attr_t 	attr;
745 	dladm_status_t		status = DLADM_STATUS_OK;
746 
747 	if (fn == NULL)
748 		return (DLADM_STATUS_BADARG);
749 
750 	if (persist) {
751 		get_db_state_t state;
752 
753 		bzero(&state, sizeof (state));
754 
755 		state.gs_linkid = linkid;
756 		state.gs_fn = fn;
757 		state.gs_arg = arg;
758 		status = i_dladm_flow_walk_rw_db(i_dladm_flow_get_db_fn,
759 		    &state, NULL);
760 		if (status != DLADM_STATUS_OK)
761 			return (status);
762 	} else {
763 		if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
764 			return (dladm_errno2status(errno));
765 
766 		bufsize = MIN_INFO_SIZE;
767 		if ((ioc = calloc(1, bufsize)) == NULL) {
768 			status = dladm_errno2status(errno);
769 			(void) close(fd);
770 			return (status);
771 		}
772 
773 		ioc->wf_linkid = linkid;
774 		ioc->wf_len = bufsize - sizeof (*ioc);
775 
776 		while (ioctl(fd, DLDIOC_WALKFLOW, ioc) < 0) {
777 			if (errno == ENOSPC) {
778 				bufsize *= 2;
779 				ioc = realloc(ioc, bufsize);
780 				if (ioc != NULL) {
781 					ioc->wf_linkid = linkid;
782 					ioc->wf_len = bufsize - sizeof (*ioc);
783 					continue;
784 				}
785 			}
786 			goto bail;
787 		}
788 
789 		flow = (dld_flowinfo_t *)(void *)(ioc + 1);
790 		for (i = 0; i < ioc->wf_nflows; i++, flow++) {
791 			bzero(&attr, sizeof (attr));
792 
793 			attr.fa_linkid = flow->fi_linkid;
794 			bcopy(&flow->fi_flowname, &attr.fa_flowname,
795 			    sizeof (attr.fa_flowname));
796 			bcopy(&flow->fi_flow_desc, &attr.fa_flow_desc,
797 			    sizeof (attr.fa_flow_desc));
798 			bcopy(&flow->fi_resource_props, &attr.fa_resource_props,
799 			    sizeof (attr.fa_resource_props));
800 
801 			if (fn(&attr, arg) == DLADM_WALK_TERMINATE)
802 				break;
803 		}
804 	}
805 
806 bail:
807 	free(ioc);
808 	(void) close(fd);
809 	return (status);
810 }
811 
812 dladm_status_t
813 dladm_flow_init(void)
814 {
815 	flow_desc_t		flowdesc;
816 	datalink_id_t		linkid;
817 	dladm_status_t		s, status = DLADM_STATUS_OK;
818 	char			name[MAXNAMELEN];
819 	char			line[MAXLINELEN];
820 	dld_flowinfo_t		attr;
821 	FILE			*fp;
822 
823 	if ((fp = fopen(DLADM_FLOW_DB, "r")) == NULL)
824 		return (DLADM_STATUS_DB_NOTFOUND);
825 
826 	while (fgets(line, MAXLINELEN, fp) != NULL) {
827 		/* skip comments */
828 		if (BLANK_LINE(line))
829 			continue;
830 
831 		(void) strtok(line, " \n");
832 
833 		s = dladm_flow_parse_db(line, &attr);
834 		if (s != DLADM_STATUS_OK) {
835 			status = s;
836 			continue;
837 		}
838 		bzero(&flowdesc, sizeof (flowdesc));
839 		bcopy(&attr.fi_flow_desc, &flowdesc, sizeof (flow_desc_t));
840 		(void) strlcpy(name, attr.fi_flowname,
841 		    sizeof (attr.fi_flowname));
842 		linkid = attr.fi_linkid;
843 
844 		s = i_dladm_flow_add(name, linkid, &flowdesc, NULL);
845 		if (s != DLADM_STATUS_OK)
846 			status = s;
847 	}
848 	s = i_dladm_init_flowprop_db();
849 	if (s != DLADM_STATUS_OK)
850 		status = s;
851 
852 	(void) fclose(fp);
853 	return (status);
854 }
855 
856 dladm_status_t
857 dladm_prefixlen2mask(int prefixlen, int maxlen, uchar_t *mask)
858 {
859 	if (prefixlen < 0 || prefixlen > maxlen)
860 		return (DLADM_STATUS_BADARG);
861 
862 	while (prefixlen > 0) {
863 		if (prefixlen >= 8) {
864 			*mask++ = 0xFF;
865 			prefixlen -= 8;
866 			continue;
867 		}
868 		*mask |= 1 << (8 - prefixlen);
869 		prefixlen--;
870 	}
871 	return (DLADM_STATUS_OK);
872 }
873 
874 dladm_status_t
875 dladm_mask2prefixlen(in6_addr_t *mask, int plen, int *prefixlen)
876 {
877 	int		bits;
878 	int		i, end;
879 
880 	switch (plen) {
881 	case IP_ABITS:
882 		end = 3;
883 		break;
884 	case IPV6_ABITS:
885 		end = 0;
886 		break;
887 	default:
888 		return (DLADM_STATUS_BADARG);
889 	}
890 
891 	for (i = 3; i >= end; i--) {
892 		if (mask->_S6_un._S6_u32[i] == 0) {
893 			plen -= 32;
894 			continue;
895 		}
896 		bits = ffs(ntohl(mask->_S6_un._S6_u32[i])) - 1;
897 		if (bits == 0)
898 			break;
899 		plen -= bits;
900 	}
901 	*prefixlen = plen;
902 	return (DLADM_STATUS_OK);
903 }
904