xref: /illumos-gate/usr/src/lib/libdladm/common/libdlflow.c (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
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 */
67 static const char *BW_LIMIT = "bw_limit";
68 static const char *PRIORITY = "priority";
69 static const char *LOCAL_IP_ADDR = "local_ip";
70 static const char *REMOTE_IP_ADDR = "remote_ip";
71 static const char *TRANSPORT = "transport";
72 static const char *LOCAL_PORT = "local_port";
73 static const char *REMOTE_PORT = "remote_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		*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;
215 done:
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  */
226 static int
227 i_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 
320 static dladm_status_t
321 i_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 
422 failed:
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 
435 typedef 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 
441 static int
442 i_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 */
458 static int
459 i_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 
477 typedef 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 
483 static dladm_status_t
484 i_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 
538 failed:
539 	(void) fclose(fp);
540 	i_dladm_flow_unlock_db(lock_fd);
541 	return (status);
542 }
543 
544 static dladm_status_t
545 i_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 
567 static dladm_status_t
568 i_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 */
584 dladm_status_t
585 dladm_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 */
643 dladm_status_t
644 dladm_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 
675 done:
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 
692 typedef 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 
705 static int
706 i_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 */
731 dladm_status_t
732 dladm_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 	uint_t			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 
796 bail:
797 	free(ioc);
798 	return (status);
799 }
800 
801 dladm_status_t
802 dladm_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 
845 dladm_status_t
846 dladm_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 
863 dladm_status_t
864 dladm_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