The patch provides the same socket option as Linux IP_ORIGDSTADDR.

Unfortunately they will have different integer value due to Linux value being already assigned in FreeBSD.

The patch is similar to IP_RECVDSTADDR but also provides the destination port value to the application.

This allows/improves implementation of transparent proxies on UDP sockets due to having the whole information on forwarded packets.

Reviewed by:	adrian, aw
Approved by:	ae (mentor)
Sponsored by:	rsync.net
Differential Revision:	D9235
This commit is contained in:
eri 2017-03-06 04:01:58 +00:00
parent 82bdb13eb8
commit f2a480c25c
13 changed files with 119 additions and 28 deletions

View File

@ -136,6 +136,37 @@ determined by the destination address, returns an
error. error.
.Pp .Pp
If the If the
.Dv IP_ORIGDSTADDR
option is enabled on a
.Dv SOCK_DGRAM
socket,
the
.Xr recvmsg 2
call will return the destination
.Tn IP
address and destination port or a
.Tn UDP
datagram.
The
.Vt msg_control
field in the
.Vt msghdr
structure points to a buffer
that contains a
.Vt cmsghdr
structure followed by the
.Tn in_sockkaddr
structre.
The
.Vt cmsghdr
fields have the following values:
.Bd -literal
cmsg_len = CMSG_LEN(sizeof(struct in_sockaddr))
cmsg_level = IPPROTO_IP
cmsg_type = IP_ORIGDSTADDR
.Ed
.Pp
If the
.Dv IP_RECVDSTADDR .Dv IP_RECVDSTADDR
option is enabled on a option is enabled on a
.Dv SOCK_DGRAM .Dv SOCK_DGRAM

View File

@ -156,6 +156,9 @@ datagrams sent on this socket.
.\" .It Dv IPV6_RECVDSTADDR Fa "int *" .\" .It Dv IPV6_RECVDSTADDR Fa "int *"
.\" Get or set the status of whether datagrams are received with .\" Get or set the status of whether datagrams are received with
.\" destination addresses. .\" destination addresses.
.\" .It Dv IPV6_ORIGDSTADDR Fa "int *"
.\" Get or set the status of whether datagrams are received with
.\" destination addresses and destination ports.
.\" .It Dv IPV6_RETOPTS .\" .It Dv IPV6_RETOPTS
.\" Get or set IPv6 options. .\" Get or set IPv6 options.
.It Dv IPV6_MULTICAST_IF Fa "u_int *" .It Dv IPV6_MULTICAST_IF Fa "u_int *"

View File

