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;
}
Exemple #2
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;
}