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