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 2015, Joyent, Inc.
14 */
15
16 /*
17 * Files based plug-in for varpd
18 *
19 * This is a dynamic varpd plug-in that has a static backing store. It's really
20 * nothing more than a glorified version of /etc/ethers, though it facilitates
21 * a bit more. The files module allows for the full set of mappings to be fixed
22 * at creation time. In addition, it also provides support for proxying ARP,
23 * NDP, and DHCP.
24 *
25 * At this time, the plugin requires that the destination type involve both an
26 * IP address and a port; however, there's no reason that this cannot be made
27 * more flexible as we have additional encapsulation algorithms that support it.
28 * The plug-in only has a single property, which is the location of the JSON
29 * file. The JSON file itself looks something like:
30 *
31 * {
32 * "aa:bb:cc:dd:ee:ff": {
33 * "arp": "10.23.69.1",
34 * "ndp": "2600:3c00::f03c:91ff:fe96:a264",
35 * "ip": "192.168.1.1",
36 * "port": 8080
37 * },
38 * ...
39 * }
40 */
41
42 #include <libvarpd_provider.h>
43 #include <umem.h>
44 #include <errno.h>
45 #include <thread.h>
46 #include <synch.h>
47 #include <strings.h>
48 #include <assert.h>
49 #include <limits.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <fcntl.h>
53 #include <libnvpair.h>
54 #include <unistd.h>
55 #include <sys/mman.h>
56 #include <sys/ethernet.h>
57 #include <sys/socket.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60
61 #include <libvarpd_files_json.h>
62
63 typedef struct varpd_files {
64 overlay_plugin_dest_t vaf_dest; /* RO */
65 varpd_provider_handle_t *vaf_hdl; /* RO */
66 char *vaf_path; /* WO */
67 nvlist_t *vaf_nvl; /* WO */
68 uint64_t vaf_nmisses; /* Atomic */
69 uint64_t vaf_narp; /* Atomic */
70 } varpd_files_t;
71
72 static const char *varpd_files_props[] = {
73 "files/config"
74 };
75
76 static boolean_t
varpd_files_valid_dest(overlay_plugin_dest_t dest)77 varpd_files_valid_dest(overlay_plugin_dest_t dest)
78 {
79 if (dest & ~(OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT))
80 return (B_FALSE);
81
82 if (!(dest & (OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT)))
83 return (B_FALSE);
84
85 return (B_TRUE);
86 }
87
88 static int
varpd_files_create(varpd_provider_handle_t * hdl,void ** outp,overlay_plugin_dest_t dest)89 varpd_files_create(varpd_provider_handle_t *hdl, void **outp,
90 overlay_plugin_dest_t dest)
91 {
92 varpd_files_t *vaf;
93
94 if (varpd_files_valid_dest(dest) == B_FALSE)
95 return (ENOTSUP);
96
97 vaf = umem_alloc(sizeof (varpd_files_t), UMEM_DEFAULT);
98 if (vaf == NULL)
99 return (ENOMEM);
100
101 bzero(vaf, sizeof (varpd_files_t));
102 vaf->vaf_dest = dest;
103 vaf->vaf_path = NULL;
104 vaf->vaf_nvl = NULL;
105 vaf->vaf_hdl = hdl;
106 *outp = vaf;
107 return (0);
108 }
109
110 static int
varpd_files_normalize_nvlist(varpd_files_t * vaf,nvlist_t * nvl)111 varpd_files_normalize_nvlist(varpd_files_t *vaf, nvlist_t *nvl)
112 {
113 int ret;
114 nvlist_t *out;
115 nvpair_t *pair;
116
117 if ((ret = nvlist_alloc(&out, NV_UNIQUE_NAME, 0)) != 0)
118 return (ret);
119
120 for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
121 pair = nvlist_next_nvpair(nvl, pair)) {
122 char *name, fname[ETHERADDRSTRL];
123 nvlist_t *data;
124 struct ether_addr ether, *e;
125 e = ðer;
126
127 if (nvpair_type(pair) != DATA_TYPE_NVLIST) {
128 nvlist_free(out);
129 return (EINVAL);
130 }
131
132 name = nvpair_name(pair);
133 if ((ret = nvpair_value_nvlist(pair, &data)) != 0) {
134 nvlist_free(out);
135 return (EINVAL);
136 }
137
138 if (ether_aton_r(name, e) == NULL) {
139 nvlist_free(out);
140 return (EINVAL);
141 }
142
143 if (ether_ntoa_r(e, fname) == NULL) {
144 nvlist_free(out);
145 return (ENOMEM);
146 }
147
148 if ((ret = nvlist_add_nvlist(out, fname, data)) != 0) {
149 nvlist_free(out);
150 return (EINVAL);
151 }
152 }
153
154 vaf->vaf_nvl = out;
155 return (0);
156 }
157
158 static int
varpd_files_start(void * arg)159 varpd_files_start(void *arg)
160 {
161 int fd, ret;
162 void *maddr;
163 struct stat st;
164 nvlist_t *nvl;
165 varpd_files_t *vaf = arg;
166
167 if (vaf->vaf_path == NULL)
168 return (EAGAIN);
169
170 if ((fd = open(vaf->vaf_path, O_RDONLY)) < 0)
171 return (errno);
172
173 if (fstat(fd, &st) != 0) {
174 ret = errno;
175 if (close(fd) != 0)
176 abort();
177 return (ret);
178 }
179
180 maddr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
181 fd, 0);
182 if (maddr == NULL) {
183 ret = errno;
184 if (close(fd) != 0)
185 abort();
186 return (ret);
187 }
188
189 ret = nvlist_parse_json(maddr, st.st_size, &nvl,
190 NVJSON_FORCE_INTEGER, NULL);
191 if (ret == 0) {
192 ret = varpd_files_normalize_nvlist(vaf, nvl);
193 nvlist_free(nvl);
194 }
195 if (munmap(maddr, st.st_size) != 0)
196 abort();
197 if (close(fd) != 0)
198 abort();
199
200 return (ret);
201 }
202
203 static void
varpd_files_stop(void * arg)204 varpd_files_stop(void *arg)
205 {
206 varpd_files_t *vaf = arg;
207
208 nvlist_free(vaf->vaf_nvl);
209 vaf->vaf_nvl = NULL;
210 }
211
212 static void
varpd_files_destroy(void * arg)213 varpd_files_destroy(void *arg)
214 {
215 varpd_files_t *vaf = arg;
216
217 assert(vaf->vaf_nvl == NULL);
218 if (vaf->vaf_path != NULL) {
219 umem_free(vaf->vaf_path, strlen(vaf->vaf_path) + 1);
220 vaf->vaf_path = NULL;
221 }
222 umem_free(vaf, sizeof (varpd_files_t));
223 }
224
225 static void
varpd_files_lookup(void * arg,varpd_query_handle_t * qh,const overlay_targ_lookup_t * otl,overlay_target_point_t * otp)226 varpd_files_lookup(void *arg, varpd_query_handle_t *qh,
227 const overlay_targ_lookup_t *otl, overlay_target_point_t *otp)
228 {
229 char macstr[ETHERADDRSTRL], *ipstr;
230 nvlist_t *nvl;
231 varpd_files_t *vaf = arg;
232 int32_t port;
233 static const uint8_t bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
234
235 /* We don't support a default */
236 if (otl == NULL) {
237 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
238 return;
239 }
240
241 if (otl->otl_sap == ETHERTYPE_ARP) {
242 libvarpd_plugin_proxy_arp(vaf->vaf_hdl, qh, otl);
243 return;
244 }
245
246 if (otl->otl_sap == ETHERTYPE_IPV6 &&
247 otl->otl_dstaddr[0] == 0x33 &&
248 otl->otl_dstaddr[1] == 0x33) {
249 libvarpd_plugin_proxy_ndp(vaf->vaf_hdl, qh, otl);
250 return;
251 }
252
253 if (otl->otl_sap == ETHERTYPE_IP &&
254 bcmp(otl->otl_dstaddr, bcast, ETHERADDRL) == 0) {
255 char *mac;
256 struct ether_addr a, *addr;
257
258 addr = &a;
259 if (ether_ntoa_r((struct ether_addr *)otl->otl_srcaddr,
260 macstr) == NULL) {
261 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
262 return;
263 }
264
265 if (nvlist_lookup_nvlist(vaf->vaf_nvl, macstr, &nvl) != 0) {
266 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
267 return;
268 }
269
270 if (nvlist_lookup_string(nvl, "dhcp-proxy", &mac) != 0) {
271 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
272 return;
273 }
274
275 if (ether_aton_r(mac, addr) == NULL) {
276 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
277 return;
278 }
279
280 libvarpd_plugin_proxy_dhcp(vaf->vaf_hdl, qh, otl);
281 return;
282 }
283
284 if (ether_ntoa_r((struct ether_addr *)otl->otl_dstaddr,
285 macstr) == NULL) {
286 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
287 return;
288 }
289
290 if (nvlist_lookup_nvlist(vaf->vaf_nvl, macstr, &nvl) != 0) {
291 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
292 return;
293 }
294
295 if (nvlist_lookup_int32(nvl, "port", &port) != 0) {
296 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
297 return;
298 }
299
300 if (port <= 0 || port > UINT16_MAX) {
301 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
302 return;
303 }
304 otp->otp_port = port;
305
306 if (nvlist_lookup_string(nvl, "ip", &ipstr) != 0) {
307 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
308 return;
309 }
310
311 /*
312 * Try to parse it as a v6 address and then if it's not, try to
313 * transform it into a v4 address which we'll then wrap it into a v4
314 * mapped address.
315 */
316 if (inet_pton(AF_INET6, ipstr, &otp->otp_ip) != 1) {
317 uint32_t v4;
318 if (inet_pton(AF_INET, ipstr, &v4) != 1) {
319 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP);
320 return;
321 }
322 IN6_IPADDR_TO_V4MAPPED(v4, &otp->otp_ip);
323 }
324
325 libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_OK);
326 }
327
328 /* ARGSUSED */
329 static int
varpd_files_nprops(void * arg,uint_t * nprops)330 varpd_files_nprops(void *arg, uint_t *nprops)
331 {
332 *nprops = 1;
333 return (0);
334 }
335
336 /* ARGSUSED */
337 static int
varpd_files_propinfo(void * arg,uint_t propid,varpd_prop_handle_t * vph)338 varpd_files_propinfo(void *arg, uint_t propid, varpd_prop_handle_t *vph)
339 {
340 if (propid != 0)
341 return (EINVAL);
342
343 libvarpd_prop_set_name(vph, varpd_files_props[0]);
344 libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW);
345 libvarpd_prop_set_type(vph, OVERLAY_PROP_T_STRING);
346 libvarpd_prop_set_nodefault(vph);
347 return (0);
348 }
349
350 static int
varpd_files_getprop(void * arg,const char * pname,void * buf,uint32_t * sizep)351 varpd_files_getprop(void *arg, const char *pname, void *buf, uint32_t *sizep)
352 {
353 varpd_files_t *vaf = arg;
354
355 if (strcmp(pname, varpd_files_props[0]) != 0)
356 return (EINVAL);
357
358 if (vaf->vaf_path != NULL) {
359 size_t len = strlen(vaf->vaf_path) + 1;
360 if (*sizep < len)
361 return (EOVERFLOW);
362 *sizep = len;
363 (void) strlcpy(buf, vaf->vaf_path, *sizep);
364
365 } else {
366 *sizep = 0;
367 }
368
369 return (0);
370 }
371
372 static int
varpd_files_setprop(void * arg,const char * pname,const void * buf,const uint32_t size)373 varpd_files_setprop(void *arg, const char *pname, const void *buf,
374 const uint32_t size)
375 {
376 varpd_files_t *vaf = arg;
377
378 if (strcmp(pname, varpd_files_props[0]) != 0)
379 return (EINVAL);
380
381 if (vaf->vaf_path != NULL)
382 umem_free(vaf->vaf_path, strlen(vaf->vaf_path) + 1);
383
384 vaf->vaf_path = umem_alloc(size, UMEM_DEFAULT);
385 if (vaf->vaf_path == NULL)
386 return (ENOMEM);
387 (void) strlcpy(vaf->vaf_path, buf, size);
388 return (0);
389 }
390
391 static int
varpd_files_save(void * arg,nvlist_t * nvp)392 varpd_files_save(void *arg, nvlist_t *nvp)
393 {
394 int ret;
395 varpd_files_t *vaf = arg;
396
397 if (vaf->vaf_path == NULL)
398 return (0);
399
400 if ((ret = nvlist_add_string(nvp, varpd_files_props[0],
401 vaf->vaf_path)) != 0)
402 return (ret);
403
404 if ((ret = nvlist_add_uint64(nvp, "files/vaf_nmisses",
405 vaf->vaf_nmisses)) != 0)
406 return (ret);
407
408 if ((ret = nvlist_add_uint64(nvp, "files/vaf_narp",
409 vaf->vaf_narp)) != 0)
410 return (ret);
411 return (0);
412 }
413
414 static int
varpd_files_restore(nvlist_t * nvp,varpd_provider_handle_t * hdl,overlay_plugin_dest_t dest,void ** outp)415 varpd_files_restore(nvlist_t *nvp, varpd_provider_handle_t *hdl,
416 overlay_plugin_dest_t dest, void **outp)
417 {
418 varpd_files_t *vaf;
419 char *str;
420 int ret;
421 uint64_t nmisses, narp;
422
423 if (varpd_files_valid_dest(dest) == B_FALSE)
424 return (EINVAL);
425
426 ret = nvlist_lookup_string(nvp, varpd_files_props[0], &str);
427 if (ret != 0 && ret != ENOENT)
428 return (ret);
429 else if (ret == ENOENT)
430 str = NULL;
431
432 if (nvlist_lookup_uint64(nvp, "files/vaf_nmisses", &nmisses) != 0)
433 return (EINVAL);
434 if (nvlist_lookup_uint64(nvp, "files/vaf_narp", &narp) != 0)
435 return (EINVAL);
436
437 vaf = umem_alloc(sizeof (varpd_files_t), UMEM_DEFAULT);
438 if (vaf == NULL)
439 return (ENOMEM);
440
441 bzero(vaf, sizeof (varpd_files_t));
442 vaf->vaf_dest = dest;
443 if (str != NULL) {
444 size_t len = strlen(str) + 1;
445 vaf->vaf_path = umem_alloc(len, UMEM_DEFAULT);
446 if (vaf->vaf_path == NULL) {
447 umem_free(vaf, sizeof (varpd_files_t));
448 return (ENOMEM);
449 }
450 (void) strlcpy(vaf->vaf_path, str, len);
451 }
452
453 vaf->vaf_hdl = hdl;
454 *outp = vaf;
455 return (0);
456 }
457
458 static void
varpd_files_proxy_arp(void * arg,varpd_arp_handle_t * vah,int kind,const struct sockaddr * sock,uint8_t * out)459 varpd_files_proxy_arp(void *arg, varpd_arp_handle_t *vah, int kind,
460 const struct sockaddr *sock, uint8_t *out)
461 {
462 varpd_files_t *vaf = arg;
463 const struct sockaddr_in *ip;
464 const struct sockaddr_in6 *ip6;
465 nvpair_t *pair;
466
467 if (kind != VARPD_QTYPE_ETHERNET) {
468 libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP);
469 return;
470 }
471
472 if (sock->sa_family != AF_INET && sock->sa_family != AF_INET6) {
473 libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP);
474 return;
475 }
476
477 ip = (const struct sockaddr_in *)sock;
478 ip6 = (const struct sockaddr_in6 *)sock;
479 for (pair = nvlist_next_nvpair(vaf->vaf_nvl, NULL); pair != NULL;
480 pair = nvlist_next_nvpair(vaf->vaf_nvl, pair)) {
481 char *mac, *ipstr;
482 nvlist_t *data;
483 struct in_addr ia;
484 struct in6_addr ia6;
485 struct ether_addr ether, *e;
486 e = ðer;
487
488 if (nvpair_type(pair) != DATA_TYPE_NVLIST)
489 continue;
490
491 mac = nvpair_name(pair);
492 if (nvpair_value_nvlist(pair, &data) != 0)
493 continue;
494
495
496 if (sock->sa_family == AF_INET) {
497 if (nvlist_lookup_string(data, "arp", &ipstr) != 0)
498 continue;
499
500 if (inet_pton(AF_INET, ipstr, &ia) != 1)
501 continue;
502
503 if (bcmp(&ia, &ip->sin_addr,
504 sizeof (struct in_addr)) != 0)
505 continue;
506 } else {
507 if (nvlist_lookup_string(data, "ndp", &ipstr) != 0)
508 continue;
509
510 if (inet_pton(AF_INET6, ipstr, &ia6) != 1)
511 continue;
512
513 if (bcmp(&ia6, &ip6->sin6_addr,
514 sizeof (struct in6_addr)) != 0)
515 continue;
516 }
517
518 if (ether_aton_r(mac, e) == NULL) {
519 libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP);
520 return;
521 }
522
523 bcopy(e, out, ETHERADDRL);
524 libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_OK);
525 return;
526 }
527
528 libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP);
529 }
530
531 static void
varpd_files_proxy_dhcp(void * arg,varpd_dhcp_handle_t * vdh,int type,const overlay_targ_lookup_t * otl,uint8_t * out)532 varpd_files_proxy_dhcp(void *arg, varpd_dhcp_handle_t *vdh, int type,
533 const overlay_targ_lookup_t *otl, uint8_t *out)
534 {
535 varpd_files_t *vaf = arg;
536 nvlist_t *nvl;
537 char macstr[ETHERADDRSTRL], *mac;
538 struct ether_addr a, *addr;
539
540 addr = &a;
541 if (type != VARPD_QTYPE_ETHERNET) {
542 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP);
543 return;
544 }
545
546 if (ether_ntoa_r((struct ether_addr *)otl->otl_srcaddr,
547 macstr) == NULL) {
548 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP);
549 return;
550 }
551
552 if (nvlist_lookup_nvlist(vaf->vaf_nvl, macstr, &nvl) != 0) {
553 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP);
554 return;
555 }
556
557 if (nvlist_lookup_string(nvl, "dhcp-proxy", &mac) != 0) {
558 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP);
559 return;
560 }
561
562 if (ether_aton_r(mac, addr) == NULL) {
563 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP);
564 return;
565 }
566
567 bcopy(addr, out, ETHERADDRL);
568 libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_OK);
569 }
570
571 static const varpd_plugin_ops_t varpd_files_ops = {
572 0,
573 varpd_files_create,
574 varpd_files_start,
575 varpd_files_stop,
576 varpd_files_destroy,
577 NULL,
578 varpd_files_lookup,
579 varpd_files_nprops,
580 varpd_files_propinfo,
581 varpd_files_getprop,
582 varpd_files_setprop,
583 varpd_files_save,
584 varpd_files_restore,
585 varpd_files_proxy_arp,
586 varpd_files_proxy_dhcp
587 };
588
589 #pragma init(varpd_files_init)
590 static void
varpd_files_init(void)591 varpd_files_init(void)
592 {
593 int err;
594 varpd_plugin_register_t *vpr;
595
596 vpr = libvarpd_plugin_alloc(VARPD_CURRENT_VERSION, &err);
597 if (vpr == NULL)
598 return;
599
600 vpr->vpr_mode = OVERLAY_TARGET_DYNAMIC;
601 vpr->vpr_name = "files";
602 vpr->vpr_ops = &varpd_files_ops;
603 (void) libvarpd_plugin_register(vpr);
604 libvarpd_plugin_free(vpr);
605 }
606