@ -433,6 +433,8 @@ __END_DECLS
#define IP_BINDANY 24 /* bool: allow bind to any address */ #define IP_BINDANY 24 /* bool: allow bind to any address */
#define IP_BINDMULTI 25 /* bool: allow multiple listeners on a tuple */ #define IP_BINDMULTI 25 /* bool: allow multiple listeners on a tuple */
#define IP_RSS_LISTEN_BUCKET 26 /* int; set RSS listen bucket */ #define IP_RSS_LISTEN_BUCKET 26 /* int; set RSS listen bucket */
#define IP_ORIGDSTADDR 27 /* bool: receive IP dst addr/port w/dgram */
#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR
/* /*
* Options for controlling the firewall and dummynet. * Options for controlling the firewall and dummynet.

View File

@ -2492,6 +2492,10 @@ db_print_inpflags(int inp_flags)
db_printf("%sINP_RECVDSTADDR", comma ? ", " : ""); db_printf("%sINP_RECVDSTADDR", comma ? ", " : "");
comma = 1; comma = 1;
} }
if (inp_flags & INP_ORIGDSTADDR) {
db_printf("%sINP_ORIGDSTADDR", comma ? ", " : "");
comma = 1;
}
if (inp_flags & INP_HDRINCL) { if (inp_flags & INP_HDRINCL) {
db_printf("%sINP_HDRINCL", comma ? ", " : ""); db_printf("%sINP_HDRINCL", comma ? ", " : "");
comma = 1; comma = 1;

View File

@ -618,6 +618,7 @@ short inp_so_options(const struct inpcb *inp);
#define INP_RECVFLOWID 0x00000100 /* populate recv datagram with flow info */ #define INP_RECVFLOWID 0x00000100 /* populate recv datagram with flow info */
#define INP_RECVRSSBUCKETID 0x00000200 /* populate recv datagram with bucket id */ #define INP_RECVRSSBUCKETID 0x00000200 /* populate recv datagram with bucket id */
#define INP_RATE_LIMIT_CHANGED 0x00000400 /* rate limit needs attention */ #define INP_RATE_LIMIT_CHANGED 0x00000400 /* rate limit needs attention */
#define INP_ORIGDSTADDR 0x00000800 /* receive IP dst address/port */
/* /*
* Flags passed to in_pcblookup*() functions. * Flags passed to in_pcblookup*() functions.

View File

@ -1065,6 +1065,7 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
case IP_MINTTL: case IP_MINTTL:
case IP_RECVOPTS: case IP_RECVOPTS:
case IP_RECVRETOPTS: case IP_RECVRETOPTS:
case IP_ORIGDSTADDR:
case IP_RECVDSTADDR: case IP_RECVDSTADDR:
case IP_RECVTTL: case IP_RECVTTL:
case IP_RECVIF: case IP_RECVIF:
@ -1126,6 +1127,10 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
OPTSET(INP_RECVDSTADDR); OPTSET(INP_RECVDSTADDR);
break; break;
case IP_ORIGDSTADDR:
OPTSET2(INP_ORIGDSTADDR, optval);
break;
case IP_RECVTTL: case IP_RECVTTL:
OPTSET(INP_RECVTTL); OPTSET(INP_RECVTTL);
break; break;
@ -1258,6 +1263,7 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
case IP_MINTTL: case IP_MINTTL:
case IP_RECVOPTS: case IP_RECVOPTS:
case IP_RECVRETOPTS: case IP_RECVRETOPTS:
case IP_ORIGDSTADDR:
case IP_RECVDSTADDR: case IP_RECVDSTADDR:
case IP_RECVTTL: case IP_RECVTTL:
case IP_RECVIF: case IP_RECVIF:
@ -1303,6 +1309,10 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
optval = OPTBIT(INP_RECVDSTADDR); optval = OPTBIT(INP_RECVDSTADDR);
break; break;
case IP_ORIGDSTADDR:
optval = OPTBIT2(INP_ORIGDSTADDR);
break;
case IP_RECVTTL: case IP_RECVTTL:
optval = OPTBIT(INP_RECVTTL); optval = OPTBIT(INP_RECVTTL);
break; break;

View File

@ -304,7 +304,7 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
{ {
struct sockaddr *append_sa; struct sockaddr *append_sa;
struct socket *so; struct socket *so;
struct mbuf *opts = NULL; struct mbuf *tmpopts, *opts = NULL;
#ifdef INET6 #ifdef INET6
struct sockaddr_in6 udp_in6; struct sockaddr_in6 udp_in6;
#endif #endif
@ -319,7 +319,7 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
if (up->u_tun_func != NULL) { if (up->u_tun_func != NULL) {
in_pcbref(inp); in_pcbref(inp);
INP_RUNLOCK(inp); INP_RUNLOCK(inp);
(*up->u_tun_func)(n, off, inp, (struct sockaddr *)udp_in, (*up->u_tun_func)(n, off, inp, (struct sockaddr *)&udp_in[0],
up->u_tun_ctx); up->u_tun_ctx);
INP_RLOCK(inp); INP_RLOCK(inp);
return (in_pcbrele_rlocked(inp)); return (in_pcbrele_rlocked(inp));
@ -355,16 +355,27 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
#endif /* INET6 */ #endif /* INET6 */
ip_savecontrol(inp, &opts, ip, n); ip_savecontrol(inp, &opts, ip, n);
} }
if ((inp->inp_vflag & INP_IPV4) && (inp->inp_flags2 & INP_ORIGDSTADDR)) {
tmpopts = sbcreatecontrol((caddr_t)&udp_in[1],
sizeof(struct sockaddr_in), IP_ORIGDSTADDR, IPPROTO_IP);
if (tmpopts) {
if (opts) {
tmpopts->m_next = opts;
opts = tmpopts;
} else
opts = tmpopts;
}
}
#ifdef INET6 #ifdef INET6
if (inp->inp_vflag & INP_IPV6) { if (inp->inp_vflag & INP_IPV6) {
bzero(&udp_in6, sizeof(udp_in6)); bzero(&udp_in6, sizeof(udp_in6));
udp_in6.sin6_len = sizeof(udp_in6); udp_in6.sin6_len = sizeof(udp_in6);
udp_in6.sin6_family = AF_INET6; udp_in6.sin6_family = AF_INET6;
in6_sin_2_v4mapsin6(udp_in, &udp_in6); in6_sin_2_v4mapsin6(&udp_in[0], &udp_in6);
append_sa = (struct sockaddr *)&udp_in6; append_sa = (struct sockaddr *)&udp_in6;
} else } else
#endif /* INET6 */ #endif /* INET6 */
append_sa = (struct sockaddr *)udp_in; append_sa = (struct sockaddr *)&udp_in[0];
m_adj(n, off); m_adj(n, off);
so = inp->inp_socket; so = inp->inp_socket;
@ -390,7 +401,7 @@ udp_input(struct mbuf **mp, int *offp, int proto)
uint16_t len, ip_len; uint16_t len, ip_len;
struct inpcbinfo *pcbinfo; struct inpcbinfo *pcbinfo;
struct ip save_ip; struct ip save_ip;
struct sockaddr_in udp_in; struct sockaddr_in udp_in[2];
struct mbuf *m; struct mbuf *m;
struct m_tag *fwd_tag; struct m_tag *fwd_tag;
int cscov_partial, iphlen; int cscov_partial, iphlen;
@ -435,11 +446,15 @@ udp_input(struct mbuf **mp, int *offp, int proto)
* Construct sockaddr format source address. Stuff source address * Construct sockaddr format source address. Stuff source address
* and datagram in user buffer. * and datagram in user buffer.
*/ */
bzero(&udp_in, sizeof(udp_in)); bzero(&udp_in[0], sizeof(struct sockaddr_in) * 2);
udp_in.sin_len = sizeof(udp_in); udp_in[0].sin_len = sizeof(struct sockaddr_in);
udp_in.sin_family = AF_INET; udp_in[0].sin_family = AF_INET;
udp_in.sin_port = uh->uh_sport; udp_in[0].sin_port = uh->uh_sport;
udp_in.sin_addr = ip->ip_src; udp_in[0].sin_addr = ip->ip_src;
udp_in[1].sin_len = sizeof(struct sockaddr_in);
udp_in[1].sin_family = AF_INET;
udp_in[1].sin_port = uh->uh_dport;
udp_in[1].sin_addr = ip->ip_dst;
/* /*
* Make mbuf data length reflect UDP length. If not enough data to * Make mbuf data length reflect UDP length. If not enough data to
@ -568,7 +583,7 @@ udp_input(struct mbuf **mp, int *offp, int proto)
blocked = imo_multi_filter(imo, ifp, blocked = imo_multi_filter(imo, ifp,
(struct sockaddr *)&group, (struct sockaddr *)&group,
(struct sockaddr *)&udp_in); (struct sockaddr *)&udp_in[0]);
if (blocked != MCAST_PASS) { if (blocked != MCAST_PASS) {
if (blocked == MCAST_NOTGMEMBER) if (blocked == MCAST_NOTGMEMBER)
IPSTAT_INC(ips_notmember); IPSTAT_INC(ips_notmember);
@ -587,7 +602,7 @@ udp_input(struct mbuf **mp, int *offp, int proto)
UDP_PROBE(receive, NULL, last, ip, UDP_PROBE(receive, NULL, last, ip,
last, uh); last, uh);
if (udp_append(last, ip, n, iphlen, if (udp_append(last, ip, n, iphlen,
&udp_in)) { udp_in)) {
goto inp_lost; goto inp_lost;
} }
} }
@ -620,7 +635,7 @@ udp_input(struct mbuf **mp, int *offp, int proto)
goto badunlocked; goto badunlocked;
} }
UDP_PROBE(receive, NULL, last, ip, last, uh); UDP_PROBE(receive, NULL, last, ip, last, uh);
if (udp_append(last, ip, m, iphlen, &udp_in) == 0) if (udp_append(last, ip, m, iphlen, udp_in) == 0)
INP_RUNLOCK(last); INP_RUNLOCK(last);
inp_lost: inp_lost:
INP_INFO_RUNLOCK(pcbinfo); INP_INFO_RUNLOCK(pcbinfo);
@ -710,7 +725,7 @@ udp_input(struct mbuf **mp, int *offp, int proto)
} }
UDP_PROBE(receive, NULL, inp, ip, inp, uh); UDP_PROBE(receive, NULL, inp, ip, inp, uh);
if (udp_append(inp, ip, m, iphlen, &udp_in) == 0) if (udp_append(inp, ip, m, iphlen, udp_in) == 0)
INP_RUNLOCK(inp); INP_RUNLOCK(inp);
return (IPPROTO_DONE); return (IPPROTO_DONE);

View File

@ -497,6 +497,9 @@ struct route_in6 {
#define IPV6_RECVFLOWID 70 /* bool; receive IP6 flowid/flowtype w/ datagram */ #define IPV6_RECVFLOWID 70 /* bool; receive IP6 flowid/flowtype w/ datagram */
#define IPV6_RECVRSSBUCKETID 71 /* bool; receive IP6 RSS bucket id w/ datagram */ #define IPV6_RECVRSSBUCKETID 71 /* bool; receive IP6 RSS bucket id w/ datagram */
#define IPV6_ORIGDSTADDR 72 /* bool: allow getting dstaddr /port info */
#define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR
/* /*
* The following option is private; do not use it from user applications. * The following option is private; do not use it from user applications.
* It is deliberately defined to the same value as IP_MSFILTER. * It is deliberately defined to the same value as IP_MSFILTER.

View File

@ -1267,7 +1267,7 @@ in6_pcblookup_mbuf(struct inpcbinfo *pcbinfo, struct in6_addr *faddr,
} }
void void
init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m) init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m, int srcordst)
{ {
struct ip6_hdr *ip; struct ip6_hdr *ip;
@ -1275,7 +1275,7 @@ init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m)
bzero(sin6, sizeof(*sin6)); bzero(sin6, sizeof(*sin6));
sin6->sin6_len = sizeof(*sin6); sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6; sin6->sin6_family = AF_INET6;
sin6->sin6_addr = ip->ip6_src; sin6->sin6_addr = srcordst ? ip->ip6_dst : ip->ip6_src;
(void)sa6_recoverscope(sin6); /* XXX: should catch errors... */ (void)sa6_recoverscope(sin6); /* XXX: should catch errors... */

