From f2a480c25cb3d60d5b3221a43f3c0c5b763099eb Mon Sep 17 00:00:00 2001 From: eri Date: Mon, 6 Mar 2017 04:01:58 +0000 Subject: [PATCH] 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 --- share/man/man4/ip.4 | 31 +++++++++++++++++++++++++++ share/man/man4/ip6.4 | 3 +++ sys/netinet/in.h | 2 ++ sys/netinet/in_pcb.c | 4 ++++ sys/netinet/in_pcb.h | 1 + sys/netinet/ip_output.c | 10 +++++++++ sys/netinet/udp_usrreq.c | 43 +++++++++++++++++++++++++------------- sys/netinet6/in6.h | 3 +++ sys/netinet6/in6_pcb.c | 4 ++-- sys/netinet6/in6_pcb.h | 2 +- sys/netinet6/ip6_output.c | 8 +++++++ sys/netinet6/raw_ip6.c | 2 +- sys/netinet6/udp6_usrreq.c | 34 +++++++++++++++++++++--------- 13 files changed, 119 insertions(+), 28 deletions(-) diff --git a/share/man/man4/ip.4 b/share/man/man4/ip.4 index 27a7d8ea33e2..98b7a6ae27ac 100644 --- a/share/man/man4/ip.4 +++ b/share/man/man4/ip.4 @@ -136,6 +136,37 @@ determined by the destination address, returns an error. .Pp 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 option is enabled on a .Dv SOCK_DGRAM diff --git a/share/man/man4/ip6.4 b/share/man/man4/ip6.4 index d28e37e3dcd7..a1b06fe8cab7 100644 --- a/share/man/man4/ip6.4 +++ b/share/man/man4/ip6.4 @@ -156,6 +156,9 @@ datagrams sent on this socket. .\" .It Dv IPV6_RECVDSTADDR Fa "int *" .\" Get or set the status of whether datagrams are received with .\" 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 .\" Get or set IPv6 options. .It Dv IPV6_MULTICAST_IF Fa "u_int *" diff --git a/sys/netinet/in.h b/sys/netinet/in.h index 7f18684177ff..9e57685dc6d9 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -433,6 +433,8 @@ __END_DECLS #define IP_BINDANY 24 /* bool: allow bind to any address */ #define IP_BINDMULTI 25 /* bool: allow multiple listeners on a tuple */ #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. diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 1b18d8788128..4025b9f2ca17 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -2492,6 +2492,10 @@ db_print_inpflags(int inp_flags) db_printf("%sINP_RECVDSTADDR", comma ? ", " : ""); comma = 1; } + if (inp_flags & INP_ORIGDSTADDR) { + db_printf("%sINP_ORIGDSTADDR", comma ? ", " : ""); + comma = 1; + } if (inp_flags & INP_HDRINCL) { db_printf("%sINP_HDRINCL", comma ? ", " : ""); comma = 1; diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 9d5260153598..5582ba03ba94 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -618,6 +618,7 @@ short inp_so_options(const struct inpcb *inp); #define INP_RECVFLOWID 0x00000100 /* populate recv datagram with flow info */ #define INP_RECVRSSBUCKETID 0x00000200 /* populate recv datagram with bucket id */ #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. diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 512116911bd4..f958285d56a3 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -1065,6 +1065,7 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt) case IP_MINTTL: case IP_RECVOPTS: case IP_RECVRETOPTS: + case IP_ORIGDSTADDR: case IP_RECVDSTADDR: case IP_RECVTTL: case IP_RECVIF: @@ -1126,6 +1127,10 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt) OPTSET(INP_RECVDSTADDR); break; + case IP_ORIGDSTADDR: + OPTSET2(INP_ORIGDSTADDR, optval); + break; + case IP_RECVTTL: OPTSET(INP_RECVTTL); break; @@ -1258,6 +1263,7 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt) case IP_MINTTL: case IP_RECVOPTS: case IP_RECVRETOPTS: + case IP_ORIGDSTADDR: case IP_RECVDSTADDR: case IP_RECVTTL: case IP_RECVIF: @@ -1303,6 +1309,10 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt) optval = OPTBIT(INP_RECVDSTADDR); break; + case IP_ORIGDSTADDR: + optval = OPTBIT2(INP_ORIGDSTADDR); + break; + case IP_RECVTTL: optval = OPTBIT(INP_RECVTTL); break; diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index 1faed394aabb..8245603c3350 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -304,7 +304,7 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off, { struct sockaddr *append_sa; struct socket *so; - struct mbuf *opts = NULL; + struct mbuf *tmpopts, *opts = NULL; #ifdef INET6 struct sockaddr_in6 udp_in6; #endif @@ -319,7 +319,7 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off, if (up->u_tun_func != NULL) { in_pcbref(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); INP_RLOCK(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 */ 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 if (inp->inp_vflag & INP_IPV6) { bzero(&udp_in6, sizeof(udp_in6)); udp_in6.sin6_len = sizeof(udp_in6); 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; } else #endif /* INET6 */ - append_sa = (struct sockaddr *)udp_in; + append_sa = (struct sockaddr *)&udp_in[0]; m_adj(n, off); so = inp->inp_socket; @@ -390,7 +401,7 @@ udp_input(struct mbuf **mp, int *offp, int proto) uint16_t len, ip_len; struct inpcbinfo *pcbinfo; struct ip save_ip; - struct sockaddr_in udp_in; + struct sockaddr_in udp_in[2]; struct mbuf *m; struct m_tag *fwd_tag; 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 * and datagram in user buffer. */ - bzero(&udp_in, sizeof(udp_in)); - udp_in.sin_len = sizeof(udp_in); - udp_in.sin_family = AF_INET; - udp_in.sin_port = uh->uh_sport; - udp_in.sin_addr = ip->ip_src; + bzero(&udp_in[0], sizeof(struct sockaddr_in) * 2); + udp_in[0].sin_len = sizeof(struct sockaddr_in); + udp_in[0].sin_family = AF_INET; + udp_in[0].sin_port = uh->uh_sport; + 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 @@ -568,7 +583,7 @@ udp_input(struct mbuf **mp, int *offp, int proto) blocked = imo_multi_filter(imo, ifp, (struct sockaddr *)&group, - (struct sockaddr *)&udp_in); + (struct sockaddr *)&udp_in[0]); if (blocked != MCAST_PASS) { if (blocked == MCAST_NOTGMEMBER) IPSTAT_INC(ips_notmember); @@ -587,7 +602,7 @@ udp_input(struct mbuf **mp, int *offp, int proto) UDP_PROBE(receive, NULL, last, ip, last, uh); if (udp_append(last, ip, n, iphlen, - &udp_in)) { + udp_in)) { goto inp_lost; } } @@ -620,7 +635,7 @@ udp_input(struct mbuf **mp, int *offp, int proto) goto badunlocked; } 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_lost: 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); - if (udp_append(inp, ip, m, iphlen, &udp_in) == 0) + if (udp_append(inp, ip, m, iphlen, udp_in) == 0) INP_RUNLOCK(inp); return (IPPROTO_DONE); diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index 568b92c34b41..ed26a7cd130b 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -497,6 +497,9 @@ struct route_in6 { #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_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. * It is deliberately defined to the same value as IP_MSFILTER. diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 3753030ae4b3..9ba7946db468 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1267,7 +1267,7 @@ in6_pcblookup_mbuf(struct inpcbinfo *pcbinfo, struct in6_addr *faddr, } 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; @@ -1275,7 +1275,7 @@ init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m) bzero(sin6, sizeof(*sin6)); sin6->sin6_len = sizeof(*sin6); 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... */ diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h index 0c61cd57bdf1..f21230bc74a0 100644 --- a/sys/netinet6/in6_pcb.h +++ b/sys/netinet6/in6_pcb.h @@ -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_selecthlim(struct in6pcb *, struct ifnet *); 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 /* !_NETINET6_IN6_PCB_H_ */ diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index eac22e9abba3..debc02955ea5 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1545,6 +1545,7 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt) #endif case IPV6_V6ONLY: case IPV6_AUTOFLOWLABEL: + case IPV6_ORIGDSTADDR: case IPV6_BINDANY: case IPV6_BINDMULTI: #ifdef RSS @@ -1730,6 +1731,9 @@ do { \ OPTSET(IN6P_AUTOFLOWLABEL); break; + case IPV6_ORIGDSTADDR: + OPTSET2(INP_ORIGDSTADDR, optval); + break; case IPV6_BINDANY: OPTSET(INP_BINDANY); break; @@ -2018,6 +2022,10 @@ do { \ optval = OPTBIT(IN6P_AUTOFLOWLABEL); break; + case IPV6_ORIGDSTADDR: + optval = OPTBIT2(INP_ORIGDSTADDR); + break; + case IPV6_BINDANY: optval = OPTBIT(INP_BINDANY); break; diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 2f2c46b9a15e..d4ca8ab30a7b 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -166,7 +166,7 @@ rip6_input(struct mbuf **mp, int *offp, int proto) RIP6STAT_INC(rip6s_ipackets); - init_sin6(&fromsa, m); /* general init */ + init_sin6(&fromsa, m, 0); /* general init */ ifp = m->m_pkthdr.rcvif; diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index c380e55788ea..15d0f010254e 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -137,7 +137,7 @@ udp6_append(struct inpcb *inp, struct mbuf *n, int off, struct sockaddr_in6 *fromsa) { struct socket *so; - struct mbuf *opts; + struct mbuf *opts = NULL, *tmp_opts; struct udpcb *up; INP_LOCK_ASSERT(inp); @@ -149,7 +149,7 @@ udp6_append(struct inpcb *inp, struct mbuf *n, int off, if (up->u_tun_func != NULL) { in_pcbref(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); INP_RLOCK(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 || inp->inp_socket->so_options & SO_TIMESTAMP) 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)); so = inp->inp_socket; 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) { SOCKBUF_UNLOCK(&so->so_rcv); m_freem(n); @@ -202,7 +214,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) int off = *offp; int cscov_partial; int plen, ulen; - struct sockaddr_in6 fromsa; + struct sockaddr_in6 fromsa[2]; struct m_tag *fwd_tag; uint16_t uh_sum; uint8_t nxt; @@ -277,8 +289,10 @@ udp6_input(struct mbuf **mp, int *offp, int proto) /* * Construct sockaddr format source address. */ - init_sin6(&fromsa, m); - fromsa.sin6_port = uh->uh_sport; + init_sin6(&fromsa[0], m, 0); + 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); 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, (struct sockaddr *)&mcaddr, - (struct sockaddr *)&fromsa); + (struct sockaddr *)&fromsa[0]); if (blocked != MCAST_PASS) { if (blocked == MCAST_NOTGMEMBER) IP6STAT_INC(ip6s_notmember); @@ -370,7 +384,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) INP_RLOCK(last); UDP_PROBE(receive, NULL, last, ip6, last, uh); - if (udp6_append(last, n, off, &fromsa)) + if (udp6_append(last, n, off, fromsa)) goto inp_lost; INP_RUNLOCK(last); } @@ -402,7 +416,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) INP_RLOCK(last); INP_INFO_RUNLOCK(pcbinfo); 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_lost: return (IPPROTO_DONE); @@ -482,7 +496,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) } } 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); return (IPPROTO_DONE);