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/*
27 * This file contains a routine used to validate a ifconfig-style interface
28 * specification
29 */
30
31#include <stdlib.h>
32#include <ctype.h>
33#include <alloca.h>
34#include <errno.h>
35#include <string.h>
36#include <libinetutil.h>
37
38/*
39 * Given a token with a logical unit spec, return the logical unit converted
40 * to a uint_t.
41 *
42 * Returns: 0 for success, nonzero if an error occurred. errno is set if
43 * necessary.
44 */
45static int
46getlun(const char *bp, int bpsize, uint_t *lun)
47{
48	char	*ep = (char *)&bp[bpsize - 1];
49	char	*sp = strchr(bp, ':'), *tp;
50
51	/* A logical unit spec looks like: <token>:<unsigned int>\0 */
52	if (isdigit(*bp) || !isdigit(*ep) || sp == NULL ||
53	    strchr(sp + 1, ':') != NULL) {
54		errno = EINVAL;
55		return (-1);
56	}
57
58	*sp++ = '\0';
59
60	/* Lun must be all digits */
61	for (tp = sp; tp < ep && isdigit(*tp); tp++)
62		/* Null body */;
63	if (tp != ep) {
64		errno = EINVAL;
65		return (-1);
66	}
67
68	*lun = atoi(sp);
69	return (0);
70}
71
72/*
73 * Given a single token ending with a ppa spec, return the ppa spec converted
74 * to a uint_t.
75 *
76 * Returns: 0 for success, nonzero if an error occurred. errno is set if
77 * necessary.
78 */
79static int
80getppa(const char *bp, int bpsize, uint_t *ppa)
81{
82	char	*ep = (char *)&bp[bpsize - 1];
83	char	*tp;
84
85	if (!isdigit(*ep)) {
86		errno = EINVAL;
87		return (-1);
88	}
89
90	for (tp = ep; tp >= bp && isdigit(*tp); tp--)
91		/* Null body */;
92
93	if (*tp == ':') {
94		errno = EINVAL;
95		return (-1);
96	}
97
98	*ppa = atoi(tp + 1);
99	return (0);
100}
101
102/*
103 * Given an ifconfig-style inet relative-path interface specification
104 * (e.g: bge0:2), validate its form and decompose the contents into a
105 * dynamically allocated ifspec_t.
106 *
107 * Returns ifspec_t for success, NULL pointer if spec is malformed.
108 */
109boolean_t
110ifparse_ifspec(const char *ifname, ifspec_t *ifsp)
111{
112	char	*lp, *tp;
113	char	ifnamecp[LIFNAMSIZ];
114
115	/* snag a copy we can modify */
116	if (strlcpy(ifnamecp, ifname, LIFNAMSIZ) >= LIFNAMSIZ) {
117		errno = EINVAL;
118		return (B_FALSE);
119	}
120
121	ifsp->ifsp_lunvalid = B_FALSE;
122
123	/*
124	 * An interface name must have the format of:
125	 * dev[ppa][:lun]
126	 *
127	 * lun - logical unit number.
128	 */
129
130	/* Any logical units? */
131	lp = strchr(ifnamecp, ':');
132	if (lp != NULL) {
133		if (getlun(lp, strlen(lp), &ifsp->ifsp_lun) != 0)
134			return (B_FALSE);
135		ifsp->ifsp_lunvalid = B_TRUE;
136	}
137
138	(void) strlcpy(ifsp->ifsp_devnm, ifnamecp, LIFNAMSIZ);
139
140	/* Find ppa  */
141	if (getppa(ifsp->ifsp_devnm, strlen(ifsp->ifsp_devnm),
142	    &ifsp->ifsp_ppa) != 0) {
143		return (B_FALSE);
144	}
145
146	/* strip the ppa off of the device name if present */
147	for (tp = &ifsp->ifsp_devnm[strlen(ifsp->ifsp_devnm) - 1];
148	    tp >= ifsp->ifsp_devnm && isdigit(*tp); tp--) {
149		*tp = '\0';
150	}
151
152	return (B_TRUE);
153}
154