xref: /illumos-gate/usr/src/lib/libinetutil/common/ifspec.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * This file contains a routine used to validate a ifconfig-style interface
30  * specification
31  */
32 
33 #include <stdlib.h>
34 #include <ctype.h>
35 #include <alloca.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <libinetutil.h>
39 
40 /*
41  * Given a token with a logical unit spec, return the logical unit converted
42  * to a uint_t.
43  *
44  * Returns: 0 for success, nonzero if an error occurred. errno is set if
45  * necessary.
46  */
47 static int
48 getlun(const char *bp, int bpsize, uint_t *lun)
49 {
50 	char	*ep = (char *)&bp[bpsize - 1];
51 	char	*sp = strchr(bp, ':'), *tp;
52 
53 	/* A logical unit spec looks like: <token>:<unsigned int>\0 */
54 	if (isdigit(*bp) || !isdigit(*ep) || sp == NULL ||
55 	    strchr(sp + 1, ':') != NULL) {
56 		errno = EINVAL;
57 		return (-1);
58 	}
59 
60 	*sp++ = '\0';
61 
62 	/* Lun must be all digits */
63 	for (tp = sp; tp < ep && isdigit(*tp); tp++)
64 		/* Null body */;
65 	if (tp != ep) {
66 		errno = EINVAL;
67 		return (-1);
68 	}
69 
70 	*lun = atoi(sp);
71 	return (0);
72 }
73 
74 /*
75  * Given a single token ending with a ppa spec, return the ppa spec converted
76  * to a uint_t.
77  *
78  * Returns: 0 for success, nonzero if an error occurred. errno is set if
79  * necessary.
80  */
81 static int
82 getppa(const char *bp, int bpsize, uint_t *ppa)
83 {
84 	char	*ep = (char *)&bp[bpsize - 1];
85 	char	*tp;
86 
87 	if (!isdigit(*ep)) {
88 		errno = EINVAL;
89 		return (-1);
90 	}
91 
92 	for (tp = ep; tp >= bp && isdigit(*tp); tp--)
93 		/* Null body */;
94 
95 	if (*tp == '.' || *tp == ':') {
96 		errno = EINVAL;
97 		return (-1);
98 	}
99 
100 	*ppa = atoi(tp + 1);
101 	return (0);
102 }
103 
104 /*
105  * Given an ifconfig-style inet relative-path interface specification
106  * (e.g: hme.[module].[module][PPA]:2), validate its form and decompose the
107  * contents into a dynamically allocated ifspec_t.
108  *
109  * Returns ifspec_t for success, NULL pointer if spec is malformed.
110  */
111 boolean_t
112 ifparse_ifspec(const char *ifname, ifspec_t *ifsp)
113 {
114 	char		*mp, *ep, *lp, *tp;
115 	char		*ifnamecp;
116 	size_t		iflen;
117 	boolean_t	have_ppa = B_FALSE;
118 
119 	iflen = strlen(ifname);
120 	if (iflen > LIFNAMSIZ) {
121 		errno = EINVAL;
122 		return (B_FALSE);
123 	}
124 
125 	/* snag a copy we can modify */
126 	ifnamecp = alloca(iflen + 1);
127 	(void) strlcpy(ifnamecp, ifname, iflen + 1);
128 
129 	ifsp->ifsp_lunvalid = B_FALSE;
130 
131 	/*
132 	 * An interface name must have the format of:
133 	 * dev[.module[.module...]][ppa][:lun]
134 	 *
135 	 * where the ppa must be specified at the end of the interface name.
136 	 * e.g. ip.foo.tun0
137 	 *
138 	 * lun - logical unit number.
139 	 *
140 	 * Produce substrings for each grouping, starting first with modules,
141 	 * then lun, devname, and finally ppa.
142 	 */
143 
144 	/* Any modules? */
145 	mp = strchr(ifnamecp, '.');
146 
147 	/* Any logical units? */
148 	lp = strchr(ifnamecp, ':');
149 
150 	if (lp != NULL && mp != NULL && lp < mp) {
151 		errno = EINVAL;
152 		return (B_FALSE);
153 	}
154 
155 	ifsp->ifsp_modcnt = 0;
156 	if (mp != NULL) {
157 		*mp++ = '\0';
158 		if (lp != NULL)
159 			*lp = '\0';
160 		while (mp != NULL && ifsp->ifsp_modcnt <= IFSP_MAXMODS) {
161 			if ((ep = strchr(mp, '.')) != NULL)
162 				*ep++ = '\0';
163 			(void) strlcpy(ifsp->ifsp_mods[ifsp->ifsp_modcnt++],
164 			    mp, LIFNAMSIZ);
165 			mp = ep;
166 		}
167 		if (lp != NULL)
168 			*lp = ':';
169 		if (ifsp->ifsp_modcnt > IFSP_MAXMODS) {
170 			errno = E2BIG;
171 			return (B_FALSE);
172 		}
173 	}
174 
175 	if (lp != NULL) {
176 		if (getlun(lp, strlen(lp), &ifsp->ifsp_lun) != 0)
177 			return (B_FALSE);
178 		ifsp->ifsp_lunvalid = B_TRUE;
179 	}
180 
181 	(void) strlcpy(ifsp->ifsp_devnm, ifnamecp, LIFNAMSIZ);
182 
183 	/*
184 	 * Find ppa - has to be part of devname or if modules exist part of
185 	 * last module name.
186 	 */
187 	if (ifsp->ifsp_modcnt != 0 &&
188 	    getppa(ifsp->ifsp_mods[ifsp->ifsp_modcnt - 1],
189 	    strlen(ifsp->ifsp_mods[ifsp->ifsp_modcnt - 1]),
190 	    &ifsp->ifsp_ppa) == 0) {
191 		have_ppa = B_TRUE;
192 	} else if (ifsp->ifsp_modcnt == 0 &&
193 	    getppa(ifsp->ifsp_devnm, strlen(ifsp->ifsp_devnm),
194 	    &ifsp->ifsp_ppa) == 0) {
195 		have_ppa = B_TRUE;
196 
197 		/* strip the ppa off of the device name if present */
198 		for (tp = &ifsp->ifsp_devnm[strlen(ifsp->ifsp_devnm) - 1];
199 		    tp >= ifsp->ifsp_devnm && isdigit(*tp); tp--)
200 			*tp = '\0';
201 	}
202 
203 	return (have_ppa);
204 }
205