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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <stdlib.h>
28 #include <strings.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <inttypes.h>
32 #include <assert.h>
33 #include <libilb.h>
34 #include <libilb_impl.h>
35 #include <locale.h>
36
37 typedef enum {
38 internal,
39 external
40 } ip_addr_type_t;
41
42 static int
sign64(int64_t n)43 sign64(int64_t n)
44 {
45 if (n >= 0)
46 return (1);
47 return (-1);
48 }
49
50 static int
sign32(int32_t n)51 sign32(int32_t n)
52 {
53 if (n >= 0)
54 return (1);
55 return (-1);
56 }
57
58 /*
59 * since the difference between two uint64_ts can be greater than
60 * what a int64_t can hold, we need to cap the result at +/- INT64_MAX
61 * return: < 0: x < y, 0: x == y, > 0: x > y
62 */
63 static int64_t
signed_diff64(uint64_t x,uint64_t y)64 signed_diff64(uint64_t x, uint64_t y)
65 {
66 uint64_t ud;
67 int s = -1;
68
69 if (x == y)
70 return (0);
71
72 /* make sure we have x < y */
73 if (x > y) {
74 uint64_t t;
75
76 s = 1;
77 t = x; x = y; y = t;
78 }
79
80 ud = y - x;
81 if (ud > INT64_MAX)
82 return (INT64_MAX * s);
83
84 return ((int64_t)ud * s);
85 }
86
87 static uint64_t
unsigned_diff64(uint64_t x,uint64_t y,int * sgn)88 unsigned_diff64(uint64_t x, uint64_t y, int *sgn)
89 {
90 int s = -1;
91
92 if (x == y)
93 return (0);
94
95 /* make sure we have x < y */
96 if (x > y) {
97 uint64_t t;
98
99 s = 1;
100 t = x; x = y; y = t;
101 }
102 *sgn = s;
103 return (y - x);
104 }
105
106 /*
107 * compare ip addresses ip1 and ip2 (as unsigned integers)
108 * return: -1: ip1 < ip2, 0: ip1 == ip2, 1: ip1 > ip2
109 * input addresses are assumed to be in network byte order
110 * diff contains the difference between the two with the same
111 * sign as the comparison result;
112 * NOTE: since ipv6 address (difference)s can be more than a 64bit
113 * value can express, the difference is capped at +/- INT64_MAX
114 */
115 static int
i_cmp_addr_impl(void * ip1,void * ip2,ip_addr_type_t atype,int64_t * diff)116 i_cmp_addr_impl(void *ip1, void *ip2, ip_addr_type_t atype, int64_t *diff)
117 {
118 struct in6_addr *a6_1, *a6_2;
119 uint32_t i1, i2;
120 uint32_t l1, l2;
121 int af, sgn;
122 int64_t d;
123
124 if (atype == internal) {
125 af = GET_AF((struct in6_addr *)ip1);
126 if (af == AF_INET) {
127 IN6_V4MAPPED_TO_IPADDR((struct in6_addr *)ip1, i1);
128 IN6_V4MAPPED_TO_IPADDR((struct in6_addr *)ip2, i2);
129
130 l1 = ntohl(i1);
131 l2 = ntohl(i2);
132 } else {
133 a6_1 = (struct in6_addr *)ip1;
134 a6_2 = (struct in6_addr *)ip2;
135 }
136 } else {
137 af = ((ilb_ip_addr_t *)ip1)->ia_af;
138 if (af == AF_INET) {
139 struct in_addr *a1, *a2;
140
141 a1 = &((ilb_ip_addr_t *)ip1)->ia_v4;
142 a2 = &((ilb_ip_addr_t *)ip2)->ia_v4;
143
144 l1 = ntohl((uint32_t)a1->s_addr);
145 l2 = ntohl((uint32_t)a2->s_addr);
146 } else {
147 a6_1 = &((ilb_ip_addr_t *)ip1)->ia_v6;
148 a6_2 = &((ilb_ip_addr_t *)ip2)->ia_v6;
149 }
150 }
151
152 if (af == AF_INET) {
153 d = l1 - l2;
154 sgn = sign32((int32_t)d);
155 } else {
156 /*
157 * we're facing the dilemma that 128-bit ipv6 addresses are
158 * larger than the largest integer type - int64_t.
159 * we handle this thus:
160 * 1. seperate high-order and low-order bits (64 each) into
161 * *h and *l variables (unsigned).
162 * 2. calculate difference for *h and *l:
163 * low: unsigned
164 * high: signed
165 * 3. if high-order diff == 0, we can take low-order
166 * diff, if necessary cap it, convert it to signed
167 * and be done
168 * 4. if high-order and low-order signs are the same, the low-
169 * order bits won't significantly impact high-order
170 * difference, so we know that we've overflowed an int64_t;
171 * if high-order diff is > 1, any low-order difference won't
172 * change the overflow.
173 * 5. (dh == 1 and l_sign <= 0) or (dh == -1 and l_sign > 0),
174 * ie, dh == +/- 2^64
175 * 5a. if dl < INT64_MAX, the result is still > INT64_MAX, so
176 * we cap again.
177 * 5b. dl >= INT64_MAX
178 * we need to express (for dh == 1):
179 * (2^64) + x (where x < 0).
180 * Since the largest number we have is
181 * 2^64 - 1 == UINT64_MAX
182 * we use
183 * (2^64 - 1) + x + 1
184 *
185 * for dh == -1, all we have is
186 * -(2^63 - 1), so to express
187 * -(2^64) + x,
188 * we first do (dl - (2^63-1)) (which is then also < 2^63),
189 * si we can then add that to -(2^63 - 1);
190 */
191 uint64_t i1h, i1l;
192 uint64_t i2h, i2l;
193 uint64_t dl;
194 int64_t dh;
195 int l_sign;
196
197 /* 1. */
198 i1h = INV6_N2H_MSB64(a6_1);
199 i1l = INV6_N2H_LSB64(a6_1);
200 i2h = INV6_N2H_MSB64(a6_2);
201 i2l = INV6_N2H_LSB64(a6_2);
202
203 /* 2. */
204 dh = signed_diff64(i1h, i2h);
205 dl = unsigned_diff64(i1l, i2l, &l_sign);
206
207 /* 3. */
208 if (dh == 0) {
209 if (dl > INT64_MAX)
210 dl = INT64_MAX;
211
212 d = dl * l_sign;
213 /* 4, */
214 } else if (l_sign == sign64(dh) || abs(dh) > 1) {
215 if (dh > 0)
216 d = INT64_MAX;
217 else
218 d = -INT64_MAX;
219 /* 5. */
220 } else {
221 if (dl < INT64_MAX) {
222 d = INT64_MAX;
223 } else {
224 if (dh == 1)
225 d = UINT64_MAX - dl + 1;
226 else
227 d = -INT64_MAX - (dl - INT64_MAX) - 1;
228 }
229 }
230 sgn = sign64(d);
231 }
232 if (diff != NULL)
233 *diff = d;
234 if (d == 0)
235 return (0);
236 return (sgn);
237 }
238
239 int
ilb_cmp_in6_addr(struct in6_addr * ip1,struct in6_addr * ip2,int64_t * diff)240 ilb_cmp_in6_addr(struct in6_addr *ip1, struct in6_addr *ip2, int64_t *diff)
241 {
242 int res;
243
244 res = i_cmp_addr_impl(ip1, ip2, internal, diff);
245 return (res);
246 }
247
248 int
ilb_cmp_ipaddr(ilb_ip_addr_t * ip1,ilb_ip_addr_t * ip2,int64_t * diff)249 ilb_cmp_ipaddr(ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2, int64_t *diff)
250 {
251 int res;
252
253 res = i_cmp_addr_impl(ip1, ip2, external, diff);
254 return (res);
255 }
256
257 /*
258 * Error strings for error values returned by libilb functions
259 */
260 const char *
ilb_errstr(ilb_status_t rc)261 ilb_errstr(ilb_status_t rc)
262 {
263 switch (rc) {
264 case ILB_STATUS_OK:
265 return (dgettext(TEXT_DOMAIN, "no error"));
266 case ILB_STATUS_INTERNAL:
267 return (dgettext(TEXT_DOMAIN, "error internal to the library"));
268 case ILB_STATUS_EINVAL:
269 return (dgettext(TEXT_DOMAIN, "invalid argument(s) - see"
270 " man page"));
271 case ILB_STATUS_ENOMEM:
272 return (dgettext(TEXT_DOMAIN, "not enough memory"
273 " for operation"));
274 case ILB_STATUS_ENOENT:
275 return (dgettext(TEXT_DOMAIN, "no such/no more element(s)"));
276 case ILB_STATUS_SOCKET:
277 return (dgettext(TEXT_DOMAIN, "socket() failed"));
278 case ILB_STATUS_READ:
279 return (dgettext(TEXT_DOMAIN, "read() failed"));
280 case ILB_STATUS_WRITE:
281 return (dgettext(TEXT_DOMAIN, "fflush() or send() failed"));
282 case ILB_STATUS_TIMER:
283 return (dgettext(TEXT_DOMAIN, "health check timer"
284 " create/setup error"));
285 case ILB_STATUS_INUSE:
286 return (dgettext(TEXT_DOMAIN, "object is in use,"
287 " cannot destroy"));
288 case ILB_STATUS_EEXIST:
289 return (dgettext(TEXT_DOMAIN, "object already exists"));
290 case ILB_STATUS_PERMIT:
291 return (dgettext(TEXT_DOMAIN, "no scf permit"));
292 case ILB_STATUS_CALLBACK:
293 return (dgettext(TEXT_DOMAIN, "scf callback error"));
294 case ILB_STATUS_INPROGRESS:
295 return (dgettext(TEXT_DOMAIN, "operation is progress"));
296 case ILB_STATUS_SEND:
297 return (dgettext(TEXT_DOMAIN, "send() failed"));
298 case ILB_STATUS_ENOHCINFO:
299 return (dgettext(TEXT_DOMAIN, "missing healthcheck info"));
300 case ILB_STATUS_INVAL_HCTESTTYPE:
301 return (dgettext(TEXT_DOMAIN, "invalid health check"
302 " test type"));
303 case ILB_STATUS_INVAL_CMD:
304 return (dgettext(TEXT_DOMAIN, "invalid command"));
305 case ILB_STATUS_DUP_RULE:
306 return (dgettext(TEXT_DOMAIN, "specified rule name already"
307 " exists"));
308 case ILB_STATUS_ENORULE:
309 return (dgettext(TEXT_DOMAIN, "specified rule does not exist"));
310 case ILB_STATUS_MISMATCHSG:
311 return (dgettext(TEXT_DOMAIN, "address family mismatch with"
312 " servergroup"));
313 case ILB_STATUS_MISMATCHH:
314 return (dgettext(TEXT_DOMAIN, "address family mismatch"
315 " with previous hosts in servergroup or with rule"));
316 case ILB_STATUS_SGUNAVAIL:
317 return (dgettext(TEXT_DOMAIN, "cannot find specified"
318 " server group"));
319 case ILB_STATUS_SGINUSE:
320 return (dgettext(TEXT_DOMAIN, "cannot remove server"
321 " group - its in use with other active rules"));
322 case ILB_STATUS_SGEXISTS:
323 return (dgettext(TEXT_DOMAIN, "servergroup already exists"));
324 case ILB_STATUS_SGFULL:
325 return (dgettext(TEXT_DOMAIN, "servergroup is full - cannot"
326 " add any more servers to this servergroup"));
327 case ILB_STATUS_SGEMPTY:
328 return (dgettext(TEXT_DOMAIN, "servergroup does not contain"
329 " any servers"));
330 case ILB_STATUS_NAMETOOLONG:
331 return (dgettext(TEXT_DOMAIN, "servergroup name can"
332 " only contain a maximum of 14 characters"));
333 case ILB_STATUS_CFGAUTH:
334 return (dgettext(TEXT_DOMAIN, "user is not authorized to"
335 " execute command"));
336 case ILB_STATUS_CFGUPDATE:
337 return (dgettext(TEXT_DOMAIN, "a failure occurred while trying"
338 " to update persistent config. Panic?"));
339 case ILB_STATUS_BADSG:
340 return (dgettext(TEXT_DOMAIN, "the rule's port range"
341 " does not match that of the servers' in associated"
342 " servergroup"));
343 case ILB_STATUS_INVAL_SRVR:
344 return (dgettext(TEXT_DOMAIN, "server cannot be added to the"
345 " servergroup, as the servergroup is associated to rule(s)"
346 " with port/port range that is incompatible"
347 "with the server's port"));
348 case ILB_STATUS_INVAL_ENBSRVR:
349 return (dgettext(TEXT_DOMAIN, "server cannot be enabled"
350 " because it's not associated with any rule"));
351 case ILB_STATUS_BADPORT:
352 return (dgettext(TEXT_DOMAIN, "the rule's port value does"
353 " not match that of the servers' in"
354 " associated servergroup"));
355 case ILB_STATUS_SRVUNAVAIL:
356 return (dgettext(TEXT_DOMAIN, "cannot find specified server"));
357 case ILB_STATUS_RULE_NO_HC:
358 return (dgettext(TEXT_DOMAIN, "rule does not have health "
359 "check enabled"));
360 case ILB_STATUS_RULE_HC_MISMATCH:
361 return (dgettext(TEXT_DOMAIN, "protocol used in rule and "
362 "health check does not match"));
363 case ILB_STATUS_HANDLE_CLOSING:
364 return (dgettext(TEXT_DOMAIN, "handle is being closed"));
365
366 default:
367 return (dgettext(TEXT_DOMAIN, "unknown error"));
368 }
369 }
370
371 /* Allocate space for a specified request to be sent to ilbd. */
372 ilb_comm_t *
i_ilb_alloc_req(ilbd_cmd_t cmd,size_t * ic_sz)373 i_ilb_alloc_req(ilbd_cmd_t cmd, size_t *ic_sz)
374 {
375 ilb_comm_t *ic;
376 size_t sz;
377
378 sz = sizeof (ilb_comm_t);
379
380 switch (cmd) {
381 case ILBD_CREATE_RULE:
382 sz += sizeof (ilb_rule_info_t);
383 break;
384
385 case ILBD_RETRIEVE_RULE:
386 case ILBD_DESTROY_RULE:
387 case ILBD_ENABLE_RULE:
388 case ILBD_DISABLE_RULE:
389 case ILBD_RETRIEVE_SG_HOSTS:
390 case ILBD_DESTROY_SERVERGROUP:
391 case ILBD_CREATE_SERVERGROUP:
392 case ILBD_DESTROY_HC:
393 case ILBD_GET_HC_INFO:
394 case ILBD_GET_HC_SRVS:
395 sz += sizeof (ilbd_name_t);
396 break;
397
398 case ILBD_ENABLE_SERVER:
399 case ILBD_DISABLE_SERVER:
400 case ILBD_ADD_SERVER_TO_GROUP:
401 case ILBD_REM_SERVER_FROM_GROUP:
402 case ILBD_SRV_ADDR2ID:
403 case ILBD_SRV_ID2ADDR:
404 sz += sizeof (ilb_sg_info_t) + sizeof (ilb_sg_srv_t);
405 break;
406
407 case ILBD_CREATE_HC:
408 sz += sizeof (ilb_hc_info_t);
409 break;
410
411 default:
412 /* Should not reach here. */
413 assert(0);
414 break;
415 }
416
417 if ((ic = calloc(1, sz)) == NULL)
418 return (NULL);
419
420 *ic_sz = sz;
421 ic->ic_cmd = cmd;
422 ic->ic_flags = 0;
423 return (ic);
424 }
425