1e2d54380Skyuho.son /* SPDX-License-Identifier: BSD-3-Clause 2e2d54380Skyuho.son * Copyright (c) 2022 Dell Inc, or its subsidiaries. 3e2d54380Skyuho.son * Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved. 4e2d54380Skyuho.son * All rights reserved. 5e2d54380Skyuho.son */ 6e2d54380Skyuho.son 7e2d54380Skyuho.son #include "spdk/stdinc.h" 8e2d54380Skyuho.son #include "spdk/env.h" 9e2d54380Skyuho.son #include "spdk/thread.h" 10e2d54380Skyuho.son #include "nvmf_internal.h" 11e2d54380Skyuho.son #include "spdk/log.h" 12e2d54380Skyuho.son #include "spdk/config.h" 13e2d54380Skyuho.son #include "spdk/nvme.h" 14e2d54380Skyuho.son #include "spdk/string.h" 15e2d54380Skyuho.son 16e2d54380Skyuho.son #ifdef SPDK_CONFIG_AVAHI 17e2d54380Skyuho.son #include <avahi-client/client.h> 18e2d54380Skyuho.son #include <avahi-client/publish.h> 19e2d54380Skyuho.son #include <avahi-client/lookup.h> 20e2d54380Skyuho.son #include <avahi-common/simple-watch.h> 21e2d54380Skyuho.son #include <avahi-common/malloc.h> 22e2d54380Skyuho.son #include <avahi-common/error.h> 23e2d54380Skyuho.son 24e2d54380Skyuho.son #define NVMF_MAX_DNS_NAME_LENGTH 255 25e2d54380Skyuho.son 26e2d54380Skyuho.son static AvahiSimplePoll *g_avahi_publish_simple_poll = NULL; 27e2d54380Skyuho.son static AvahiClient *g_avahi_publish_client = NULL; 28e2d54380Skyuho.son static AvahiEntryGroup *g_avahi_entry_group = NULL; 29e2d54380Skyuho.son 30e2d54380Skyuho.son struct mdns_publish_ctx { 31e2d54380Skyuho.son struct spdk_poller *poller; 32e2d54380Skyuho.son struct spdk_nvmf_subsystem *subsystem; 33e2d54380Skyuho.son struct spdk_nvmf_tgt *tgt; 34e2d54380Skyuho.son }; 35e2d54380Skyuho.son 36e2d54380Skyuho.son static struct mdns_publish_ctx *g_mdns_publish_ctx = NULL; 37e2d54380Skyuho.son 38e2d54380Skyuho.son static void 39e2d54380Skyuho.son nvmf_avahi_publish_destroy(struct mdns_publish_ctx *ctx) 40e2d54380Skyuho.son { 41e2d54380Skyuho.son if (g_avahi_entry_group) { 42e2d54380Skyuho.son avahi_entry_group_free(g_avahi_entry_group); 43e2d54380Skyuho.son g_avahi_entry_group = NULL; 44e2d54380Skyuho.son } 45e2d54380Skyuho.son 46e2d54380Skyuho.son if (g_avahi_publish_client) { 47e2d54380Skyuho.son avahi_client_free(g_avahi_publish_client); 48e2d54380Skyuho.son g_avahi_publish_client = NULL; 49e2d54380Skyuho.son } 50e2d54380Skyuho.son 51e2d54380Skyuho.son if (g_avahi_publish_simple_poll) { 52e2d54380Skyuho.son avahi_simple_poll_free(g_avahi_publish_simple_poll); 53e2d54380Skyuho.son g_avahi_publish_simple_poll = NULL; 54e2d54380Skyuho.son } 55e2d54380Skyuho.son 56e2d54380Skyuho.son g_mdns_publish_ctx = NULL; 57e2d54380Skyuho.son free(ctx); 58e2d54380Skyuho.son } 59e2d54380Skyuho.son 60e2d54380Skyuho.son static int 61e2d54380Skyuho.son nvmf_avahi_publish_iterate(void *arg) 62e2d54380Skyuho.son { 63e2d54380Skyuho.son struct mdns_publish_ctx *ctx = arg; 64e2d54380Skyuho.son int rc; 65e2d54380Skyuho.son 66e2d54380Skyuho.son if (ctx == NULL) { 67e2d54380Skyuho.son assert(false); 68e2d54380Skyuho.son return SPDK_POLLER_IDLE; 69e2d54380Skyuho.son } 70e2d54380Skyuho.son 71e2d54380Skyuho.son rc = avahi_simple_poll_iterate(g_avahi_publish_simple_poll, 0); 72e2d54380Skyuho.son if (rc && rc != -EAGAIN) { 73e2d54380Skyuho.son SPDK_ERRLOG("avahi publish poll returned error\n"); 74e2d54380Skyuho.son spdk_poller_unregister(&ctx->poller); 75e2d54380Skyuho.son nvmf_avahi_publish_destroy(ctx); 76e2d54380Skyuho.son return SPDK_POLLER_BUSY; 77e2d54380Skyuho.son } 78e2d54380Skyuho.son 79e2d54380Skyuho.son return SPDK_POLLER_BUSY; 80e2d54380Skyuho.son } 81e2d54380Skyuho.son 82e2d54380Skyuho.son static void 83e2d54380Skyuho.son nvmf_ctx_stop_mdns_prr(struct mdns_publish_ctx *ctx) 84e2d54380Skyuho.son { 85f1fabd08Skyuho.son SPDK_INFOLOG(nvmf, "Stopping avahi publish poller\n"); 86f1fabd08Skyuho.son spdk_poller_unregister(&ctx->poller); 87f1fabd08Skyuho.son nvmf_avahi_publish_destroy(ctx); 88e2d54380Skyuho.son } 89e2d54380Skyuho.son 9021eda816Skyuho.son static bool 9121eda816Skyuho.son nvmf_tgt_is_mdns_running(struct spdk_nvmf_tgt *tgt) 9221eda816Skyuho.son { 9321eda816Skyuho.son if (g_mdns_publish_ctx && g_mdns_publish_ctx->tgt == tgt) { 9421eda816Skyuho.son return true; 9521eda816Skyuho.son } 9621eda816Skyuho.son return false; 9721eda816Skyuho.son } 9821eda816Skyuho.son 9948352fbbSkyuho.son void 10048352fbbSkyuho.son nvmf_tgt_stop_mdns_prr(struct spdk_nvmf_tgt *tgt) 10148352fbbSkyuho.son { 10221eda816Skyuho.son if (nvmf_tgt_is_mdns_running(tgt) == true) { 10348352fbbSkyuho.son nvmf_ctx_stop_mdns_prr(g_mdns_publish_ctx); 10448352fbbSkyuho.son return; 10548352fbbSkyuho.son } 10648352fbbSkyuho.son } 10748352fbbSkyuho.son 108188cdd86Skyuho.son static void 109188cdd86Skyuho.son avahi_entry_group_add_listeners(AvahiEntryGroup *avahi_entry_group, 110188cdd86Skyuho.son struct spdk_nvmf_subsystem *subsystem) 111e2d54380Skyuho.son { 112e2d54380Skyuho.son struct spdk_nvmf_subsystem_listener *listener; 113e2d54380Skyuho.son const char *name_base = "spdk"; 114e2d54380Skyuho.son const char *type_base = "_nvme-disc"; 115e2d54380Skyuho.son const char *domain = "local"; 116e2d54380Skyuho.son char *protocol; 117e2d54380Skyuho.son char name[NVMF_MAX_DNS_NAME_LENGTH]; 118e2d54380Skyuho.son char type[NVMF_MAX_DNS_NAME_LENGTH]; 119e2d54380Skyuho.son char txt_protocol[NVMF_MAX_DNS_NAME_LENGTH]; 120e2d54380Skyuho.son char txt_nqn[NVMF_MAX_DNS_NAME_LENGTH]; 121e2d54380Skyuho.son AvahiStringList *txt = NULL; 122e2d54380Skyuho.son uint16_t port; 123e2d54380Skyuho.son uint16_t id = 0; 124e2d54380Skyuho.son 125e2d54380Skyuho.son TAILQ_FOREACH(listener, &subsystem->listeners, link) { 126e2d54380Skyuho.son if (listener->trid->trtype == SPDK_NVME_TRANSPORT_TCP) { 127e2d54380Skyuho.son protocol = "tcp"; 128e2d54380Skyuho.son } else if (listener->trid->trtype == SPDK_NVME_TRANSPORT_RDMA) { 129e2d54380Skyuho.son SPDK_ERRLOG("Current SPDK doesn't distinguish RoCE(udp) and iWARP(tcp). Skip adding listener id %d to avahi entry", 130e2d54380Skyuho.son listener->id); 131e2d54380Skyuho.son continue; 132e2d54380Skyuho.son } else { 133e2d54380Skyuho.son SPDK_ERRLOG("mDNS PRR does not support trtype %d", listener->trid->trtype); 134e2d54380Skyuho.son continue; 135e2d54380Skyuho.son } 136e2d54380Skyuho.son 137e2d54380Skyuho.son snprintf(type, sizeof(type), "%s._%s", type_base, protocol); 138e2d54380Skyuho.son snprintf(name, sizeof(name), "%s%d", name_base, id++); 139e2d54380Skyuho.son snprintf(txt_protocol, sizeof(txt_protocol), "p=%s", protocol); 140e2d54380Skyuho.son snprintf(txt_nqn, sizeof(txt_nqn), "nqn=%s", SPDK_NVMF_DISCOVERY_NQN); 141e2d54380Skyuho.son txt = avahi_string_list_add(txt, txt_protocol); 142e2d54380Skyuho.son txt = avahi_string_list_add(txt, txt_nqn); 143e2d54380Skyuho.son port = spdk_strtol(listener->trid->trsvcid, 10); 144e2d54380Skyuho.son 145188cdd86Skyuho.son if (avahi_entry_group_add_service_strlst(avahi_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 146e2d54380Skyuho.son 0, name, type, domain, NULL, port, txt) < 0) { 147188cdd86Skyuho.son SPDK_ERRLOG("Failed to add avahi service name: %s, type: %s, domain: %s, port: %d", 148188cdd86Skyuho.son name, type, domain, port); 149e2d54380Skyuho.son } 15088c9e0c4Skyuho.son avahi_string_list_free(txt); 15188c9e0c4Skyuho.son txt = NULL; 152e2d54380Skyuho.son } 153e2d54380Skyuho.son 154188cdd86Skyuho.son avahi_entry_group_commit(avahi_entry_group); 155188cdd86Skyuho.son } 156188cdd86Skyuho.son 157*001db1eaSkyuho.son int 158*001db1eaSkyuho.son nvmf_tgt_update_mdns_prr(struct spdk_nvmf_tgt *tgt) 159*001db1eaSkyuho.son { 160*001db1eaSkyuho.son int rc; 161*001db1eaSkyuho.son 162*001db1eaSkyuho.son if (nvmf_tgt_is_mdns_running(tgt) == false || g_avahi_entry_group == NULL) { 163*001db1eaSkyuho.son SPDK_INFOLOG(nvmf, 164*001db1eaSkyuho.son "nvmf_tgt_update_mdns_prr is only supported when mDNS servier is running on target\n"); 165*001db1eaSkyuho.son return 0; 166*001db1eaSkyuho.son } 167*001db1eaSkyuho.son 168*001db1eaSkyuho.son rc = avahi_entry_group_reset(g_avahi_entry_group); 169*001db1eaSkyuho.son if (rc) { 170*001db1eaSkyuho.son SPDK_ERRLOG("Failed to reset avahi_entry_group"); 171*001db1eaSkyuho.son return -EINVAL; 172*001db1eaSkyuho.son } 173*001db1eaSkyuho.son 174*001db1eaSkyuho.son avahi_entry_group_add_listeners(g_avahi_entry_group, g_mdns_publish_ctx->subsystem); 175*001db1eaSkyuho.son 176*001db1eaSkyuho.son return 0; 177*001db1eaSkyuho.son } 178*001db1eaSkyuho.son 179188cdd86Skyuho.son static int 180188cdd86Skyuho.son publish_pull_registration_request(AvahiClient *client, struct mdns_publish_ctx *publish_ctx) 181188cdd86Skyuho.son { 182188cdd86Skyuho.son struct spdk_nvmf_subsystem *subsystem = publish_ctx->subsystem; 183188cdd86Skyuho.son 184188cdd86Skyuho.son if (g_avahi_entry_group != NULL) { 185188cdd86Skyuho.son return 0; 186188cdd86Skyuho.son } 187188cdd86Skyuho.son 188188cdd86Skyuho.son g_avahi_entry_group = avahi_entry_group_new(client, NULL, NULL); 189188cdd86Skyuho.son if (g_avahi_entry_group == NULL) { 190188cdd86Skyuho.son SPDK_ERRLOG("avahi_entry_group_new failure: %s\n", avahi_strerror(avahi_client_errno(client))); 191188cdd86Skyuho.son return -1; 192188cdd86Skyuho.son } 193188cdd86Skyuho.son 194188cdd86Skyuho.son avahi_entry_group_add_listeners(g_avahi_entry_group, subsystem); 195e2d54380Skyuho.son 196e2d54380Skyuho.son return 0; 197e2d54380Skyuho.son } 198e2d54380Skyuho.son 199e2d54380Skyuho.son static void 200e2d54380Skyuho.son publish_client_new_callback(AvahiClient *client, AvahiClientState avahi_state, 201e2d54380Skyuho.son AVAHI_GCC_UNUSED void *user_data) 202e2d54380Skyuho.son { 203e2d54380Skyuho.son int rc; 204e2d54380Skyuho.son struct mdns_publish_ctx *publish_ctx = user_data; 205e2d54380Skyuho.son 206e2d54380Skyuho.son switch (avahi_state) { 207e2d54380Skyuho.son case AVAHI_CLIENT_S_RUNNING: 208e2d54380Skyuho.son rc = publish_pull_registration_request(client, publish_ctx); 209e2d54380Skyuho.son if (rc) { 210e2d54380Skyuho.son nvmf_ctx_stop_mdns_prr(publish_ctx); 211e2d54380Skyuho.son } 212e2d54380Skyuho.son break; 213e2d54380Skyuho.son case AVAHI_CLIENT_CONNECTING: 214e2d54380Skyuho.son SPDK_INFOLOG(nvmf, "Avahi client waiting for avahi-daemon"); 215e2d54380Skyuho.son break; 216e2d54380Skyuho.son case AVAHI_CLIENT_S_REGISTERING: 217e2d54380Skyuho.son SPDK_INFOLOG(nvmf, "Avahi client registering service"); 218e2d54380Skyuho.son break; 219e2d54380Skyuho.son case AVAHI_CLIENT_FAILURE: 220e2d54380Skyuho.son SPDK_ERRLOG("Server connection failure: %s\n", avahi_strerror(avahi_client_errno(client))); 221e2d54380Skyuho.son nvmf_ctx_stop_mdns_prr(publish_ctx); 222e2d54380Skyuho.son break; 223e2d54380Skyuho.son case AVAHI_CLIENT_S_COLLISION: 224e2d54380Skyuho.son SPDK_ERRLOG("Avahi client name is already used in the mDNS"); 225e2d54380Skyuho.son nvmf_ctx_stop_mdns_prr(publish_ctx); 226e2d54380Skyuho.son break; 227e2d54380Skyuho.son default: 228e2d54380Skyuho.son SPDK_ERRLOG("Avahi client is in unsupported state"); 229e2d54380Skyuho.son break; 230e2d54380Skyuho.son } 231e2d54380Skyuho.son } 232e2d54380Skyuho.son 233e2d54380Skyuho.son int 234e2d54380Skyuho.son nvmf_publish_mdns_prr(struct spdk_nvmf_tgt *tgt) 235e2d54380Skyuho.son { 236e2d54380Skyuho.son int error; 237e2d54380Skyuho.son struct mdns_publish_ctx *publish_ctx = NULL; 238e2d54380Skyuho.son struct spdk_nvmf_subsystem *subsystem = NULL; 239e2d54380Skyuho.son 240e2d54380Skyuho.son if (g_mdns_publish_ctx != NULL) { 241e2d54380Skyuho.son if (g_mdns_publish_ctx->tgt == tgt) { 242e2d54380Skyuho.son SPDK_ERRLOG("mDNS server is already running on target %s.\n", tgt->name); 243e2d54380Skyuho.son return -EEXIST; 244e2d54380Skyuho.son } 245e2d54380Skyuho.son SPDK_ERRLOG("mDNS server does not support publishing multiple targets simultaneously."); 246e2d54380Skyuho.son return -EINVAL; 247e2d54380Skyuho.son } 248e2d54380Skyuho.son 249e2d54380Skyuho.son subsystem = spdk_nvmf_tgt_find_subsystem(tgt, SPDK_NVMF_DISCOVERY_NQN); 250e2d54380Skyuho.son if (TAILQ_EMPTY(&subsystem->listeners)) { 251e2d54380Skyuho.son SPDK_ERRLOG("Discovery subsystem has no listeners.\n"); 252e2d54380Skyuho.son return -EINVAL; 253e2d54380Skyuho.son } 254e2d54380Skyuho.son 255e2d54380Skyuho.son publish_ctx = calloc(1, sizeof(*publish_ctx)); 256e2d54380Skyuho.son if (publish_ctx == NULL) { 257e2d54380Skyuho.son SPDK_ERRLOG("Error creating mDNS publish ctx\n"); 258e2d54380Skyuho.son return -ENOMEM; 259e2d54380Skyuho.son } 260e2d54380Skyuho.son publish_ctx->subsystem = subsystem; 261e2d54380Skyuho.son publish_ctx->tgt = tgt; 262e2d54380Skyuho.son /* Allocate main loop object */ 263e2d54380Skyuho.son g_avahi_publish_simple_poll = avahi_simple_poll_new(); 264e2d54380Skyuho.son if (g_avahi_publish_simple_poll == NULL) { 265e2d54380Skyuho.son SPDK_ERRLOG("Failed to create poll object for mDNS publish.\n"); 266e2d54380Skyuho.son nvmf_avahi_publish_destroy(publish_ctx); 267e2d54380Skyuho.son return -ENOMEM; 268e2d54380Skyuho.son } 269e2d54380Skyuho.son 270e2d54380Skyuho.son assert(g_avahi_publish_client == NULL); 271e2d54380Skyuho.son 272e2d54380Skyuho.son /* Allocate a new client */ 273e2d54380Skyuho.son g_avahi_publish_client = avahi_client_new(avahi_simple_poll_get(g_avahi_publish_simple_poll), 274e2d54380Skyuho.son 0, publish_client_new_callback, publish_ctx, &error); 275e2d54380Skyuho.son /* Check whether creating the client object succeeded */ 276e2d54380Skyuho.son if (g_avahi_publish_client == NULL) { 277e2d54380Skyuho.son SPDK_ERRLOG("Failed to create mDNS client Error: %s\n", avahi_strerror(error)); 278e2d54380Skyuho.son nvmf_avahi_publish_destroy(publish_ctx); 279e2d54380Skyuho.son return -ENOMEM; 280e2d54380Skyuho.son } 281e2d54380Skyuho.son 282e2d54380Skyuho.son g_mdns_publish_ctx = publish_ctx; 283e2d54380Skyuho.son publish_ctx->poller = SPDK_POLLER_REGISTER(nvmf_avahi_publish_iterate, publish_ctx, 100 * 1000); 284e2d54380Skyuho.son return 0; 285e2d54380Skyuho.son } 286e2d54380Skyuho.son #endif 287