View File

@ -113,7 +113,7 @@ int in6_mapped_sockaddr(struct socket *so, struct sockaddr **nam);
int in6_mapped_peeraddr(struct socket *so, struct sockaddr **nam); int in6_mapped_peeraddr(struct socket *so, struct sockaddr **nam);
int in6_selecthlim(struct in6pcb *, struct ifnet *); int in6_selecthlim(struct in6pcb *, struct ifnet *);
int in6_pcbsetport(struct in6_addr *, struct inpcb *, struct ucred *); int in6_pcbsetport(struct in6_addr *, struct inpcb *, struct ucred *);
void init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m); void init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m, int);
#endif /* _KERNEL */ #endif /* _KERNEL */
#endif /* !_NETINET6_IN6_PCB_H_ */ #endif /* !_NETINET6_IN6_PCB_H_ */

View File

@ -1545,6 +1545,7 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt)
#endif #endif
case IPV6_V6ONLY: case IPV6_V6ONLY:
case IPV6_AUTOFLOWLABEL: case IPV6_AUTOFLOWLABEL:
case IPV6_ORIGDSTADDR:
case IPV6_BINDANY: case IPV6_BINDANY:
case IPV6_BINDMULTI: case IPV6_BINDMULTI:
#ifdef RSS #ifdef RSS
@ -1730,6 +1731,9 @@ do { \
OPTSET(IN6P_AUTOFLOWLABEL); OPTSET(IN6P_AUTOFLOWLABEL);
break; break;
case IPV6_ORIGDSTADDR:
OPTSET2(INP_ORIGDSTADDR, optval);
break;
case IPV6_BINDANY: case IPV6_BINDANY:
OPTSET(INP_BINDANY); OPTSET(INP_BINDANY);
break; break;
@ -2018,6 +2022,10 @@ do { \
optval = OPTBIT(IN6P_AUTOFLOWLABEL); optval = OPTBIT(IN6P_AUTOFLOWLABEL);
break; break;
case IPV6_ORIGDSTADDR:
optval = OPTBIT2(INP_ORIGDSTADDR);
break;
case IPV6_BINDANY: case IPV6_BINDANY:
optval = OPTBIT(INP_BINDANY); optval = OPTBIT(INP_BINDANY);
break; break;

View File

@ -166,7 +166,7 @@ rip6_input(struct mbuf **mp, int *offp, int proto)
RIP6STAT_INC(rip6s_ipackets); RIP6STAT_INC(rip6s_ipackets);
init_sin6(&fromsa, m); /* general init */ init_sin6(&fromsa, m, 0); /* general init */
ifp = m->m_pkthdr.rcvif; ifp = m->m_pkthdr.rcvif;

View File

@ -137,7 +137,7 @@ udp6_append(struct inpcb *inp, struct mbuf *n, int off,
struct sockaddr_in6 *fromsa) struct sockaddr_in6 *fromsa)
{ {
struct socket *so; struct socket *so;
struct mbuf *opts; struct mbuf *opts = NULL, *tmp_opts;
struct udpcb *up; struct udpcb *up;
INP_LOCK_ASSERT(inp); INP_LOCK_ASSERT(inp);
@ -149,7 +149,7 @@ udp6_append(struct inpcb *inp, struct mbuf *n, int off,
if (up->u_tun_func != NULL) { if (up->u_tun_func != NULL) {
in_pcbref(inp); in_pcbref(inp);
INP_RUNLOCK(inp); INP_RUNLOCK(inp);
(*up->u_tun_func)(n, off, inp, (struct sockaddr *)fromsa, (*up->u_tun_func)(n, off, inp, (struct sockaddr *)&fromsa[0],
up->u_tun_ctx); up->u_tun_ctx);
INP_RLOCK(inp); INP_RLOCK(inp);
return (in_pcbrele_rlocked(inp)); return (in_pcbrele_rlocked(inp));
@ -173,11 +173,23 @@ udp6_append(struct inpcb *inp, struct mbuf *n, int off,
if (inp->inp_flags & INP_CONTROLOPTS || if (inp->inp_flags & INP_CONTROLOPTS ||
inp->inp_socket->so_options & SO_TIMESTAMP) inp->inp_socket->so_options & SO_TIMESTAMP)
ip6_savecontrol(inp, n, &opts); ip6_savecontrol(inp, n, &opts);
if ((inp->inp_vflag & INP_IPV6) && (inp->inp_flags2 & INP_ORIGDSTADDR)) {
tmp_opts = sbcreatecontrol((caddr_t)&fromsa[1],
sizeof(struct sockaddr_in6), IPV6_ORIGDSTADDR, IPPROTO_IPV6);
if (tmp_opts) {
if (opts) {
tmp_opts->m_next = opts;
opts = tmp_opts;
} else
opts = tmp_opts;
}
}
m_adj(n, off + sizeof(struct udphdr)); m_adj(n, off + sizeof(struct udphdr));
so = inp->inp_socket; so = inp->inp_socket;
SOCKBUF_LOCK(&so->so_rcv); SOCKBUF_LOCK(&so->so_rcv);
if (sbappendaddr_locked(&so->so_rcv, (struct sockaddr *)fromsa, n, if (sbappendaddr_locked(&so->so_rcv, (struct sockaddr *)&fromsa[0], n,
opts) == 0) { opts) == 0) {
SOCKBUF_UNLOCK(&so->so_rcv); SOCKBUF_UNLOCK(&so->so_rcv);
m_freem(n); m_freem(n);
@ -202,7 +214,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
int off = *offp; int off = *offp;
int cscov_partial; int cscov_partial;
int plen, ulen; int plen, ulen;
struct sockaddr_in6 fromsa; struct sockaddr_in6 fromsa[2];
struct m_tag *fwd_tag; struct m_tag *fwd_tag;
uint16_t uh_sum; uint16_t uh_sum;
uint8_t nxt; uint8_t nxt;
@ -277,8 +289,10 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
/* /*
* Construct sockaddr format source address. * Construct sockaddr format source address.
*/ */
init_sin6(&fromsa, m); init_sin6(&fromsa[0], m, 0);
fromsa.sin6_port = uh->uh_sport; fromsa[0].sin6_port = uh->uh_sport;
init_sin6(&fromsa[1], m, 1);
fromsa[1].sin6_port = uh->uh_dport;
pcbinfo = udp_get_inpcbinfo(nxt); pcbinfo = udp_get_inpcbinfo(nxt);
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
@ -349,7 +363,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
blocked = im6o_mc_filter(imo, ifp, blocked = im6o_mc_filter(imo, ifp,
(struct sockaddr *)&mcaddr, (struct sockaddr *)&mcaddr,
(struct sockaddr *)&fromsa); (struct sockaddr *)&fromsa[0]);
if (blocked != MCAST_PASS) { if (blocked != MCAST_PASS) {
if (blocked == MCAST_NOTGMEMBER) if (blocked == MCAST_NOTGMEMBER)
IP6STAT_INC(ip6s_notmember); IP6STAT_INC(ip6s_notmember);
@ -370,7 +384,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
INP_RLOCK(last); INP_RLOCK(last);
UDP_PROBE(receive, NULL, last, ip6, UDP_PROBE(receive, NULL, last, ip6,
last, uh); last, uh);
if (udp6_append(last, n, off, &fromsa)) if (udp6_append(last, n, off, fromsa))
goto inp_lost; goto inp_lost;
INP_RUNLOCK(last); INP_RUNLOCK(last);
} }
@ -402,7 +416,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
INP_RLOCK(last); INP_RLOCK(last);
INP_INFO_RUNLOCK(pcbinfo); INP_INFO_RUNLOCK(pcbinfo);
UDP_PROBE(receive, NULL, last, ip6, last, uh); UDP_PROBE(receive, NULL, last, ip6, last, uh);
if (udp6_append(last, m, off, &fromsa) == 0) if (udp6_append(last, m, off, fromsa) == 0)
INP_RUNLOCK(last); INP_RUNLOCK(last);
inp_lost: inp_lost:
return (IPPROTO_DONE); return (IPPROTO_DONE);
@ -482,7 +496,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
} }
} }
UDP_PROBE(receive, NULL, inp, ip6, inp, uh); UDP_PROBE(receive, NULL, inp, ip6, inp, uh);
if (udp6_append(inp, m, off, &fromsa) == 0) if (udp6_append(inp, m, off, fromsa) == 0)
INP_RUNLOCK(inp); INP_RUNLOCK(inp);
return (IPPROTO_DONE); return (IPPROTO_DONE);