summaryrefslogtreecommitdiff
path: root/sys/netinet/in_pcb.c
diff options
context:
space:
mode:
authorChristopher Zimmermann <chrisz@cvs.openbsd.org>2014-04-06 17:13:24 +0000
committerChristopher Zimmermann <chrisz@cvs.openbsd.org>2014-04-06 17:13:24 +0000
commit2ef232cd086cf8e3f8a6aabd9722dc2291bbd6b4 (patch)
tree1c6998c160cc8a25a96ac15d41b51adb463d7c7e /sys/netinet/in_pcb.c
parenteaf0b040e78e4a37272256631157db668381a70f (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.c101
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)
{