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
varpd_direct_valid_dest(overlay_plugin_dest_t dest)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
varpd_direct_create(varpd_provider_handle_t * hdl,void ** outp,overlay_plugin_dest_t dest)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
varpd_direct_start(void * arg)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
varpd_direct_stop(void * arg)118 varpd_direct_stop(void *arg)
119 {
120 }
121
122 static void
varpd_direct_destroy(void * arg)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
varpd_direct_default(void * arg,overlay_target_point_t * otp)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
varpd_direct_nprops(void * arg,uint_t * nprops)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
varpd_direct_propinfo(void * arg,uint_t propid,varpd_prop_handle_t * vph)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
varpd_direct_getprop(void * arg,const char * pname,void * buf,uint32_t * sizep)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
varpd_direct_setprop(void * arg,const char * pname,const void * buf,const uint32_t size)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
varpd_direct_save(void * arg,nvlist_t * nvp)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
varpd_direct_restore(nvlist_t * nvp,varpd_provider_handle_t * hdl,overlay_plugin_dest_t dest,void ** outp)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
varpd_direct_init(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