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