1*36589d6bSRobert Mustacchi /*
2*36589d6bSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*36589d6bSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*36589d6bSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*36589d6bSRobert Mustacchi  * 1.0 of the CDDL.
6*36589d6bSRobert Mustacchi  *
7*36589d6bSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*36589d6bSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*36589d6bSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*36589d6bSRobert Mustacchi  */
11*36589d6bSRobert Mustacchi 
12*36589d6bSRobert Mustacchi /*
13*36589d6bSRobert Mustacchi  * Copyright 2015 Joyent, Inc.
14*36589d6bSRobert Mustacchi  */
15*36589d6bSRobert Mustacchi 
16*36589d6bSRobert Mustacchi /*
17*36589d6bSRobert Mustacchi  * Common routines for implementing proxy arp
18*36589d6bSRobert Mustacchi  */
19*36589d6bSRobert Mustacchi 
20*36589d6bSRobert Mustacchi #include <sys/types.h>
21*36589d6bSRobert Mustacchi #include <net/if.h>
22*36589d6bSRobert Mustacchi #include <netinet/if_ether.h>
23*36589d6bSRobert Mustacchi #include <netinet/ip.h>
24*36589d6bSRobert Mustacchi #include <netinet/ip6.h>
25*36589d6bSRobert Mustacchi #include <netinet/icmp6.h>
26*36589d6bSRobert Mustacchi #include <netinet/udp.h>
27*36589d6bSRobert Mustacchi #include <netinet/dhcp.h>
28*36589d6bSRobert Mustacchi #include <libvarpd_impl.h>
29*36589d6bSRobert Mustacchi #include <sys/vlan.h>
30*36589d6bSRobert Mustacchi #include <strings.h>
31*36589d6bSRobert Mustacchi #include <assert.h>
32*36589d6bSRobert Mustacchi 
33*36589d6bSRobert Mustacchi #define	IPV6_VERSION	6
34*36589d6bSRobert Mustacchi 
35*36589d6bSRobert Mustacchi typedef struct varpd_arp_query {
36*36589d6bSRobert Mustacchi 	int				vaq_type;
37*36589d6bSRobert Mustacchi 	char				vaq_buf[ETHERMAX + VLAN_TAGSZ];
38*36589d6bSRobert Mustacchi 	size_t				vaq_bsize;
39*36589d6bSRobert Mustacchi 	uint8_t				vaq_lookup[ETHERADDRL];
40*36589d6bSRobert Mustacchi 	struct sockaddr_storage		vaq_sock;
41*36589d6bSRobert Mustacchi 	varpd_instance_t		*vaq_inst;
42*36589d6bSRobert Mustacchi 	struct ether_arp		*vaq_ea;
43*36589d6bSRobert Mustacchi 	varpd_query_handle_t		*vaq_query;
44*36589d6bSRobert Mustacchi 	const overlay_targ_lookup_t	*vaq_otl;
45*36589d6bSRobert Mustacchi 	ip6_t				*vaq_ip6;
46*36589d6bSRobert Mustacchi 	nd_neighbor_solicit_t		*vaq_ns;
47*36589d6bSRobert Mustacchi } varpd_arp_query_t;
48*36589d6bSRobert Mustacchi 
49*36589d6bSRobert Mustacchi typedef struct varpd_dhcp_query {
50*36589d6bSRobert Mustacchi 	char				vdq_buf[ETHERMAX + VLAN_TAGSZ];
51*36589d6bSRobert Mustacchi 	size_t				vdq_bsize;
52*36589d6bSRobert Mustacchi 	uint8_t				vdq_lookup[ETHERADDRL];
53*36589d6bSRobert Mustacchi 	const overlay_targ_lookup_t	*vdq_otl;
54*36589d6bSRobert Mustacchi 	varpd_instance_t		*vdq_inst;
55*36589d6bSRobert Mustacchi 	varpd_query_handle_t		*vdq_query;
56*36589d6bSRobert Mustacchi 	struct ether_header		*vdq_ether;
57*36589d6bSRobert Mustacchi } varpd_dhcp_query_t;
58*36589d6bSRobert Mustacchi 
59*36589d6bSRobert Mustacchi static const uint8_t libvarpd_arp_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff,
60*36589d6bSRobert Mustacchi 	    0xff };
61*36589d6bSRobert Mustacchi 
62*36589d6bSRobert Mustacchi void
libvarpd_plugin_proxy_arp(varpd_provider_handle_t * hdl,varpd_query_handle_t * vqh,const overlay_targ_lookup_t * otl)63*36589d6bSRobert Mustacchi libvarpd_plugin_proxy_arp(varpd_provider_handle_t *hdl,
64*36589d6bSRobert Mustacchi     varpd_query_handle_t *vqh, const overlay_targ_lookup_t *otl)
65*36589d6bSRobert Mustacchi {
66*36589d6bSRobert Mustacchi 	varpd_arp_query_t *vaq;
67*36589d6bSRobert Mustacchi 	varpd_instance_t *inst = (varpd_instance_t *)hdl;
68*36589d6bSRobert Mustacchi 	struct ether_arp *ea;
69*36589d6bSRobert Mustacchi 	struct sockaddr_in *ip;
70*36589d6bSRobert Mustacchi 
71*36589d6bSRobert Mustacchi 	vaq = umem_alloc(sizeof (varpd_arp_query_t), UMEM_DEFAULT);
72*36589d6bSRobert Mustacchi 	if (vaq == NULL) {
73*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
74*36589d6bSRobert Mustacchi 		return;
75*36589d6bSRobert Mustacchi 	}
76*36589d6bSRobert Mustacchi 	vaq->vaq_bsize = sizeof (vaq->vaq_buf);
77*36589d6bSRobert Mustacchi 
78*36589d6bSRobert Mustacchi 	if (otl->otl_sap != ETHERTYPE_ARP) {
79*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
80*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
81*36589d6bSRobert Mustacchi 		return;
82*36589d6bSRobert Mustacchi 	}
83*36589d6bSRobert Mustacchi 
84*36589d6bSRobert Mustacchi 	/*
85*36589d6bSRobert Mustacchi 	 * An ARP packet should not be very large because it's definited to only
86*36589d6bSRobert Mustacchi 	 * be allowed to have a single entry at a given time. But our data must
87*36589d6bSRobert Mustacchi 	 * be at least as large as an ether_arp and our header must be at least
88*36589d6bSRobert Mustacchi 	 * as large as a standard ethernet header.
89*36589d6bSRobert Mustacchi 	 */
90*36589d6bSRobert Mustacchi 	if (otl->otl_hdrsize + otl->otl_pktsize > vaq->vaq_bsize ||
91*36589d6bSRobert Mustacchi 	    otl->otl_pktsize < sizeof (struct ether_arp) ||
92*36589d6bSRobert Mustacchi 	    otl->otl_hdrsize < sizeof (struct ether_header)) {
93*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
94*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
95*36589d6bSRobert Mustacchi 		return;
96*36589d6bSRobert Mustacchi 	}
97*36589d6bSRobert Mustacchi 
98*36589d6bSRobert Mustacchi 	if (libvarpd_overlay_packet(inst->vri_impl, otl, vaq->vaq_buf,
99*36589d6bSRobert Mustacchi 	    &vaq->vaq_bsize) != 0) {
100*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
101*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
102*36589d6bSRobert Mustacchi 		return;
103*36589d6bSRobert Mustacchi 	}
104*36589d6bSRobert Mustacchi 
105*36589d6bSRobert Mustacchi 	if (otl->otl_hdrsize + otl->otl_pktsize < vaq->vaq_bsize) {
106*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
107*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
108*36589d6bSRobert Mustacchi 		return;
109*36589d6bSRobert Mustacchi 	}
110*36589d6bSRobert Mustacchi 
111*36589d6bSRobert Mustacchi 	ea = (void *)((uintptr_t)vaq->vaq_buf + (uintptr_t)otl->otl_hdrsize);
112*36589d6bSRobert Mustacchi 
113*36589d6bSRobert Mustacchi 	/*
114*36589d6bSRobert Mustacchi 	 * Make sure it matches something that we know about.
115*36589d6bSRobert Mustacchi 	 */
116*36589d6bSRobert Mustacchi 	if (ntohs(ea->ea_hdr.ar_hrd) != ARPHRD_ETHER ||
117*36589d6bSRobert Mustacchi 	    ntohs(ea->ea_hdr.ar_pro) != ETHERTYPE_IP ||
118*36589d6bSRobert Mustacchi 	    ea->ea_hdr.ar_hln != ETHERADDRL ||
119*36589d6bSRobert Mustacchi 	    ea->ea_hdr.ar_pln != sizeof (ea->arp_spa) ||
120*36589d6bSRobert Mustacchi 	    ntohs(ea->ea_hdr.ar_op) != ARPOP_REQUEST) {
121*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
122*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
123*36589d6bSRobert Mustacchi 		return;
124*36589d6bSRobert Mustacchi 	}
125*36589d6bSRobert Mustacchi 
126*36589d6bSRobert Mustacchi 	/*
127*36589d6bSRobert Mustacchi 	 * Now that we've verified that our data is sane, see if we're doing a
128*36589d6bSRobert Mustacchi 	 * gratuitous arp and if so, drop it. Otherwise, we may end up
129*36589d6bSRobert Mustacchi 	 * triggering duplicate address detection.
130*36589d6bSRobert Mustacchi 	 */
131*36589d6bSRobert Mustacchi 	if (bcmp(ea->arp_spa, ea->arp_tpa, sizeof (ea->arp_spa)) == 0) {
132*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
133*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
134*36589d6bSRobert Mustacchi 		return;
135*36589d6bSRobert Mustacchi 	}
136*36589d6bSRobert Mustacchi 
137*36589d6bSRobert Mustacchi 	bzero(&vaq->vaq_sock, sizeof (struct sockaddr_storage));
138*36589d6bSRobert Mustacchi 	ip = (struct sockaddr_in *)&vaq->vaq_sock;
139*36589d6bSRobert Mustacchi 	ip->sin_family = AF_INET;
140*36589d6bSRobert Mustacchi 	bcopy(ea->arp_tpa, &ip->sin_addr, sizeof (ea->arp_tpa));
141*36589d6bSRobert Mustacchi 
142*36589d6bSRobert Mustacchi 	vaq->vaq_type = AF_INET;
143*36589d6bSRobert Mustacchi 	vaq->vaq_inst = inst;
144*36589d6bSRobert Mustacchi 	vaq->vaq_ea = ea;
145*36589d6bSRobert Mustacchi 	vaq->vaq_query = vqh;
146*36589d6bSRobert Mustacchi 	vaq->vaq_otl = otl;
147*36589d6bSRobert Mustacchi 
148*36589d6bSRobert Mustacchi 	if (inst->vri_plugin->vpp_ops->vpo_arp == NULL)
149*36589d6bSRobert Mustacchi 		libvarpd_panic("%s plugin asked to do arp, but has no method",
150*36589d6bSRobert Mustacchi 		    inst->vri_plugin->vpp_name);
151*36589d6bSRobert Mustacchi 
152*36589d6bSRobert Mustacchi 	inst->vri_plugin->vpp_ops->vpo_arp(inst->vri_private,
153*36589d6bSRobert Mustacchi 	    (varpd_arp_handle_t *)vaq, VARPD_QTYPE_ETHERNET,
154*36589d6bSRobert Mustacchi 	    (struct sockaddr *)ip, vaq->vaq_lookup);
155*36589d6bSRobert Mustacchi }
156*36589d6bSRobert Mustacchi 
157*36589d6bSRobert Mustacchi static void
libvarpd_proxy_arp_fini(varpd_arp_query_t * vaq)158*36589d6bSRobert Mustacchi libvarpd_proxy_arp_fini(varpd_arp_query_t *vaq)
159*36589d6bSRobert Mustacchi {
160*36589d6bSRobert Mustacchi 	struct ether_header *ether;
161*36589d6bSRobert Mustacchi 	struct sockaddr_in *ip;
162*36589d6bSRobert Mustacchi 
163*36589d6bSRobert Mustacchi 	ip = (struct sockaddr_in *)&vaq->vaq_sock;
164*36589d6bSRobert Mustacchi 	/*
165*36589d6bSRobert Mustacchi 	 * Modify our packet in place for a reply. We need to swap around the
166*36589d6bSRobert Mustacchi 	 * sender and target addresses.
167*36589d6bSRobert Mustacchi 	 */
168*36589d6bSRobert Mustacchi 	vaq->vaq_ea->ea_hdr.ar_op = htons(ARPOP_REPLY);
169*36589d6bSRobert Mustacchi 	bcopy(vaq->vaq_ea->arp_sha, vaq->vaq_ea->arp_tha, ETHERADDRL);
170*36589d6bSRobert Mustacchi 	bcopy(vaq->vaq_lookup, vaq->vaq_ea->arp_sha, ETHERADDRL);
171*36589d6bSRobert Mustacchi 	bcopy(vaq->vaq_ea->arp_spa, &ip->sin_addr,
172*36589d6bSRobert Mustacchi 	    sizeof (vaq->vaq_ea->arp_spa));
173*36589d6bSRobert Mustacchi 	bcopy(vaq->vaq_ea->arp_tpa, vaq->vaq_ea->arp_spa,
174*36589d6bSRobert Mustacchi 	    sizeof (vaq->vaq_ea->arp_spa));
175*36589d6bSRobert Mustacchi 	bcopy(&ip->sin_addr, vaq->vaq_ea->arp_tpa,
176*36589d6bSRobert Mustacchi 	    sizeof (vaq->vaq_ea->arp_spa));
177*36589d6bSRobert Mustacchi 
178*36589d6bSRobert Mustacchi 	/*
179*36589d6bSRobert Mustacchi 	 * Finally go ahead and fix up the mac header and reply to the sender
180*36589d6bSRobert Mustacchi 	 * explicitly.
181*36589d6bSRobert Mustacchi 	 */
182*36589d6bSRobert Mustacchi 	ether = (struct ether_header *)vaq->vaq_buf;
183*36589d6bSRobert Mustacchi 	bcopy(&ether->ether_shost, &ether->ether_dhost, ETHERADDRL);
184*36589d6bSRobert Mustacchi 	bcopy(vaq->vaq_lookup, &ether->ether_shost, ETHERADDRL);
185*36589d6bSRobert Mustacchi 
186*36589d6bSRobert Mustacchi 	(void) libvarpd_overlay_inject(vaq->vaq_inst->vri_impl, vaq->vaq_otl,
187*36589d6bSRobert Mustacchi 	    vaq->vaq_buf, vaq->vaq_bsize);
188*36589d6bSRobert Mustacchi 
189*36589d6bSRobert Mustacchi 	libvarpd_plugin_query_reply(vaq->vaq_query, VARPD_LOOKUP_DROP);
190*36589d6bSRobert Mustacchi 	umem_free(vaq, sizeof (varpd_arp_query_t));
191*36589d6bSRobert Mustacchi }
192*36589d6bSRobert Mustacchi 
193*36589d6bSRobert Mustacchi static uint16_t
libvarpd_icmpv6_checksum(const ip6_t * v6hdr,const uint16_t * buf,uint16_t mlen)194*36589d6bSRobert Mustacchi libvarpd_icmpv6_checksum(const ip6_t *v6hdr, const uint16_t *buf, uint16_t mlen)
195*36589d6bSRobert Mustacchi {
196*36589d6bSRobert Mustacchi 	int i;
197*36589d6bSRobert Mustacchi 	uint16_t *v;
198*36589d6bSRobert Mustacchi 	uint32_t sum = 0;
199*36589d6bSRobert Mustacchi 
200*36589d6bSRobert Mustacchi 	assert(mlen % 2 == 0);
201*36589d6bSRobert Mustacchi 	v = (uint16_t *)&v6hdr->ip6_src;
202*36589d6bSRobert Mustacchi 	for (i = 0; i < sizeof (struct in6_addr); i += 2, v++)
203*36589d6bSRobert Mustacchi 		sum += *v;
204*36589d6bSRobert Mustacchi 	v = (uint16_t *)&v6hdr->ip6_dst;
205*36589d6bSRobert Mustacchi 	for (i = 0; i < sizeof (struct in6_addr); i += 2, v++)
206*36589d6bSRobert Mustacchi 		sum += *v;
207*36589d6bSRobert Mustacchi 	sum += htons(mlen);
208*36589d6bSRobert Mustacchi #ifdef _BIG_ENDIAN
209*36589d6bSRobert Mustacchi 	sum += IPPROTO_ICMPV6;
210*36589d6bSRobert Mustacchi #else
211*36589d6bSRobert Mustacchi 	sum += IPPROTO_ICMPV6 << 8;
212*36589d6bSRobert Mustacchi #endif	/* _BIG_ENDIAN */
213*36589d6bSRobert Mustacchi 
214*36589d6bSRobert Mustacchi 	for (i = 0; i < mlen; i += 2, buf++)
215*36589d6bSRobert Mustacchi 		sum += *buf;
216*36589d6bSRobert Mustacchi 
217*36589d6bSRobert Mustacchi 	while ((sum >> 16) != 0)
218*36589d6bSRobert Mustacchi 		sum = (sum & 0xffff) + (sum >> 16);
219*36589d6bSRobert Mustacchi 
220*36589d6bSRobert Mustacchi 	return (sum & 0xffff);
221*36589d6bSRobert Mustacchi }
222*36589d6bSRobert Mustacchi 
223*36589d6bSRobert Mustacchi /*
224*36589d6bSRobert Mustacchi  * Proxying NDP is much more involved than proxying ARP. For starters, NDP
225*36589d6bSRobert Mustacchi  * neighbor solicitations are implemented in terms of IPv6 ICMP as opposed to
226*36589d6bSRobert Mustacchi  * its own Ethertype. Therefore, we're going to have to grab a packet if it's a
227*36589d6bSRobert Mustacchi  * multicast packet and then determine if we actually want to do anything with
228*36589d6bSRobert Mustacchi  * it.
229*36589d6bSRobert Mustacchi  */
230*36589d6bSRobert Mustacchi void
libvarpd_plugin_proxy_ndp(varpd_provider_handle_t * hdl,varpd_query_handle_t * vqh,const overlay_targ_lookup_t * otl)231*36589d6bSRobert Mustacchi libvarpd_plugin_proxy_ndp(varpd_provider_handle_t *hdl,
232*36589d6bSRobert Mustacchi     varpd_query_handle_t *vqh, const overlay_targ_lookup_t *otl)
233*36589d6bSRobert Mustacchi {
234*36589d6bSRobert Mustacchi 	size_t bsize, plen;
235*36589d6bSRobert Mustacchi 	varpd_arp_query_t *vaq;
236*36589d6bSRobert Mustacchi 	ip6_t *v6hdr;
237*36589d6bSRobert Mustacchi 	nd_neighbor_solicit_t *ns;
238*36589d6bSRobert Mustacchi 	nd_opt_hdr_t *opt;
239*36589d6bSRobert Mustacchi 	struct sockaddr_in6 *s6;
240*36589d6bSRobert Mustacchi 
241*36589d6bSRobert Mustacchi 	varpd_instance_t *inst = (varpd_instance_t *)hdl;
242*36589d6bSRobert Mustacchi 	uint8_t *eth = NULL;
243*36589d6bSRobert Mustacchi 
244*36589d6bSRobert Mustacchi 	vaq = umem_alloc(sizeof (varpd_arp_query_t), UMEM_DEFAULT);
245*36589d6bSRobert Mustacchi 	if (vaq == NULL) {
246*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
247*36589d6bSRobert Mustacchi 		return;
248*36589d6bSRobert Mustacchi 	}
249*36589d6bSRobert Mustacchi 	vaq->vaq_bsize = sizeof (vaq->vaq_buf);
250*36589d6bSRobert Mustacchi 
251*36589d6bSRobert Mustacchi 	if (otl->otl_dstaddr[0] != 0x33 ||
252*36589d6bSRobert Mustacchi 	    otl->otl_dstaddr[1] != 0x33) {
253*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
254*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
255*36589d6bSRobert Mustacchi 		return;
256*36589d6bSRobert Mustacchi 	}
257*36589d6bSRobert Mustacchi 
258*36589d6bSRobert Mustacchi 	/*
259*36589d6bSRobert Mustacchi 	 * If we have more than a standard frame size for the ICMP neighbor
260*36589d6bSRobert Mustacchi 	 * solicitation, drop it. Similarly if there isn't enough data present
261*36589d6bSRobert Mustacchi 	 * for us, drop it.
262*36589d6bSRobert Mustacchi 	 */
263*36589d6bSRobert Mustacchi 	if (otl->otl_hdrsize + otl->otl_pktsize > vaq->vaq_bsize) {
264*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
265*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
266*36589d6bSRobert Mustacchi 		return;
267*36589d6bSRobert Mustacchi 	}
268*36589d6bSRobert Mustacchi 
269*36589d6bSRobert Mustacchi 	if (otl->otl_pktsize < sizeof (ip6_t) +
270*36589d6bSRobert Mustacchi 	    sizeof (nd_neighbor_solicit_t)) {
271*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
272*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
273*36589d6bSRobert Mustacchi 		return;
274*36589d6bSRobert Mustacchi 	}
275*36589d6bSRobert Mustacchi 
276*36589d6bSRobert Mustacchi 	if (libvarpd_overlay_packet(inst->vri_impl, otl, vaq->vaq_buf,
277*36589d6bSRobert Mustacchi 	    &vaq->vaq_bsize) != 0) {
278*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
279*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
280*36589d6bSRobert Mustacchi 		return;
281*36589d6bSRobert Mustacchi 	}
282*36589d6bSRobert Mustacchi 
283*36589d6bSRobert Mustacchi 	bsize = vaq->vaq_bsize;
284*36589d6bSRobert Mustacchi 	bsize -= otl->otl_hdrsize;
285*36589d6bSRobert Mustacchi 	assert(bsize > sizeof (ip6_t));
286*36589d6bSRobert Mustacchi 
287*36589d6bSRobert Mustacchi 	v6hdr = (ip6_t *)(vaq->vaq_buf + otl->otl_hdrsize);
288*36589d6bSRobert Mustacchi 	if (((v6hdr->ip6_vfc & 0xf0) >> 4) != IPV6_VERSION) {
289*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
290*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
291*36589d6bSRobert Mustacchi 		return;
292*36589d6bSRobert Mustacchi 	}
293*36589d6bSRobert Mustacchi 
294*36589d6bSRobert Mustacchi 	if (v6hdr->ip6_nxt != IPPROTO_ICMPV6) {
295*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
296*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
297*36589d6bSRobert Mustacchi 		return;
298*36589d6bSRobert Mustacchi 	}
299*36589d6bSRobert Mustacchi 
300*36589d6bSRobert Mustacchi 	/*
301*36589d6bSRobert Mustacchi 	 * In addition to getting these requests on the multicast address for
302*36589d6bSRobert Mustacchi 	 * node solicitation, we may also end up getting them on a generic
303*36589d6bSRobert Mustacchi 	 * multicast address due to timeouts or other choices by various OSes.
304*36589d6bSRobert Mustacchi 	 * We should fairly liberal and accept both, even though the standard
305*36589d6bSRobert Mustacchi 	 * wants them to a solicitation address.
306*36589d6bSRobert Mustacchi 	 */
307*36589d6bSRobert Mustacchi 	if (!IN6_IS_ADDR_MC_SOLICITEDNODE(&v6hdr->ip6_dst) &&
308*36589d6bSRobert Mustacchi 	    !IN6_IS_ADDR_MC_LINKLOCAL(&v6hdr->ip6_dst)) {
309*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
310*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
311*36589d6bSRobert Mustacchi 		return;
312*36589d6bSRobert Mustacchi 	}
313*36589d6bSRobert Mustacchi 
314*36589d6bSRobert Mustacchi 	bsize -= sizeof (ip6_t);
315*36589d6bSRobert Mustacchi 	plen = ntohs(v6hdr->ip6_plen);
316*36589d6bSRobert Mustacchi 	if (bsize < plen) {
317*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
318*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
319*36589d6bSRobert Mustacchi 		return;
320*36589d6bSRobert Mustacchi 	}
321*36589d6bSRobert Mustacchi 
322*36589d6bSRobert Mustacchi 	/*
323*36589d6bSRobert Mustacchi 	 * Now we know that this is an ICMPv6 request targeting the right
324*36589d6bSRobert Mustacchi 	 * IPv6 multicast prefix. Let's go through and verify that ICMPv6
325*36589d6bSRobert Mustacchi 	 * indicates that we have the real thing and ensure that per RFC 4861
326*36589d6bSRobert Mustacchi 	 * the target address is not a multicast address. Further, because this
327*36589d6bSRobert Mustacchi 	 * is a multicast on Ethernet, we must have a source link-layer address.
328*36589d6bSRobert Mustacchi 	 *
329*36589d6bSRobert Mustacchi 	 * We should probably enforce that we have a valid ICMP checksum at some
330*36589d6bSRobert Mustacchi 	 * point.
331*36589d6bSRobert Mustacchi 	 */
332*36589d6bSRobert Mustacchi 	ns = (nd_neighbor_solicit_t *)(vaq->vaq_buf + otl->otl_hdrsize +
333*36589d6bSRobert Mustacchi 	    sizeof (ip6_t));
334*36589d6bSRobert Mustacchi 	if (ns->nd_ns_type != ND_NEIGHBOR_SOLICIT && ns->nd_ns_code != 0) {
335*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
336*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
337*36589d6bSRobert Mustacchi 		return;
338*36589d6bSRobert Mustacchi 	}
339*36589d6bSRobert Mustacchi 
340*36589d6bSRobert Mustacchi 	if (IN6_IS_ADDR_MULTICAST(&ns->nd_ns_target) ||
341*36589d6bSRobert Mustacchi 	    IN6_IS_ADDR_V4MAPPED(&ns->nd_ns_target) ||
342*36589d6bSRobert Mustacchi 	    IN6_IS_ADDR_LOOPBACK(&ns->nd_ns_target)) {
343*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
344*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
345*36589d6bSRobert Mustacchi 		return;
346*36589d6bSRobert Mustacchi 	}
347*36589d6bSRobert Mustacchi 
348*36589d6bSRobert Mustacchi 	plen -= sizeof (nd_neighbor_solicit_t);
349*36589d6bSRobert Mustacchi 	opt = (nd_opt_hdr_t *)(ns+1);
350*36589d6bSRobert Mustacchi 	while (plen >= sizeof (struct nd_opt_hdr)) {
351*36589d6bSRobert Mustacchi 		/* If we have an option with no lenght, that's clear bogus */
352*36589d6bSRobert Mustacchi 		if (opt->nd_opt_len == 0) {
353*36589d6bSRobert Mustacchi 			libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
354*36589d6bSRobert Mustacchi 			umem_free(vaq, sizeof (varpd_arp_query_t));
355*36589d6bSRobert Mustacchi 			return;
356*36589d6bSRobert Mustacchi 		}
357*36589d6bSRobert Mustacchi 
358*36589d6bSRobert Mustacchi 		if (opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR) {
359*36589d6bSRobert Mustacchi 			eth = (uint8_t *)((uintptr_t)opt +
360*36589d6bSRobert Mustacchi 			    sizeof (nd_opt_hdr_t));
361*36589d6bSRobert Mustacchi 		}
362*36589d6bSRobert Mustacchi 		plen -= opt->nd_opt_len * 8;
363*36589d6bSRobert Mustacchi 		opt = (nd_opt_hdr_t *)((uintptr_t)opt +
364*36589d6bSRobert Mustacchi 		    opt->nd_opt_len * 8);
365*36589d6bSRobert Mustacchi 	}
366*36589d6bSRobert Mustacchi 
367*36589d6bSRobert Mustacchi 	if (eth == NULL) {
368*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
369*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
370*36589d6bSRobert Mustacchi 		return;
371*36589d6bSRobert Mustacchi 	}
372*36589d6bSRobert Mustacchi 
373*36589d6bSRobert Mustacchi 	bzero(&vaq->vaq_sock, sizeof (struct sockaddr_storage));
374*36589d6bSRobert Mustacchi 	s6 = (struct sockaddr_in6 *)&vaq->vaq_sock;
375*36589d6bSRobert Mustacchi 	s6->sin6_family = AF_INET6;
376*36589d6bSRobert Mustacchi 	bcopy(&ns->nd_ns_target, &s6->sin6_addr, sizeof (s6->sin6_addr));
377*36589d6bSRobert Mustacchi 
378*36589d6bSRobert Mustacchi 	if (inst->vri_plugin->vpp_ops->vpo_arp == NULL)
379*36589d6bSRobert Mustacchi 		libvarpd_panic("%s plugin asked to do arp, but has no method",
380*36589d6bSRobert Mustacchi 		    inst->vri_plugin->vpp_name);
381*36589d6bSRobert Mustacchi 
382*36589d6bSRobert Mustacchi 	vaq->vaq_type = AF_INET6;
383*36589d6bSRobert Mustacchi 	vaq->vaq_inst = inst;
384*36589d6bSRobert Mustacchi 	vaq->vaq_ea = NULL;
385*36589d6bSRobert Mustacchi 	vaq->vaq_query = vqh;
386*36589d6bSRobert Mustacchi 	vaq->vaq_otl = otl;
387*36589d6bSRobert Mustacchi 	vaq->vaq_ns = ns;
388*36589d6bSRobert Mustacchi 	vaq->vaq_ip6 = v6hdr;
389*36589d6bSRobert Mustacchi 	inst->vri_plugin->vpp_ops->vpo_arp(inst->vri_private,
390*36589d6bSRobert Mustacchi 	    (varpd_arp_handle_t *)vaq,  VARPD_QTYPE_ETHERNET,
391*36589d6bSRobert Mustacchi 	    (struct sockaddr *)s6, vaq->vaq_lookup);
392*36589d6bSRobert Mustacchi }
393*36589d6bSRobert Mustacchi 
394*36589d6bSRobert Mustacchi static void
libvarpd_proxy_ndp_fini(varpd_arp_query_t * vaq)395*36589d6bSRobert Mustacchi libvarpd_proxy_ndp_fini(varpd_arp_query_t *vaq)
396*36589d6bSRobert Mustacchi {
397*36589d6bSRobert Mustacchi 	char resp[ETHERMAX + VLAN_TAGSZ];
398*36589d6bSRobert Mustacchi 	struct ether_header *ether;
399*36589d6bSRobert Mustacchi 	nd_neighbor_advert_t *na;
400*36589d6bSRobert Mustacchi 	nd_opt_hdr_t *opt;
401*36589d6bSRobert Mustacchi 	ip6_t *v6hdr;
402*36589d6bSRobert Mustacchi 	size_t roff = 0;
403*36589d6bSRobert Mustacchi 
404*36589d6bSRobert Mustacchi 	/*
405*36589d6bSRobert Mustacchi 	 * Now we need to assemble an RA as a response. Unlike with arp, we opt
406*36589d6bSRobert Mustacchi 	 * to use a new packet just to make things a bit simpler saner here.
407*36589d6bSRobert Mustacchi 	 */
408*36589d6bSRobert Mustacchi 	v6hdr = vaq->vaq_ip6;
409*36589d6bSRobert Mustacchi 	bcopy(vaq->vaq_buf, resp, vaq->vaq_otl->otl_hdrsize);
410*36589d6bSRobert Mustacchi 	ether = (struct ether_header *)resp;
411*36589d6bSRobert Mustacchi 	bcopy(&ether->ether_shost, &ether->ether_dhost, ETHERADDRL);
412*36589d6bSRobert Mustacchi 	bcopy(vaq->vaq_lookup, &ether->ether_shost, ETHERADDRL);
413*36589d6bSRobert Mustacchi 	roff += vaq->vaq_otl->otl_hdrsize;
414*36589d6bSRobert Mustacchi 	bcopy(v6hdr, resp + roff, sizeof (ip6_t));
415*36589d6bSRobert Mustacchi 	v6hdr = (ip6_t *)(resp + roff);
416*36589d6bSRobert Mustacchi 	bcopy(&v6hdr->ip6_src, &v6hdr->ip6_dst, sizeof (struct in6_addr));
417*36589d6bSRobert Mustacchi 	bcopy(&vaq->vaq_ns->nd_ns_target, &v6hdr->ip6_src,
418*36589d6bSRobert Mustacchi 	    sizeof (struct in6_addr));
419*36589d6bSRobert Mustacchi 	roff += sizeof (ip6_t);
420*36589d6bSRobert Mustacchi 	na = (nd_neighbor_advert_t *)(resp + roff);
421*36589d6bSRobert Mustacchi 	na->nd_na_type = ND_NEIGHBOR_ADVERT;
422*36589d6bSRobert Mustacchi 	na->nd_na_code = 0;
423*36589d6bSRobert Mustacchi 	/*
424*36589d6bSRobert Mustacchi 	 * RFC 4443 defines that we should set the checksum to zero before we
425*36589d6bSRobert Mustacchi 	 * calculate it.
426*36589d6bSRobert Mustacchi 	 */
427*36589d6bSRobert Mustacchi 	na->nd_na_cksum = 0;
428*36589d6bSRobert Mustacchi 	/*
429*36589d6bSRobert Mustacchi 	 * Nota bene, the header <netinet/icmp6.h> has already transformed this
430*36589d6bSRobert Mustacchi 	 * into the appropriate host order. Don't use htonl.
431*36589d6bSRobert Mustacchi 	 */
432*36589d6bSRobert Mustacchi 	na->nd_na_flags_reserved = ND_NA_FLAG_SOLICITED | ND_NA_FLAG_OVERRIDE;
433*36589d6bSRobert Mustacchi 	bcopy(&vaq->vaq_ns->nd_ns_target, &na->nd_na_target,
434*36589d6bSRobert Mustacchi 	    sizeof (struct in6_addr));
435*36589d6bSRobert Mustacchi 	roff += sizeof (nd_neighbor_advert_t);
436*36589d6bSRobert Mustacchi 
437*36589d6bSRobert Mustacchi 	opt = (nd_opt_hdr_t *)(resp + roff);
438*36589d6bSRobert Mustacchi 	opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
439*36589d6bSRobert Mustacchi 	opt->nd_opt_len = 1;
440*36589d6bSRobert Mustacchi 	roff += sizeof (nd_opt_hdr_t);
441*36589d6bSRobert Mustacchi 	bcopy(vaq->vaq_lookup, resp + roff, ETHERADDRL);
442*36589d6bSRobert Mustacchi 	roff += ETHERADDRL;
443*36589d6bSRobert Mustacchi 
444*36589d6bSRobert Mustacchi 	/*
445*36589d6bSRobert Mustacchi 	 * Now that we've filled in the packet, go back and compute the checksum
446*36589d6bSRobert Mustacchi 	 * and fill in the IPv6 payload size.
447*36589d6bSRobert Mustacchi 	 */
448*36589d6bSRobert Mustacchi 	v6hdr->ip6_plen = htons(roff - sizeof (ip6_t) -
449*36589d6bSRobert Mustacchi 	    vaq->vaq_otl->otl_hdrsize);
450*36589d6bSRobert Mustacchi 	na->nd_na_cksum = ~libvarpd_icmpv6_checksum(v6hdr, (uint16_t *)na,
451*36589d6bSRobert Mustacchi 	    ntohs(v6hdr->ip6_plen)) & 0xffff;
452*36589d6bSRobert Mustacchi 
453*36589d6bSRobert Mustacchi 	(void) libvarpd_overlay_inject(vaq->vaq_inst->vri_impl, vaq->vaq_otl,
454*36589d6bSRobert Mustacchi 	    resp, roff);
455*36589d6bSRobert Mustacchi 
456*36589d6bSRobert Mustacchi 	libvarpd_plugin_query_reply(vaq->vaq_query, VARPD_LOOKUP_DROP);
457*36589d6bSRobert Mustacchi 	umem_free(vaq, sizeof (varpd_arp_query_t));
458*36589d6bSRobert Mustacchi }
459*36589d6bSRobert Mustacchi 
460*36589d6bSRobert Mustacchi void
libvarpd_plugin_arp_reply(varpd_arp_handle_t * vah,int action)461*36589d6bSRobert Mustacchi libvarpd_plugin_arp_reply(varpd_arp_handle_t *vah, int action)
462*36589d6bSRobert Mustacchi {
463*36589d6bSRobert Mustacchi 	varpd_arp_query_t *vaq = (varpd_arp_query_t *)vah;
464*36589d6bSRobert Mustacchi 
465*36589d6bSRobert Mustacchi 	if (vaq == NULL)
466*36589d6bSRobert Mustacchi 		libvarpd_panic("unknown plugin passed invalid "
467*36589d6bSRobert Mustacchi 		    "varpd_arp_handle_t");
468*36589d6bSRobert Mustacchi 
469*36589d6bSRobert Mustacchi 	if (action == VARPD_LOOKUP_DROP) {
470*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vaq->vaq_query, VARPD_LOOKUP_DROP);
471*36589d6bSRobert Mustacchi 		umem_free(vaq, sizeof (varpd_arp_query_t));
472*36589d6bSRobert Mustacchi 		return;
473*36589d6bSRobert Mustacchi 	} else if (action != VARPD_LOOKUP_OK)
474*36589d6bSRobert Mustacchi 		libvarpd_panic("%s plugin returned invalid action %d",
475*36589d6bSRobert Mustacchi 		    vaq->vaq_inst->vri_plugin->vpp_name, action);
476*36589d6bSRobert Mustacchi 
477*36589d6bSRobert Mustacchi 	switch (vaq->vaq_type) {
478*36589d6bSRobert Mustacchi 	case AF_INET:
479*36589d6bSRobert Mustacchi 		libvarpd_proxy_arp_fini(vaq);
480*36589d6bSRobert Mustacchi 		break;
481*36589d6bSRobert Mustacchi 	case AF_INET6:
482*36589d6bSRobert Mustacchi 		libvarpd_proxy_ndp_fini(vaq);
483*36589d6bSRobert Mustacchi 		break;
484*36589d6bSRobert Mustacchi 	default:
485*36589d6bSRobert Mustacchi 		libvarpd_panic("encountered unknown vaq_type: %d",
486*36589d6bSRobert Mustacchi 		    vaq->vaq_type);
487*36589d6bSRobert Mustacchi 	}
488*36589d6bSRobert Mustacchi }
489*36589d6bSRobert Mustacchi 
490*36589d6bSRobert Mustacchi void
libvarpd_plugin_proxy_dhcp(varpd_provider_handle_t * hdl,varpd_query_handle_t * vqh,const overlay_targ_lookup_t * otl)491*36589d6bSRobert Mustacchi libvarpd_plugin_proxy_dhcp(varpd_provider_handle_t *hdl,
492*36589d6bSRobert Mustacchi     varpd_query_handle_t *vqh, const overlay_targ_lookup_t *otl)
493*36589d6bSRobert Mustacchi {
494*36589d6bSRobert Mustacchi 	varpd_dhcp_query_t *vdq;
495*36589d6bSRobert Mustacchi 	struct ether_header *ether;
496*36589d6bSRobert Mustacchi 	struct ip *ip;
497*36589d6bSRobert Mustacchi 	struct udphdr *udp;
498*36589d6bSRobert Mustacchi 	varpd_instance_t *inst = (varpd_instance_t *)hdl;
499*36589d6bSRobert Mustacchi 
500*36589d6bSRobert Mustacchi 	vdq = umem_alloc(sizeof (varpd_dhcp_query_t), UMEM_DEFAULT);
501*36589d6bSRobert Mustacchi 	if (vdq == NULL) {
502*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
503*36589d6bSRobert Mustacchi 		return;
504*36589d6bSRobert Mustacchi 	}
505*36589d6bSRobert Mustacchi 	vdq->vdq_bsize = sizeof (vdq->vdq_buf);
506*36589d6bSRobert Mustacchi 
507*36589d6bSRobert Mustacchi 	if (otl->otl_sap != ETHERTYPE_IP) {
508*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
509*36589d6bSRobert Mustacchi 		umem_free(vdq, sizeof (varpd_dhcp_query_t));
510*36589d6bSRobert Mustacchi 		return;
511*36589d6bSRobert Mustacchi 	}
512*36589d6bSRobert Mustacchi 
513*36589d6bSRobert Mustacchi 	if (bcmp(otl->otl_dstaddr, libvarpd_arp_bcast, ETHERADDRL) != 0) {
514*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
515*36589d6bSRobert Mustacchi 		umem_free(vdq, sizeof (varpd_dhcp_query_t));
516*36589d6bSRobert Mustacchi 		return;
517*36589d6bSRobert Mustacchi 	}
518*36589d6bSRobert Mustacchi 
519*36589d6bSRobert Mustacchi 	if (otl->otl_hdrsize + otl->otl_pktsize > vdq->vdq_bsize ||
520*36589d6bSRobert Mustacchi 	    otl->otl_pktsize < sizeof (struct ip) + sizeof (struct udphdr) +
521*36589d6bSRobert Mustacchi 	    sizeof (struct dhcp) ||
522*36589d6bSRobert Mustacchi 	    otl->otl_hdrsize < sizeof (struct ether_header)) {
523*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
524*36589d6bSRobert Mustacchi 		umem_free(vdq, sizeof (varpd_dhcp_query_t));
525*36589d6bSRobert Mustacchi 		return;
526*36589d6bSRobert Mustacchi 	}
527*36589d6bSRobert Mustacchi 
528*36589d6bSRobert Mustacchi 	if (libvarpd_overlay_packet(inst->vri_impl, otl, vdq->vdq_buf,
529*36589d6bSRobert Mustacchi 	    &vdq->vdq_bsize) != 0) {
530*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
531*36589d6bSRobert Mustacchi 		umem_free(vdq, sizeof (varpd_dhcp_query_t));
532*36589d6bSRobert Mustacchi 		return;
533*36589d6bSRobert Mustacchi 	}
534*36589d6bSRobert Mustacchi 
535*36589d6bSRobert Mustacchi 	if (vdq->vdq_bsize != otl->otl_hdrsize + otl->otl_pktsize) {
536*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
537*36589d6bSRobert Mustacchi 		umem_free(vdq, sizeof (varpd_dhcp_query_t));
538*36589d6bSRobert Mustacchi 		return;
539*36589d6bSRobert Mustacchi 	}
540*36589d6bSRobert Mustacchi 
541*36589d6bSRobert Mustacchi 	ether = (struct ether_header *)vdq->vdq_buf;
542*36589d6bSRobert Mustacchi 	ip = (struct ip *)(vdq->vdq_buf + otl->otl_hdrsize);
543*36589d6bSRobert Mustacchi 
544*36589d6bSRobert Mustacchi 	if (ip->ip_v != IPVERSION && ip->ip_p != IPPROTO_UDP) {
545*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
546*36589d6bSRobert Mustacchi 		umem_free(vdq, sizeof (varpd_dhcp_query_t));
547*36589d6bSRobert Mustacchi 		return;
548*36589d6bSRobert Mustacchi 	}
549*36589d6bSRobert Mustacchi 
550*36589d6bSRobert Mustacchi 	if (otl->otl_hdrsize + ip->ip_hl * 4 + sizeof (struct udphdr) >
551*36589d6bSRobert Mustacchi 	    vdq->vdq_bsize) {
552*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
553*36589d6bSRobert Mustacchi 		umem_free(vdq, sizeof (varpd_dhcp_query_t));
554*36589d6bSRobert Mustacchi 		return;
555*36589d6bSRobert Mustacchi 	}
556*36589d6bSRobert Mustacchi 
557*36589d6bSRobert Mustacchi 	udp = (struct udphdr *)(vdq->vdq_buf + otl->otl_hdrsize +
558*36589d6bSRobert Mustacchi 	    ip->ip_hl * 4);
559*36589d6bSRobert Mustacchi 
560*36589d6bSRobert Mustacchi 	if (ntohs(udp->uh_sport) != IPPORT_BOOTPC ||
561*36589d6bSRobert Mustacchi 	    ntohs(udp->uh_dport) != IPPORT_BOOTPS) {
562*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
563*36589d6bSRobert Mustacchi 		umem_free(vdq, sizeof (varpd_dhcp_query_t));
564*36589d6bSRobert Mustacchi 		return;
565*36589d6bSRobert Mustacchi 	}
566*36589d6bSRobert Mustacchi 
567*36589d6bSRobert Mustacchi 	vdq->vdq_ether = ether;
568*36589d6bSRobert Mustacchi 	vdq->vdq_inst = inst;
569*36589d6bSRobert Mustacchi 	vdq->vdq_query = vqh;
570*36589d6bSRobert Mustacchi 	vdq->vdq_otl = otl;
571*36589d6bSRobert Mustacchi 
572*36589d6bSRobert Mustacchi 	if (inst->vri_plugin->vpp_ops->vpo_dhcp == NULL)
573*36589d6bSRobert Mustacchi 		libvarpd_panic("%s plugin asked to do dhcp, but has no method",
574*36589d6bSRobert Mustacchi 		    inst->vri_plugin->vpp_name);
575*36589d6bSRobert Mustacchi 
576*36589d6bSRobert Mustacchi 	inst->vri_plugin->vpp_ops->vpo_dhcp(inst->vri_private,
577*36589d6bSRobert Mustacchi 	    (varpd_dhcp_handle_t *)vdq, VARPD_QTYPE_ETHERNET, otl,
578*36589d6bSRobert Mustacchi 	    vdq->vdq_lookup);
579*36589d6bSRobert Mustacchi }
580*36589d6bSRobert Mustacchi 
581*36589d6bSRobert Mustacchi void
libvarpd_plugin_dhcp_reply(varpd_dhcp_handle_t * vdh,int action)582*36589d6bSRobert Mustacchi libvarpd_plugin_dhcp_reply(varpd_dhcp_handle_t *vdh, int action)
583*36589d6bSRobert Mustacchi {
584*36589d6bSRobert Mustacchi 	varpd_dhcp_query_t *vdq = (varpd_dhcp_query_t *)vdh;
585*36589d6bSRobert Mustacchi 
586*36589d6bSRobert Mustacchi 	if (vdq == NULL)
587*36589d6bSRobert Mustacchi 		libvarpd_panic("unknown plugin passed invalid "
588*36589d6bSRobert Mustacchi 		    "varpd_dhcp_handle_t");
589*36589d6bSRobert Mustacchi 
590*36589d6bSRobert Mustacchi 	if (action == VARPD_LOOKUP_DROP) {
591*36589d6bSRobert Mustacchi 		libvarpd_plugin_query_reply(vdq->vdq_query, VARPD_LOOKUP_DROP);
592*36589d6bSRobert Mustacchi 		umem_free(vdq, sizeof (varpd_dhcp_query_t));
593*36589d6bSRobert Mustacchi 		return;
594*36589d6bSRobert Mustacchi 	} else if (action != VARPD_LOOKUP_OK)
595*36589d6bSRobert Mustacchi 		libvarpd_panic("%s plugin returned invalid action %d",
596*36589d6bSRobert Mustacchi 		    vdq->vdq_inst->vri_plugin->vpp_name, action);
597*36589d6bSRobert Mustacchi 
598*36589d6bSRobert Mustacchi 	bcopy(vdq->vdq_lookup, &vdq->vdq_ether->ether_dhost, ETHERADDRL);
599*36589d6bSRobert Mustacchi 	(void) libvarpd_overlay_resend(vdq->vdq_inst->vri_impl, vdq->vdq_otl,
600*36589d6bSRobert Mustacchi 	    vdq->vdq_buf, vdq->vdq_bsize);
601*36589d6bSRobert Mustacchi 
602*36589d6bSRobert Mustacchi 	libvarpd_plugin_query_reply(vdq->vdq_query, VARPD_LOOKUP_DROP);
603*36589d6bSRobert Mustacchi 	umem_free(vdq, sizeof (varpd_dhcp_query_t));
604*36589d6bSRobert Mustacchi }
605*36589d6bSRobert Mustacchi 
606*36589d6bSRobert Mustacchi /*
607*36589d6bSRobert Mustacchi  * Inject a gratuitous ARP packet to the specified mac address.
608*36589d6bSRobert Mustacchi  */
609*36589d6bSRobert Mustacchi void
libvarpd_inject_arp(varpd_provider_handle_t * vph,const uint16_t vlan,const uint8_t * srcmac,const struct in_addr * srcip,const uint8_t * dstmac)610*36589d6bSRobert Mustacchi libvarpd_inject_arp(varpd_provider_handle_t *vph, const uint16_t vlan,
611*36589d6bSRobert Mustacchi     const uint8_t *srcmac, const struct in_addr *srcip, const uint8_t *dstmac)
612*36589d6bSRobert Mustacchi {
613*36589d6bSRobert Mustacchi 	char buf[500];
614*36589d6bSRobert Mustacchi 	size_t bsize = 0;
615*36589d6bSRobert Mustacchi 	struct ether_arp *ea;
616*36589d6bSRobert Mustacchi 	varpd_instance_t *inst = (varpd_instance_t *)vph;
617*36589d6bSRobert Mustacchi 
618*36589d6bSRobert Mustacchi 	if (vlan != 0) {
619*36589d6bSRobert Mustacchi 		struct ether_vlan_header *eh;
620*36589d6bSRobert Mustacchi 		eh = (struct ether_vlan_header *)(buf + bsize);
621*36589d6bSRobert Mustacchi 		bsize += sizeof (struct ether_vlan_header);
622*36589d6bSRobert Mustacchi 		bcopy(dstmac, &eh->ether_dhost, ETHERADDRL);
623*36589d6bSRobert Mustacchi 		bcopy(srcmac, &eh->ether_shost, ETHERADDRL);
624*36589d6bSRobert Mustacchi 		eh->ether_tpid = htons(ETHERTYPE_VLAN);
625*36589d6bSRobert Mustacchi 		eh->ether_tci = htons(VLAN_TCI(0, ETHER_CFI, vlan));
626*36589d6bSRobert Mustacchi 		eh->ether_type = htons(ETHERTYPE_ARP);
627*36589d6bSRobert Mustacchi 	} else {
628*36589d6bSRobert Mustacchi 		struct ether_header *eh;
629*36589d6bSRobert Mustacchi 		eh = (struct ether_header *)(buf + bsize);
630*36589d6bSRobert Mustacchi 		bsize += sizeof (struct ether_header);
631*36589d6bSRobert Mustacchi 		bcopy(dstmac, &eh->ether_dhost, ETHERADDRL);
632*36589d6bSRobert Mustacchi 		bcopy(srcmac, &eh->ether_shost, ETHERADDRL);
633*36589d6bSRobert Mustacchi 		eh->ether_type = htons(ETHERTYPE_ARP);
634*36589d6bSRobert Mustacchi 	}
635*36589d6bSRobert Mustacchi 
636*36589d6bSRobert Mustacchi 	ea = (struct ether_arp *)(buf + bsize);
637*36589d6bSRobert Mustacchi 	bsize += sizeof (struct ether_arp);
638*36589d6bSRobert Mustacchi 	ea->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
639*36589d6bSRobert Mustacchi 	ea->ea_hdr.ar_pro = htons(ETHERTYPE_IP);
640*36589d6bSRobert Mustacchi 	ea->ea_hdr.ar_hln = ETHERADDRL;
641*36589d6bSRobert Mustacchi 	ea->ea_hdr.ar_pln = sizeof (struct in_addr);
642*36589d6bSRobert Mustacchi 	ea->ea_hdr.ar_op = htons(ARPOP_REQUEST);
643*36589d6bSRobert Mustacchi 	bcopy(srcmac, ea->arp_sha, ETHERADDRL);
644*36589d6bSRobert Mustacchi 	bcopy(srcip, ea->arp_spa, sizeof (struct in_addr));
645*36589d6bSRobert Mustacchi 	bcopy(libvarpd_arp_bcast, ea->arp_tha, ETHERADDRL);
646*36589d6bSRobert Mustacchi 	bcopy(srcip, ea->arp_tpa, sizeof (struct in_addr));
647*36589d6bSRobert Mustacchi 
648*36589d6bSRobert Mustacchi 	(void) libvarpd_overlay_instance_inject(inst, buf, bsize);
649*36589d6bSRobert Mustacchi }
650