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 <sys/types.h> 28 #include <sys/stat.h> 29 #include <sys/socket.h> 30 #include <sys/mman.h> 31 #include <sys/varargs.h> 32 #include <sys/vlan.h> 33 #include <errno.h> 34 #include <ctype.h> 35 #include <fcntl.h> 36 #include <unistd.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <netinet/in.h> 41 #include <arpa/inet.h> 42 #include <net/if.h> /* LIFNAMSIZ */ 43 #include <netinet/vrrp.h> 44 #include <libdladm.h> 45 #include <libdlvnic.h> 46 #include <libdlvlan.h> 47 #include <libdllink.h> 48 #include <libintl.h> 49 #include <libvrrpadm.h> 50 51 typedef vrrp_err_t vrrp_cmd_func_t(int, void *); 52 53 static vrrp_err_t 54 vrrp_cmd_request(void *cmd, size_t csize, vrrp_cmd_func_t func, void *arg) 55 { 56 struct sockaddr_un to; 57 int sock, flags; 58 size_t len, cur_size = 0; 59 vrrp_ret_t ret; 60 vrrp_err_t err; 61 62 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) 63 return (VRRP_ECMD); 64 65 /* 66 * Set it to be non-blocking. 67 */ 68 flags = fcntl(sock, F_GETFL, 0); 69 (void) fcntl(sock, F_SETFL, (flags | O_NONBLOCK)); 70 71 (void) memset(&to, 0, sizeof (to)); 72 to.sun_family = AF_UNIX; 73 (void) strlcpy(to.sun_path, VRRPD_SOCKET, sizeof (to.sun_path)); 74 75 /* 76 * Connect to vrrpd 77 */ 78 if (connect(sock, (const struct sockaddr *)&to, sizeof (to)) < 0) { 79 (void) close(sock); 80 return (VRRP_ECMD); 81 } 82 83 /* 84 * Send the request 85 */ 86 while (cur_size < csize) { 87 len = write(sock, (char *)cmd + cur_size, csize - cur_size); 88 if (len == (size_t)-1 && errno == EAGAIN) { 89 continue; 90 } else if (len > 0) { 91 cur_size += len; 92 continue; 93 } 94 (void) close(sock); 95 return (VRRP_ECMD); 96 } 97 98 /* 99 * Expect the ack, first get the error code. 100 */ 101 cur_size = 0; 102 while (cur_size < sizeof (vrrp_err_t)) { 103 len = read(sock, (char *)&ret + cur_size, 104 sizeof (vrrp_err_t) - cur_size); 105 106 if (len == (size_t)-1 && errno == EAGAIN) { 107 continue; 108 } else if (len > 0) { 109 cur_size += len; 110 continue; 111 } 112 (void) close(sock); 113 return (VRRP_ECMD); 114 } 115 116 if ((err = ret.vr_err) != VRRP_SUCCESS) 117 goto done; 118 119 /* 120 * The specific callback gets the rest of the information. 121 */ 122 if (func != NULL) 123 err = func(sock, arg); 124 125 done: 126 (void) close(sock); 127 return (err); 128 } 129 130 /* 131 * public APIs 132 */ 133 const char * 134 vrrp_err2str(vrrp_err_t err) 135 { 136 switch (err) { 137 case VRRP_SUCCESS: 138 return (dgettext(TEXT_DOMAIN, "success")); 139 case VRRP_ENOMEM: 140 return (dgettext(TEXT_DOMAIN, "not enough memory")); 141 case VRRP_EINVALVRNAME: 142 return (dgettext(TEXT_DOMAIN, "invalid router name")); 143 case VRRP_ENOPRIM: 144 return (dgettext(TEXT_DOMAIN, "no primary IP")); 145 case VRRP_EEXIST: 146 return (dgettext(TEXT_DOMAIN, "already exists")); 147 case VRRP_ENOVIRT: 148 return (dgettext(TEXT_DOMAIN, "no virtual IPs")); 149 case VRRP_EIPADM: 150 return (dgettext(TEXT_DOMAIN, "ip configuration failure")); 151 case VRRP_EDLADM: 152 return (dgettext(TEXT_DOMAIN, "data-link configuration " 153 "failure")); 154 case VRRP_EDB: 155 return (dgettext(TEXT_DOMAIN, "configuration update error")); 156 case VRRP_EBADSTATE: 157 return (dgettext(TEXT_DOMAIN, "invalid state")); 158 case VRRP_EVREXIST: 159 return (dgettext(TEXT_DOMAIN, "VRRP router already exists")); 160 case VRRP_ETOOSMALL: 161 return (dgettext(TEXT_DOMAIN, "not enough space")); 162 case VRRP_EINSTEXIST: 163 return (dgettext(TEXT_DOMAIN, "router name already exists")); 164 case VRRP_ENOTFOUND: 165 return (dgettext(TEXT_DOMAIN, "VRRP router not found")); 166 case VRRP_ECMD: 167 return (dgettext(TEXT_DOMAIN, "failed to communicate to " 168 "vrrpd")); 169 case VRRP_EINVALADDR: 170 return (dgettext(TEXT_DOMAIN, "invalid IP address")); 171 case VRRP_EINVALAF: 172 return (dgettext(TEXT_DOMAIN, "invalid IP address family")); 173 case VRRP_EINVALLINK: 174 return (dgettext(TEXT_DOMAIN, "invalid data-link")); 175 case VRRP_EPERM: 176 return (dgettext(TEXT_DOMAIN, "permission denied")); 177 case VRRP_ESYS: 178 return (dgettext(TEXT_DOMAIN, "system error")); 179 case VRRP_EAGAIN: 180 return (dgettext(TEXT_DOMAIN, "try again")); 181 case VRRP_EALREADY: 182 return (dgettext(TEXT_DOMAIN, "operation already in progress")); 183 case VRRP_ENOVNIC: 184 return (dgettext(TEXT_DOMAIN, "VRRP VNIC has not been " 185 "created")); 186 case VRRP_ENOLINK: 187 return (dgettext(TEXT_DOMAIN, "the data-link does not exist")); 188 case VRRP_EINVAL: 189 default: 190 return (dgettext(TEXT_DOMAIN, "invalid argument")); 191 } 192 } 193 194 const char * 195 vrrp_state2str(vrrp_state_t state) 196 { 197 switch (state) { 198 case VRRP_STATE_NONE: 199 return (dgettext(TEXT_DOMAIN, "NONE")); 200 case VRRP_STATE_INIT: 201 return (dgettext(TEXT_DOMAIN, "INIT")); 202 case VRRP_STATE_MASTER: 203 return (dgettext(TEXT_DOMAIN, "MASTER")); 204 case VRRP_STATE_BACKUP: 205 return (dgettext(TEXT_DOMAIN, "BACKUP")); 206 default: 207 return (dgettext(TEXT_DOMAIN, "INVALID")); 208 } 209 } 210 211 vrrp_err_t 212 vrrp_open(vrrp_handle_t *vh) 213 { 214 dladm_handle_t dh; 215 216 if (dladm_open(&dh) != DLADM_STATUS_OK) 217 return (VRRP_EDLADM); 218 219 if ((*vh = malloc(sizeof (struct vrrp_handle))) == NULL) { 220 dladm_close(dh); 221 return (VRRP_ENOMEM); 222 } 223 (*vh)->vh_dh = dh; 224 return (VRRP_SUCCESS); 225 } 226 227 void 228 vrrp_close(vrrp_handle_t vh) 229 { 230 if (vh != NULL) { 231 dladm_close(vh->vh_dh); 232 free(vh); 233 } 234 } 235 236 boolean_t 237 vrrp_valid_name(const char *name) 238 { 239 const char *c; 240 241 /* 242 * The legal characters in a valid router name are: 243 * alphanumeric (a-z, A-Z, 0-9), underscore ('_'), and '.'. 244 */ 245 for (c = name; *c != '\0'; c++) { 246 if ((isalnum(*c) == 0) && (*c != '_')) 247 return (B_FALSE); 248 } 249 250 return (B_TRUE); 251 } 252 253 /*ARGSUSED*/ 254 vrrp_err_t 255 vrrp_create(vrrp_handle_t vh, vrrp_vr_conf_t *conf) 256 { 257 vrrp_cmd_create_t cmd; 258 vrrp_err_t err; 259 260 cmd.vcc_cmd = VRRP_CMD_CREATE; 261 (void) memcpy(&cmd.vcc_conf, conf, sizeof (vrrp_vr_conf_t)); 262 263 err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL); 264 return (err); 265 } 266 267 /*ARGSUSED*/ 268 vrrp_err_t 269 vrrp_delete(vrrp_handle_t vh, const char *vn) 270 { 271 vrrp_cmd_delete_t cmd; 272 vrrp_err_t err; 273 274 cmd.vcd_cmd = VRRP_CMD_DELETE; 275 if (strlcpy(cmd.vcd_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX) 276 return (VRRP_EINVAL); 277 278 err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL); 279 return (err); 280 } 281 282 /*ARGSUSED*/ 283 vrrp_err_t 284 vrrp_enable(vrrp_handle_t vh, const char *vn) 285 { 286 vrrp_cmd_enable_t cmd; 287 vrrp_err_t err; 288 289 cmd.vcs_cmd = VRRP_CMD_ENABLE; 290 if (strlcpy(cmd.vcs_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX) 291 return (VRRP_EINVAL); 292 293 err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL); 294 return (err); 295 } 296 297 /*ARGSUSED*/ 298 vrrp_err_t 299 vrrp_disable(vrrp_handle_t vh, const char *vn) 300 { 301 vrrp_cmd_disable_t cmd; 302 vrrp_err_t err; 303 304 cmd.vcx_cmd = VRRP_CMD_DISABLE; 305 if (strlcpy(cmd.vcx_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX) 306 return (VRRP_EINVAL); 307 308 err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL); 309 return (err); 310 } 311 312 /*ARGSUSED*/ 313 vrrp_err_t 314 vrrp_modify(vrrp_handle_t vh, vrrp_vr_conf_t *conf, uint32_t mask) 315 { 316 vrrp_cmd_modify_t cmd; 317 vrrp_err_t err; 318 319 cmd.vcm_cmd = VRRP_CMD_MODIFY; 320 cmd.vcm_mask = mask; 321 (void) memcpy(&cmd.vcm_conf, conf, sizeof (vrrp_vr_conf_t)); 322 323 err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL); 324 return (err); 325 } 326 327 typedef struct vrrp_cmd_list_arg { 328 uint32_t *vfl_cnt; 329 char *vfl_names; 330 } vrrp_cmd_list_arg_t; 331 332 static vrrp_err_t 333 vrrp_list_func(int sock, void *arg) 334 { 335 vrrp_cmd_list_arg_t *list_arg = arg; 336 uint32_t in_cnt = *(list_arg->vfl_cnt); 337 uint32_t out_cnt; 338 vrrp_ret_list_t ret; 339 size_t len, cur_size = 0; 340 341 /* 342 * Get the rest of vrrp_ret_list_t besides the error code. 343 */ 344 cur_size = sizeof (vrrp_err_t); 345 while (cur_size < sizeof (vrrp_ret_list_t)) { 346 len = read(sock, (char *)&ret + cur_size, 347 sizeof (vrrp_ret_list_t) - cur_size); 348 349 if (len == (size_t)-1 && errno == EAGAIN) { 350 continue; 351 } else if (len > 0) { 352 cur_size += len; 353 continue; 354 } 355 return (VRRP_ECMD); 356 } 357 358 *(list_arg->vfl_cnt) = out_cnt = ret.vrl_cnt; 359 out_cnt = (in_cnt <= out_cnt) ? in_cnt : out_cnt; 360 cur_size = 0; 361 362 while (cur_size < VRRP_NAME_MAX * out_cnt) { 363 len = read(sock, (char *)list_arg->vfl_names + cur_size, 364 VRRP_NAME_MAX * out_cnt - cur_size); 365 366 if (len == (size_t)-1 && errno == EAGAIN) { 367 continue; 368 } else if (len > 0) { 369 cur_size += len; 370 continue; 371 } 372 return (VRRP_ECMD); 373 } 374 return (VRRP_SUCCESS); 375 } 376 377 /* 378 * Looks up the vrrp instances that matches the given variable. 379 * 380 * If the given cnt is 0, names should be set to NULL. In this case, only 381 * the count of the matched instances is returned. 382 * 383 * If the given cnt is non-zero, caller must allocate "names" whose size 384 * is (cnt * VRRP_NAME_MAX). 385 * 386 * Return value: the current count of matched instances, and names will be 387 * points to the list of the current vrrp instances names. Note that 388 * only MIN(in_cnt, out_cnt) number of names will be returned. 389 */ 390 /*ARGSUSED*/ 391 vrrp_err_t 392 vrrp_list(vrrp_handle_t vh, vrid_t vrid, const char *intf, int af, 393 uint32_t *cnt, char *names) 394 { 395 vrrp_cmd_list_t cmd; 396 vrrp_err_t err; 397 vrrp_cmd_list_arg_t list_arg; 398 399 if ((cnt == NULL) || (*cnt != 0 && names == NULL)) 400 return (VRRP_EINVAL); 401 402 cmd.vcl_ifname[0] = '\0'; 403 if (intf != NULL && (strlcpy(cmd.vcl_ifname, intf, 404 LIFNAMSIZ) >= LIFNAMSIZ)) { 405 return (VRRP_EINVAL); 406 } 407 408 cmd.vcl_cmd = VRRP_CMD_LIST; 409 cmd.vcl_vrid = vrid; 410 cmd.vcl_af = af; 411 412 list_arg.vfl_cnt = cnt; 413 list_arg.vfl_names = names; 414 415 err = vrrp_cmd_request(&cmd, sizeof (cmd), vrrp_list_func, &list_arg); 416 return (err); 417 } 418 419 static vrrp_err_t 420 vrrp_query_func(int sock, void *arg) 421 { 422 vrrp_queryinfo_t *qinfo = arg; 423 size_t len, cur_size = 0, total; 424 uint32_t in_cnt = qinfo->show_va.va_vipcnt; 425 uint32_t out_cnt; 426 427 /* 428 * Expect the ack, first get the vrrp_ret_t. 429 */ 430 total = sizeof (vrrp_queryinfo_t); 431 while (cur_size < total) { 432 len = read(sock, (char *)qinfo + cur_size, total - cur_size); 433 if (len == (size_t)-1 && errno == EAGAIN) { 434 continue; 435 } else if (len > 0) { 436 cur_size += len; 437 continue; 438 } 439 return (VRRP_ECMD); 440 } 441 442 out_cnt = qinfo->show_va.va_vipcnt; 443 444 /* 445 * Even if there is no IP virtual IP address, there is always 446 * space in the vrrp_queryinfo_t structure for one virtual 447 * IP address. 448 */ 449 out_cnt = (out_cnt == 0) ? 1 : out_cnt; 450 out_cnt = (in_cnt < out_cnt ? in_cnt : out_cnt) - 1; 451 total += out_cnt * sizeof (vrrp_addr_t); 452 453 while (cur_size < total) { 454 len = read(sock, (char *)qinfo + cur_size, total - cur_size); 455 if (len == (size_t)-1 && errno == EAGAIN) { 456 continue; 457 } else if (len > 0) { 458 cur_size += len; 459 continue; 460 } 461 return (VRRP_ECMD); 462 } 463 return (VRRP_SUCCESS); 464 } 465 466 /* 467 * *vqp is allocated inside this function and must be freed by the caller. 468 */ 469 /*ARGSUSED*/ 470 vrrp_err_t 471 vrrp_query(vrrp_handle_t vh, const char *vn, vrrp_queryinfo_t **vqp) 472 { 473 vrrp_cmd_query_t cmd; 474 vrrp_queryinfo_t *qinfo; 475 vrrp_err_t err; 476 size_t size; 477 uint32_t vipcnt = 1; 478 479 if (strlcpy(cmd.vcq_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX) 480 return (VRRP_EINVAL); 481 482 cmd.vcq_cmd = VRRP_CMD_QUERY; 483 484 /* 485 * Allocate enough room for virtual IPs. 486 */ 487 again: 488 size = sizeof (vrrp_queryinfo_t); 489 size += (vipcnt == 0) ? 0 : (vipcnt - 1) * sizeof (vrrp_addr_t); 490 if ((qinfo = malloc(size)) == NULL) { 491 err = VRRP_ENOMEM; 492 goto done; 493 } 494 495 qinfo->show_va.va_vipcnt = vipcnt; 496 err = vrrp_cmd_request(&cmd, sizeof (cmd), vrrp_query_func, qinfo); 497 if (err != VRRP_SUCCESS) { 498 free(qinfo); 499 goto done; 500 } 501 502 /* 503 * If the returned number of virtual IPs is greater than we expected, 504 * allocate more room and try again. 505 */ 506 if (qinfo->show_va.va_vipcnt > vipcnt) { 507 vipcnt = qinfo->show_va.va_vipcnt; 508 free(qinfo); 509 goto again; 510 } 511 512 *vqp = qinfo; 513 514 done: 515 return (err); 516 } 517 518 struct lookup_vnic_arg { 519 vrid_t lva_vrid; 520 datalink_id_t lva_linkid; 521 int lva_af; 522 uint16_t lva_vid; 523 vrrp_handle_t lva_vh; 524 char lva_vnic[MAXLINKNAMELEN]; 525 }; 526 527 /* 528 * Is this a special VNIC interface created for VRRP? If so, return 529 * the linkid the VNIC was created on, the VRRP ID and address family. 530 */ 531 boolean_t 532 vrrp_is_vrrp_vnic(vrrp_handle_t vh, datalink_id_t vnicid, 533 datalink_id_t *linkidp, uint16_t *vidp, vrid_t *vridp, int *afp) 534 { 535 dladm_vnic_attr_t vattr; 536 537 if (dladm_vnic_info(vh->vh_dh, vnicid, &vattr, DLADM_OPT_ACTIVE) != 538 DLADM_STATUS_OK) { 539 return (B_FALSE); 540 } 541 542 *vridp = vattr.va_vrid; 543 *vidp = vattr.va_vid; 544 *afp = vattr.va_af; 545 *linkidp = vattr.va_link_id; 546 return (vattr.va_vrid != VRRP_VRID_NONE); 547 } 548 549 static int 550 lookup_vnic(dladm_handle_t dh, datalink_id_t vnicid, void *arg) 551 { 552 vrid_t vrid; 553 uint16_t vid; 554 datalink_id_t linkid; 555 int af; 556 struct lookup_vnic_arg *lva = arg; 557 558 if (vrrp_is_vrrp_vnic(lva->lva_vh, vnicid, &linkid, &vid, &vrid, 559 &af) && lva->lva_vrid == vrid && lva->lva_linkid == linkid && 560 lva->lva_vid == vid && lva->lva_af == af) { 561 if (dladm_datalink_id2info(dh, vnicid, NULL, NULL, NULL, 562 lva->lva_vnic, sizeof (lva->lva_vnic)) == DLADM_STATUS_OK) { 563 return (DLADM_WALK_TERMINATE); 564 } 565 } 566 return (DLADM_WALK_CONTINUE); 567 } 568 569 /* 570 * Given the primary link name, find the assoicated VRRP vnic name, if 571 * the vnic does not exist yet, return the linkid, vid of the primary link. 572 */ 573 vrrp_err_t 574 vrrp_get_vnicname(vrrp_handle_t vh, vrid_t vrid, int af, char *link, 575 datalink_id_t *linkidp, uint16_t *vidp, char *vnic, size_t len) 576 { 577 datalink_id_t linkid; 578 uint32_t flags; 579 uint16_t vid = VLAN_ID_NONE; 580 datalink_class_t class; 581 dladm_vlan_attr_t vlan_attr; 582 struct lookup_vnic_arg lva; 583 uint32_t media; 584 585 if ((strlen(link) == 0) || dladm_name2info(vh->vh_dh, 586 link, &linkid, &flags, &class, &media) != 587 DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE)) { 588 return (VRRP_EINVAL); 589 } 590 591 if (class == DATALINK_CLASS_VLAN) { 592 if (dladm_vlan_info(vh->vh_dh, linkid, &vlan_attr, 593 DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) { 594 return (VRRP_EINVAL); 595 } 596 linkid = vlan_attr.dv_linkid; 597 vid = vlan_attr.dv_vid; 598 if ((dladm_datalink_id2info(vh->vh_dh, linkid, NULL, 599 &class, &media, NULL, 0)) != DLADM_STATUS_OK) { 600 return (VRRP_EINVAL); 601 } 602 } 603 604 /* 605 * For now, Only VRRP over aggr and physical ethernet links is supported 606 */ 607 if ((class != DATALINK_CLASS_PHYS && class != DATALINK_CLASS_AGGR) || 608 media != DL_ETHER) { 609 return (VRRP_EINVAL); 610 } 611 612 if (linkidp != NULL) 613 *linkidp = linkid; 614 if (vidp != NULL) 615 *vidp = vid; 616 617 /* 618 * Find the assoicated vnic with the given vrid/vid/af/linkid 619 */ 620 lva.lva_vrid = vrid; 621 lva.lva_vid = vid; 622 lva.lva_af = af; 623 lva.lva_linkid = linkid; 624 lva.lva_vh = vh; 625 lva.lva_vnic[0] = '\0'; 626 627 (void) dladm_walk_datalink_id(lookup_vnic, vh->vh_dh, &lva, 628 DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 629 if (strlen(lva.lva_vnic) != 0) { 630 (void) strlcpy(vnic, lva.lva_vnic, len); 631 return (VRRP_SUCCESS); 632 } 633 634 return (VRRP_ENOVNIC); 635 } 636