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 43 sign64(int64_t n) 44 { 45 if (n >= 0) 46 return (1); 47 return (-1); 48 } 49 50 static int 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 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 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 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 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 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 * 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 * 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