1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (c) 2012 Joyent, Inc. All rights reserved.
14 * Use is subject to license terms.
15 */
16
17 #include <sys/types.h>
18 #include <stdio.h>
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <strings.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <stdarg.h>
26
27 #include <libipd.h>
28 #include <sys/ipd.h>
29
30 __thread ipd_errno_t ipd_errno = 0;
31 __thread char ipd_errmsg[512];
32
33 struct ipd_stat {
34 uint_t is_nzones;
35 zoneid_t *is_zoneids;
36 struct ipd_config *is_configs;
37 };
38
39 static ipd_errno_t
xlate_errno(int e)40 xlate_errno(int e)
41 {
42 switch (e) {
43 case 0:
44 return (EIPD_NOERROR);
45 case ENOMEM:
46 case EAGAIN:
47 return (EIPD_NOMEM);
48 case ERANGE:
49 return (EIPD_RANGE);
50 case EPERM:
51 return (EIPD_PERM);
52 case EFAULT:
53 return (EIPD_FAULT);
54 case ENOTTY:
55 return (EIPD_INTERNAL);
56 default:
57 return (EIPD_UNKNOWN);
58 }
59 }
60
61 const char *
ipd_strerror(ipd_errno_t e)62 ipd_strerror(ipd_errno_t e)
63 {
64 switch (e) {
65 case EIPD_NOERROR:
66 return ("no error");
67 case EIPD_NOMEM:
68 return ("out of memory");
69 case EIPD_ZC_NOENT:
70 return ("zone does not exist or is not using ipd");
71 case EIPD_RANGE:
72 return ("argument out of range");
73 case EIPD_PERM:
74 return ("permission denied");
75 case EIPD_FAULT:
76 return ("bad pointer");
77 case EIPD_INTERNAL:
78 return ("internal library error");
79 case EIPD_UNKNOWN:
80 default:
81 return ("unknown error");
82 }
83 }
84
85 static int
ipd_set_errno(ipd_errno_t e,const char * fmt,...)86 ipd_set_errno(ipd_errno_t e, const char *fmt, ...)
87 {
88 va_list ap;
89
90 ipd_errno = e;
91 if (fmt != NULL) {
92 va_start(ap, fmt);
93 (void) vsnprintf(ipd_errmsg, sizeof (ipd_errmsg), fmt, ap);
94 va_end(ap);
95 } else {
96 (void) strlcpy(ipd_errmsg,
97 ipd_strerror(e), sizeof (ipd_errmsg));
98 }
99
100 return (-1);
101 }
102
103 int
ipd_open(const char * path)104 ipd_open(const char *path)
105 {
106 int fd;
107
108 if (path == NULL)
109 path = IPD_DEV_PATH;
110
111 fd = open(path, O_RDWR);
112 if (fd < 0) {
113 return (ipd_set_errno(xlate_errno(errno),
114 "unable to open %s: %s", path, strerror(errno)));
115 }
116
117 return (fd);
118 }
119
120 int
ipd_close(int fd)121 ipd_close(int fd)
122 {
123 (void) close(fd);
124 return (0);
125 }
126
127 int
ipd_status_read(int fd,ipd_stathdl_t * ispp)128 ipd_status_read(int fd, ipd_stathdl_t *ispp)
129 {
130 struct ipd_stat *isp = NULL;
131 ipd_ioc_list_t ipil;
132 uint_t rzones;
133 uint_t i;
134
135 bzero(&ipil, sizeof (ipil));
136 if (ioctl(fd, IPDIOC_LIST, &ipil) != 0) {
137 return (ipd_set_errno(xlate_errno(errno),
138 "unable to retrieve ipd zone list: %s", strerror(errno)));
139 }
140
141 for (;;) {
142 if ((rzones = ipil.ipil_nzones) == 0)
143 break;
144
145 ipil.ipil_info =
146 malloc(sizeof (ipd_ioc_info_t) * ipil.ipil_nzones);
147 if (ipil.ipil_info == NULL)
148 return (ipd_set_errno(EIPD_NOMEM, NULL));
149
150 if (ioctl(fd, IPDIOC_LIST, &ipil) != 0) {
151 free(ipil.ipil_info);
152 return (ipd_set_errno(xlate_errno(errno),
153 "unable to retrieve ipd zone list: %s",
154 strerror(errno)));
155 }
156
157 if (ipil.ipil_nzones <= rzones)
158 break;
159
160 free(ipil.ipil_info);
161 }
162
163 if ((isp = malloc(sizeof (struct ipd_stat))) == NULL) {
164 free(ipil.ipil_info);
165 return (ipd_set_errno(EIPD_NOMEM, NULL));
166 }
167
168 isp->is_nzones = ipil.ipil_nzones;
169
170 if (isp->is_nzones == 0) {
171 isp->is_zoneids = NULL;
172 isp->is_configs = NULL;
173 *ispp = isp;
174 return (0);
175 }
176
177 isp->is_zoneids = malloc(sizeof (zoneid_t) * ipil.ipil_nzones);
178 if (isp->is_zoneids == NULL) {
179 free(ipil.ipil_info);
180 free(isp);
181 return (ipd_set_errno(EIPD_NOMEM, NULL));
182 }
183 isp->is_configs = malloc(sizeof (struct ipd_config) * ipil.ipil_nzones);
184 if (isp->is_configs == NULL) {
185 free(ipil.ipil_info);
186 free(isp->is_zoneids);
187 free(isp);
188 return (ipd_set_errno(EIPD_NOMEM, NULL));
189 }
190
191 for (i = 0; i < isp->is_nzones; i++) {
192 isp->is_zoneids[i] = ipil.ipil_info[i].ipii_zoneid;
193
194 isp->is_configs[i].ic_corrupt = ipil.ipil_info[i].ipii_corrupt;
195 isp->is_configs[i].ic_drop = ipil.ipil_info[i].ipii_drop;
196 isp->is_configs[i].ic_delay = ipil.ipil_info[i].ipii_delay;
197
198 isp->is_configs[i].ic_mask =
199 ((!!isp->is_configs[i].ic_corrupt) * IPDM_CORRUPT) |
200 ((!!isp->is_configs[i].ic_drop) * IPDM_DROP) |
201 ((!!isp->is_configs[i].ic_delay) * IPDM_DELAY);
202 }
203
204 *ispp = isp;
205 return (0);
206 }
207
208 void
ipd_status_foreach_zone(const ipd_stathdl_t hdl,ipd_status_cb_f f,void * arg)209 ipd_status_foreach_zone(const ipd_stathdl_t hdl, ipd_status_cb_f f, void *arg)
210 {
211 const struct ipd_stat *isp = hdl;
212 uint_t i;
213
214 for (i = 0; i < isp->is_nzones; i++)
215 f(isp->is_zoneids[i], &isp->is_configs[i], arg);
216 }
217
218 int
ipd_status_get_config(const ipd_stathdl_t hdl,zoneid_t z,ipd_config_t ** icpp)219 ipd_status_get_config(const ipd_stathdl_t hdl, zoneid_t z, ipd_config_t **icpp)
220 {
221 const struct ipd_stat *isp = hdl;
222 uint_t i;
223
224 for (i = 0; i < isp->is_nzones; i++) {
225 if (isp->is_zoneids[i] == z) {
226 *icpp = &isp->is_configs[i];
227 return (0);
228 }
229 }
230
231 return (ipd_set_errno(EIPD_ZC_NOENT,
232 "zone %d does not exist or has no ipd configuration", z));
233 }
234
235 void
ipd_status_free(ipd_stathdl_t hdl)236 ipd_status_free(ipd_stathdl_t hdl)
237 {
238 struct ipd_stat *isp = hdl;
239
240 if (isp != NULL) {
241 free(isp->is_zoneids);
242 free(isp->is_configs);
243 }
244 free(isp);
245 }
246
247 int
ipd_ctl(int fd,zoneid_t z,const ipd_config_t * icp)248 ipd_ctl(int fd, zoneid_t z, const ipd_config_t *icp)
249 {
250 ipd_ioc_perturb_t ipip;
251
252 bzero(&ipip, sizeof (ipd_ioc_perturb_t));
253 ipip.ipip_zoneid = z;
254
255 if (icp->ic_mask & IPDM_CORRUPT) {
256 if (icp->ic_corrupt == 0)
257 ipip.ipip_arg |= IPD_CORRUPT;
258 }
259 if (icp->ic_mask & IPDM_DELAY) {
260 if (icp->ic_delay == 0)
261 ipip.ipip_arg |= IPD_DELAY;
262 }
263 if (icp->ic_mask & IPDM_DROP) {
264 if (icp->ic_drop == 0)
265 ipip.ipip_arg |= IPD_DROP;
266 }
267
268 if (ipip.ipip_arg != 0 && ioctl(fd, IPDIOC_REMOVE, &ipip) != 0) {
269 return (ipd_set_errno(xlate_errno(errno),
270 "unable to remove cleared ipd settings: %s",
271 strerror(errno)));
272 }
273
274 if ((icp->ic_mask & IPDM_CORRUPT) && icp->ic_corrupt != 0) {
275 ipip.ipip_zoneid = z;
276 ipip.ipip_arg = icp->ic_corrupt;
277 if (ioctl(fd, IPDIOC_CORRUPT, &ipip) != 0) {
278 return (ipd_set_errno(xlate_errno(errno),
279 "unable to set corruption rate to %d: %s",
280 ipip.ipip_arg, strerror(errno)));
281 }
282 }
283 if ((icp->ic_mask & IPDM_DELAY) && icp->ic_delay != 0) {
284 ipip.ipip_zoneid = z;
285 ipip.ipip_arg = icp->ic_delay;
286 if (ioctl(fd, IPDIOC_DELAY, &ipip) != 0) {
287 return (ipd_set_errno(xlate_errno(errno),
288 "unable to set delay time to %d: %s",
289 ipip.ipip_arg, strerror(errno)));
290 }
291 }
292 if ((icp->ic_mask & IPDM_DROP) && icp->ic_drop != 0) {
293 ipip.ipip_zoneid = z;
294 ipip.ipip_arg = icp->ic_drop;
295 if (ioctl(fd, IPDIOC_DROP, &ipip) != 0) {
296 return (ipd_set_errno(xlate_errno(errno),
297 "unable to set drop probability to %d: %s",
298 ipip.ipip_arg, strerror(errno)));
299 }
300 }
301
302 return (0);
303 }
304