/* in_pcb.c 4.28 82/06/20 */
#include "../h/socketvar.h"
#include "../net/in_systm.h"
#include "../net/route.h"
#include "../net/in_pcb.h"
#include "../h/protosw.h"
* Routines to manage internet protocol control blocks.
* At PRU_ATTACH time a protocol control block is allocated in
* in_pcballoc() and inserted on a doubly-linked list of such blocks
* for the protocol. A port address is either requested (and verified
* to not be in use) or assigned at this time. We also allocate
* space in the socket sockbuf structures here, although this is
* not a clearly correct place to put this function.
* A connectionless protocol will have its protocol control block
* removed at PRU_DETACH time, when the socket will be freed (freeing
* the space reserved) and the block will be removed from the list of
* blocks for its protocol.
* A connection-based protocol may be connected to a remote peer at
* PRU_CONNECT time through the routine in_pcbconnect(). In the normal
* case a PRU_DISCONNECT occurs causing a in_pcbdisconnect().
* It is also possible that higher-level routines will opt out of the
* relationship with the connection before the connection shut down
* is complete. This often occurs in protocols like TCP where we must
* hold on to the protocol control block for a unreasonably long time
* after the connection is used up to avoid races in later connection
* establishment. To handle this we allow higher-level routines to
* disassociate themselves from the socket, marking it SS_USERGONE while
* the disconnect is in progress. We notice that this has happened
* when the disconnect is complete, and perform the PRU_DETACH operation,
struct in_addr zeroin_addr
;
* Allocate a protocol control block, space
* for send and receive data, and local host information.
* Return error. If no error make socket point at pcb.
in_pcbattach(so
, head
, sndcc
, rcvcc
, sin
)
register struct inpcb
*inp
;
if (sin
->sin_family
!= AF_INET
)
if (sin
->sin_addr
.s_addr
) {
int tport
= sin
->sin_port
;
sin
->sin_port
= 0; /* yech... */
if (if_ifwithaddr((struct sockaddr
*)sin
) == 0)
if (aport
< IPPORT_RESERVED
&& u
.u_uid
!= 0)
if ((so
->so_proto
->pr_flags
& PR_CONNREQUIRED
) == 0 ||
(so
->so_options
& SO_ACCEPTCONN
) == 0)
wild
= INPLOOKUP_WILDCARD
;
zeroin_addr
, 0, sin
->sin_addr
, lport
, wild
))
m
= m_getclr(M_DONTWAIT
);
if (sbreserve(&so
->so_snd
, sndcc
) == 0)
if (sbreserve(&so
->so_rcv
, rcvcc
) == 0)
inp
= mtod(m
, struct inpcb
*);
inp
->inp_laddr
= sin
->sin_addr
;
if (head
->inp_lport
++ < IPPORT_RESERVED
)
head
->inp_lport
= IPPORT_RESERVED
;
lport
= htons(head
->inp_lport
);
} while (in_pcblookup(head
,
zeroin_addr
, 0, inp
->inp_laddr
, lport
, 0));
so
->so_pcb
= (caddr_t
)inp
;
* Connect from a socket to a specified address.
* Both address and port must be specified in argument sin.
* If don't have a local address for this socket yet,
struct sockaddr_in
*ifaddr
;
if (sin
->sin_family
!= AF_INET
)
if (sin
->sin_addr
.s_addr
== 0 || sin
->sin_port
== 0)
if (inp
->inp_laddr
.s_addr
== 0) {
ifp
= if_ifonnetof(in_netof(sin
->sin_addr
));
* We should select the interface based on
* the route to be used, but for udp this would
* result in two calls to rtalloc for each packet
* sent; hardly worthwhile...
ifp
= if_ifwithaf(AF_INET
);
ifaddr
= (struct sockaddr_in
*)&ifp
->if_addr
;
if (in_pcblookup(inp
->inp_head
,
inp
->inp_laddr
.s_addr
? inp
->inp_laddr
: ifaddr
->sin_addr
,
if (inp
->inp_laddr
.s_addr
== 0)
inp
->inp_laddr
= ifaddr
->sin_addr
;
inp
->inp_faddr
= sin
->sin_addr
;
inp
->inp_fport
= sin
->sin_port
;
inp
->inp_faddr
.s_addr
= 0;
if (inp
->inp_socket
->so_state
& SS_USERGONE
)
struct socket
*so
= inp
->inp_socket
;
if (inp
->inp_route
.ro_rt
)
rtfree(inp
->inp_route
.ro_rt
);
(void) m_free(dtom(inp
));
register struct sockaddr_in
*sin
;
register struct inpcb
*inp
;
if (sin
== 0 || inp
== 0)
bzero((caddr_t
)sin
, sizeof (*sin
));
sin
->sin_family
= AF_INET
;
sin
->sin_port
= inp
->inp_lport
;
sin
->sin_addr
= inp
->inp_laddr
;
* Pass an error to all internet connections
* associated with address sin. Call the
* protocol specific routine to clean up the
in_pcbnotify(head
, dst
, errno
, abort
)
register struct in_addr
*dst
;
register struct inpcb
*inp
, *oinp
;
for (inp
= head
->inp_next
; inp
!= head
;) {
if (inp
->inp_faddr
.s_addr
!= dst
->s_addr
) {
if (inp
->inp_socket
== 0)
inp
->inp_socket
->so_error
= errno
;
* SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
in_pcblookup(head
, faddr
, fport
, laddr
, lport
, flags
)
struct in_addr faddr
, laddr
;
register struct inpcb
*inp
, *match
= 0;
int matchwild
= 3, wildcard
;
for (inp
= head
->inp_next
; inp
!= head
; inp
= inp
->inp_next
) {
if (inp
->inp_lport
!= lport
)
if (inp
->inp_laddr
.s_addr
!= 0) {
else if (inp
->inp_laddr
.s_addr
!= laddr
.s_addr
)
if (inp
->inp_faddr
.s_addr
!= 0) {
else if (inp
->inp_faddr
.s_addr
!= faddr
.s_addr
||
if (wildcard
&& (flags
& INPLOOKUP_WILDCARD
) == 0)
if (wildcard
< matchwild
) {