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 2016 Joyent, Inc. 14 */ 15 16 /* 17 * Point to point plug-in for varpd. 18 * 19 * This plugin implements a simple point to point plugin for a packet. It 20 * represents the traditional tunnel, just in overlay form. As such, the only 21 * properties it needs are those to determine where to send everything. At this 22 * time, we don't allow a multicast address; however, there's no reason that the 23 * direct plugin shouldn't in theory support multicast, though when implementing 24 * it the best path will become clear. 25 * 26 * In general this module has been designed to make it easy to support a 27 * destination of either IP or IP and port; however, we restrict it to the 28 * latter as we don't currently have an implementation that would allow us to 29 * test that. 30 */ 31 32 #include <libvarpd_provider.h> 33 #include <umem.h> 34 #include <errno.h> 35 #include <thread.h> 36 #include <synch.h> 37 #include <strings.h> 38 #include <assert.h> 39 #include <limits.h> 40 #include <sys/types.h> 41 #include <sys/socket.h> 42 #include <netinet/in.h> 43 #include <arpa/inet.h> 44 #include <libnvpair.h> 45 46 typedef struct varpd_direct { 47 overlay_plugin_dest_t vad_dest; /* RO */ 48 mutex_t vad_lock; /* Protects the rest */ 49 boolean_t vad_hip; 50 boolean_t vad_hport; 51 struct in6_addr vad_ip; 52 uint16_t vad_port; 53 } varpd_direct_t; 54 55 static const char *varpd_direct_props[] = { 56 "direct/dest_ip", 57 "direct/dest_port" 58 }; 59 60 static boolean_t 61 varpd_direct_valid_dest(overlay_plugin_dest_t dest) 62 { 63 if (dest & ~(OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT)) 64 return (B_FALSE); 65 66 if (!(dest & (OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT))) 67 return (B_FALSE); 68 69 return (B_TRUE); 70 } 71 72 /* ARGSUSED */ 73 static int 74 varpd_direct_create(varpd_provider_handle_t *hdl, void **outp, 75 overlay_plugin_dest_t dest) 76 { 77 int ret; 78 varpd_direct_t *vdp; 79 80 if (varpd_direct_valid_dest(dest) == B_FALSE) 81 return (ENOTSUP); 82 83 vdp = umem_alloc(sizeof (varpd_direct_t), UMEM_DEFAULT); 84 if (vdp == NULL) 85 return (ENOMEM); 86 87 if ((ret = mutex_init(&vdp->vad_lock, USYNC_THREAD | LOCK_ERRORCHECK, 88 NULL)) != 0) { 89 umem_free(vdp, sizeof (varpd_direct_t)); 90 return (ret); 91 } 92 93 vdp->vad_dest = dest; 94 vdp->vad_hip = B_FALSE; 95 vdp->vad_hport = B_FALSE; 96 *outp = vdp; 97 return (0); 98 } 99 100 static int 101 varpd_direct_start(void *arg) 102 { 103 varpd_direct_t *vdp = arg; 104 105 mutex_enter(&vdp->vad_lock); 106 if (vdp->vad_hip == B_FALSE ||((vdp->vad_dest & OVERLAY_PLUGIN_D_IP) && 107 vdp->vad_hport == B_FALSE)) { 108 mutex_exit(&vdp->vad_lock); 109 return (EAGAIN); 110 } 111 mutex_exit(&vdp->vad_lock); 112 113 return (0); 114 } 115 116 /* ARGSUSED */ 117 static void 118 varpd_direct_stop(void *arg) 119 { 120 } 121 122 static void 123 varpd_direct_destroy(void *arg) 124 { 125 varpd_direct_t *vdp = arg; 126 127 if (mutex_destroy(&vdp->vad_lock) != 0) 128 abort(); 129 umem_free(vdp, sizeof (varpd_direct_t)); 130 } 131 132 static int 133 varpd_direct_default(void *arg, overlay_target_point_t *otp) 134 { 135 varpd_direct_t *vdp = arg; 136 137 mutex_enter(&vdp->vad_lock); 138 bcopy(&vdp->vad_ip, &otp->otp_ip, sizeof (struct in6_addr)); 139 otp->otp_port = vdp->vad_port; 140 mutex_exit(&vdp->vad_lock); 141 142 return (VARPD_LOOKUP_OK); 143 } 144 145 static int 146 varpd_direct_nprops(void *arg, uint_t *nprops) 147 { 148 const varpd_direct_t *vdp = arg; 149 150 *nprops = 0; 151 if (vdp->vad_dest & OVERLAY_PLUGIN_D_ETHERNET) 152 *nprops += 1; 153 154 if (vdp->vad_dest & OVERLAY_PLUGIN_D_IP) 155 *nprops += 1; 156 157 if (vdp->vad_dest & OVERLAY_PLUGIN_D_PORT) 158 *nprops += 1; 159 160 assert(*nprops == 1 || *nprops == 2); 161 162 return (0); 163 } 164 165 static int 166 varpd_direct_propinfo(void *arg, uint_t propid, varpd_prop_handle_t *vph) 167 { 168 varpd_direct_t *vdp = arg; 169 170 /* 171 * Because we only support IP + port combos right now, prop 0 should 172 * always be the IP. We don't support a port without an IP. 173 */ 174 assert(vdp->vad_dest & OVERLAY_PLUGIN_D_IP); 175 if (propid == 0) { 176 libvarpd_prop_set_name(vph, varpd_direct_props[0]); 177 libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW); 178 libvarpd_prop_set_type(vph, OVERLAY_PROP_T_IP); 179 libvarpd_prop_set_nodefault(vph); 180 return (0); 181 } 182 183 if (propid == 1 && vdp->vad_dest & OVERLAY_PLUGIN_D_PORT) { 184 libvarpd_prop_set_name(vph, varpd_direct_props[1]); 185 libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW); 186 libvarpd_prop_set_type(vph, OVERLAY_PROP_T_UINT); 187 libvarpd_prop_set_nodefault(vph); 188 libvarpd_prop_set_range_uint32(vph, 1, UINT16_MAX); 189 return (0); 190 } 191 192 return (EINVAL); 193 } 194 195 static int 196 varpd_direct_getprop(void *arg, const char *pname, void *buf, uint32_t *sizep) 197 { 198 varpd_direct_t *vdp = arg; 199 200 /* direct/dest_ip */ 201 if (strcmp(pname, varpd_direct_props[0]) == 0) { 202 if (*sizep < sizeof (struct in6_addr)) 203 return (EOVERFLOW); 204 mutex_enter(&vdp->vad_lock); 205 if (vdp->vad_hip == B_FALSE) { 206 *sizep = 0; 207 } else { 208 bcopy(&vdp->vad_ip, buf, sizeof (struct in6_addr)); 209 *sizep = sizeof (struct in6_addr); 210 } 211 mutex_exit(&vdp->vad_lock); 212 return (0); 213 } 214 215 /* direct/dest_port */ 216 if (strcmp(pname, varpd_direct_props[1]) == 0) { 217 uint64_t val; 218 219 if (*sizep < sizeof (uint64_t)) 220 return (EOVERFLOW); 221 mutex_enter(&vdp->vad_lock); 222 if (vdp->vad_hport == B_FALSE) { 223 *sizep = 0; 224 } else { 225 val = vdp->vad_port; 226 bcopy(&val, buf, sizeof (uint64_t)); 227 *sizep = sizeof (uint64_t); 228 } 229 mutex_exit(&vdp->vad_lock); 230 return (0); 231 } 232 233 return (EINVAL); 234 } 235 236 static int 237 varpd_direct_setprop(void *arg, const char *pname, const void *buf, 238 const uint32_t size) 239 { 240 varpd_direct_t *vdp = arg; 241 242 /* direct/dest_ip */ 243 if (strcmp(pname, varpd_direct_props[0]) == 0) { 244 const struct in6_addr *ipv6 = buf; 245 246 if (size < sizeof (struct in6_addr)) 247 return (EOVERFLOW); 248 249 if (IN6_IS_ADDR_V4COMPAT(ipv6)) 250 return (EINVAL); 251 252 if (IN6_IS_ADDR_6TO4(ipv6)) 253 return (EINVAL); 254 255 mutex_enter(&vdp->vad_lock); 256 bcopy(buf, &vdp->vad_ip, sizeof (struct in6_addr)); 257 vdp->vad_hip = B_TRUE; 258 mutex_exit(&vdp->vad_lock); 259 return (0); 260 } 261 262 /* direct/dest_port */ 263 if (strcmp(pname, varpd_direct_props[1]) == 0) { 264 const uint64_t *valp = buf; 265 if (size < sizeof (uint64_t)) 266 return (EOVERFLOW); 267 268 if (*valp == 0 || *valp > UINT16_MAX) 269 return (EINVAL); 270 271 mutex_enter(&vdp->vad_lock); 272 vdp->vad_port = (uint16_t)*valp; 273 vdp->vad_hport = B_TRUE; 274 mutex_exit(&vdp->vad_lock); 275 return (0); 276 } 277 278 return (EINVAL); 279 } 280 281 static int 282 varpd_direct_save(void *arg, nvlist_t *nvp) 283 { 284 int ret; 285 varpd_direct_t *vdp = arg; 286 287 mutex_enter(&vdp->vad_lock); 288 if (vdp->vad_hport == B_TRUE) { 289 if ((ret = nvlist_add_uint16(nvp, varpd_direct_props[1], 290 vdp->vad_port)) != 0) { 291 mutex_exit(&vdp->vad_lock); 292 return (ret); 293 } 294 } 295 296 if (vdp->vad_hip == B_TRUE) { 297 char buf[INET6_ADDRSTRLEN]; 298 299 if (inet_ntop(AF_INET6, &vdp->vad_ip, buf, sizeof (buf)) == 300 NULL) 301 abort(); 302 if ((ret = nvlist_add_string(nvp, varpd_direct_props[0], 303 buf)) != 0) { 304 mutex_exit(&vdp->vad_lock); 305 return (ret); 306 } 307 } 308 mutex_exit(&vdp->vad_lock); 309 310 return (0); 311 } 312 313 /* ARGSUSED */ 314 static int 315 varpd_direct_restore(nvlist_t *nvp, varpd_provider_handle_t *hdl, 316 overlay_plugin_dest_t dest, void **outp) 317 { 318 int ret; 319 char *ipstr; 320 varpd_direct_t *vdp; 321 322 if (varpd_direct_valid_dest(dest) == B_FALSE) 323 return (ENOTSUP); 324 325 vdp = umem_alloc(sizeof (varpd_direct_t), UMEM_DEFAULT); 326 if (vdp == NULL) 327 return (ENOMEM); 328 329 if ((ret = mutex_init(&vdp->vad_lock, USYNC_THREAD | LOCK_ERRORCHECK, 330 NULL)) != 0) { 331 umem_free(vdp, sizeof (varpd_direct_t)); 332 return (ret); 333 } 334 335 if ((ret = nvlist_lookup_uint16(nvp, varpd_direct_props[1], 336 &vdp->vad_port)) != 0) { 337 if (ret != ENOENT) { 338 if (mutex_destroy(&vdp->vad_lock) != 0) 339 abort(); 340 umem_free(vdp, sizeof (varpd_direct_t)); 341 return (ret); 342 } 343 vdp->vad_hport = B_FALSE; 344 } else { 345 vdp->vad_hport = B_TRUE; 346 } 347 348 if ((ret = nvlist_lookup_string(nvp, varpd_direct_props[0], 349 &ipstr)) != 0) { 350 if (ret != ENOENT) { 351 if (mutex_destroy(&vdp->vad_lock) != 0) 352 abort(); 353 umem_free(vdp, sizeof (varpd_direct_t)); 354 return (ret); 355 } 356 vdp->vad_hip = B_FALSE; 357 } else { 358 ret = inet_pton(AF_INET6, ipstr, &vdp->vad_ip); 359 /* 360 * inet_pton is only defined to return -1 with errno set to 361 * EAFNOSUPPORT, which really, shouldn't happen. 362 */ 363 if (ret == -1) { 364 assert(errno == EAFNOSUPPORT); 365 abort(); 366 } 367 if (ret == 0) { 368 if (mutex_destroy(&vdp->vad_lock) != 0) 369 abort(); 370 umem_free(vdp, sizeof (varpd_direct_t)); 371 return (EINVAL); 372 } 373 } 374 375 *outp = vdp; 376 return (0); 377 } 378 379 static const varpd_plugin_ops_t varpd_direct_ops = { 380 0, 381 varpd_direct_create, 382 varpd_direct_start, 383 varpd_direct_stop, 384 varpd_direct_destroy, 385 varpd_direct_default, 386 NULL, 387 varpd_direct_nprops, 388 varpd_direct_propinfo, 389 varpd_direct_getprop, 390 varpd_direct_setprop, 391 varpd_direct_save, 392 varpd_direct_restore 393 }; 394 395 #pragma init(varpd_direct_init) 396 static void 397 varpd_direct_init(void) 398 { 399 int err; 400 varpd_plugin_register_t *vpr; 401 402 vpr = libvarpd_plugin_alloc(VARPD_CURRENT_VERSION, &err); 403 if (vpr == NULL) 404 return; 405 406 vpr->vpr_mode = OVERLAY_TARGET_POINT; 407 vpr->vpr_name = "direct"; 408 vpr->vpr_ops = &varpd_direct_ops; 409 (void) libvarpd_plugin_register(vpr); 410 libvarpd_plugin_free(vpr); 411 } 412