date and time created 82/03/27 22:57:41 by sam
[unix-history] / usr / src / sys / netinet / in_pcb.c
... / ...
CommitLineData
1/* in_pcb.c 4.21 82/03/23 */
2
3#include "../h/param.h"
4#include "../h/systm.h"
5#include "../h/dir.h"
6#include "../h/user.h"
7#include "../h/mbuf.h"
8#include "../h/socket.h"
9#include "../h/socketvar.h"
10#include "../net/in.h"
11#include "../net/in_systm.h"
12#include "../net/if.h"
13#include "../net/in_pcb.h"
14#include "../h/protosw.h"
15
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.
44 *
45 * TODO:
46 * use hashing
47 */
48struct in_addr zeroin_addr;
49
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 */
55in_pcbattach(so, head, sndcc, rcvcc, sin)
56 struct socket *so;
57 struct inpcb *head;
58 int sndcc, rcvcc;
59 struct sockaddr_in *sin;
60{
61 struct mbuf *m;
62 register struct inpcb *inp;
63 u_short lport = 0;
64
65COUNT(IN_PCBATTACH);
66 if (ifnet == 0)
67 return (EADDRNOTAVAIL);
68 if (sin) {
69 if (sin->sin_family != AF_INET)
70 return (EAFNOSUPPORT);
71 if (sin->sin_addr.s_addr &&
72 if_ifwithaddr(sin->sin_addr) == 0)
73 return (EADDRNOTAVAIL);
74 lport = sin->sin_port;
75 if (lport) {
76 u_short aport = lport;
77 int wild = 0;
78#if vax
79 aport = htons(aport);
80#endif
81 /* GROSS */
82 if (aport < IPPORT_RESERVED && u.u_uid != 0)
83 return (EPERM);
84 if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
85 (so->so_options & SO_ACCEPTCONN) == 0)
86 wild = INPLOOKUP_WILDCARD;
87 if (in_pcblookup(head,
88 zeroin_addr, 0, sin->sin_addr, lport, wild))
89 return (EADDRINUSE);
90 }
91 }
92 m = m_getclr(M_DONTWAIT);
93 if (m == 0)
94 return (ENOBUFS);
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 *);
100 inp->inp_head = head;
101 if (sin)
102 inp->inp_laddr = sin->sin_addr;
103 if (lport == 0)
104 do {
105 if (head->inp_lport++ < IPPORT_RESERVED)
106 head->inp_lport = IPPORT_RESERVED;
107 lport = htons(head->inp_lport);
108 } while (in_pcblookup(head,
109 zeroin_addr, 0, inp->inp_laddr, lport, 0));
110 inp->inp_lport = lport;
111 inp->inp_socket = so;
112 insque(inp, head);
113 so->so_pcb = (caddr_t)inp;
114 in_setsockaddr(inp);
115 return (0);
116bad2:
117 sbrelease(&so->so_snd);
118bad:
119 (void) m_free(m);
120 return (ENOBUFS);
121}
122
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 */
129in_pcbconnect(inp, sin)
130 struct inpcb *inp;
131 struct sockaddr_in *sin;
132{
133 struct ifnet *ifp;
134
135COUNT(IN_PCBCONNECT);
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);
140 if (inp->inp_laddr.s_addr == 0) {
141 ifp = if_ifonnetof(sin->sin_addr);
142 if (ifp == 0)
143 ifp = ifnet;
144 }
145 if (in_pcblookup(inp->inp_head,
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))
151 return (EADDRINUSE);
152 if (inp->inp_laddr.s_addr == 0) {
153 struct sockaddr_in *sin2 =
154 (struct sockaddr_in *)&inp->inp_socket->so_addr;
155
156 inp->inp_laddr = ifp->if_addr;
157 sin2->sin_addr = inp->inp_laddr;
158 }
159 inp->inp_faddr = sin->sin_addr;
160 inp->inp_fport = sin->sin_port;
161 return (0);
162}
163
164in_setsockaddr(inp)
165 struct inpcb *inp;
166{
167 register struct sockaddr_in *sin =
168 (struct sockaddr_in *)&inp->inp_socket->so_addr;
169
170 sin->sin_family = AF_INET;
171 sin->sin_addr = inp->inp_laddr;
172 sin->sin_port = inp->inp_lport;
173}
174
175in_pcbdisconnect(inp)
176 struct inpcb *inp;
177{
178
179COUNT(IN_PCBDISCONNECT);
180 inp->inp_faddr.s_addr = 0;
181 inp->inp_fport = 0;
182 if (inp->inp_socket->so_state & SS_USERGONE)
183 in_pcbdetach(inp);
184}
185
186in_pcbdetach(inp)
187 struct inpcb *inp;
188{
189 struct socket *so = inp->inp_socket;
190
191 so->so_pcb = 0;
192 sofree(so);
193 remque(inp);
194 (void) m_free(dtom(inp));
195}
196
197/*
198 * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
199 */
200struct inpcb *
201in_pcblookup(head, faddr, fport, laddr, lport, flags)
202 struct inpcb *head;
203 struct in_addr faddr, laddr;
204 u_short fport, lport;
205 int flags;
206{
207 register struct inpcb *inp, *match = 0;
208 int matchwild = 3, wildcard;
209
210 for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
211 if (inp->inp_lport != lport)
212 continue;
213 wildcard = 0;
214 if (inp->inp_laddr.s_addr != 0) {
215 if (laddr.s_addr == 0)
216 wildcard++;
217 else if (inp->inp_laddr.s_addr != laddr.s_addr)
218 continue;
219 } else {
220 if (laddr.s_addr != 0)
221 wildcard++;
222 }
223 if (inp->inp_faddr.s_addr != 0) {
224 if (faddr.s_addr == 0)
225 wildcard++;
226 else if (inp->inp_faddr.s_addr != faddr.s_addr ||
227 inp->inp_fport != fport)
228 continue;
229 } else {
230 if (faddr.s_addr != 0)
231 wildcard++;
232 }
233 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
234 continue;
235 if (wildcard < matchwild) {
236 match = inp;
237 matchwild = wildcard;
238 if (matchwild == 0)
239 break;
240 }
241 }
242 return (match);
243}