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(ðer->ether_shost, ðer->ether_dhost, ETHERADDRL);
184 bcopy(vaq->vaq_lookup, ðer->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(ðer->ether_shost, ðer->ether_dhost, ETHERADDRL);
412 bcopy(vaq->vaq_lookup, ðer->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