int ndisc_send_na(int ifindex, const struct in6_addr *src, const struct in6_addr *dst, const struct in6_addr *target, uint32_t flags) { struct nd_neighbor_advert *na; struct iovec iov[2]; uint8_t l2addr[L2ADDR_MAX_SIZE]; int len, iovlen = 0; memset(iov, 0, sizeof(iov)); if ((len = ndisc_l2addr_to_opt(ifindex, l2addr)) < 0) return -EINVAL; na = icmp6_create(iov, ND_NEIGHBOR_ADVERT, iovlen++); if (na == NULL) return -ENOMEM; if (len > 0) { if (nd_opt_create(&iov[iovlen], ND_OPT_TARGET_LINKADDR, len, l2addr) == NULL) { free_iov_data(iov, iovlen); return -ENOMEM; } iovlen++; } na->nd_na_target = *target; na->nd_na_flags_reserved = flags; icmp6_send(ifindex, 255, src, dst, iov, iovlen); free_iov_data(iov, iovlen); statistics_inc(&mipl_stat, MIPL_STATISTICS_OUT_NA); return 0; }
static int ndisc_send_unspec(int oif, const struct in6_addr *dest, uint8_t *hdr, int hdrlen, struct iovec *optv, size_t optvlen) { struct { struct ip6_hdr ip; struct icmp6_hdr icmp; uint8_t data[1500]; } frame; struct msghdr msgh; struct cmsghdr *cmsg; struct in6_pktinfo *pinfo; struct sockaddr_in6 dst; char cbuf[CMSG_SPACE(sizeof(*pinfo))]; struct iovec iov; uint8_t *data = (uint8_t *)(&frame.icmp); int type, fd, ret, remlen, datalen = 0, written = 0, v = 1; if (hdr == NULL || hdrlen < 0 || (size_t)hdrlen < sizeof(struct icmp6_hdr) || (size_t)hdrlen > (sizeof(frame) - sizeof(struct ip6_hdr))) return -EINVAL; if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)) < 0) return -1; if (setsockopt(fd, IPPROTO_IPV6, IP_HDRINCL, &v, sizeof(v)) < 0) { dbg("cannot set IP_HDRINCL: %s\n", strerror(errno)); close(fd); return -errno; } memset(&frame, 0, sizeof(frame)); memset(&dst, 0, sizeof(dst)); dst.sin6_addr = *dest; /* Copy ICMPv6 header and update various length values */ memcpy(data, hdr, hdrlen); data += hdrlen; datalen += hdrlen; remlen = sizeof(frame) - sizeof(struct ip6_hdr) - hdrlen; /* Prepare for csum: write trailing options by linearizing iov */ if ((iov_linearize(data, remlen, optv, optvlen, &written) != 0) || (written & 0x1)) /* Ensure length is even for csum() */ return -1; datalen += written; /* Fill in the IPv6 header */ frame.ip.ip6_vfc = 0x60; frame.ip.ip6_plen = htons(datalen); frame.ip.ip6_nxt = IPPROTO_ICMPV6; frame.ip.ip6_hlim = 255; frame.ip.ip6_dst = *dest; /* all other fields are already set to zero */ frame.icmp.icmp6_cksum = in6_cksum(&in6addr_any, dest, &frame.icmp, datalen, IPPROTO_ICMPV6); iov.iov_base = &frame; iov.iov_len = sizeof(frame.ip) + datalen; dst.sin6_family = AF_INET6; msgh.msg_name = &dst; msgh.msg_namelen = sizeof(dst); msgh.msg_iov = &iov; msgh.msg_iovlen = 1; msgh.msg_flags = 0; memset(cbuf, 0, CMSG_SPACE(sizeof(*pinfo))); cmsg = (struct cmsghdr *)cbuf; pinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); pinfo->ipi6_ifindex = oif; cmsg->cmsg_len = CMSG_LEN(sizeof(*pinfo)); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; msgh.msg_control = cmsg; msgh.msg_controllen = cmsg->cmsg_len; ret = sendmsg(fd, &msgh, 0); if (ret < 0) dbg("sendmsg: %s\n", strerror(errno)); close(fd); type = hdr[0]; if (type == ND_NEIGHBOR_SOLICIT) { statistics_inc(&mipl_stat, MIPL_STATISTICS_OUT_NS_UNSPEC); } else if (type == ND_ROUTER_SOLICIT) { statistics_inc(&mipl_stat, MIPL_STATISTICS_OUT_RS_UNSPEC); } return ret; }