symbolic links
[unix-history] / usr / src / sys / netinet / in_pcb.c
CommitLineData
e6b33a03 1/* in_pcb.c 4.16 82/02/15 */
af092e86
BJ
2
3#include "../h/param.h"
4ad99bae
BJ
4#include "../h/systm.h"
5#include "../h/dir.h"
6#include "../h/user.h"
af092e86
BJ
7#include "../h/mbuf.h"
8#include "../h/socket.h"
9#include "../h/socketvar.h"
8a13b737
BJ
10#include "../net/in.h"
11#include "../net/in_systm.h"
4ad99bae 12#include "../net/if.h"
8a13b737 13#include "../net/in_pcb.h"
af092e86 14
405c9168
BJ
15/*
16 * Routines to manage internet protocol control blocks.
17 *
18 * At PRU_ATTACH time a protocol control block is allocated in
19 * in_pcballoc() and inserted on a doubly-linked list of such blocks
20 * for the protocol. A port address is either requested (and verified
21 * to not be in use) or assigned at this time. We also allocate
22 * space in the socket sockbuf structures here, although this is
23 * not a clearly correct place to put this function.
24 *
25 * A connectionless protocol will have its protocol control block
26 * removed at PRU_DETACH time, when the socket will be freed (freeing
27 * the space reserved) and the block will be removed from the list of
28 * blocks for its protocol.
29 *
30 * A connection-based protocol may be connected to a remote peer at
31 * PRU_CONNECT time through the routine in_pcbconnect(). In the normal
32 * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect().
33 * It is also possible that higher-level routines will opt out of the
34 * relationship with the connection before the connection shut down
35 * is complete. This often occurs in protocols like TCP where we must
36 * hold on to the protocol control block for a unreasonably long time
37 * after the connection is used up to avoid races in later connection
38 * establishment. To handle this we allow higher-level routines to
39 * disassociate themselves from the socket, marking it SS_USERGONE while
40 * the disconnect is in progress. We notice that this has happened
41 * when the disconnect is complete, and perform the PRU_DETACH operation,
42 * freeing the socket.
b454c3ea
BJ
43 *
44 * TODO:
45 * use hashing
405c9168 46 */
1a3dbf93 47struct in_addr zeroin_addr;
405c9168 48
4ad99bae
BJ
49/*
50 * Allocate a protocol control block, space
51 * for send and receive data, and local host information.
52 * Return error. If no error make socket point at pcb.
53 */
405c9168 54in_pcbattach(so, head, sndcc, rcvcc, sin)
4ad99bae
BJ
55 struct socket *so;
56 struct inpcb *head;
57 int sndcc, rcvcc;
58 struct sockaddr_in *sin;
af092e86 59{
af092e86 60 struct mbuf *m;
1a3dbf93 61 register struct inpcb *inp;
4ad99bae 62 struct ifnet *ifp;
1a3dbf93 63 u_short lport;
af092e86 64
405c9168 65COUNT(IN_PCBATTACH);
4ad99bae
BJ
66 if (sin) {
67 if (sin->sin_family != AF_INET)
68 return (EAFNOSUPPORT);
f845c22c
BJ
69 if (ifnet && sin->sin_addr.s_addr == 0)
70 sin->sin_addr = ifnet->if_addr;
4ad99bae 71 ifp = if_ifwithaddr(sin->sin_addr);
4ad99bae 72 lport = sin->sin_port;
b454c3ea 73 if (lport &&
1a3dbf93 74 in_pcblookup(head, zeroin_addr, 0, sin->sin_addr, lport))
b454c3ea 75 return (EADDRINUSE);
4ad99bae 76 } else {
f845c22c 77 ifp = ifnet;
4ad99bae
BJ
78 lport = 0;
79 }
f845c22c
BJ
80 if (ifp == 0)
81 return (EADDRNOTAVAIL);
e6b33a03 82 m = m_getclr(M_DONTWAIT);
4ad99bae 83 if (m == 0)
7545cf09 84 return (ENOBUFS);
4ad99bae
BJ
85 if (sbreserve(&so->so_snd, sndcc) == 0)
86 goto bad;
87 if (sbreserve(&so->so_rcv, rcvcc) == 0)
88 goto bad2;
89 inp = mtod(m, struct inpcb *);
b454c3ea 90 inp->inp_head = head;
4ad99bae 91 inp->inp_laddr = ifp->if_addr;
b454c3ea
BJ
92 if (lport == 0)
93 do {
94 if (head->inp_lport++ < 1024)
95 head->inp_lport = 1024;
96 lport = htons(head->inp_lport);
1a3dbf93 97 } while (in_pcblookup(head, zeroin_addr, 0, inp->inp_laddr, lport));
4ad99bae 98 inp->inp_lport = lport;
b454c3ea 99 inp->inp_socket = so;
7545cf09 100 insque(inp, head);
4ad99bae 101 so->so_pcb = (caddr_t)inp;
d8bd11cd
BJ
102 sin = (struct sockaddr_in *)&so->so_addr;
103 sin->sin_family = AF_INET;
104 sin->sin_addr = inp->inp_laddr;
105 sin->sin_port = inp->inp_lport;
4ad99bae
BJ
106 return (0);
107bad2:
108 sbrelease(&so->so_snd);
109bad:
2752c877 110 (void) m_free(m);
4ad99bae 111 return (ENOBUFS);
af092e86
BJ
112}
113
405c9168 114in_pcbconnect(inp, sin)
4ad99bae
BJ
115 struct inpcb *inp;
116 struct sockaddr_in *sin;
2b4b57cd 117{
b454c3ea 118 struct inpcb *xp;
2b4b57cd 119
405c9168 120COUNT(IN_PCBCONNECT);
4ad99bae
BJ
121 if (sin->sin_family != AF_INET)
122 return (EAFNOSUPPORT);
123 if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
124 return (EADDRNOTAVAIL);
b454c3ea 125 xp = in_pcblookup(inp->inp_head, sin->sin_addr, sin->sin_port, inp->inp_laddr, inp->inp_lport);
1a3dbf93 126 if (xp->inp_faddr.s_addr)
b454c3ea 127 return (EADDRINUSE);
4ad99bae
BJ
128 inp->inp_faddr = sin->sin_addr;
129 inp->inp_fport = sin->sin_port;
2b4b57cd
BJ
130 return (0);
131}
132
f6f20b2c
BJ
133in_pcbconnaddr(inp, sp)
134 struct inpcb *inp;
135 struct sockaddr *sp;
136{
137 register struct sockaddr_in *sin = (struct sockaddr_in *)sp;
138
139 sin->sin_family = AF_INET;
140 sin->sin_port = inp->inp_fport;
141 sin->sin_addr = inp->inp_faddr;
142}
143
405c9168
BJ
144in_pcbdisconnect(inp)
145 struct inpcb *inp;
146{
147
148COUNT(IN_PCBDISCONNECT);
149 inp->inp_faddr.s_addr = 0;
150 if (inp->inp_socket->so_state & SS_USERGONE)
151 in_pcbdetach(inp);
152}
153
154in_pcbdetach(inp)
af092e86
BJ
155 struct inpcb *inp;
156{
157 struct socket *so = inp->inp_socket;
158
ba55c9af
BJ
159 so->so_pcb = 0;
160 sofree(so);
7545cf09 161 remque(inp);
cdad2eb1 162 (void) m_free(dtom(inp));
af092e86
BJ
163}
164
405c9168
BJ
165/*
166 * Look for a control block to accept a segment.
167 * First choice is an exact address match.
168 * Second choice is a match of local address, with
169 * unspecified foreign address.
170 */
cdad2eb1 171struct inpcb *
4ad99bae 172in_pcblookup(head, faddr, fport, laddr, lport)
af092e86 173 struct inpcb *head;
4ad99bae 174 struct in_addr faddr, laddr;
af092e86
BJ
175 u_short fport, lport;
176{
177 register struct inpcb *inp;
405c9168 178 struct inpcb *match = 0;
af092e86 179
405c9168
BJ
180 for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
181 if (inp->inp_laddr.s_addr != laddr.s_addr ||
182 inp->inp_lport != lport)
183 continue;
184 if (inp->inp_faddr.s_addr == 0) {
185 match = inp;
186 continue;
187 }
4ad99bae 188 if (inp->inp_faddr.s_addr == faddr.s_addr &&
405c9168 189 inp->inp_fport == fport)
af092e86 190 return (inp);
405c9168
BJ
191 }
192 return (match);
af092e86 193}