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 2015, Joyent, Inc. 14 */ 15 16 /* 17 * Files based plug-in for varpd 18 * 19 * This is a dynamic varpd plug-in that has a static backing store. It's really 20 * nothing more than a glorified version of /etc/ethers, though it facilitates 21 * a bit more. The files module allows for the full set of mappings to be fixed 22 * at creation time. In addition, it also provides support for proxying ARP, 23 * NDP, and DHCP. 24 * 25 * At this time, the plugin requires that the destination type involve both an 26 * IP address and a port; however, there's no reason that this cannot be made 27 * more flexible as we have additional encapsulation algorithms that support it. 28 * The plug-in only has a single property, which is the location of the JSON 29 * file. The JSON file itself looks something like: 30 * 31 * { 32 * "aa:bb:cc:dd:ee:ff": { 33 * "arp": "10.23.69.1", 34 * "ndp": "2600:3c00::f03c:91ff:fe96:a264", 35 * "ip": "192.168.1.1", 36 * "port": 8080 37 * }, 38 * ... 39 * } 40 */ 41 42 #include <libvarpd_provider.h> 43 #include <umem.h> 44 #include <errno.h> 45 #include <thread.h> 46 #include <synch.h> 47 #include <strings.h> 48 #include <assert.h> 49 #include <limits.h> 50 #include <sys/types.h> 51 #include <sys/stat.h> 52 #include <fcntl.h> 53 #include <libnvpair.h> 54 #include <unistd.h> 55 #include <sys/mman.h> 56 #include <sys/ethernet.h> 57 #include <sys/socket.h> 58 #include <netinet/in.h> 59 #include <arpa/inet.h> 60 61 #include <libvarpd_files_json.h> 62 63 typedef struct varpd_files { 64 overlay_plugin_dest_t vaf_dest; /* RO */ 65 varpd_provider_handle_t *vaf_hdl; /* RO */ 66 char *vaf_path; /* WO */ 67 nvlist_t *vaf_nvl; /* WO */ 68 uint64_t vaf_nmisses; /* Atomic */ 69 uint64_t vaf_narp; /* Atomic */ 70 } varpd_files_t; 71 72 static const char *varpd_files_props[] = { 73 "files/config" 74 }; 75 76 static boolean_t 77 varpd_files_valid_dest(overlay_plugin_dest_t dest) 78 { 79 if (dest & ~(OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT)) 80 return (B_FALSE); 81 82 if (!(dest & (OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT))) 83 return (B_FALSE); 84 85 return (B_TRUE); 86 } 87 88 static int 89 varpd_files_create(varpd_provider_handle_t *hdl, void **outp, 90 overlay_plugin_dest_t dest) 91 { 92 varpd_files_t *vaf; 93 94 if (varpd_files_valid_dest(dest) == B_FALSE) 95 return (ENOTSUP); 96 97 vaf = umem_alloc(sizeof (varpd_files_t), UMEM_DEFAULT); 98 if (vaf == NULL) 99 return (ENOMEM); 100 101 bzero(vaf, sizeof (varpd_files_t)); 102 vaf->vaf_dest = dest; 103 vaf->vaf_path = NULL; 104 vaf->vaf_nvl = NULL; 105 vaf->vaf_hdl = hdl; 106 *outp = vaf; 107 return (0); 108 } 109 110 static int 111 varpd_files_normalize_nvlist(varpd_files_t *vaf, nvlist_t *nvl) 112 { 113 int ret; 114 nvlist_t *out; 115 nvpair_t *pair; 116 117 if ((ret = nvlist_alloc(&out, NV_UNIQUE_NAME, 0)) != 0) 118 return (ret); 119 120 for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL; 121 pair = nvlist_next_nvpair(nvl, pair)) { 122 char *name, fname[ETHERADDRSTRL]; 123 nvlist_t *data; 124 struct ether_addr ether, *e; 125 e = ðer; 126 127 if (nvpair_type(pair) != DATA_TYPE_NVLIST) { 128 nvlist_free(out); 129 return (EINVAL); 130 } 131 132 name = nvpair_name(pair); 133 if ((ret = nvpair_value_nvlist(pair, &data)) != 0) { 134 nvlist_free(out); 135 return (EINVAL); 136 } 137 138 if (ether_aton_r(name, e) == NULL) { 139 nvlist_free(out); 140 return (EINVAL); 141 } 142 143 if (ether_ntoa_r(e, fname) == NULL) { 144 nvlist_free(out); 145 return (ENOMEM); 146 } 147 148 if ((ret = nvlist_add_nvlist(out, fname, data)) != 0) { 149 nvlist_free(out); 150 return (EINVAL); 151 } 152 } 153 154 vaf->vaf_nvl = out; 155 return (0); 156 } 157 158 static int 159 varpd_files_start(void *arg) 160 { 161 int fd, ret; 162 void *maddr; 163 struct stat st; 164 nvlist_t *nvl; 165 varpd_files_t *vaf = arg; 166 167 if (vaf->vaf_path == NULL) 168 return (EAGAIN); 169 170 if ((fd = open(vaf->vaf_path, O_RDONLY)) < 0) 171 return (errno); 172 173 if (fstat(fd, &st) != 0) { 174 ret = errno; 175 if (close(fd) != 0) 176 abort(); 177 return (ret); 178 } 179 180 maddr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, 181 fd, 0); 182 if (maddr == NULL) { 183 ret = errno; 184 if (close(fd) != 0) 185 abort(); 186 return (ret); 187 } 188 189 ret = nvlist_parse_json(maddr, st.st_size, &nvl, 190 NVJSON_FORCE_INTEGER, NULL); 191 if (ret == 0) { 192 ret = varpd_files_normalize_nvlist(vaf, nvl); 193 nvlist_free(nvl); 194 } 195 if (munmap(maddr, st.st_size) != 0) 196 abort(); 197 if (close(fd) != 0) 198 abort(); 199 200 return (ret); 201 } 202 203 static void 204 varpd_files_stop(void *arg) 205 { 206 varpd_files_t *vaf = arg; 207 208 nvlist_free(vaf->vaf_nvl); 209 vaf->vaf_nvl = NULL; 210 } 211 212 static void 213 varpd_files_destroy(void *arg) 214 { 215 varpd_files_t *vaf = arg; 216 217 assert(vaf->vaf_nvl == NULL); 218 if (vaf->vaf_path != NULL) { 219 umem_free(vaf->vaf_path, strlen(vaf->vaf_path) + 1); 220 vaf->vaf_path = NULL; 221 } 222 umem_free(vaf, sizeof (varpd_files_t)); 223 } 224 225 static void 226 varpd_files_lookup(void *arg, varpd_query_handle_t *qh, 227 const overlay_targ_lookup_t *otl, overlay_target_point_t *otp) 228 { 229 char macstr[ETHERADDRSTRL], *ipstr; 230 nvlist_t *nvl; 231 varpd_files_t *vaf = arg; 232 int32_t port; 233 static const uint8_t bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 234 235 /* We don't support a default */ 236 if (otl == NULL) { 237 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 238 return; 239 } 240 241 if (otl->otl_sap == ETHERTYPE_ARP) { 242 libvarpd_plugin_proxy_arp(vaf->vaf_hdl, qh, otl); 243 return; 244 } 245 246 if (otl->otl_sap == ETHERTYPE_IPV6 && 247 otl->otl_dstaddr[0] == 0x33 && 248 otl->otl_dstaddr[1] == 0x33) { 249 libvarpd_plugin_proxy_ndp(vaf->vaf_hdl, qh, otl); 250 return; 251 } 252 253 if (otl->otl_sap == ETHERTYPE_IP && 254 bcmp(otl->otl_dstaddr, bcast, ETHERADDRL) == 0) { 255 char *mac; 256 struct ether_addr a, *addr; 257 258 addr = &a; 259 if (ether_ntoa_r((struct ether_addr *)otl->otl_srcaddr, 260 macstr) == NULL) { 261 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 262 return; 263 } 264 265 if (nvlist_lookup_nvlist(vaf->vaf_nvl, macstr, &nvl) != 0) { 266 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 267 return; 268 } 269 270 if (nvlist_lookup_string(nvl, "dhcp-proxy", &mac) != 0) { 271 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 272 return; 273 } 274 275 if (ether_aton_r(mac, addr) == NULL) { 276 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 277 return; 278 } 279 280 libvarpd_plugin_proxy_dhcp(vaf->vaf_hdl, qh, otl); 281 return; 282 } 283 284 if (ether_ntoa_r((struct ether_addr *)otl->otl_dstaddr, 285 macstr) == NULL) { 286 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 287 return; 288 } 289 290 if (nvlist_lookup_nvlist(vaf->vaf_nvl, macstr, &nvl) != 0) { 291 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 292 return; 293 } 294 295 if (nvlist_lookup_int32(nvl, "port", &port) != 0) { 296 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 297 return; 298 } 299 300 if (port <= 0 || port > UINT16_MAX) { 301 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 302 return; 303 } 304 otp->otp_port = port; 305 306 if (nvlist_lookup_string(nvl, "ip", &ipstr) != 0) { 307 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 308 return; 309 } 310 311 /* 312 * Try to parse it as a v6 address and then if it's not, try to 313 * transform it into a v4 address which we'll then wrap it into a v4 314 * mapped address. 315 */ 316 if (inet_pton(AF_INET6, ipstr, &otp->otp_ip) != 1) { 317 uint32_t v4; 318 if (inet_pton(AF_INET, ipstr, &v4) != 1) { 319 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); 320 return; 321 } 322 IN6_IPADDR_TO_V4MAPPED(v4, &otp->otp_ip); 323 } 324 325 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_OK); 326 } 327 328 /* ARGSUSED */ 329 static int 330 varpd_files_nprops(void *arg, uint_t *nprops) 331 { 332 *nprops = 1; 333 return (0); 334 } 335 336 /* ARGSUSED */ 337 static int 338 varpd_files_propinfo(void *arg, uint_t propid, varpd_prop_handle_t *vph) 339 { 340 if (propid != 0) 341 return (EINVAL); 342 343 libvarpd_prop_set_name(vph, varpd_files_props[0]); 344 libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW); 345 libvarpd_prop_set_type(vph, OVERLAY_PROP_T_STRING); 346 libvarpd_prop_set_nodefault(vph); 347 return (0); 348 } 349 350 static int 351 varpd_files_getprop(void *arg, const char *pname, void *buf, uint32_t *sizep) 352 { 353 varpd_files_t *vaf = arg; 354 355 if (strcmp(pname, varpd_files_props[0]) != 0) 356 return (EINVAL); 357 358 if (vaf->vaf_path != NULL) { 359 size_t len = strlen(vaf->vaf_path) + 1; 360 if (*sizep < len) 361 return (EOVERFLOW); 362 *sizep = len; 363 (void) strlcpy(buf, vaf->vaf_path, *sizep); 364 365 } else { 366 *sizep = 0; 367 } 368 369 return (0); 370 } 371 372 static int 373 varpd_files_setprop(void *arg, const char *pname, const void *buf, 374 const uint32_t size) 375 { 376 varpd_files_t *vaf = arg; 377 378 if (strcmp(pname, varpd_files_props[0]) != 0) 379 return (EINVAL); 380 381 if (vaf->vaf_path != NULL) 382 umem_free(vaf->vaf_path, strlen(vaf->vaf_path) + 1); 383 384 vaf->vaf_path = umem_alloc(size, UMEM_DEFAULT); 385 if (vaf->vaf_path == NULL) 386 return (ENOMEM); 387 (void) strlcpy(vaf->vaf_path, buf, size); 388 return (0); 389 } 390 391 static int 392 varpd_files_save(void *arg, nvlist_t *nvp) 393 { 394 int ret; 395 varpd_files_t *vaf = arg; 396 397 if (vaf->vaf_path == NULL) 398 return (0); 399 400 if ((ret = nvlist_add_string(nvp, varpd_files_props[0], 401 vaf->vaf_path)) != 0) 402 return (ret); 403 404 if ((ret = nvlist_add_uint64(nvp, "files/vaf_nmisses", 405 vaf->vaf_nmisses)) != 0) 406 return (ret); 407 408 if ((ret = nvlist_add_uint64(nvp, "files/vaf_narp", 409 vaf->vaf_narp)) != 0) 410 return (ret); 411 return (0); 412 } 413 414 static int 415 varpd_files_restore(nvlist_t *nvp, varpd_provider_handle_t *hdl, 416 overlay_plugin_dest_t dest, void **outp) 417 { 418 varpd_files_t *vaf; 419 char *str; 420 int ret; 421 uint64_t nmisses, narp; 422 423 if (varpd_files_valid_dest(dest) == B_FALSE) 424 return (EINVAL); 425 426 ret = nvlist_lookup_string(nvp, varpd_files_props[0], &str); 427 if (ret != 0 && ret != ENOENT) 428 return (ret); 429 else if (ret == ENOENT) 430 str = NULL; 431 432 if (nvlist_lookup_uint64(nvp, "files/vaf_nmisses", &nmisses) != 0) 433 return (EINVAL); 434 if (nvlist_lookup_uint64(nvp, "files/vaf_narp", &narp) != 0) 435 return (EINVAL); 436 437 vaf = umem_alloc(sizeof (varpd_files_t), UMEM_DEFAULT); 438 if (vaf == NULL) 439 return (ENOMEM); 440 441 bzero(vaf, sizeof (varpd_files_t)); 442 vaf->vaf_dest = dest; 443 if (str != NULL) { 444 size_t len = strlen(str) + 1; 445 vaf->vaf_path = umem_alloc(len, UMEM_DEFAULT); 446 if (vaf->vaf_path == NULL) { 447 umem_free(vaf, sizeof (varpd_files_t)); 448 return (ENOMEM); 449 } 450 (void) strlcpy(vaf->vaf_path, str, len); 451 } 452 453 vaf->vaf_hdl = hdl; 454 *outp = vaf; 455 return (0); 456 } 457 458 static void 459 varpd_files_proxy_arp(void *arg, varpd_arp_handle_t *vah, int kind, 460 const struct sockaddr *sock, uint8_t *out) 461 { 462 varpd_files_t *vaf = arg; 463 const struct sockaddr_in *ip; 464 const struct sockaddr_in6 *ip6; 465 nvpair_t *pair; 466 467 if (kind != VARPD_QTYPE_ETHERNET) { 468 libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP); 469 return; 470 } 471 472 if (sock->sa_family != AF_INET && sock->sa_family != AF_INET6) { 473 libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP); 474 return; 475 } 476 477 ip = (const struct sockaddr_in *)sock; 478 ip6 = (const struct sockaddr_in6 *)sock; 479 for (pair = nvlist_next_nvpair(vaf->vaf_nvl, NULL); pair != NULL; 480 pair = nvlist_next_nvpair(vaf->vaf_nvl, pair)) { 481 char *mac, *ipstr; 482 nvlist_t *data; 483 struct in_addr ia; 484 struct in6_addr ia6; 485 struct ether_addr ether, *e; 486 e = ðer; 487 488 if (nvpair_type(pair) != DATA_TYPE_NVLIST) 489 continue; 490 491 mac = nvpair_name(pair); 492 if (nvpair_value_nvlist(pair, &data) != 0) 493 continue; 494 495 496 if (sock->sa_family == AF_INET) { 497 if (nvlist_lookup_string(data, "arp", &ipstr) != 0) 498 continue; 499 500 if (inet_pton(AF_INET, ipstr, &ia) != 1) 501 continue; 502 503 if (bcmp(&ia, &ip->sin_addr, 504 sizeof (struct in_addr)) != 0) 505 continue; 506 } else { 507 if (nvlist_lookup_string(data, "ndp", &ipstr) != 0) 508 continue; 509 510 if (inet_pton(AF_INET6, ipstr, &ia6) != 1) 511 continue; 512 513 if (bcmp(&ia6, &ip6->sin6_addr, 514 sizeof (struct in6_addr)) != 0) 515 continue; 516 } 517 518 if (ether_aton_r(mac, e) == NULL) { 519 libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP); 520 return; 521 } 522 523 bcopy(e, out, ETHERADDRL); 524 libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_OK); 525 return; 526 } 527 528 libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP); 529 } 530 531 static void 532 varpd_files_proxy_dhcp(void *arg, varpd_dhcp_handle_t *vdh, int type, 533 const overlay_targ_lookup_t *otl, uint8_t *out) 534 { 535 varpd_files_t *vaf = arg; 536 nvlist_t *nvl; 537 char macstr[ETHERADDRSTRL], *mac; 538 struct ether_addr a, *addr; 539 540 addr = &a; 541 if (type != VARPD_QTYPE_ETHERNET) { 542 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP); 543 return; 544 } 545 546 if (ether_ntoa_r((struct ether_addr *)otl->otl_srcaddr, 547 macstr) == NULL) { 548 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP); 549 return; 550 } 551 552 if (nvlist_lookup_nvlist(vaf->vaf_nvl, macstr, &nvl) != 0) { 553 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP); 554 return; 555 } 556 557 if (nvlist_lookup_string(nvl, "dhcp-proxy", &mac) != 0) { 558 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP); 559 return; 560 } 561 562 if (ether_aton_r(mac, addr) == NULL) { 563 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP); 564 return; 565 } 566 567 bcopy(addr, out, ETHERADDRL); 568 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_OK); 569 } 570 571 static const varpd_plugin_ops_t varpd_files_ops = { 572 0, 573 varpd_files_create, 574 varpd_files_start, 575 varpd_files_stop, 576 varpd_files_destroy, 577 NULL, 578 varpd_files_lookup, 579 varpd_files_nprops, 580 varpd_files_propinfo, 581 varpd_files_getprop, 582 varpd_files_setprop, 583 varpd_files_save, 584 varpd_files_restore, 585 varpd_files_proxy_arp, 586 varpd_files_proxy_dhcp 587 }; 588 589 #pragma init(varpd_files_init) 590 static void 591 varpd_files_init(void) 592 { 593 int err; 594 varpd_plugin_register_t *vpr; 595 596 vpr = libvarpd_plugin_alloc(VARPD_CURRENT_VERSION, &err); 597 if (vpr == NULL) 598 return; 599 600 vpr->vpr_mode = OVERLAY_TARGET_DYNAMIC; 601 vpr->vpr_name = "files"; 602 vpr->vpr_ops = &varpd_files_ops; 603 (void) libvarpd_plugin_register(vpr); 604 libvarpd_plugin_free(vpr); 605 } 606