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 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 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 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 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 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 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 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 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 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