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.

Sponsored-by: rsync.net
Differential Revision: D9235
Reviewed-by: adrian
This commit is contained in:
Ermal Luçi 2017-02-10 05:16:14 +00:00
parent 281e4f2dd4
commit ed55edceef
11 changed files with 86 additions and 28 deletions

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 udpin[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,16 @@ 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(&udpin[0], sizeof(struct sockaddr_in));
udp_in.sin_len = sizeof(udp_in); udpin[0].sin_len = sizeof(struct sockaddr_in);
udp_in.sin_family = AF_INET; udpin[0].sin_family = AF_INET;
udp_in.sin_port = uh->uh_sport; udpin[0].sin_port = uh->uh_sport;
udp_in.sin_addr = ip->ip_src; udpin[0].sin_addr = ip->ip_src;
bzero(&udpin[1], sizeof(struct sockaddr_in));
udpin[1].sin_len = sizeof(struct sockaddr_in);
udpin[1].sin_family = AF_INET;
udpin[1].sin_port = uh->uh_dport;
udpin[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 +584,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 *)&udpin[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 +603,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)) { udpin)) {
goto inp_lost; goto inp_lost;
} }
} }
@ -620,7 +636,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 +726,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 65 /* 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), IP_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);