IPV6_PKTINFO support for v4-mapped IPv6 sockets
When using v4-mapped IPv6 sockets with IPV6_PKTINFO we do not respect the given v4-mapped src address on the IPv4 socket. Implement the needed functionality. This allows single-socket UDP applications (such as OpenVPN) to work better on FreeBSD. Requested by: Gert Doering (gert greenie.net), pfsense Tested by: Gert Doering (gert greenie.net) Reviewed by: melifaro MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D24135
This commit is contained in:
parent
9f91d464a9
commit
a9839c4aee
@ -163,7 +163,7 @@ VNET_PCPUSTAT_SYSUNINIT(udpstat);
|
|||||||
#ifdef INET
|
#ifdef INET
|
||||||
static void udp_detach(struct socket *so);
|
static void udp_detach(struct socket *so);
|
||||||
static int udp_output(struct inpcb *, struct mbuf *, struct sockaddr *,
|
static int udp_output(struct inpcb *, struct mbuf *, struct sockaddr *,
|
||||||
struct mbuf *, struct thread *);
|
struct mbuf *, struct thread *, int);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1083,9 +1083,65 @@ udp_ctloutput(struct socket *so, struct sockopt *sopt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef INET
|
#ifdef INET
|
||||||
|
#ifdef INET6
|
||||||
|
/* The logic here is derived from ip6_setpktopt(). See comments there. */
|
||||||
|
static int
|
||||||
|
udp_v4mapped_pktinfo(struct cmsghdr *cm, struct sockaddr_in * src,
|
||||||
|
struct inpcb *inp, int flags)
|
||||||
|
{
|
||||||
|
struct ifnet *ifp;
|
||||||
|
struct in6_pktinfo *pktinfo;
|
||||||
|
struct in_addr ia;
|
||||||
|
|
||||||
|
if ((flags & PRUS_IPV6) == 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
if (cm->cmsg_level != IPPROTO_IPV6)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
if (cm->cmsg_type != IPV6_2292PKTINFO &&
|
||||||
|
cm->cmsg_type != IPV6_PKTINFO)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
if (cm->cmsg_len !=
|
||||||
|
CMSG_LEN(sizeof(struct in6_pktinfo)))
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm);
|
||||||
|
if (!IN6_IS_ADDR_V4MAPPED(&pktinfo->ipi6_addr) &&
|
||||||
|
!IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr))
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
/* Validate the interface index if specified. */
|
||||||
|
if (pktinfo->ipi6_ifindex > V_if_index)
|
||||||
|
return (ENXIO);
|
||||||
|
|
||||||
|
ifp = NULL;
|
||||||
|
if (pktinfo->ipi6_ifindex) {
|
||||||
|
ifp = ifnet_byindex(pktinfo->ipi6_ifindex);
|
||||||
|
if (ifp == NULL)
|
||||||
|
return (ENXIO);
|
||||||
|
}
|
||||||
|
if (ifp != NULL && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
|
||||||
|
|
||||||
|
ia.s_addr = pktinfo->ipi6_addr.s6_addr32[3];
|
||||||
|
if (in_ifhasaddr(ifp, ia) == 0)
|
||||||
|
return (EADDRNOTAVAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero(src, sizeof(*src));
|
||||||
|
src->sin_family = AF_INET;
|
||||||
|
src->sin_len = sizeof(*src);
|
||||||
|
src->sin_port = inp->inp_lport;
|
||||||
|
src->sin_addr.s_addr = pktinfo->ipi6_addr.s6_addr32[3];
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr,
|
udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr,
|
||||||
struct mbuf *control, struct thread *td)
|
struct mbuf *control, struct thread *td, int flags)
|
||||||
{
|
{
|
||||||
struct udpiphdr *ui;
|
struct udpiphdr *ui;
|
||||||
int len = m->m_pkthdr.len;
|
int len = m->m_pkthdr.len;
|
||||||
@ -1149,6 +1205,11 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr,
|
|||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#ifdef INET6
|
||||||
|
error = udp_v4mapped_pktinfo(cm, &src, inp, flags);
|
||||||
|
if (error != 0)
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
if (cm->cmsg_level != IPPROTO_IP)
|
if (cm->cmsg_level != IPPROTO_IP)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -1696,7 +1757,7 @@ udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
|
|||||||
|
|
||||||
inp = sotoinpcb(so);
|
inp = sotoinpcb(so);
|
||||||
KASSERT(inp != NULL, ("udp_send: inp == NULL"));
|
KASSERT(inp != NULL, ("udp_send: inp == NULL"));
|
||||||
return (udp_output(inp, m, addr, control, td));
|
return (udp_output(inp, m, addr, control, td, flags));
|
||||||
}
|
}
|
||||||
#endif /* INET */
|
#endif /* INET */
|
||||||
|
|
||||||
|
@ -784,7 +784,7 @@ udp6_output(struct socket *so, int flags_arg, struct mbuf *m,
|
|||||||
in6_sin6_2_sin_in_sock((struct sockaddr *)sin6);
|
in6_sin6_2_sin_in_sock((struct sockaddr *)sin6);
|
||||||
pru = inetsw[ip_protox[nxt]].pr_usrreqs;
|
pru = inetsw[ip_protox[nxt]].pr_usrreqs;
|
||||||
/* addr will just be freed in sendit(). */
|
/* addr will just be freed in sendit(). */
|
||||||
return ((*pru->pru_send)(so, flags_arg, m,
|
return ((*pru->pru_send)(so, flags_arg | PRUS_IPV6, m,
|
||||||
(struct sockaddr *)sin6, control, td));
|
(struct sockaddr *)sin6, control, td));
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@ -210,6 +210,7 @@ struct pr_usrreqs {
|
|||||||
#define PRUS_EOF 0x2
|
#define PRUS_EOF 0x2
|
||||||
#define PRUS_MORETOCOME 0x4
|
#define PRUS_MORETOCOME 0x4
|
||||||
#define PRUS_NOTREADY 0x8
|
#define PRUS_NOTREADY 0x8
|
||||||
|
#define PRUS_IPV6 0x10
|
||||||
int (*pru_ready)(struct socket *so, struct mbuf *m, int count);
|
int (*pru_ready)(struct socket *so, struct mbuf *m, int count);
|
||||||
int (*pru_sense)(struct socket *so, struct stat *sb);
|
int (*pru_sense)(struct socket *so, struct stat *sb);
|
||||||
int (*pru_shutdown)(struct socket *so);
|
int (*pru_shutdown)(struct socket *so);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user