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 2018 Joyent, Inc.
  14  */
  15 
  16 /*
  17  * Interactions with /dev/overlay
  18  */
  19 
  20 #include <sys/types.h>
  21 #include <sys/stat.h>
  22 #include <fcntl.h>
  23 #include <errno.h>
  24 #include <assert.h>
  25 #include <unistd.h>
  26 #include <stdlib.h>
  27 #include <stropts.h>
  28 #include <strings.h>
  29 #include <umem.h>
  30 
  31 #include <libvarpd_impl.h>
  32 #include <sys/overlay_target.h>
  33 
  34 #define OVERLAY_PATH    "/dev/overlay"
  35 
  36 int
  37 libvarpd_overlay_init(varpd_impl_t *vip)
  38 {
  39         vip->vdi_overlayfd = open(OVERLAY_PATH, O_RDWR | O_EXCL);
  40         if (vip->vdi_overlayfd == -1)
  41                 return (errno);
  42         return (0);
  43 }
  44 
  45 void
  46 libvarpd_overlay_fini(varpd_impl_t *vip)
  47 {
  48         assert(vip->vdi_overlayfd > 0);
  49         if (close(vip->vdi_overlayfd) != 0)
  50                 libvarpd_panic("failed to close /dev/overlay fd %d: %d",
  51                     vip->vdi_overlayfd, errno);
  52 }
  53 
  54 int
  55 libvarpd_overlay_info(varpd_impl_t *vip, datalink_id_t linkid,
  56     overlay_plugin_dest_t *destp, uint64_t *flags, uint64_t *vnetid,
  57     uint32_t *dcid)
  58 {
  59         overlay_targ_info_t oti;
  60 
  61         oti.oti_linkid = linkid;
  62         if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_INFO, &oti) != 0)
  63                 return (errno);
  64 
  65         if (destp != NULL)
  66                 *destp = oti.oti_needs;
  67         if (flags != NULL)
  68                 *flags = oti.oti_flags;
  69         if (vnetid != NULL)
  70                 *vnetid = oti.oti_vnetid;
  71         if (dcid != NULL)
  72                 *dcid = oti.oti_dcid;
  73         return (0);
  74 }
  75 
  76 int
  77 libvarpd_overlay_associate(varpd_instance_t *inst)
  78 {
  79         overlay_targ_associate_t ota;
  80         varpd_impl_t *vip = inst->vri_impl;
  81 
  82         bzero(&ota, sizeof (overlay_targ_associate_t));
  83         ota.ota_linkid = inst->vri_linkid;
  84         ota.ota_mode = inst->vri_mode;
  85         ota.ota_id = inst->vri_id;
  86         ota.ota_provides = inst->vri_dest;
  87 
  88         if (ota.ota_mode == OVERLAY_TARGET_POINT) {
  89                 int ret;
  90                 ret = inst->vri_plugin->vpp_ops->vpo_default(inst->vri_private,
  91                     &ota.ota_point);
  92                 if (ret != VARPD_LOOKUP_OK)
  93                         return (ret);
  94         }
  95 
  96         if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_ASSOCIATE, &ota) != 0)
  97                 return (errno);
  98 
  99         return (0);
 100 }
 101 
 102 int
 103 libvarpd_overlay_disassociate(varpd_instance_t *inst)
 104 {
 105         overlay_targ_id_t otid;
 106         varpd_impl_t *vip = inst->vri_impl;
 107 
 108         otid.otid_linkid = inst->vri_linkid;
 109         if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_DISASSOCIATE, &otid) != 0)
 110                 return (errno);
 111         return (0);
 112 }
 113 
 114 int
 115 libvarpd_overlay_degrade_datalink(varpd_impl_t *vip, datalink_id_t linkid,
 116     const char *msg)
 117 {
 118         overlay_targ_degrade_t otd;
 119 
 120         otd.otd_linkid = linkid;
 121         (void) strlcpy(otd.otd_buf, msg, OVERLAY_STATUS_BUFLEN);
 122         if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_DEGRADE, &otd) != 0)
 123                 return (errno);
 124         return (0);
 125 
 126 }
 127 
 128 int
 129 libvarpd_overlay_degrade(varpd_instance_t *inst, const char *msg)
 130 {
 131         return (libvarpd_overlay_degrade_datalink(inst->vri_impl,
 132             inst->vri_linkid, msg));
 133 }
 134 
 135 int
 136 libvarpd_overlay_restore(varpd_instance_t *inst)
 137 {
 138         overlay_targ_id_t otid;
 139         varpd_impl_t *vip = inst->vri_impl;
 140 
 141         otid.otid_linkid = inst->vri_linkid;
 142         if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_RESTORE, &otid) != 0)
 143                 return (errno);
 144         return (0);
 145 }
 146 
 147 int
 148 libvarpd_overlay_packet(varpd_impl_t *vip, const overlay_targ_lookup_t *otl,
 149     void *buf, size_t *buflen)
 150 {
 151         int ret;
 152         overlay_targ_pkt_t otp;
 153 
 154         otp.otp_linkid = UINT64_MAX;
 155         otp.otp_reqid = otl->otl_reqid;
 156         otp.otp_size = *buflen;
 157         otp.otp_buf = buf;
 158 
 159         do {
 160                 ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_PKT, &otp);
 161         } while (ret != 0 && errno == EINTR);
 162         if (ret != 0 && errno == EFAULT)
 163                 libvarpd_panic("OVERLAY_TARG_PKT ioctl efault");
 164         else if (ret != 0)
 165                 ret = errno;
 166 
 167         if (ret == 0)
 168                 *buflen = otp.otp_size;
 169 
 170         return (ret);
 171 }
 172 
 173 static int
 174 libvarpd_overlay_inject_common(varpd_impl_t *vip, varpd_instance_t *inst,
 175     const overlay_targ_lookup_t *otl, void *buf, size_t buflen, int cmd)
 176 {
 177         int ret;
 178         overlay_targ_pkt_t otp;
 179 
 180         if (otl == NULL) {
 181                 otp.otp_linkid = inst->vri_linkid;
 182                 otp.otp_reqid = 0;
 183         } else {
 184                 otp.otp_linkid = UINT64_MAX;
 185                 otp.otp_reqid = otl->otl_reqid;
 186         }
 187         otp.otp_size = buflen;
 188         otp.otp_buf = buf;
 189 
 190         do {
 191                 ret = ioctl(vip->vdi_overlayfd, cmd, &otp);
 192         } while (ret != 0 && errno == EINTR);
 193         if (ret != 0 && errno == EFAULT)
 194                 libvarpd_panic("overlay_inject_common ioctl EFAULT");
 195         else if (ret != 0)
 196                 ret = errno;
 197 
 198         return (ret);
 199 }
 200 
 201 int
 202 libvarpd_overlay_inject(varpd_impl_t *vip, const overlay_targ_lookup_t *otl,
 203     void *buf, size_t buflen)
 204 {
 205         return (libvarpd_overlay_inject_common(vip, NULL, otl, buf, buflen,
 206             OVERLAY_TARG_INJECT));
 207 }
 208 
 209 int
 210 libvarpd_overlay_instance_inject(varpd_instance_t *inst, void *buf,
 211     size_t buflen)
 212 {
 213         return (libvarpd_overlay_inject_common(inst->vri_impl, inst, NULL, buf,
 214             buflen, OVERLAY_TARG_INJECT));
 215 }
 216 
 217 int
 218 libvarpd_overlay_resend(varpd_impl_t *vip, const overlay_targ_lookup_t *otl,
 219     void *buf, size_t buflen)
 220 {
 221         return (libvarpd_overlay_inject_common(vip, NULL, otl, buf, buflen,
 222             OVERLAY_TARG_RESEND));
 223 }
 224 
 225 static void
 226 libvarpd_overlay_lookup_reply(varpd_impl_t *vip,
 227     const overlay_targ_lookup_t *otl, overlay_targ_resp_t *otr, int cmd)
 228 {
 229         int ret;
 230 
 231         otr->otr_reqid = otl->otl_reqid;
 232         do {
 233                 ret = ioctl(vip->vdi_overlayfd, cmd, otr);
 234         } while (ret != 0 && errno == EINTR);
 235 
 236         /*
 237          * The only errors that should cause us to end up here are due to
 238          * programmer errors. Aruably the EINAVL case indicates that something
 239          * is a bit off; however, at this time we don't opt to kill varpd.
 240          */
 241         if (ret != 0 && errno != EINVAL)
 242                 libvarpd_panic("receieved bad errno from lookup_reply "
 243                     "(cmd %d): %d\n", cmd, errno);
 244 }
 245 
 246 static void
 247 libvarpd_overlay_lookup_handle(varpd_impl_t *vip)
 248 {
 249         int ret;
 250         varpd_query_t *vqp;
 251         overlay_targ_lookup_t *otl;
 252         overlay_targ_resp_t *otr;
 253         varpd_instance_t *inst;
 254 
 255         vqp = umem_cache_alloc(vip->vdi_qcache, UMEM_DEFAULT);
 256         otl = &vqp->vq_lookup;
 257         otr = &vqp->vq_response;
 258 
 259         /*
 260          * abort doesn't really help here that much, maybe we can instead try
 261          * and for a reap or something?
 262          */
 263         if (vqp == NULL)
 264                 libvarpd_panic("failed to allocate memory for lookup "
 265                     "handle..., we should not panic()");
 266         ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_LOOKUP, otl);
 267         if (ret != 0 && errno != ETIME && errno != EINTR)
 268                 libvarpd_panic("received bad errno from OVERLAY_TARG_LOOKUP: "
 269                     "%d", errno);
 270 
 271         if (ret != 0) {
 272                 umem_cache_free(vip->vdi_qcache, vqp);
 273                 return;
 274         }
 275 
 276         inst = (varpd_instance_t *)libvarpd_instance_lookup(
 277             (varpd_handle_t *)vip, otl->otl_varpdid);
 278         if (inst == NULL) {
 279                 libvarpd_overlay_lookup_reply(vip, otl, otr,
 280                     OVERLAY_TARG_DROP);
 281                 umem_cache_free(vip->vdi_qcache, vqp);
 282                 return;
 283         }
 284         vqp->vq_instance = inst;
 285 
 286         inst->vri_plugin->vpp_ops->vpo_lookup(inst->vri_private,
 287             (varpd_query_handle_t *)vqp, otl, &otr->otr_answer,
 288             &otr->otr_route, &otr->otr_mac);
 289 }
 290 
 291 void
 292 libvarpd_overlay_lookup_run(varpd_handle_t *vhp)
 293 {
 294         varpd_impl_t *vip = (varpd_impl_t *)vhp;
 295 
 296         mutex_enter(&vip->vdi_lock);
 297         if (vip->vdi_lthr_quiesce == B_TRUE) {
 298                 mutex_exit(&vip->vdi_lock);
 299                 return;
 300         }
 301         vip->vdi_lthr_count++;
 302 
 303         for (;;) {
 304                 mutex_exit(&vip->vdi_lock);
 305                 libvarpd_overlay_lookup_handle(vip);
 306                 mutex_enter(&vip->vdi_lock);
 307                 if (vip->vdi_lthr_quiesce == B_TRUE)
 308                         break;
 309         }
 310         assert(vip->vdi_lthr_count > 0);
 311         vip->vdi_lthr_count--;
 312         (void) cond_signal(&vip->vdi_lthr_cv);
 313         mutex_exit(&vip->vdi_lock);
 314 }
 315 
 316 void
 317 libvarpd_overlay_lookup_quiesce(varpd_handle_t *vhp)
 318 {
 319         varpd_impl_t *vip = (varpd_impl_t *)vhp;
 320 
 321         mutex_enter(&vip->vdi_lock);
 322         if (vip->vdi_lthr_count == 0) {
 323                 mutex_exit(&vip->vdi_lock);
 324                 return;
 325         }
 326         vip->vdi_lthr_quiesce = B_TRUE;
 327         while (vip->vdi_lthr_count > 0)
 328                 (void) cond_wait(&vip->vdi_lthr_cv, &vip->vdi_lock);
 329         vip->vdi_lthr_quiesce = B_FALSE;
 330         mutex_exit(&vip->vdi_lock);
 331 }
 332 
 333 int
 334 libvarpd_overlay_iter(varpd_impl_t *vip, libvarpd_overlay_iter_f func,
 335     void *arg)
 336 {
 337         uint32_t curents = 0, i;
 338         size_t size;
 339         overlay_targ_list_t *otl;
 340 
 341         for (;;) {
 342                 size = sizeof (overlay_targ_list_t) +
 343                     sizeof (uint32_t) * curents;
 344                 otl = umem_alloc(size, UMEM_DEFAULT);
 345                 if (otl == NULL)
 346                         return (ENOMEM);
 347 
 348                 otl->otl_nents = curents;
 349                 if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_LIST, otl) != 0) {
 350                         if (errno == EFAULT)
 351                                 libvarpd_panic("OVERLAY_TARG_LIST ioctl "
 352                                     "efault");
 353                         umem_free(otl, size);
 354                         if (errno == EINTR)
 355                                 continue;
 356                         else
 357                                 return (errno);
 358                 }
 359 
 360                 if (otl->otl_nents == curents)
 361                         break;
 362 
 363                 curents = otl->otl_nents;
 364                 umem_free(otl, size);
 365         }
 366 
 367         for (i = 0; i < otl->otl_nents; i++) {
 368                 if (func(vip, otl->otl_ents[i], arg) != 0)
 369                         break;
 370         }
 371         umem_free(otl, size);
 372         return (0);
 373 }
 374 
 375 int
 376 libvarpd_overlay_cache_flush(varpd_instance_t *inst)
 377 {
 378         int ret;
 379         overlay_targ_cache_t cache;
 380         varpd_impl_t *vip = inst->vri_impl;
 381 
 382         bzero(&cache, sizeof (overlay_targ_cache_t));
 383         cache.otc_linkid = inst->vri_linkid;
 384 
 385         ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_FLUSH, &cache);
 386         if (ret != 0 && errno == EFAULT)
 387                 libvarpd_panic("OVERLAY_TARG_CACHE_FLUSH ioctl efault");
 388         else if (ret != 0)
 389                 ret = errno;
 390 
 391         return (ret);
 392 }
 393 
 394 int
 395 libvarpd_overlay_cache_delete(varpd_instance_t *inst, uint32_t dcid,
 396     const uint8_t *key)
 397 {
 398         int ret;
 399         overlay_targ_cache_t cache;
 400         varpd_impl_t *vip = inst->vri_impl;
 401 
 402         bzero(&cache, sizeof (overlay_targ_cache_t));
 403         cache.otc_linkid = inst->vri_linkid;
 404         cache.otc_entry.otce_mac.otm_dcid = dcid;
 405         bcopy(key, cache.otc_entry.otce_mac.otm_mac, ETHERADDRL);
 406 
 407         ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_REMOVE, &cache);
 408         if (ret != 0 && errno == EFAULT)
 409                 libvarpd_panic("OVERLAY_TARG_CACHE_REMOVE ioctl efault");
 410         else if (ret != 0)
 411                 ret = errno;
 412 
 413         return (ret);
 414 
 415 }
 416 
 417 int
 418 libvarpd_overlay_cache_get(varpd_instance_t *inst, const uint8_t *key,
 419     varpd_client_cache_entry_t *entry)
 420 {
 421         int ret;
 422         overlay_targ_cache_t cache = { 0 };
 423         varpd_impl_t *vip = inst->vri_impl;
 424 
 425         cache.otc_linkid = inst->vri_linkid;
 426         bcopy(key, cache.otc_entry.otce_mac.otm_mac, ETHERADDRL);
 427 
 428         ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_GET, &cache);
 429         if (ret != 0 && errno == EFAULT)
 430                 libvarpd_panic("OVERLAY_TARG_CACHE_GET ioctl efault");
 431         else if (ret != 0)
 432                 return (errno);
 433 
 434         bcopy(cache.otc_entry.otce_dest.otp_mac, &entry->vcp_mac, ETHERADDRL);
 435         entry->vcp_flags = cache.otc_entry.otce_flags;
 436         entry->vcp_ip = cache.otc_entry.otce_dest.otp_ip;
 437         entry->vcp_port = cache.otc_entry.otce_dest.otp_port;
 438 
 439         return (0);
 440 }
 441 
 442 int
 443 libvarpd_overlay_cache_set(varpd_instance_t *inst, uint32_t dcid,
 444     const uint8_t *key, const varpd_client_cache_entry_t *entry)
 445 {
 446         int ret;
 447         overlay_targ_cache_t cache = { 0 };
 448         varpd_impl_t *vip = inst->vri_impl;
 449 
 450         cache.otc_linkid = inst->vri_linkid;
 451         cache.otc_entry.otce_mac.otm_dcid = dcid;
 452         bcopy(key, cache.otc_entry.otce_mac.otm_mac, ETHERADDRL);
 453         bcopy(&entry->vcp_mac, cache.otc_entry.otce_dest.otp_mac, ETHERADDRL);
 454         cache.otc_entry.otce_flags = entry->vcp_flags;
 455         cache.otc_entry.otce_dest.otp_ip = entry->vcp_ip;
 456         cache.otc_entry.otce_dest.otp_port = entry->vcp_port;
 457 
 458         ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_SET, &cache);
 459         if (ret != 0 && errno == EFAULT)
 460                 libvarpd_panic("OVERLAY_TARG_CACHE_SET ioctl efault");
 461         else if (ret != 0)
 462                 return (errno);
 463 
 464         return (0);
 465 }
 466 
 467 int
 468 libvarpd_overlay_cache_walk_fill(varpd_instance_t *inst, uint64_t *markerp,
 469     uint64_t *countp, overlay_targ_cache_entry_t *ents)
 470 {
 471         int ret;
 472         size_t asize;
 473         overlay_targ_cache_iter_t *iter;
 474         varpd_impl_t *vip = inst->vri_impl;
 475 
 476         if (*countp > 200)
 477                 return (E2BIG);
 478 
 479         asize = sizeof (overlay_targ_cache_iter_t) +
 480             *countp * sizeof (overlay_targ_cache_entry_t);
 481         iter = umem_alloc(asize, UMEM_DEFAULT);
 482         if (iter == NULL)
 483                 return (ENOMEM);
 484 
 485         iter->otci_linkid = inst->vri_linkid;
 486         iter->otci_marker[0] = markerp[0];
 487         iter->otci_marker[1] = markerp[1];
 488         iter->otci_count = *countp;
 489         ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_ITER, iter);
 490         if (ret != 0 && errno == EFAULT)
 491                 libvarpd_panic("OVERLAY_TARG_CACHE_ITER ioctl efault");
 492         else if (ret != 0) {
 493                 ret = errno;
 494                 goto out;
 495         }
 496 
 497         markerp[0] = iter->otci_marker[0];
 498         markerp[1] = iter->otci_marker[1];
 499         *countp = iter->otci_count;
 500         bcopy(iter->otci_ents, ents,
 501             *countp * sizeof (overlay_targ_cache_entry_t));
 502 out:
 503         umem_free(iter, asize);
 504         return (ret);
 505 }
 506 
 507 void
 508 libvarpd_plugin_query_reply(varpd_query_handle_t *vqh, int action)
 509 {
 510         varpd_query_t *vqp = (varpd_query_t *)vqh;
 511 
 512         if (vqp == NULL)
 513                 libvarpd_panic("unkonwn plugin passed invalid "
 514                     "varpd_query_handle_t");
 515 
 516         if (action == VARPD_LOOKUP_DROP)
 517                 libvarpd_overlay_lookup_reply(vqp->vq_instance->vri_impl,
 518                     &vqp->vq_lookup, &vqp->vq_response, OVERLAY_TARG_DROP);
 519         else if (action == VARPD_LOOKUP_OK)
 520                 libvarpd_overlay_lookup_reply(vqp->vq_instance->vri_impl,
 521                     &vqp->vq_lookup, &vqp->vq_response, OVERLAY_TARG_RESPOND);
 522         else
 523                 libvarpd_panic("plugin %s passed in an invalid action: %d",
 524                     vqp->vq_instance->vri_plugin->vpp_name, action);
 525 
 526         umem_cache_free(vqp->vq_instance->vri_impl->vdi_qcache, vqp);
 527 }
 528 
 529 void
 530 libvarpd_inject_varp(varpd_provider_handle_t *vph, const uint8_t *mac,
 531     const overlay_target_point_t *otp)
 532 {
 533         int ret;
 534         overlay_targ_cache_t otc = { 0 };
 535         varpd_instance_t *inst = (varpd_instance_t *)vph;
 536         varpd_impl_t *vip = inst->vri_impl;
 537 
 538         if (otp == NULL) {
 539                 (void) libvarpd_overlay_cache_delete(inst, 0, mac);
 540                 return;
 541         }
 542 
 543         otc.otc_linkid = inst->vri_linkid;
 544         otc.otc_entry.otce_flags = 0;
 545         if (IN6_IS_ADDR_UNSPECIFIED(&otp->otp_ip) && otp->otp_port == 0)
 546                 otc.otc_entry.otce_flags |= OVERLAY_TARGET_CACHE_ROUTER;
 547         bcopy(mac, otc.otc_entry.otce_mac.otm_mac, ETHERADDRL);
 548         bcopy(otp, &otc.otc_entry.otce_dest, sizeof (overlay_target_point_t));
 549 
 550         ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_SET, &otc);
 551         if (ret != 0) {
 552                 switch (errno) {
 553                 case EBADF:
 554                 case EFAULT:
 555                 case ENOTSUP:
 556                         libvarpd_panic("received bad errno from "
 557                             "OVERLAY_TARG_CACHE_SET: %d", errno);
 558                 default:
 559                         break;
 560                 }
 561         }
 562 }
 563 
 564 void
 565 libvarpd_fma_degrade(varpd_provider_handle_t *vph, const char *msg)
 566 {
 567         int ret;
 568         varpd_instance_t *inst = (varpd_instance_t *)vph;
 569 
 570         ret = libvarpd_overlay_degrade(inst, msg);
 571         switch (ret) {
 572         case ENOENT:
 573         case EFAULT:
 574                 libvarpd_panic("received bad errno from degrade ioctl: %d",
 575                     errno);
 576         default:
 577                 break;
 578         }
 579 }
 580 
 581 void
 582 libvarpd_fma_restore(varpd_provider_handle_t *vph)
 583 {
 584         int ret;
 585         varpd_instance_t *inst = (varpd_instance_t *)vph;
 586 
 587         ret = libvarpd_overlay_restore(inst);
 588         switch (ret) {
 589         case ENOENT:
 590         case EFAULT:
 591                 libvarpd_panic("received bad errno from restore ioctl: %d",
 592                     errno);
 593         default:
 594                 break;
 595         }
 596 }