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