Vincent Gross <vgr...@openbsd.org> writes:

> Le Mon, 13 Jun 2016 07:35:16 +0200,
> j...@wxcvbn.org (Jeremie Courreges-Anglas) a écrit :
>
>> j...@wxcvbn.org (Jeremie Courreges-Anglas) writes:
>> 
>> > cc'ing sthen since he also has interest in IP_SENDSRCADDR
>> >
>> > Jeremie Courreges-Anglas <j...@wxcvbn.org> writes:
>> >  
>> >> Vincent Gross <vgr...@openbsd.org> writes:
>> >>  
>> >>> This diff adds support for IP_SENDSRCADDR cmsg on UDP sockets. As
>> >>> for udp6_output(), we check that the source address+port is
>> >>> available only if inp_laddr != *  
>> >>
>> >> Your last IP_SENDSRCADDR diff didn't have that check, I think it is
>> >> harmful.  If the socket is not bound then there is effectively no
>> >> check performed by in_pcbaddrisavail(), thus I can use any random
>> >> address. Other than this additional bypass check, your diff looks
>> >> good to me.
>> >>
> [...]
>> >>
>> >> I haven't checked yet whether udp6_output is also affected.  If you
>> >> folks already know that it isn't, please let me know.  
>> 
>> The answer is "no", a few tests can't trigger the same problem.  IIUC
>> in6_selectsrc is responsible for rejection of non-local systems.
>> Maybe we should take the same approach in netinet/, and extend
>> in_selectsrc()?
>> 
>> --
>
> While validating source address inside selection functions is the right
> direction, I don't think it would be a good thing to extend further
> in_selectsrc() prototype.

I find it nice to have all the source address selection in one place.
Or do you have another refactoring in mind?

> However it is easy to add a check while
> processing cmsg.
>
> rev2 below. Ok ?

Nits below, looks fine otherwise.  The checks do detect addresses not
configured on the system and overlaps of bound sockets.

>
> diff --git a/share/man/man4/ip.4 b/share/man/man4/ip.4
> index 111432b..154b0d1 100644
> --- a/share/man/man4/ip.4
> +++ b/share/man/man4/ip.4
> @@ -290,6 +290,27 @@ cmsg_len = CMSG_LEN(sizeof(u_int))
>  cmsg_level = IPPROTO_IP
>  cmsg_type = IP_RECVRTABLE
>  .Ed
> +.Pp
> +If the
> +.Dv IP_SENDSRCADDR
> +option is passed to a
> +.Xr sendmsg 2
> +call on a
> +.Dv SOCK_DGRAM
> +socket, the address passed along the
> +.Vt cmsghdr
> +structure will be used as the source of the outgoing
> +.Tn UDP
> +datagram.  The
> +.Vt cmsghdr
> +fields for
> +.Xr sendmsg 2
> +have the following values:

I would have worded it "should have" here, since these are the values
that the developer is supposed to pass.

> +.Bd -literal -offset indent
> +cmsg_len = CMSG_LEN(sizeof(struct in_addr))
> +cmsg_level = IPPROTO_IP
> +cmsg_type = IP_SENDSRCADDR
> +.Ed
>  .Ss "Multicast Options"
>  .Tn IP
>  multicasting is supported only on
> diff --git a/sys/netinet/in.h b/sys/netinet/in.h
> index adb1b30..bf8c95d 100644
> --- a/sys/netinet/in.h
> +++ b/sys/netinet/in.h
> @@ -307,6 +307,7 @@ struct ip_opts {
>  #define IP_RECVRTABLE                35   /* bool; receive rdomain w/dgram */
>  #define IP_IPSECFLOWINFO     36   /* bool; IPsec flow info for dgram */
>  #define IP_IPDEFTTL          37   /* int; IP TTL system default */
> +#define IP_SENDSRCADDR               38   /* struct in_addr; source address 
> to use */
>  
>  #define IP_RTABLE            0x1021  /* int; routing table, see SO_RTABLE */
>  #define IP_DIVERTFL          0x1022  /* int; divert direction flag opt */
> diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
> index 1feea11..401ed7a 100644
> --- a/sys/netinet/udp_usrreq.c
> +++ b/sys/netinet/udp_usrreq.c
> @@ -888,6 +888,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct mbuf 
> *addr,
>       struct sockaddr_in *sin = NULL;
>       struct udpiphdr *ui;
>       u_int32_t ipsecflowinfo = 0;
> +     struct sockaddr_in src_sin;
>       int len = m->m_pkthdr.len;
>       struct in_addr *laddr;
>       int error = 0;
> @@ -906,6 +907,8 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct mbuf 
> *addr,
>               goto release;
>       }
>  
> +     memset(&src_sin, 0, sizeof(src_sin));
> +
>       if (control) {
>               u_int clen;
>               struct cmsghdr *cm;
> @@ -939,9 +942,20 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct 
> mbuf *addr,
>                           cm->cmsg_level == IPPROTO_IP &&
>                           cm->cmsg_type == IP_IPSECFLOWINFO) {
>                               ipsecflowinfo = *(u_int32_t *)CMSG_DATA(cm);
> -                             break;
> -                     }
> +                     } else
>  #endif
> +                     if (cm->cmsg_len == CMSG_LEN(sizeof(struct in_addr)) &&
> +                         cm->cmsg_level == IPPROTO_IP &&
> +                         cm->cmsg_type == IP_SENDSRCADDR) {
> +                             bzero(&src_sin, sizeof(src_sin));

No need for bzero here since you unconditionally call memset above.

> +                             memcpy(&src_sin.sin_addr, CMSG_DATA(cm),
> +                                 sizeof(struct in_addr));
> +                             src_sin.sin_family = AF_INET;
> +                             src_sin.sin_len = sizeof(src_sin);
> +                             /* no check on reuse done when sin->sin_port == 
> 0 */
> +                             if ((error = in_pcbaddrisavail(inp, &src_sin, 
> 0, curproc)))

Lines > 80 chars.

> +                                     goto release;
> +                     }
>                       clen -= CMSG_ALIGN(cm->cmsg_len);
>                       cmsgs += CMSG_ALIGN(cm->cmsg_len);
>               } while (clen);
> @@ -980,6 +994,17 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct 
> mbuf *addr,
>                       if (error)
>                               goto release;
>               }
> +
> +             if (src_sin.sin_len > 0 &&
> +                 src_sin.sin_addr.s_addr != INADDR_ANY &&
> +                 src_sin.sin_addr.s_addr != inp->inp_laddr.s_addr) {
> +                     src_sin.sin_port = inp->inp_lport;
> +                     if (inp->inp_laddr.s_addr != INADDR_ANY &&
> +                         (error =
> +                         in_pcbaddrisavail(inp, &src_sin, 0, curproc)))
> +                             goto release;
> +                     laddr = &src_sin.sin_addr;
> +             }
>       } else {
>               if (inp->inp_faddr.s_addr == INADDR_ANY) {
>                       error = ENOTCONN;
>
>

-- 
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE

Reply via email to