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