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