diff options
author | Christopher Zimmermann <chrisz@cvs.openbsd.org> | 2014-04-06 17:13:24 +0000 |
---|---|---|
committer | Christopher Zimmermann <chrisz@cvs.openbsd.org> | 2014-04-06 17:13:24 +0000 |
commit | 2ef232cd086cf8e3f8a6aabd9722dc2291bbd6b4 (patch) | |
tree | 1c6998c160cc8a25a96ac15d41b51adb463d7c7e /sys/netinet/in_pcb.c | |
parent | eaf0b040e78e4a37272256631157db668381a70f (diff) |
factor out source and destination address mangling from in_pcbconnect()
for later reuse in udp_output().
"Apart from that OK" claudio@
Diffstat (limited to 'sys/netinet/in_pcb.c')
-rw-r--r-- | sys/netinet/in_pcb.c | 101 |
1 files changed, 63 insertions, 38 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index ba420f7cfdb..4784d8248b8 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.c,v 1.150 2014/04/06 16:49:40 chrisz Exp $ */ +/* $OpenBSD: in_pcb.c,v 1.151 2014/04/06 17:13:23 chrisz Exp $ */ /* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */ /* @@ -370,8 +370,9 @@ in_pcbbind(struct inpcb *inp, struct mbuf *nam, struct proc *p) int in_pcbconnect(struct inpcb *inp, struct mbuf *nam) { - struct sockaddr_in *ifaddr = NULL; struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); + struct in_addr laddr; + int error; #ifdef INET6 if (sotopf(inp->inp_socket) == PF_INET6) @@ -382,56 +383,27 @@ in_pcbconnect(struct inpcb *inp, struct mbuf *nam) if (nam->m_len != sizeof (*sin)) return (EINVAL); - if (sin->sin_family != AF_INET) - return (EAFNOSUPPORT); - if (sin->sin_port == 0) - return (EADDRNOTAVAIL); - if (!TAILQ_EMPTY(&in_ifaddr)) { - /* - * If the destination address is INADDR_ANY, - * use the primary local address. - * If the supplied address is INADDR_BROADCAST, - * and the primary interface supports broadcast, - * choose the broadcast address for that interface. - */ - if (sin->sin_addr.s_addr == INADDR_ANY) - sin->sin_addr = TAILQ_FIRST(&in_ifaddr)->ia_addr.sin_addr; - else if (sin->sin_addr.s_addr == INADDR_BROADCAST && - (TAILQ_FIRST(&in_ifaddr)->ia_ifp->if_flags & IFF_BROADCAST) && - TAILQ_FIRST(&in_ifaddr)->ia_broadaddr.sin_addr.s_addr) - sin->sin_addr = - TAILQ_FIRST(&in_ifaddr)->ia_broadaddr.sin_addr; - } - if (inp->inp_laddr.s_addr == INADDR_ANY) { - int error; - ifaddr = in_selectsrc(sin, &inp->inp_route, - inp->inp_socket->so_options, inp->inp_moptions, &error, - inp->inp_rtableid); - if (ifaddr == NULL) { - if (error == 0) - error = EADDRNOTAVAIL; - return error; - } - } + + if ((error = in_fixaddr(inp, sin, &laddr))) + return (error); + if (in_pcbhashlookup(inp->inp_table, sin->sin_addr, sin->sin_port, - inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, - inp->inp_lport, inp->inp_rtableid) != 0) + laddr, inp->inp_lport, inp->inp_rtableid) != 0) return (EADDRINUSE); KASSERT(inp->inp_laddr.s_addr == INADDR_ANY || inp->inp_lport != 0); if (inp->inp_laddr.s_addr == INADDR_ANY) { if (inp->inp_lport == 0 && in_pcbbind(inp, NULL, curproc) == EADDRNOTAVAIL) return (EADDRNOTAVAIL); - inp->inp_laddr = ifaddr->sin_addr; + inp->inp_laddr = laddr; } inp->inp_faddr = sin->sin_addr; inp->inp_fport = sin->sin_port; in_pcbrehash(inp); #ifdef IPSEC { - int error; /* This is just ignored */ - /* Cause an IPsec SA to be established. */ + /* error is just ignored */ ipsp_spd_inp(NULL, AF_INET, 0, &error, IPSP_DIRECTION_OUT, NULL, inp, NULL); } @@ -867,6 +839,59 @@ in_selectsrc(struct sockaddr_in *sin, struct route *ro, int soopts, return (&ia->ia_addr); } +/* + * Fix up source (*laddr) and destination addresses (*sin). + * + * If the destination address is INADDR_ANY, use the primary local address. + * If the supplied address is INADDR_BROADCAST + * and the primary interface supports broadcast, + * choose the broadcast address for that interface. + * + * If *inp is already bound to a local address, return it in *laddr, + * otherwise call in_selectsrc() to get a suitable local address. + */ +int +in_fixaddr(struct inpcb *inp, + struct sockaddr_in *sin, struct in_addr *laddr) +{ + if (sin->sin_family != AF_INET) + return (EAFNOSUPPORT); + if (sin->sin_port == 0) + return (EADDRNOTAVAIL); + if (!TAILQ_EMPTY(&in_ifaddr)) { + if (sin->sin_addr.s_addr == INADDR_ANY) + sin->sin_addr = TAILQ_FIRST(&in_ifaddr)->ia_addr.sin_addr; + else if (sin->sin_addr.s_addr == INADDR_BROADCAST && + (TAILQ_FIRST(&in_ifaddr)->ia_ifp->if_flags & IFF_BROADCAST) && + TAILQ_FIRST(&in_ifaddr)->ia_broadaddr.sin_addr.s_addr) + sin->sin_addr = + TAILQ_FIRST(&in_ifaddr)->ia_broadaddr.sin_addr; + } + + if (laddr != NULL) { + if (inp->inp_laddr.s_addr != INADDR_ANY) { + *laddr = inp->inp_laddr; + } + else { + struct sockaddr_in *ifaddr; + int error; + + ifaddr = in_selectsrc(sin, &inp->inp_route, + inp->inp_socket->so_options, + inp->inp_moptions, + &error, inp->inp_rtableid); + if (ifaddr == NULL) { + if (error == 0) + error = EADDRNOTAVAIL; + return (error); + } + *laddr = ifaddr->sin_addr; + } + } + + return (0); +} + void in_pcbrehash(struct inpcb *inp) { |