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