1da14cebeSEric Cheng /*
2da14cebeSEric Cheng  * CDDL HEADER START
3da14cebeSEric Cheng  *
4da14cebeSEric Cheng  * The contents of this file are subject to the terms of the
5da14cebeSEric Cheng  * Common Development and Distribution License (the "License").
6da14cebeSEric Cheng  * You may not use this file except in compliance with the License.
7da14cebeSEric Cheng  *
8da14cebeSEric Cheng  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da14cebeSEric Cheng  * or http://www.opensolaris.org/os/licensing.
10da14cebeSEric Cheng  * See the License for the specific language governing permissions
11da14cebeSEric Cheng  * and limitations under the License.
12da14cebeSEric Cheng  *
13da14cebeSEric Cheng  * When distributing Covered Code, include this CDDL HEADER in each
14da14cebeSEric Cheng  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da14cebeSEric Cheng  * If applicable, add the following below this CDDL HEADER, with the
16da14cebeSEric Cheng  * fields enclosed by brackets "[]" replaced with your own identifying
17da14cebeSEric Cheng  * information: Portions Copyright [yyyy] [name of copyright owner]
18da14cebeSEric Cheng  *
19da14cebeSEric Cheng  * CDDL HEADER END
20da14cebeSEric Cheng  */
21da14cebeSEric Cheng /*
22*6ba597c5SAnurag S. Maskey  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23da14cebeSEric Cheng  * Use is subject to license terms.
24da14cebeSEric Cheng  */
25da14cebeSEric Cheng 
26da14cebeSEric Cheng #include <stdio.h>
27da14cebeSEric Cheng #include <sys/types.h>
28da14cebeSEric Cheng #include <sys/socket.h>
29da14cebeSEric Cheng #include <sys/ethernet.h>
30da14cebeSEric Cheng #include <netinet/in.h>
31da14cebeSEric Cheng #include <arpa/inet.h>
32da14cebeSEric Cheng #include <sys/stat.h>
3382a2fc47SJames Carlson #include <sys/dld_ioc.h>
34da14cebeSEric Cheng #include <string.h>
35da14cebeSEric Cheng #include <fcntl.h>
36da14cebeSEric Cheng #include <unistd.h>
37da14cebeSEric Cheng #include <stropts.h>
38da14cebeSEric Cheng #include <stdlib.h>
39da14cebeSEric Cheng #include <errno.h>
40da14cebeSEric Cheng #include <strings.h>
41da14cebeSEric Cheng #include <libintl.h>
42da14cebeSEric Cheng #include <netdb.h>
43da14cebeSEric Cheng #include <net/if_types.h>
44da14cebeSEric Cheng #include <net/if_dl.h>
45da14cebeSEric Cheng #include <inet/ip.h>
46da14cebeSEric Cheng #include <inet/ip6.h>
47da14cebeSEric Cheng #include <libdlflow.h>
48da14cebeSEric Cheng #include <libdlflow_impl.h>
49da14cebeSEric Cheng #include <libdladm_impl.h>
50da14cebeSEric Cheng 
51da14cebeSEric Cheng /* minimum buffer size for DLDIOCWALKFLOW */
52da14cebeSEric Cheng #define	MIN_INFO_SIZE	(4 * 1024)
53da14cebeSEric Cheng 
54da14cebeSEric Cheng #define	DLADM_FLOW_DB		"/etc/dladm/flowadm.conf"
55da14cebeSEric Cheng #define	DLADM_FLOW_DB_TMP	"/etc/dladm/flowadm.conf.new"
56da14cebeSEric Cheng #define	DLADM_FLOW_DB_LOCK	"/tmp/flowadm.conf.lock"
57da14cebeSEric Cheng 
58da14cebeSEric Cheng #define	DLADM_FLOW_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
59da14cebeSEric Cheng #define	DLADM_FLOW_DB_OWNER	UID_DLADM
60*6ba597c5SAnurag S. Maskey #define	DLADM_FLOW_DB_GROUP	GID_NETADM
61da14cebeSEric Cheng 
62da14cebeSEric Cheng #define	BLANK_LINE(s)	((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
63da14cebeSEric Cheng #define	MAXLINELEN	1024
64da14cebeSEric Cheng #define	MAXPATHLEN	1024
65da14cebeSEric Cheng 
66da14cebeSEric Cheng /* database file parameters */
67da14cebeSEric Cheng static const char *BW_LIMIT = "bw_limit";
68da14cebeSEric Cheng static const char *PRIORITY = "priority";
69da14cebeSEric Cheng static const char *LOCAL_IP_ADDR = "local_ip";
70da14cebeSEric Cheng static const char *REMOTE_IP_ADDR = "remote_ip";
71da14cebeSEric Cheng static const char *TRANSPORT = "transport";
72da14cebeSEric Cheng static const char *LOCAL_PORT = "local_port";
7325ec3e3dSEric Cheng static const char *REMOTE_PORT = "remote_port";
74da14cebeSEric Cheng static const char *DSFIELD = "dsfield";
75da14cebeSEric Cheng 
76da14cebeSEric Cheng /*
77da14cebeSEric Cheng  * Open and lock the flowadm configuration file lock. The lock is
78da14cebeSEric Cheng  * acquired as a reader (F_RDLCK) or writer (F_WRLCK).
79da14cebeSEric Cheng  */
80da14cebeSEric Cheng static int
i_dladm_flow_lock_db(short type)81da14cebeSEric Cheng i_dladm_flow_lock_db(short type)
82da14cebeSEric Cheng {
83da14cebeSEric Cheng 	int lock_fd;
84da14cebeSEric Cheng 	struct flock lock;
85da14cebeSEric Cheng 
86da14cebeSEric Cheng 	if ((lock_fd = open(DLADM_FLOW_DB_LOCK, O_RDWR | O_CREAT | O_TRUNC,
87da14cebeSEric Cheng 	    DLADM_FLOW_DB_PERMS)) < 0)
88da14cebeSEric Cheng 		return (-1);
89da14cebeSEric Cheng 
90da14cebeSEric Cheng 	lock.l_type = type;
91da14cebeSEric Cheng 	lock.l_whence = SEEK_SET;
92da14cebeSEric Cheng 	lock.l_start = 0;
93da14cebeSEric Cheng 	lock.l_len = 0;
94da14cebeSEric Cheng 
95da14cebeSEric Cheng 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
96da14cebeSEric Cheng 		(void) close(lock_fd);
97da14cebeSEric Cheng 		(void) unlink(DLADM_FLOW_DB_LOCK);
98da14cebeSEric Cheng 		return (-1);
99da14cebeSEric Cheng 	}
100da14cebeSEric Cheng 	return (lock_fd);
101da14cebeSEric Cheng }
102da14cebeSEric Cheng 
103da14cebeSEric Cheng /*
104da14cebeSEric Cheng  * Unlock and close the specified file.
105da14cebeSEric Cheng  */
106da14cebeSEric Cheng static void
i_dladm_flow_unlock_db(int fd)107da14cebeSEric Cheng i_dladm_flow_unlock_db(int fd)
108da14cebeSEric Cheng {
109da14cebeSEric Cheng 	struct flock lock;
110da14cebeSEric Cheng 
111da14cebeSEric Cheng 	if (fd < 0)
112da14cebeSEric Cheng 		return;
113da14cebeSEric Cheng 
114da14cebeSEric Cheng 	lock.l_type = F_UNLCK;
115da14cebeSEric Cheng 	lock.l_whence = SEEK_SET;
116da14cebeSEric Cheng 	lock.l_start = 0;
117da14cebeSEric Cheng 	lock.l_len = 0;
118da14cebeSEric Cheng 
119da14cebeSEric Cheng 	(void) fcntl(fd, F_SETLKW, &lock);
120da14cebeSEric Cheng 	(void) close(fd);
121da14cebeSEric Cheng 	(void) unlink(DLADM_FLOW_DB_LOCK);
122da14cebeSEric Cheng }
123da14cebeSEric Cheng 
124da14cebeSEric Cheng /*
125da14cebeSEric Cheng  * Parse one line of the link flowadm DB
126da14cebeSEric Cheng  * Returns -1 on failure, 0 on success.
127da14cebeSEric Cheng  */
128da14cebeSEric Cheng dladm_status_t
dladm_flow_parse_db(char * line,dld_flowinfo_t * attr)129da14cebeSEric Cheng dladm_flow_parse_db(char *line, dld_flowinfo_t *attr)
130da14cebeSEric Cheng {
131da14cebeSEric Cheng 	char		*token;
132da14cebeSEric Cheng 	char		*value, *name = NULL;
133da14cebeSEric Cheng 	char		*lasts = NULL;
134da14cebeSEric Cheng 	dladm_status_t	status = DLADM_STATUS_FLOW_DB_PARSE_ERR;
135da14cebeSEric Cheng 
136da14cebeSEric Cheng 	bzero(attr, sizeof (*attr));
137da14cebeSEric Cheng 
138da14cebeSEric Cheng 	/* flow name */
139da14cebeSEric Cheng 	if ((token = strtok_r(line, " \t", &lasts)) == NULL)
140da14cebeSEric Cheng 		goto done;
141da14cebeSEric Cheng 
142da000602SGirish Moodalbail 	if (strlcpy(attr->fi_flowname, token, MAXFLOWNAMELEN) >= MAXFLOWNAMELEN)
143da14cebeSEric Cheng 		goto done;
144da14cebeSEric Cheng 
145da14cebeSEric Cheng 	/* resource control and flow descriptor parameters */
146da14cebeSEric Cheng 	while ((token = strtok_r(NULL, " \t", &lasts)) != NULL) {
147da14cebeSEric Cheng 		if ((name = strdup(token)) == NULL)
148da14cebeSEric Cheng 			goto done;
149da14cebeSEric Cheng 
150da14cebeSEric Cheng 		(void) strtok(name, "=");
151da14cebeSEric Cheng 		value = strtok(NULL, "=");
152da14cebeSEric Cheng 		if (value == NULL)
153da14cebeSEric Cheng 			goto done;
154da14cebeSEric Cheng 
155da14cebeSEric Cheng 		if (strcmp(name, "linkid") == 0) {
156da14cebeSEric Cheng 			if ((attr->fi_linkid =
15725ec3e3dSEric Cheng 			    (uint32_t)strtol(value, NULL, 10)) ==
158da14cebeSEric Cheng 			    DATALINK_INVALID_LINKID)
159da14cebeSEric Cheng 				goto done;
160da14cebeSEric Cheng 
161da14cebeSEric Cheng 		} else if (strcmp(name, BW_LIMIT) == 0) {
162da14cebeSEric Cheng 			attr->fi_resource_props.mrp_mask |=
163da14cebeSEric Cheng 			    MRP_MAXBW;
164da14cebeSEric Cheng 			attr->fi_resource_props.mrp_maxbw =
16525ec3e3dSEric Cheng 			    (uint64_t)strtol(value, NULL, 0);
166da14cebeSEric Cheng 
167da14cebeSEric Cheng 		} else if (strcmp(name, PRIORITY) == 0) {
168da14cebeSEric Cheng 			attr->fi_resource_props.mrp_mask |= MRP_PRIORITY;
169da14cebeSEric Cheng 			status = dladm_str2pri(value,
170da14cebeSEric Cheng 			    &attr->fi_resource_props.mrp_priority);
171da14cebeSEric Cheng 			if (status != DLADM_STATUS_OK)
172da14cebeSEric Cheng 				goto done;
173da14cebeSEric Cheng 
174da14cebeSEric Cheng 		} else if (strcmp(name, DSFIELD) == 0) {
175da14cebeSEric Cheng 			status = do_check_dsfield(value,
176da14cebeSEric Cheng 			    &attr->fi_flow_desc);
177da14cebeSEric Cheng 			if (status != DLADM_STATUS_OK)
178da14cebeSEric Cheng 				goto done;
179da14cebeSEric Cheng 
180da14cebeSEric Cheng 		} else if (strcmp(name, LOCAL_IP_ADDR) == 0) {
181da14cebeSEric Cheng 			status = do_check_ip_addr(value, B_TRUE,
182da14cebeSEric Cheng 			    &attr->fi_flow_desc);
183da14cebeSEric Cheng 			if (status != DLADM_STATUS_OK)
184da14cebeSEric Cheng 				goto done;
185da14cebeSEric Cheng 
186da14cebeSEric Cheng 		} else if (strcmp(name, REMOTE_IP_ADDR) == 0) {
187da14cebeSEric Cheng 			status = do_check_ip_addr(value, B_FALSE,
188da14cebeSEric Cheng 			    &attr->fi_flow_desc);
189da14cebeSEric Cheng 			if (status != DLADM_STATUS_OK)
190da14cebeSEric Cheng 				goto done;
191da14cebeSEric Cheng 
192da14cebeSEric Cheng 		} else if (strcmp(name, TRANSPORT) == 0) {
193da14cebeSEric Cheng 			attr->fi_flow_desc.fd_mask |= FLOW_IP_PROTOCOL;
194da14cebeSEric Cheng 			attr->fi_flow_desc.fd_protocol =
19525ec3e3dSEric Cheng 			    (uint8_t)strtol(value, NULL, 0);
196da14cebeSEric Cheng 
197da14cebeSEric Cheng 		} else if (strcmp(name, LOCAL_PORT) == 0) {
198da14cebeSEric Cheng 			attr->fi_flow_desc.fd_mask |= FLOW_ULP_PORT_LOCAL;
199da14cebeSEric Cheng 			attr->fi_flow_desc.fd_local_port =
20025ec3e3dSEric Cheng 			    (uint16_t)strtol(value, NULL, 10);
201da14cebeSEric Cheng 			attr->fi_flow_desc.fd_local_port =
202da14cebeSEric Cheng 			    htons(attr->fi_flow_desc.fd_local_port);
20325ec3e3dSEric Cheng 		} else if (strcmp(name, REMOTE_PORT) == 0) {
20425ec3e3dSEric Cheng 			attr->fi_flow_desc.fd_mask |= FLOW_ULP_PORT_REMOTE;
20525ec3e3dSEric Cheng 			attr->fi_flow_desc.fd_remote_port =
20625ec3e3dSEric Cheng 			    (uint16_t)strtol(value, NULL, 10);
20725ec3e3dSEric Cheng 			attr->fi_flow_desc.fd_remote_port =
20825ec3e3dSEric Cheng 			    htons(attr->fi_flow_desc.fd_remote_port);
209da14cebeSEric Cheng 		}
210da14cebeSEric Cheng 		free(name);
211da14cebeSEric Cheng 		name = NULL;
212da14cebeSEric Cheng 	}
213da14cebeSEric Cheng 	if (attr->fi_linkid != DATALINK_INVALID_LINKID)
214da14cebeSEric Cheng 		status = DLADM_STATUS_OK;
215da14cebeSEric Cheng done:
216da14cebeSEric Cheng 	free(name);
217da14cebeSEric Cheng 	return (status);
218da14cebeSEric Cheng }
219da14cebeSEric Cheng 
220da14cebeSEric Cheng #define	FPRINTF_ERR(fcall) if ((fcall) < 0) return (-1);
221da14cebeSEric Cheng 
222da14cebeSEric Cheng /*
223da14cebeSEric Cheng  * Write the attribute of a group to the specified file. Returns 0 on
224da14cebeSEric Cheng  * success, -1 on failure.
225da14cebeSEric Cheng  */
226da14cebeSEric Cheng static int
i_dladm_flow_fput_grp(FILE * fp,dld_flowinfo_t * attr)227da14cebeSEric Cheng i_dladm_flow_fput_grp(FILE *fp, dld_flowinfo_t *attr)
228da14cebeSEric Cheng {
229da14cebeSEric Cheng 
230da14cebeSEric Cheng 	FPRINTF_ERR(fprintf(fp, "%s\tlinkid=%d\t",
231da14cebeSEric Cheng 	    attr->fi_flowname, attr->fi_linkid));
232da14cebeSEric Cheng 
233da14cebeSEric Cheng 	/* flow policy */
234da14cebeSEric Cheng 	if (attr->fi_resource_props.mrp_mask & MRP_MAXBW)
235da14cebeSEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%" PRIu64 "\t", BW_LIMIT,
236da14cebeSEric Cheng 		    attr->fi_resource_props.mrp_maxbw));
237da14cebeSEric Cheng 
238da14cebeSEric Cheng 	if (attr->fi_resource_props.mrp_mask & MRP_PRIORITY)
239da14cebeSEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", PRIORITY,
240da14cebeSEric Cheng 		    attr->fi_resource_props.mrp_priority));
241da14cebeSEric Cheng 
242da14cebeSEric Cheng 	/* flow descriptor */
243da14cebeSEric Cheng 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_DSFIELD)
244da14cebeSEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%x:%x\t", DSFIELD,
245da14cebeSEric Cheng 		    attr->fi_flow_desc.fd_dsfield,
246da14cebeSEric Cheng 		    attr->fi_flow_desc.fd_dsfield_mask));
247da14cebeSEric Cheng 
248da14cebeSEric Cheng 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_LOCAL) {
249da14cebeSEric Cheng 		char abuf[INET6_ADDRSTRLEN], *ap;
250da14cebeSEric Cheng 		struct in_addr ipaddr;
251da14cebeSEric Cheng 		int prefix_len, prefix_max;
252da14cebeSEric Cheng 
253da14cebeSEric Cheng 		if (attr->fi_flow_desc.fd_ipversion != 6) {
254da14cebeSEric Cheng 			ipaddr.s_addr =
255da14cebeSEric Cheng 			    attr->fi_flow_desc.
256da14cebeSEric Cheng 			    fd_local_addr._S6_un._S6_u32[3];
257da14cebeSEric Cheng 
258da14cebeSEric Cheng 			ap = inet_ntoa(ipaddr);
259da14cebeSEric Cheng 			prefix_max = IP_ABITS;
260da14cebeSEric Cheng 		} else {
261da14cebeSEric Cheng 			(void) inet_ntop(AF_INET6,
262da14cebeSEric Cheng 			    &attr->fi_flow_desc.fd_local_addr,
263da14cebeSEric Cheng 			    abuf, INET6_ADDRSTRLEN);
264da14cebeSEric Cheng 
265da14cebeSEric Cheng 			ap = abuf;
266da14cebeSEric Cheng 			prefix_max = IPV6_ABITS;
267da14cebeSEric Cheng 		}
268da14cebeSEric Cheng 		(void) dladm_mask2prefixlen(
269da14cebeSEric Cheng 		    &attr->fi_flow_desc.fd_local_netmask, prefix_max,
270da14cebeSEric Cheng 		    &prefix_len);
271da14cebeSEric Cheng 
272da14cebeSEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", LOCAL_IP_ADDR,
273da14cebeSEric Cheng 		    ap, prefix_len));
274da14cebeSEric Cheng 	}
275da14cebeSEric Cheng 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_REMOTE) {
276da14cebeSEric Cheng 		char abuf[INET6_ADDRSTRLEN], *ap;
277da14cebeSEric Cheng 		struct in_addr ipaddr;
278da14cebeSEric Cheng 		int prefix_len, prefix_max;
279da14cebeSEric Cheng 
280da14cebeSEric Cheng 		if (attr->fi_flow_desc.fd_ipversion != 6) {
281da14cebeSEric Cheng 			ipaddr.s_addr =
282da14cebeSEric Cheng 			    attr->fi_flow_desc.
283da14cebeSEric Cheng 			    fd_remote_addr._S6_un._S6_u32[3];
284da14cebeSEric Cheng 
285da14cebeSEric Cheng 			ap = inet_ntoa(ipaddr);
286da14cebeSEric Cheng 			prefix_max = IP_ABITS;
287da14cebeSEric Cheng 		} else {
288da14cebeSEric Cheng 			(void) inet_ntop(AF_INET6,
289da14cebeSEric Cheng 			    &(attr->fi_flow_desc.fd_remote_addr),
290da14cebeSEric Cheng 			    abuf, INET6_ADDRSTRLEN);
291da14cebeSEric Cheng 
292da14cebeSEric Cheng 			ap = abuf;
293da14cebeSEric Cheng 			prefix_max = IPV6_ABITS;
294da14cebeSEric Cheng 		}
295da14cebeSEric Cheng 		(void) dladm_mask2prefixlen(
296da14cebeSEric Cheng 		    &attr->fi_flow_desc.fd_remote_netmask, prefix_max,
297da14cebeSEric Cheng 		    &prefix_len);
298da14cebeSEric Cheng 
299da14cebeSEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", REMOTE_IP_ADDR,
300da14cebeSEric Cheng 		    ap, prefix_len));
301da14cebeSEric Cheng 	}
302da14cebeSEric Cheng 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_PROTOCOL)
303da14cebeSEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", TRANSPORT,
304da14cebeSEric Cheng 		    attr->fi_flow_desc.fd_protocol));
305da14cebeSEric Cheng