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