date and time created 82/03/27 22:57:41 by sam
[unix-history] / usr / src / sys / netinet / in_pcb.c
CommitLineData
c48203e2 1/* in_pcb.c 4.21 82/03/23 */
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"
1acff8ec 14#include "../h/protosw.h"
af092e86 15
405c9168
BJ
16/*
17 * Routines to manage internet protocol control blocks.
18 *
19 * At PRU_ATTACH time a protocol control block is allocated in
20 * in_pcballoc() and inserted on a doubly-linked list of such blocks
21 * for the protocol. A port address is either requested (and verified
22 * to not be in use) or assigned at this time. We also allocate
23 * space in the socket sockbuf structures here, although this is
24 * not a clearly correct place to put this function.
25 *
26 * A connectionless protocol will have its protocol control block
27 * removed at PRU_DETACH time, when the socket will be freed (freeing
28 * the space reserved) and the block will be removed from the list of
29 * blocks for its protocol.
30 *
31 * A connection-based protocol may be connected to a remote peer at
32 * PRU_CONNECT time through the routine in_pcbconnect(). In the normal
33 * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect().
34 * It is also possible that higher-level routines will opt out of the
35 * relationship with the connection before the connection shut down
36 * is complete. This often occurs in protocols like TCP where we must
37 * hold on to the protocol control block for a unreasonably long time
38 * after the connection is used up to avoid races in later connection
39 * establishment. To handle this we allow higher-level routines to
40 * disassociate themselves from the socket, marking it SS_USERGONE while
41 * the disconnect is in progress. We notice that this has happened
42 * when the disconnect is complete, and perform the PRU_DETACH operation,
43 * freeing the socket.
b454c3ea
BJ
44 *
45 * TODO:
46 * use hashing
405c9168 47 */
1a3dbf93 48struct in_addr zeroin_addr;
405c9168 49
4ad99bae
BJ
50/*
51 * Allocate a protocol control block, space
52 * for send and receive data, and local host information.
53 * Return error. If no error make socket point at pcb.
54 */
405c9168 55in_pcbattach(so, head, sndcc, rcvcc, sin)
4ad99bae
BJ
56 struct socket *so;
57 struct inpcb *head;
58 int sndcc, rcvcc;
59 struct sockaddr_in *sin;
af092e86 60{
af092e86 61 struct mbuf *m;
1a3dbf93 62 register struct inpcb *inp;
1aa87517 63 u_short lport = 0;
af092e86 64
405c9168 65COUNT(IN_PCBATTACH);
1aa87517
BJ
66 if (ifnet == 0)
67 return (EADDRNOTAVAIL);
4ad99bae
BJ
68 if (sin) {
69 if (sin->sin_family != AF_INET)
70 return (EAFNOSUPPORT);
1aa87517 71 if (sin->sin_addr.s_addr &&
668cc26d 72 if_ifwithaddr(sin->sin_addr) == 0)
1aa87517 73 return (EADDRNOTAVAIL);
4ad99bae 74 lport = sin->sin_port;
1aa87517
BJ
75 if (lport) {
76 u_short aport = lport;
1acff8ec 77 int wild = 0;
1aa87517
BJ
78#if vax
79 aport = htons(aport);
80#endif
81 /* GROSS */
82 if (aport < IPPORT_RESERVED && u.u_uid != 0)
83 return (EPERM);
1acff8ec
BJ
84 if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
85 (so->so_options & SO_ACCEPTCONN) == 0)
86 wild = INPLOOKUP_WILDCARD;
1aa87517 87 if (in_pcblookup(head,
1acff8ec 88 zeroin_addr, 0, sin->sin_addr, lport, wild))
1aa87517
BJ
89 return (EADDRINUSE);
90 }
4ad99bae 91 }
e6b33a03 92 m = m_getclr(M_DONTWAIT);
4ad99bae 93 if (m == 0)
7545cf09 94 return (ENOBUFS);
4ad99bae
BJ
95 if (sbreserve(&so->so_snd, sndcc) == 0)
96 goto bad;
97 if (sbreserve(&so->so_rcv, rcvcc) == 0)
98 goto bad2;
99 inp = mtod(m, struct inpcb *);
b454c3ea 100 inp->inp_head = head;
1aa87517
BJ
101 if (sin)
102 inp->inp_laddr = sin->sin_addr;
b454c3ea
BJ
103 if (lport == 0)
104 do {
1aa87517
BJ
105 if (head->inp_lport++ < IPPORT_RESERVED)
106 head->inp_lport = IPPORT_RESERVED;
b454c3ea 107 lport = htons(head->inp_lport);
1aa87517
BJ
108 } while (in_pcblookup(head,
109 zeroin_addr, 0, inp->inp_laddr, lport, 0));
4ad99bae 110 inp->inp_lport = lport;
b454c3ea 111 inp->inp_socket = so;
7545cf09 112 insque(inp, head);
4ad99bae 113 so->so_pcb = (caddr_t)inp;
1aa87517 114 in_setsockaddr(inp);
4ad99bae
BJ
115 return (0);
116bad2:
117 sbrelease(&so->so_snd);
118bad:
2752c877 119 (void) m_free(m);
4ad99bae 120 return (ENOBUFS);
af092e86
BJ
121}
122
1acff8ec
BJ
123/*
124 * Connect from a socket to a specified address.
125 * Both address and port must be specified in argument sin.
126 * If don't have a local address for this socket yet,
127 * then pick one.
128 */
405c9168 129in_pcbconnect(inp, sin)
4ad99bae
BJ
130 struct inpcb *inp;
131 struct sockaddr_in *sin;
2b4b57cd 132{
1aa87517 133 struct ifnet *ifp;
2b4b57cd 134
405c9168 135COUNT(IN_PCBCONNECT);
4ad99bae
BJ
136 if (sin->sin_family != AF_INET)
137 return (EAFNOSUPPORT);
138 if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
139 return (EADDRNOTAVAIL);
1aa87517 140 if (inp->inp_laddr.s_addr == 0) {
668cc26d 141 ifp = if_ifonnetof(sin->sin_addr);
1aa87517
BJ
142 if (ifp == 0)
143 ifp = ifnet;
1aa87517
BJ
144 }
145 if (in_pcblookup(inp->inp_head,
1acff8ec
BJ
146 sin->sin_addr,
147 sin->sin_port,
148 inp->inp_laddr.s_addr ? inp->inp_laddr : ifp->if_addr,
149 inp->inp_lport,
150 0))
b454c3ea 151 return (EADDRINUSE);
c48203e2
BJ
152 if (inp->inp_laddr.s_addr == 0) {
153 struct sockaddr_in *sin2 =
154 (struct sockaddr_in *)&inp->inp_socket->so_addr;
155
ebcadd38 156 inp->inp_laddr = ifp->if_addr;
c48203e2
BJ
157 sin2->sin_addr = inp->inp_laddr;
158 }
4ad99bae
BJ
159 inp->inp_faddr = sin->sin_addr;
160 inp->inp_fport = sin->sin_port;
2b4b57cd
BJ
161 return (0);
162}
163
1aa87517 164in_setsockaddr(inp)
f6f20b2c 165 struct inpcb *inp;
f6f20b2c 166{
1aa87517
BJ
167 register struct sockaddr_in *sin =
168 (struct sockaddr_in *)&inp->inp_socket->so_addr;
f6f20b2c
BJ
169
170 sin->sin_family = AF_INET;
1aa87517
BJ
171 sin->sin_addr = inp->inp_laddr;
172 sin->sin_port = inp->inp_lport;
f6f20b2c
BJ
173}
174
405c9168
BJ
175in_pcbdisconnect(inp)
176 struct inpcb *inp;
177{
178
179COUNT(IN_PCBDISCONNECT);
180 inp->inp_faddr.s_addr = 0;
ebcadd38 181 inp->inp_fport = 0;
405c9168
BJ
182 if (inp->inp_socket->so_state & SS_USERGONE)
183 in_pcbdetach(inp);
184}
185
186in_pcbdetach(inp)
af092e86
BJ
187 struct inpcb *inp;
188{
189 struct socket *so = inp->inp_socket;
190
ba55c9af
BJ
191 so->so_pcb = 0;
192 sofree(so);
7545cf09 193 remque(inp);
cdad2eb1 194 (void) m_free(dtom(inp));
af092e86
BJ
195}
196
405c9168 197/*
1aa87517 198 * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
405c9168 199 */
cdad2eb1 200struct inpcb *
ebcadd38 201in_pcblookup(head, faddr, fport, laddr, lport, flags)
af092e86 202 struct inpcb *head;
4ad99bae 203 struct in_addr faddr, laddr;
af092e86 204 u_short fport, lport;
ebcadd38 205 int flags;
af092e86 206{
1aa87517
BJ
207 register struct inpcb *inp, *match = 0;
208 int matchwild = 3, wildcard;
af092e86 209
405c9168 210 for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
1aa87517 211 if (inp->inp_lport != lport)
405c9168 212 continue;
1aa87517
BJ
213 wildcard = 0;
214 if (inp->inp_laddr.s_addr != 0) {
1acff8ec
BJ
215 if (laddr.s_addr == 0)
216 wildcard++;
217 else if (inp->inp_laddr.s_addr != laddr.s_addr)
1aa87517
BJ
218 continue;
219 } else {
220 if (laddr.s_addr != 0)
221 wildcard++;
222 }
223 if (inp->inp_faddr.s_addr != 0) {
1acff8ec
BJ
224 if (faddr.s_addr == 0)
225 wildcard++;
226 else if (inp->inp_faddr.s_addr != faddr.s_addr ||
ebcadd38 227 inp->inp_fport != fport)
1aa87517
BJ
228 continue;
229 } else {
230 if (faddr.s_addr != 0)
231 wildcard++;
232 }
ebcadd38 233 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
405c9168 234 continue;
1aa87517
BJ
235 if (wildcard < matchwild) {
236 match = inp;
237 matchwild = wildcard;
238 if (matchwild == 0)
239 break;
405c9168 240 }
1aa87517 241 }
405c9168 242 return (match);
af092e86 243}