136589d6bSRobert Mustacchi /*
236589d6bSRobert Mustacchi  * This file and its contents are supplied under the terms of the
336589d6bSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
436589d6bSRobert Mustacchi  * You may only use this file in accordance with the terms of version
536589d6bSRobert Mustacchi  * 1.0 of the CDDL.
636589d6bSRobert Mustacchi  *
736589d6bSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
836589d6bSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
936589d6bSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
1036589d6bSRobert Mustacchi  */
1136589d6bSRobert Mustacchi 
1236589d6bSRobert Mustacchi /*
1336589d6bSRobert Mustacchi  * Copyright 2018 Joyent, Inc.
14*8bcd8eeaSBrian Bennett  * Copyright 2022 MNX Cloud, Inc.
1536589d6bSRobert Mustacchi  */
1636589d6bSRobert Mustacchi 
1736589d6bSRobert Mustacchi /*
1836589d6bSRobert Mustacchi  * VXLAN encapsulation module
1936589d6bSRobert Mustacchi  *
2036589d6bSRobert Mustacchi  *
2136589d6bSRobert Mustacchi  * The VXLAN header looks as follows in network byte order:
2236589d6bSRobert Mustacchi  *
2336589d6bSRobert Mustacchi  * |0        3| 4 |5                     31|
2436589d6bSRobert Mustacchi  * +----------+---+------------------------+
2536589d6bSRobert Mustacchi  * | Reserved | I | Reserved               |
2636589d6bSRobert Mustacchi  * +---------------------------------------+
2736589d6bSRobert Mustacchi  * | Virtual Network ID         | Reserved |
2836589d6bSRobert Mustacchi  * +----------------------------+----------+
2936589d6bSRobert Mustacchi  * |0                         23|24      31|
3036589d6bSRobert Mustacchi  *
3136589d6bSRobert Mustacchi  * All reserved values must be 0. The I bit must be 1. We call the top
3236589d6bSRobert Mustacchi  * word the VXLAN magic field for the time being. The second word is
3336589d6bSRobert Mustacchi  * definitely not the most friendly way to operate. Specifically, the ID
3436589d6bSRobert Mustacchi  * is a 24-bit big endian value, but we have to make sure not to use the
3536589d6bSRobert Mustacchi  * reserved byte.
3636589d6bSRobert Mustacchi  *
3736589d6bSRobert Mustacchi  * For us, VXLAN encapsulation is a fairly straightforward implementation. It
3836589d6bSRobert Mustacchi  * only has two properties, a listen_ip and a listen_port. These determine on
3936589d6bSRobert Mustacchi  * what address we should be listening on. While we do not have a default
4036589d6bSRobert Mustacchi  * address to listen upon, we do have a default port, which is the IANA assigned
4136589d6bSRobert Mustacchi  * port for VXLAN -- 4789.
4236589d6bSRobert Mustacchi  */
4336589d6bSRobert Mustacchi 
4436589d6bSRobert Mustacchi #include <sys/overlay_plugin.h>
4536589d6bSRobert Mustacchi #include <sys/modctl.h>
4636589d6bSRobert Mustacchi #include <sys/errno.h>
4736589d6bSRobert Mustacchi #include <sys/byteorder.h>
4836589d6bSRobert Mustacchi #include <sys/vxlan.h>
4936589d6bSRobert Mustacchi #include <inet/ip.h>
5036589d6bSRobert Mustacchi #include <netinet/in.h>
5136589d6bSRobert Mustacchi #include <sys/strsun.h>
5236589d6bSRobert Mustacchi #include <netinet/udp.h>
5336589d6bSRobert Mustacchi 
5436589d6bSRobert Mustacchi static const char *vxlan_ident = "vxlan";
5536589d6bSRobert Mustacchi static uint16_t vxlan_defport = IPPORT_VXLAN;
5636589d6bSRobert Mustacchi 
5736589d6bSRobert Mustacchi /*
5836589d6bSRobert Mustacchi  * Should we enable UDP source port hashing for fanout.
5936589d6bSRobert Mustacchi  */
6036589d6bSRobert Mustacchi boolean_t vxlan_fanout = B_TRUE;
6136589d6bSRobert Mustacchi 
6236589d6bSRobert Mustacchi /*
6336589d6bSRobert Mustacchi  * This represents the size in bytes that we want to allocate when allocating a
6436589d6bSRobert Mustacchi  * vxlan header block. This is intended such that lower levels can try and use
6536589d6bSRobert Mustacchi  * the message block that we allocate for the IP and UPD header. The hope is
6636589d6bSRobert Mustacchi  * that even if this is tunneled, that this is enough space.
6736589d6bSRobert Mustacchi  *
6836589d6bSRobert Mustacchi  * The vxlan_noalloc_min value represents the minimum amount of space we need to
6936589d6bSRobert Mustacchi  * consider not allocating a message block and just passing it down the stack in
7036589d6bSRobert Mustacchi  * this form. This number assumes that we have a VLAN tag, so 18 byte Ethernet
7136589d6bSRobert Mustacchi  * header, 20 byte IP header, 8 byte UDP header, and 8 byte VXLAN header.
7236589d6bSRobert Mustacchi  */
7336589d6bSRobert Mustacchi uint_t vxlan_alloc_size = 128;
7436589d6bSRobert Mustacchi uint_t vxlan_noalloc_min = 54;
7536589d6bSRobert Mustacchi 
7636589d6bSRobert Mustacchi static const char *vxlan_props[] = {
7736589d6bSRobert Mustacchi 	"vxlan/listen_ip",
7836589d6bSRobert Mustacchi 	"vxlan/listen_port",
7936589d6bSRobert Mustacchi 	NULL
8036589d6bSRobert Mustacchi };
8136589d6bSRobert Mustacchi 
8236589d6bSRobert Mustacchi typedef struct vxlan {
8336589d6bSRobert Mustacchi 	kmutex_t vxl_lock;
8436589d6bSRobert Mustacchi 	overlay_handle_t vxl_oh;
8536589d6bSRobert Mustacchi 	uint16_t vxl_lport;
8636589d6bSRobert Mustacchi 	boolean_t vxl_hladdr;
8736589d6bSRobert Mustacchi 	struct in6_addr vxl_laddr;
8836589d6bSRobert Mustacchi } vxlan_t;
8936589d6bSRobert Mustacchi 
9036589d6bSRobert Mustacchi static int
vxlan_o_init(overlay_handle_t oh,void ** outp)9136589d6bSRobert Mustacchi vxlan_o_init(overlay_handle_t oh, void **outp)
9236589d6bSRobert Mustacchi {
9336589d6bSRobert Mustacchi 	vxlan_t *vxl;
9436589d6bSRobert Mustacchi 
9536589d6bSRobert Mustacchi 	vxl = kmem_alloc(sizeof (vxlan_t), KM_SLEEP);
9636589d6bSRobert Mustacchi 	*outp = vxl;
9736589d6bSRobert Mustacchi 	mutex_init(&vxl->vxl_lock, NULL, MUTEX_DRIVER, NULL);
9836589d6bSRobert Mustacchi 	vxl->vxl_oh = oh;
9936589d6bSRobert Mustacchi 	vxl->vxl_lport = vxlan_defport;
10036589d6bSRobert Mustacchi 	vxl->vxl_hladdr = B_FALSE;
10136589d6bSRobert Mustacchi 
10236589d6bSRobert Mustacchi 	return (0);
10336589d6bSRobert Mustacchi }
10436589d6bSRobert Mustacchi 
10536589d6bSRobert Mustacchi static void
vxlan_o_fini(void * arg)10636589d6bSRobert Mustacchi vxlan_o_fini(void *arg)
10736589d6bSRobert Mustacchi {
10836589d6bSRobert Mustacchi 	vxlan_t *vxl = arg;
10936589d6bSRobert Mustacchi 
11036589d6bSRobert Mustacchi 	mutex_destroy(&vxl->vxl_lock);
11136589d6bSRobert Mustacchi 	kmem_free(arg, sizeof (vxlan_t));
11236589d6bSRobert Mustacchi }
11336589d6bSRobert Mustacchi 
11436589d6bSRobert Mustacchi static int
vxlan_o_socket(void * arg,int * dp,int * fp,int * pp,struct sockaddr * addr,socklen_t * slenp)11536589d6bSRobert Mustacchi vxlan_o_socket(void *arg, int *dp, int *fp, int *pp, struct sockaddr *addr,
11636589d6bSRobert Mustacchi     socklen_t *slenp)
11736589d6bSRobert Mustacchi {
11836589d6bSRobert Mustacchi 	vxlan_t *vxl = arg;
11936589d6bSRobert Mustacchi 	struct sockaddr_in6 *in;
12036589d6bSRobert Mustacchi 
12136589d6bSRobert Mustacchi 	in = (struct sockaddr_in6 *)addr;
12236589d6bSRobert Mustacchi 	*dp = AF_INET6;
12336589d6bSRobert Mustacchi 	*fp = SOCK_DGRAM;
12436589d6bSRobert Mustacchi 	*pp = 0;
12536589d6bSRobert Mustacchi 	bzero(in, sizeof (struct sockaddr_in6));
12636589d6bSRobert Mustacchi 	in->sin6_family = AF_INET6;
12736589d6bSRobert Mustacchi 
12836589d6bSRobert Mustacchi 	/*
12936589d6bSRobert Mustacchi 	 * We should consider a more expressive private errno set that
13036589d6bSRobert Mustacchi 	 * provider's can use.
13136589d6bSRobert Mustacchi 	 */
13236589d6bSRobert Mustacchi 	mutex_enter(&vxl->vxl_lock);
13336589d6bSRobert Mustacchi 	if (vxl->vxl_hladdr == B_FALSE) {
13436589d6bSRobert Mustacchi 		mutex_exit(&vxl->vxl_lock);
13536589d6bSRobert Mustacchi 		return (EINVAL);
13636589d6bSRobert Mustacchi 	}
13736589d6bSRobert Mustacchi 	in->sin6_port = htons(vxl->vxl_lport);
13836589d6bSRobert Mustacchi 	in->sin6_addr = vxl->vxl_laddr;
13936589d6bSRobert Mustacchi 	mutex_exit(&vxl->vxl_lock);
14036589d6bSRobert Mustacchi 	*slenp = sizeof (struct sockaddr_in6);
14136589d6bSRobert Mustacchi 
14236589d6bSRobert Mustacchi 	return (0);
14336589d6bSRobert Mustacchi }
14436589d6bSRobert Mustacchi 
14536589d6bSRobert Mustacchi static int
vxlan_o_sockopt(ksocket_t ksock)14636589d6bSRobert Mustacchi vxlan_o_sockopt(ksocket_t ksock)
14736589d6bSRobert Mustacchi {
14836589d6bSRobert Mustacchi 	int val, err;
14936589d6bSRobert Mustacchi 	if (vxlan_fanout == B_FALSE)
15036589d6bSRobert Mustacchi 		return (0);
15136589d6bSRobert Mustacchi 
15236589d6bSRobert Mustacchi 	val = UDP_HASH_VXLAN;
15336589d6bSRobert Mustacchi 	err = ksocket_setsockopt(ksock, IPPROTO_UDP, UDP_SRCPORT_HASH, &val,
15436589d6bSRobert Mustacchi 	    sizeof (val), kcred);
15536589d6bSRobert Mustacchi 	return (err);
15636589d6bSRobert Mustacchi }
15736589d6bSRobert Mustacchi 
15836589d6bSRobert Mustacchi /* ARGSUSED */
15936589d6bSRobert Mustacchi static int
vxlan_o_encap(void * arg,mblk_t * mp,ovep_encap_info_t * einfop,mblk_t ** outp)16036589d6bSRobert Mustacchi vxlan_o_encap(void *arg, mblk_t *mp, ovep_encap_info_t *einfop,
16136589d6bSRobert Mustacchi     mblk_t **outp)
16236589d6bSRobert Mustacchi {
16336589d6bSRobert Mustacchi 	mblk_t *ob;
16436589d6bSRobert Mustacchi 	vxlan_hdr_t *vxh;
16536589d6bSRobert Mustacchi 
16636589d6bSRobert Mustacchi 	ASSERT(einfop->ovdi_id < (1 << 24));
16736589d6bSRobert Mustacchi 
16836589d6bSRobert Mustacchi 	if (DB_REF(mp) != 1 || mp->b_rptr - vxlan_noalloc_min < DB_BASE(mp)) {
16936589d6bSRobert Mustacchi 		/*
17036589d6bSRobert Mustacchi 		 * This allocation could get hot. We may want to have a good
17136589d6bSRobert Mustacchi 		 * way to cache and handle this allocation the same way that IP
17236589d6bSRobert Mustacchi 		 * does with keeping around a message block per entry, or
17336589d6bSRobert Mustacchi 		 * basically treating this as an immutable message block in the
17436589d6bSRobert Mustacchi 		 * system. Basically freemsg() will be a nop, but we'll do the
17536589d6bSRobert Mustacchi 		 * right thing with respect to the rest of the chain.
17636589d6bSRobert Mustacchi 		 */
17736589d6bSRobert Mustacchi 		ob = allocb(vxlan_alloc_size, 0);
17836589d6bSRobert Mustacchi 		if (ob == NULL)
17936589d6bSRobert Mustacchi 			return (ENOMEM);
18036589d6bSRobert Mustacchi 
18136589d6bSRobert Mustacchi 		ob->b_wptr = DB_LIM(ob);
18236589d6bSRobert Mustacchi 		ob->b_rptr = ob->b_wptr;
18336589d6bSRobert Mustacchi 		ob->b_cont = mp;
18436589d6bSRobert Mustacchi 	} else {
18536589d6bSRobert Mustacchi 		ob = mp;
18636589d6bSRobert Mustacchi 	}
18736589d6bSRobert Mustacchi 	ob->b_rptr -= VXLAN_HDR_LEN;
18836589d6bSRobert Mustacchi 
18936589d6bSRobert Mustacchi 	vxh = (vxlan_hdr_t *)ob->b_rptr;
19036589d6bSRobert Mustacchi 	vxh->vxlan_flags = ntohl(VXLAN_F_VDI);
19136589d6bSRobert Mustacchi 	vxh->vxlan_id = htonl((uint32_t)einfop->ovdi_id << VXLAN_ID_SHIFT);
19236589d6bSRobert Mustacchi 	*outp = ob;
19336589d6bSRobert Mustacchi 
19436589d6bSRobert Mustacchi 	return (0);
19536589d6bSRobert Mustacchi }
19636589d6bSRobert Mustacchi 
19736589d6bSRobert Mustacchi /* ARGSUSED */
19836589d6bSRobert Mustacchi static int
vxlan_o_decap(void * arg,mblk_t * mp,ovep_encap_info_t * dinfop)19936589d6bSRobert Mustacchi vxlan_o_decap(void *arg, mblk_t *mp, ovep_encap_info_t *dinfop)
20036589d6bSRobert Mustacchi {
20136589d6bSRobert Mustacchi 	vxlan_hdr_t *vxh;
20236589d6bSRobert Mustacchi 
20336589d6bSRobert Mustacchi 	if (MBLKL(mp) < sizeof (vxlan_hdr_t))
20436589d6bSRobert Mustacchi 		return (EINVAL);
20536589d6bSRobert Mustacchi 	vxh = (vxlan_hdr_t *)mp->b_rptr;
20636589d6bSRobert Mustacchi 	if ((ntohl(vxh->vxlan_flags) & VXLAN_F_VDI) == 0)
20736589d6bSRobert Mustacchi 		return (EINVAL);
20836589d6bSRobert Mustacchi 
20936589d6bSRobert Mustacchi 	dinfop->ovdi_id = ntohl(vxh->vxlan_id) >> VXLAN_ID_SHIFT;
21036589d6bSRobert Mustacchi 	dinfop->ovdi_hdr_size = VXLAN_HDR_LEN;
21136589d6bSRobert Mustacchi 
21236589d6bSRobert Mustacchi 	return (0);
21336589d6bSRobert Mustacchi }
21436589d6bSRobert Mustacchi 
21536589d6bSRobert Mustacchi static int
vxlan_o_getprop(void * arg,const char * pr_name,void * buf,uint32_t * bufsize)21636589d6bSRobert Mustacchi vxlan_o_getprop(void *arg, const char *pr_name, void *buf, uint32_t *bufsize)
21736589d6bSRobert Mustacchi {
21836589d6bSRobert Mustacchi 	vxlan_t *vxl = arg;
21936589d6bSRobert Mustacchi 
22036589d6bSRobert Mustacchi 	/* vxlan/listen_ip */
22136589d6bSRobert Mustacchi 	if (strcmp(pr_name, vxlan_props[0]) == 0) {
22236589d6bSRobert Mustacchi 		if (*bufsize < sizeof (struct in6_addr))
22336589d6bSRobert Mustacchi 			return (EOVERFLOW);
22436589d6bSRobert Mustacchi 
22536589d6bSRobert Mustacchi 		mutex_enter(&vxl->vxl_lock);
22636589d6bSRobert Mustacchi 		if (vxl->vxl_hladdr == B_FALSE) {
22736589d6bSRobert Mustacchi 			*bufsize = 0;
22836589d6bSRobert Mustacchi 		} else {
22936589d6bSRobert Mustacchi 			bcopy(&vxl->vxl_laddr, buf, sizeof (struct in6_addr));
23036589d6bSRobert Mustacchi 			*bufsize = sizeof (struct in6_addr);
23136589d6bSRobert Mustacchi 		}
23236589d6bSRobert Mustacchi 		mutex_exit(&vxl->vxl_lock);
23336589d6bSRobert Mustacchi 		return (0);
23436589d6bSRobert Mustacchi 	}
23536589d6bSRobert Mustacchi 
23636589d6bSRobert Mustacchi 	/* vxlan/listen_port */
23736589d6bSRobert Mustacchi 	if (strcmp(pr_name, vxlan_props[1]) == 0) {
23836589d6bSRobert Mustacchi 		uint64_t val;
23936589d6bSRobert Mustacchi 		if (*bufsize < sizeof (uint64_t))
24036589d6bSRobert Mustacchi 			return (EOVERFLOW);
24136589d6bSRobert Mustacchi 
24236589d6bSRobert Mustacchi 		mutex_enter(&vxl->vxl_lock);
24336589d6bSRobert Mustacchi 		val = vxl->vxl_lport;
24436589d6bSRobert Mustacchi 		bcopy(&val, buf, sizeof (uint64_t));
24536589d6bSRobert Mustacchi 		*bufsize = sizeof (uint64_t);
24636589d6bSRobert Mustacchi 		mutex_exit(&vxl->vxl_lock);
24736589d6bSRobert Mustacchi 		return (0);
24836589d6bSRobert Mustacchi 	}
24936589d6bSRobert Mustacchi 
25036589d6bSRobert Mustacchi 	return (EINVAL);
25136589d6bSRobert Mustacchi }
25236589d6bSRobert Mustacchi 
25336589d6bSRobert Mustacchi static int
vxlan_o_setprop(void * arg,const char * pr_name,const void * buf,uint32_t bufsize)25436589d6bSRobert Mustacchi vxlan_o_setprop(void *arg, const char *pr_name, const void *buf,
25536589d6bSRobert Mustacchi     uint32_t bufsize)
25636589d6bSRobert Mustacchi {
25736589d6bSRobert Mustacchi 	vxlan_t *vxl = arg;
25836589d6bSRobert Mustacchi 
25936589d6bSRobert Mustacchi 	/* vxlan/listen_ip */
26036589d6bSRobert Mustacchi 	if (strcmp(pr_name, vxlan_props[0]) == 0) {
26136589d6bSRobert Mustacchi 		const struct in6_addr *ipv6 = buf;
26236589d6bSRobert Mustacchi 		if (bufsize != sizeof (struct in6_addr))
26336589d6bSRobert Mustacchi 			return (EINVAL);
26436589d6bSRobert Mustacchi 
26536589d6bSRobert Mustacchi 		if (IN6_IS_ADDR_V4COMPAT(ipv6))
26636589d6bSRobert Mustacchi 			return (EINVAL);
26736589d6bSRobert Mustacchi 
26836589d6bSRobert Mustacchi 		if (IN6_IS_ADDR_MULTICAST(ipv6))
26936589d6bSRobert Mustacchi 			return (EINVAL);
27036589d6bSRobert Mustacchi 
27136589d6bSRobert Mustacchi 		if (IN6_IS_ADDR_6TO4(ipv6))
27236589d6bSRobert Mustacchi 			return (EINVAL);
27336589d6bSRobert Mustacchi 
27436589d6bSRobert Mustacchi 		if (IN6_IS_ADDR_V4MAPPED(ipv6)) {
27536589d6bSRobert Mustacchi 			ipaddr_t v4;
27636589d6bSRobert Mustacchi 			IN6_V4MAPPED_TO_IPADDR(ipv6, v4);
277*8bcd8eeaSBrian Bennett 			if (IN_MULTICAST(ntohl(v4)))
27836589d6bSRobert Mustacchi 				return (EINVAL);
27936589d6bSRobert Mustacchi 		}
28036589d6bSRobert Mustacchi 
28136589d6bSRobert Mustacchi 		mutex_enter(&vxl->vxl_lock);
28236589d6bSRobert Mustacchi 		vxl->vxl_hladdr = B_TRUE;
28336589d6bSRobert Mustacchi 		bcopy(ipv6, &vxl->vxl_laddr, sizeof (struct in6_addr));
28436589d6bSRobert Mustacchi 		mutex_exit(&vxl->vxl_lock);
28536589d6bSRobert Mustacchi 
28636589d6bSRobert Mustacchi 		return (0);
28736589d6bSRobert Mustacchi 	}
28836589d6bSRobert Mustacchi 
28936589d6bSRobert Mustacchi 	/* vxlan/listen_port */
29036589d6bSRobert Mustacchi 	if (strcmp(pr_name, vxlan_props[1]) == 0) {
29136589d6bSRobert Mustacchi 		const uint64_t *valp = buf;
29236589d6bSRobert Mustacchi 		if (bufsize != 8)
29336589d6bSRobert Mustacchi 			return (EINVAL);
29436589d6bSRobert Mustacchi 
29536589d6bSRobert Mustacchi 		if (*valp == 0 || *valp > UINT16_MAX)
29636589d6bSRobert Mustacchi 			return (EINVAL);
29736589d6bSRobert Mustacchi 
29836589d6bSRobert Mustacchi 		mutex_enter(&vxl->vxl_lock);
29936589d6bSRobert Mustacchi 		vxl->vxl_lport = *valp;
30036589d6bSRobert Mustacchi 		mutex_exit(&vxl->vxl_lock);
30136589d6bSRobert Mustacchi 		return (0);
30236589d6bSRobert Mustacchi 	}
30336589d6bSRobert Mustacchi 	return (EINVAL);
30436589d6bSRobert Mustacchi }
30536589d6bSRobert Mustacchi 
30636589d6bSRobert Mustacchi static int
vxlan_o_propinfo(const char * pr_name,overlay_prop_handle_t phdl)30736589d6bSRobert Mustacchi vxlan_o_propinfo(const char *pr_name, overlay_prop_handle_t phdl)
30836589d6bSRobert Mustacchi {
30936589d6bSRobert Mustacchi 	/* vxlan/listen_ip */
31036589d6bSRobert Mustacchi 	if (strcmp(pr_name, vxlan_props[0]) == 0) {
31136589d6bSRobert Mustacchi 		overlay_prop_set_name(phdl, vxlan_props[0]);
31236589d6bSRobert Mustacchi 		overlay_prop_set_prot(phdl, OVERLAY_PROP_PERM_RRW);
31336589d6bSRobert Mustacchi 		overlay_prop_set_type(phdl, OVERLAY_PROP_T_IP);
31436589d6bSRobert Mustacchi 		overlay_prop_set_nodefault(phdl);
31536589d6bSRobert Mustacchi 		return (0);
31636589d6bSRobert Mustacchi 	}
31736589d6bSRobert Mustacchi 
31836589d6bSRobert Mustacchi 	if (strcmp(pr_name, vxlan_props[1]) == 0) {
31936589d6bSRobert Mustacchi 		overlay_prop_set_name(phdl, vxlan_props[1]);
32036589d6bSRobert Mustacchi 		overlay_prop_set_prot(phdl, OVERLAY_PROP_PERM_RRW);
32136589d6bSRobert Mustacchi 		overlay_prop_set_type(phdl, OVERLAY_PROP_T_UINT);
32236589d6bSRobert Mustacchi 		(void) overlay_prop_set_default(phdl, &vxlan_defport,
32336589d6bSRobert Mustacchi 		    sizeof (vxlan_defport));
32436589d6bSRobert Mustacchi 		overlay_prop_set_range_uint32(phdl, 1, UINT16_MAX);
32536589d6bSRobert Mustacchi 		return (0);
32636589d6bSRobert Mustacchi 	}
32736589d6bSRobert Mustacchi 
32836589d6bSRobert Mustacchi 	return (EINVAL);
32936589d6bSRobert Mustacchi }
33036589d6bSRobert Mustacchi 
33136589d6bSRobert Mustacchi static struct overlay_plugin_ops vxlan_o_ops = {
33236589d6bSRobert Mustacchi 	0,
33336589d6bSRobert Mustacchi 	vxlan_o_init,
33436589d6bSRobert Mustacchi 	vxlan_o_fini,
33536589d6bSRobert Mustacchi 	vxlan_o_encap,
33636589d6bSRobert Mustacchi 	vxlan_o_decap,
33736589d6bSRobert Mustacchi 	vxlan_o_socket,
33836589d6bSRobert Mustacchi 	vxlan_o_sockopt,
33936589d6bSRobert Mustacchi 	vxlan_o_getprop,
34036589d6bSRobert Mustacchi 	vxlan_o_setprop,
34136589d6bSRobert Mustacchi 	vxlan_o_propinfo
34236589d6bSRobert Mustacchi };
34336589d6bSRobert Mustacchi 
34436589d6bSRobert Mustacchi static struct modlmisc vxlan_modlmisc = {
34536589d6bSRobert Mustacchi 	&mod_miscops,
34636589d6bSRobert Mustacchi 	"VXLAN encap plugin"
34736589d6bSRobert Mustacchi };
34836589d6bSRobert Mustacchi 
34936589d6bSRobert Mustacchi static struct modlinkage vxlan_modlinkage = {
35036589d6bSRobert Mustacchi 	MODREV_1,
35136589d6bSRobert Mustacchi 	&vxlan_modlmisc
35236589d6bSRobert Mustacchi };
35336589d6bSRobert Mustacchi 
35436589d6bSRobert Mustacchi int
_init(void)35536589d6bSRobert Mustacchi _init(void)
35636589d6bSRobert Mustacchi {
35736589d6bSRobert Mustacchi 	int err;
35836589d6bSRobert Mustacchi 	overlay_plugin_register_t *ovrp;
35936589d6bSRobert Mustacchi 
36036589d6bSRobert Mustacchi 	ovrp = overlay_plugin_alloc(OVEP_VERSION);
36136589d6bSRobert Mustacchi 	if (ovrp == NULL)
36236589d6bSRobert Mustacchi 		return (ENOTSUP);
36336589d6bSRobert Mustacchi 	ovrp->ovep_name = vxlan_ident;
36436589d6bSRobert Mustacchi 	ovrp->ovep_ops = &vxlan_o_ops;
36536589d6bSRobert Mustacchi 	ovrp->ovep_id_size = VXLAN_ID_LEN;
36636589d6bSRobert Mustacchi 	ovrp->ovep_flags = OVEP_F_VLAN_TAG;
36736589d6bSRobert Mustacchi 	ovrp->ovep_dest = OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT;
36836589d6bSRobert Mustacchi 	ovrp->ovep_props = vxlan_props;
36936589d6bSRobert Mustacchi 
37036589d6bSRobert Mustacchi 	if ((err = overlay_plugin_register(ovrp)) == 0) {
37136589d6bSRobert Mustacchi 		if ((err = mod_install(&vxlan_modlinkage)) != 0) {
37236589d6bSRobert Mustacchi 			(void) overlay_plugin_unregister(vxlan_ident);
37336589d6bSRobert Mustacchi 		}
37436589d6bSRobert Mustacchi 	}
37536589d6bSRobert Mustacchi 
37636589d6bSRobert Mustacchi 	overlay_plugin_free(ovrp);
37736589d6bSRobert Mustacchi 	return (err);
37836589d6bSRobert Mustacchi }
37936589d6bSRobert Mustacchi 
38036589d6bSRobert Mustacchi int
_info(struct modinfo * modinfop)38136589d6bSRobert Mustacchi _info(struct modinfo *modinfop)
38236589d6bSRobert Mustacchi {
38336589d6bSRobert Mustacchi 	return (mod_info(&vxlan_modlinkage, modinfop));
38436589d6bSRobert Mustacchi }
38536589d6bSRobert Mustacchi 
38636589d6bSRobert Mustacchi int
_fini(void)38736589d6bSRobert Mustacchi _fini(void)
38836589d6bSRobert Mustacchi {
38936589d6bSRobert Mustacchi 	int err;
39036589d6bSRobert Mustacchi 
39136589d6bSRobert Mustacchi 	if ((err = overlay_plugin_unregister(vxlan_ident)) != 0)
39236589d6bSRobert Mustacchi 		return (err);
39336589d6bSRobert Mustacchi 
39436589d6bSRobert Mustacchi 	return (mod_remove(&vxlan_modlinkage));
39536589d6bSRobert Mustacchi }
396