Code:  static int myrecvfrom6(int sockfd, void *buf, size_t *buflen, int flags,
		       struct in6_addr *addr, uint32_t *ifindex)
 {
 	struct sockaddr_in6 sin6;
	unsigned char cbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
 	struct iovec iovec;
 	struct msghdr msghdr;
 	struct cmsghdr *cmsghdr;
	ssize_t len;

	iovec.iov_len = *buflen;
	iovec.iov_base = buf;
	memset(&msghdr, 0, sizeof(msghdr));
	msghdr.msg_name = &sin6;
	msghdr.msg_namelen = sizeof(sin6);
	msghdr.msg_iov = &iovec;
	msghdr.msg_iovlen = 1;
	msghdr.msg_control = cbuf;
	msghdr.msg_controllen = sizeof(cbuf);

	len = recvmsg(sockfd, &msghdr, flags);
	if (len == -1)
		return -errno;
	*buflen = len;

	/* Set ifindex to scope_id now. But since scope_id gets not
	 * set by kernel for linklocal addresses, use pktinfo to obtain that
	 * value right after.
	 */
 	*ifindex = sin6.sin6_scope_id;
         for (cmsghdr = CMSG_FIRSTHDR(&msghdr); cmsghdr;
 	     cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
		if (cmsghdr->cmsg_level == IPPROTO_IPV6 &&
		    cmsghdr->cmsg_type == IPV6_PKTINFO &&
		    cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
			struct in6_pktinfo *pktinfo;
 
			pktinfo = (struct in6_pktinfo *) CMSG_DATA(cmsghdr);
			*ifindex = pktinfo->ipi6_ifindex;
 		}
 	}
 	*addr = sin6.sin6_addr;

	return 0;
}

Commit Message: libndp: validate the IPv6 hop limit

None of the NDP messages should ever come from a non-local network; as
stated in RFC4861's 6.1.1 (RS), 6.1.2 (RA), 7.1.1 (NS), 7.1.2 (NA),
and 8.1. (redirect):

  - The IP Hop Limit field has a value of 255, i.e., the packet
    could not possibly have been forwarded by a router.

This fixes CVE-2016-3698.

Reported by: Julien BERNARD <julien.bernard@viagenie.ca>
Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
CWE ID: CWE-284
 static int myrecvfrom6(int sockfd, void *buf, size_t *buflen, int flags,
		       struct in6_addr *addr, uint32_t *ifindex, int *hoplimit)
 {
 	struct sockaddr_in6 sin6;
	unsigned char cbuf[2 * CMSG_SPACE(sizeof(struct in6_pktinfo))];
 	struct iovec iovec;
 	struct msghdr msghdr;
 	struct cmsghdr *cmsghdr;
	ssize_t len;

	iovec.iov_len = *buflen;
	iovec.iov_base = buf;
	memset(&msghdr, 0, sizeof(msghdr));
	msghdr.msg_name = &sin6;
	msghdr.msg_namelen = sizeof(sin6);
	msghdr.msg_iov = &iovec;
	msghdr.msg_iovlen = 1;
	msghdr.msg_control = cbuf;
	msghdr.msg_controllen = sizeof(cbuf);

	len = recvmsg(sockfd, &msghdr, flags);
	if (len == -1)
		return -errno;
	*buflen = len;

	/* Set ifindex to scope_id now. But since scope_id gets not
	 * set by kernel for linklocal addresses, use pktinfo to obtain that
	 * value right after.
	 */
 	*ifindex = sin6.sin6_scope_id;
         for (cmsghdr = CMSG_FIRSTHDR(&msghdr); cmsghdr;
 	     cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
		if (cmsghdr->cmsg_level != IPPROTO_IPV6)
			continue;

		switch(cmsghdr->cmsg_type) {
		case IPV6_PKTINFO:
			if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
				struct in6_pktinfo *pktinfo;

				pktinfo = (struct in6_pktinfo *) CMSG_DATA(cmsghdr);
				*ifindex = pktinfo->ipi6_ifindex;
			}
			break;
		case IPV6_HOPLIMIT:
			if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) {
				int *val;
 
				val = (int *) CMSG_DATA(cmsghdr);
				*hoplimit = *val;
			}
			break;
 		}
 	}
 	*addr = sin6.sin6_addr;

	return 0;
}
