/*
- * Copyright (c) 1982, 1986 Regents of the University of California.
- * All rights reserved. The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1982, 1986, 1991 Regents of the University of California.
+ * All rights reserved.
*
- * @(#)in_pcb.c 7.5 (Berkeley) %G%
+ * %sccs.include.redist.c%
+ *
+ * @(#)in_pcb.c 7.23 (Berkeley) %G%
*/
-#include "param.h"
-#include "systm.h"
-#include "dir.h"
-#include "user.h"
-#include "mbuf.h"
-#include "socket.h"
-#include "socketvar.h"
-#include "ioctl.h"
-#include "in.h"
-#include "in_systm.h"
-#include "../net/if.h"
-#include "../net/route.h"
-#include "in_pcb.h"
-#include "in_var.h"
-#include "protosw.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+
+#ifdef MULTICAST
+#include <netinet/ip_var.h>
+#endif
struct in_addr zeroin_addr;
struct mbuf *m;
register struct inpcb *inp;
- m = m_getclr(M_DONTWAIT, MT_PCB);
- if (m == NULL)
+ MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_WAITOK);
+ if (inp == NULL)
return (ENOBUFS);
- inp = mtod(m, struct inpcb *);
+ bzero((caddr_t)inp, sizeof(*inp));
inp->inp_head = head;
inp->inp_socket = so;
insque(inp, head);
}
lport = sin->sin_port;
if (lport) {
+ struct inpcb *t;
u_short aport = ntohs(lport);
int wild = 0;
/* GROSS */
- if (aport < IPPORT_RESERVED && u.u_uid != 0)
+ if (aport < IPPORT_RESERVED && (so->so_state & SS_PRIV) == 0)
return (EACCES);
/* even GROSSER, but this is the Internet */
if ((so->so_options & SO_REUSEADDR) == 0 &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
(so->so_options & SO_ACCEPTCONN) == 0))
wild = INPLOOKUP_WILDCARD;
- if (in_pcblookup(head,
- zeroin_addr, 0, sin->sin_addr, lport, wild))
+ t = in_pcblookup(head, zeroin_addr, 0,
+ sin->sin_addr, lport, wild);
+ if (t && !((so->so_options & t->inp_socket->so_options) &
+ SO_REUSEPORT))
return (EADDRINUSE);
}
inp->inp_laddr = sin->sin_addr;
ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
/* No route yet, so try to acquire one */
ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
sin->sin_addr;
rtalloc(ro);
* to our address on another net goes to loopback).
*/
if (ro->ro_rt && (ifp = ro->ro_rt->rt_ifp) &&
- (ifp->if_flags & IFF_LOOPBACK) == 0)
+ (ifp->if_flags & IFF_LOOPBACK) == 0 &&
+ (ia = (struct in_ifaddr *)ro->ro_rt->rt_ifa) == 0)
for (ia = in_ifaddr; ia; ia = ia->ia_next)
if (ia->ia_ifp == ifp)
break;
if (ia == 0)
return (EADDRNOTAVAIL);
}
+#ifdef MULTICAST
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
+ inp->inp_moptions != NULL) {
+ struct ip_moptions *imo;
+
+ imo = inp->inp_moptions;
+ if (imo->imo_multicast_ifp != NULL) {
+ ifp = imo->imo_multicast_ifp;
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ }
+#endif
ifaddr = (struct sockaddr_in *)&ia->ia_addr;
}
if (in_pcblookup(inp->inp_head,
(void)m_free(inp->inp_options);
if (inp->inp_route.ro_rt)
rtfree(inp->inp_route.ro_rt);
+#ifdef MULTICAST
+ ip_freemoptions(inp->inp_moptions);
+#endif
remque(inp);
- (void) m_free(dtom(inp));
+ FREE(inp, M_PCB);
}
in_setsockaddr(inp, nam)
sin = mtod(nam, struct sockaddr_in *);
bzero((caddr_t)sin, sizeof (*sin));
sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
sin->sin_port = inp->inp_lport;
sin->sin_addr = inp->inp_laddr;
}
sin = mtod(nam, struct sockaddr_in *);
bzero((caddr_t)sin, sizeof (*sin));
sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
sin->sin_port = inp->inp_fport;
sin->sin_addr = inp->inp_faddr;
}
/*
* Pass some notification to all connections of a protocol
- * associated with address dst. Call the protocol specific
- * routine (if any) to handle each connection.
+ * associated with address dst. The local address and/or port numbers
+ * may be specified to limit the search. The "usual action" will be
+ * taken, depending on the ctlinput cmd. The caller must filter any
+ * cmds that are uninteresting (e.g., no error in the map).
+ * Call the protocol specific routine (if any) to report
+ * any errors for each matching socket.
+ *
+ * Must be called at splnet.
*/
-in_pcbnotify(head, dst, errno, notify)
+in_pcbnotify(head, dst, fport, laddr, lport, cmd, notify)
struct inpcb *head;
- register struct in_addr *dst;
- int errno, (*notify)();
+ struct sockaddr *dst;
+ u_short fport, lport;
+ struct in_addr laddr;
+ int cmd, (*notify)();
{
register struct inpcb *inp, *oinp;
- int s = splimp();
+ struct in_addr faddr;
+ int errno;
+ int in_rtchange();
+ extern u_char inetctlerrmap[];
+
+ if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
+ return;
+ faddr = ((struct sockaddr_in *)dst)->sin_addr;
+ if (faddr.s_addr == INADDR_ANY)
+ return;
+ /*
+ * Redirects go to all references to the destination,
+ * and use in_rtchange to invalidate the route cache.
+ * Dead host indications: notify all references to the destination.
+ * Otherwise, if we have knowledge of the local port and address,
+ * deliver only to that socket.
+ */
+ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
+ fport = 0;
+ lport = 0;
+ laddr.s_addr = 0;
+ if (cmd != PRC_HOSTDEAD)
+ notify = in_rtchange;
+ }
+ errno = inetctlerrmap[cmd];
for (inp = head->inp_next; inp != head;) {
- if (inp->inp_faddr.s_addr != dst->s_addr ||
- inp->inp_socket == 0) {
+ if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_socket == 0 ||
+ (lport && inp->inp_lport != lport) ||
+ (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
+ (fport && inp->inp_fport != fport)) {
inp = inp->inp_next;
continue;
}
- if (errno)
- inp->inp_socket->so_error = errno;
oinp = inp;
inp = inp->inp_next;
if (notify)
- (*notify)(oinp);
+ (*notify)(oinp, errno);
}
- splx(s);
}
/*
struct inpcb *inp;
{
register struct rtentry *rt;
+ struct rt_addrinfo info;
if ((rt = inp->inp_route.ro_rt)) {
- if (rt->rt_flags & RTF_DYNAMIC)
- (void) rtrequest((int)SIOCDELRT, rt);
- rtfree(rt);
inp->inp_route.ro_rt = 0;
+ bzero((caddr_t)&info, sizeof(info));
+ info.rti_info[RTAX_DST] =
+ (struct sockaddr *)&inp->inp_route.ro_dst;
+ info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
+ info.rti_info[RTAX_NETMASK] = rt_mask(rt);
+ rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
+ if (rt->rt_flags & RTF_DYNAMIC)
+ (void) rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags,
+ (struct rtentry **)0);
+ else
/*
* A new route can be allocated
* the next time output is attempted.
*/
+ rtfree(rt);
}
}