xref: /titanic_51/usr/src/lib/libdladm/common/libdlflow.c (revision 33f2fefd46350ca5992567761c46a5b70f864340)
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 2009 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, MAXFLOWNAMELEN) >= MAXFLOWNAMELEN)
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(dladm_handle_t handle, char *flowname, datalink_id_t linkid,
537     flow_desc_t *flowdesc, mac_resource_props_t *mrp)
538 {
539 	dld_ioc_addflow_t	attr;
540 
541 	/* create flow */
542 	bzero(&attr, sizeof (attr));
543 	bcopy(flowdesc, &attr.af_flow_desc, sizeof (flow_desc_t));
544 	if (mrp != NULL) {
545 		bcopy(mrp, &attr.af_resource_props,
546 		    sizeof (mac_resource_props_t));
547 	}
548 
549 	(void) strlcpy(attr.af_name, flowname, sizeof (attr.af_name));
550 	attr.af_linkid = linkid;
551 
552 	if (ioctl(dladm_dld_fd(handle), DLDIOC_ADDFLOW, &attr) < 0)
553 		return (dladm_errno2status(errno));
554 
555 	return (DLADM_STATUS_OK);
556 }
557 
558 static dladm_status_t
559 i_dladm_flow_remove(dladm_handle_t handle, char *flowname)
560 {
561 	dld_ioc_removeflow_t	attr;
562 	dladm_status_t		status = DLADM_STATUS_OK;
563 
564 	(void) strlcpy(attr.rf_name, flowname,
565 	    sizeof (attr.rf_name));
566 
567 	if (ioctl(dladm_dld_fd(handle), DLDIOC_REMOVEFLOW, &attr) < 0)
568 		status = dladm_errno2status(errno);
569 
570 	return (status);
571 }
572 
573 
574 /* ARGSUSED */
575 dladm_status_t
576 dladm_flow_add(dladm_handle_t handle, datalink_id_t linkid,
577     dladm_arg_list_t *attrlist, dladm_arg_list_t *proplist, char *flowname,
578     boolean_t tempop, const char *root)
579 {
580 	dld_flowinfo_t		db_attr;
581 	flow_desc_t		flowdesc;
582 	mac_resource_props_t	mrp;
583 	dladm_status_t		status;
584 
585 	/* Extract flow attributes from attrlist */
586 	bzero(&flowdesc, sizeof (flow_desc_t));
587 	if (attrlist != NULL && (status = dladm_flow_attrlist_extract(attrlist,
588 	    &flowdesc)) != DLADM_STATUS_OK) {
589 		return (status);
590 	}
591 
592 	/* Extract resource_ctl and cpu_list from proplist */
593 	bzero(&mrp, sizeof (mac_resource_props_t));
594 	if (proplist != NULL && (status = dladm_flow_proplist_extract(proplist,
595 	    &mrp)) != DLADM_STATUS_OK) {
596 		return (status);
597 	}
598 
599 	/* Add flow in kernel */
600 	status = i_dladm_flow_add(handle, flowname, linkid, &flowdesc, &mrp);
601 	if (status != DLADM_STATUS_OK)
602 		return (status);
603 
604 	/* Add flow to DB */
605 	if (!tempop) {
606 		bzero(&db_attr, sizeof (db_attr));
607 		bcopy(&flowdesc, &db_attr.fi_flow_desc, sizeof (flow_desc_t));
608 		(void) strlcpy(db_attr.fi_flowname, flowname,
609 		    sizeof (db_attr.fi_flowname));
610 		db_attr.fi_linkid = linkid;
611 
612 		if ((status = i_dladm_flow_create_db(&db_attr, root)) !=
613 		    DLADM_STATUS_OK) {
614 			(void) i_dladm_flow_remove(handle, flowname);
615 			return (status);
616 		}
617 		/* set flow properties */
618 		if (proplist != NULL) {
619 			status = i_dladm_set_flow_proplist_db(handle, flowname,
620 			    proplist);
621 			if (status != DLADM_STATUS_OK) {
622 				(void) i_dladm_flow_remove(handle, flowname);
623 				return (status);
624 			}
625 		}
626 	}
627 	return (status);
628 }
629 
630 /*
631  * Remove a flow.
632  */
633 /* ARGSUSED */
634 dladm_status_t
635 dladm_flow_remove(dladm_handle_t handle, char *flowname, boolean_t tempop,
636     const char *root)
637 {
638 	remove_db_state_t		state;
639 	dladm_status_t			status = DLADM_STATUS_OK;
640 	dladm_status_t			s = DLADM_STATUS_OK;
641 
642 	/* remove flow */
643 	status = i_dladm_flow_remove(handle, flowname);
644 	if ((status != DLADM_STATUS_OK) &&
645 	    (tempop || status != DLADM_STATUS_NOTFOUND))
646 		goto done;
647 
648 	/* remove flow from DB */
649 	if (!tempop) {
650 		bzero(&state, sizeof (state));
651 		(void) strlcpy(state.rs_newattr.fi_flowname, flowname,
652 		    sizeof (state.rs_newattr.fi_flowname));
653 		state.rs_found = B_FALSE;
654 
655 		/* flow DB */
656 		if (i_dladm_flow_remove_db(&state, root) < 0) {
657 			s = dladm_errno2status(errno);
658 			goto done;
659 		}
660 
661 		/* flow prop DB */
662 		s = dladm_set_flowprop(handle, flowname, NULL, NULL, 0,
663 		    DLADM_OPT_PERSIST, NULL);
664 	}
665 
666 done:
667 	if (!tempop) {
668 		if (s == DLADM_STATUS_OK) {
669 			if (status == DLADM_STATUS_NOTFOUND)
670 				status = s;
671 		} else {
672 			if (s != DLADM_STATUS_NOTFOUND)
673 				status = s;
674 		}
675 	}
676 	return (status);
677 }
678 
679 /*
680  * Get an existing flow in the DB.
681  */
682 
683 typedef struct get_db_state {
684 	int		(*gs_fn)(dladm_flow_attr_t *, void *);
685 	void		*gs_arg;
686 	datalink_id_t	gs_linkid;
687 } get_db_state_t;
688 
689 /*
690  * For each flow which matches the linkid, copy all flow information
691  * to a new dladm_flow_attr_t structure and call the provided
692  * function.  This is used to display perisistent flows from
693  * the database.
694  */
695 
696 static int
697 i_dladm_flow_get_db_fn(void *arg, dld_flowinfo_t *grp)
698 {
699 	get_db_state_t		*state = (get_db_state_t *)arg;
700 	dladm_flow_attr_t	attr;
701 
702 	if (grp->fi_linkid == state->gs_linkid) {
703 		attr.fa_linkid = state->gs_linkid;
704 		bcopy(grp->fi_flowname, &attr.fa_flowname,
705 		    sizeof (attr.fa_flowname));
706 		bcopy(&grp->fi_flow_desc, &attr.fa_flow_desc,
707 		    sizeof (attr.fa_flow_desc));
708 		bcopy(&grp->fi_resource_props, &attr.fa_resource_props,
709 		    sizeof (attr.fa_resource_props));
710 		(void) state->gs_fn(&attr, state->gs_arg);
711 	}
712 	return (0);
713 }
714 
715 /*
716  * Walk through the flows defined on the system and for each flow
717  * invoke <fn>(<arg>, <flow>);
718  * Currently used for show-flow.
719  */
720 /* ARGSUSED */
721 dladm_status_t
722 dladm_walk_flow(int (*fn)(dladm_flow_attr_t *, void *), dladm_handle_t handle,
723     datalink_id_t linkid, void *arg, boolean_t persist)
724 {
725 	dld_flowinfo_t		*flow;
726 	int			i, bufsize;
727 	dld_ioc_walkflow_t	*ioc = NULL;
728 	dladm_flow_attr_t 	attr;
729 	dladm_status_t		status = DLADM_STATUS_OK;
730 
731 	if (fn == NULL)
732 		return (DLADM_STATUS_BADARG);
733 
734 	if (persist) {
735 		get_db_state_t state;
736 
737 		bzero(&state, sizeof (state));
738 
739 		state.gs_linkid = linkid;
740 		state.gs_fn = fn;
741 		state.gs_arg = arg;
742 		status = i_dladm_flow_walk_rw_db(i_dladm_flow_get_db_fn,
743 		    &state, NULL);
744 		if (status != DLADM_STATUS_OK)
745 			return (status);
746 	} else {
747 		bufsize = MIN_INFO_SIZE;
748 		if ((ioc = calloc(1, bufsize)) == NULL) {
749 			status = dladm_errno2status(errno);
750 			return (status);
751 		}
752 
753 		ioc->wf_linkid = linkid;
754 		ioc->wf_len = bufsize - sizeof (*ioc);
755 
756 		while (ioctl(dladm_dld_fd(handle), DLDIOC_WALKFLOW, ioc) < 0) {
757 			if (errno == ENOSPC) {
758 				bufsize *= 2;
759 				ioc = realloc(ioc, bufsize);
760 				if (ioc != NULL) {
761 					ioc->wf_linkid = linkid;
762 					ioc->wf_len = bufsize - sizeof (*ioc);
763 					continue;
764 				}
765 			}
766 			goto bail;
767 		}
768 
769 		flow = (dld_flowinfo_t *)(void *)(ioc + 1);
770 		for (i = 0; i < ioc->wf_nflows; i++, flow++) {
771 			bzero(&attr, sizeof (attr));
772 
773 			attr.fa_linkid = flow->fi_linkid;
774 			bcopy(&flow->fi_flowname, &attr.fa_flowname,
775 			    sizeof (attr.fa_flowname));
776 			bcopy(&flow->fi_flow_desc, &attr.fa_flow_desc,
777 			    sizeof (attr.fa_flow_desc));
778 			bcopy(&flow->fi_resource_props, &attr.fa_resource_props,
779 			    sizeof (attr.fa_resource_props));
780 
781 			if (fn(&attr, arg) == DLADM_WALK_TERMINATE)
782 				break;
783 		}
784 	}
785 
786 bail:
787 	free(ioc);
788 	return (status);
789 }
790 
791 dladm_status_t
792 dladm_flow_init(dladm_handle_t handle)
793 {
794 	flow_desc_t		flowdesc;
795 	datalink_id_t		linkid;
796 	dladm_status_t		s, status = DLADM_STATUS_OK;
797 	char			name[MAXFLOWNAMELEN];
798 	char			line[MAXLINELEN];
799 	dld_flowinfo_t		attr;
800 	FILE			*fp;
801 
802 	if ((fp = fopen(DLADM_FLOW_DB, "r")) == NULL)
803 		return (DLADM_STATUS_DB_NOTFOUND);
804 
805 	while (fgets(line, MAXLINELEN, fp) != NULL) {
806 		/* skip comments */
807 		if (BLANK_LINE(line))
808 			continue;
809 
810 		(void) strtok(line, " \n");
811 
812 		s = dladm_flow_parse_db(line, &attr);
813 		if (s != DLADM_STATUS_OK) {
814 			status = s;
815 			continue;
816 		}
817 		bzero(&flowdesc, sizeof (flowdesc));
818 		bcopy(&attr.fi_flow_desc, &flowdesc, sizeof (flow_desc_t));
819 		(void) strlcpy(name, attr.fi_flowname,
820 		    sizeof (attr.fi_flowname));
821 		linkid = attr.fi_linkid;
822 
823 		s = i_dladm_flow_add(handle, name, linkid, &flowdesc, NULL);
824 		if (s != DLADM_STATUS_OK)
825 			status = s;
826 	}
827 	s = i_dladm_init_flowprop_db(handle);
828 	if (s != DLADM_STATUS_OK)
829 		status = s;
830 
831 	(void) fclose(fp);
832 	return (status);
833 }
834 
835 dladm_status_t
836 dladm_prefixlen2mask(int prefixlen, int maxlen, uchar_t *mask)
837 {
838 	if (prefixlen < 0 || prefixlen > maxlen)
839 		return (DLADM_STATUS_BADARG);
840 
841 	while (prefixlen > 0) {
842 		if (prefixlen >= 8) {
843 			*mask++ = 0xFF;
844 			prefixlen -= 8;
845 			continue;
846 		}
847 		*mask |= 1 << (8 - prefixlen);
848 		prefixlen--;
849 	}
850 	return (DLADM_STATUS_OK);
851 }
852 
853 dladm_status_t
854 dladm_mask2prefixlen(in6_addr_t *mask, int plen, int *prefixlen)
855 {
856 	int		bits;
857 	int		i, end;
858 
859 	switch (plen) {
860 	case IP_ABITS:
861 		end = 3;
862 		break;
863 	case IPV6_ABITS:
864 		end = 0;
865 		break;
866 	default:
867 		return (DLADM_STATUS_BADARG);
868 	}
869 
870 	for (i = 3; i >= end; i--) {
871 		if (mask->_S6_un._S6_u32[i] == 0) {
872 			plen -= 32;
873 			continue;
874 		}
875 		bits = ffs(ntohl(mask->_S6_un._S6_u32[i])) - 1;
876 		if (bits == 0)
877 			break;
878 		plen -= bits;
879 	}
880 	*prefixlen = plen;
881 	return (DLADM_STATUS_OK);
882 }
883