xref: /original-bsd/sys/netinet/in_pcb.c (revision 8583c8cb)
1 /*	in_pcb.c	4.17	82/02/27	*/
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 
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.
43  *
44  * TODO:
45  *	use hashing
46  */
47 struct	in_addr zeroin_addr;
48 
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  */
54 in_pcbattach(so, head, sndcc, rcvcc, sin)
55 	struct socket *so;
56 	struct inpcb *head;
57 	int sndcc, rcvcc;
58 	struct sockaddr_in *sin;
59 {
60 	struct mbuf *m;
61 	register struct inpcb *inp;
62 	u_short lport = 0;
63 
64 COUNT(IN_PCBATTACH);
65 	if (ifnet == 0)
66 		return (EADDRNOTAVAIL);
67 	if (sin) {
68 		if (sin->sin_family != AF_INET)
69 			return (EAFNOSUPPORT);
70 		if (sin->sin_addr.s_addr &&
71 		    if_ifwithaddr(sin->sin_addr.s_addr) == 0)
72 			return (EADDRNOTAVAIL);
73 		lport = sin->sin_port;
74 		if (lport) {
75 			u_short aport = lport;
76 #if vax
77 			aport = htons(aport);
78 #endif
79 			/* GROSS */
80 			if (aport < IPPORT_RESERVED && u.u_uid != 0)
81 				return (EPERM);
82 			if (in_pcblookup(head,
83 			    zeroin_addr, 0, sin->sin_addr, lport, 0))
84 				return (EADDRINUSE);
85 		}
86 	}
87 	m = m_getclr(M_DONTWAIT);
88 	if (m == 0)
89 		return (ENOBUFS);
90 	if (sbreserve(&so->so_snd, sndcc) == 0)
91 		goto bad;
92 	if (sbreserve(&so->so_rcv, rcvcc) == 0)
93 		goto bad2;
94 	inp = mtod(m, struct inpcb *);
95 	inp->inp_head = head;
96 	if (sin)
97 		inp->inp_laddr = sin->sin_addr;
98 	if (lport == 0)
99 		do {
100 			if (head->inp_lport++ < IPPORT_RESERVED)
101 				head->inp_lport = IPPORT_RESERVED;
102 			lport = htons(head->inp_lport);
103 		} while (in_pcblookup(head,
104 			    zeroin_addr, 0, inp->inp_laddr, lport, 0));
105 	inp->inp_lport = lport;
106 	inp->inp_socket = so;
107 	insque(inp, head);
108 	so->so_pcb = (caddr_t)inp;
109 	in_setsockaddr(inp);
110 	return (0);
111 bad2:
112 	sbrelease(&so->so_snd);
113 bad:
114 	(void) m_free(m);
115 	return (ENOBUFS);
116 }
117 
118 in_pcbconnect(inp, sin)
119 	struct inpcb *inp;
120 	struct sockaddr_in *sin;
121 {
122 	struct ifnet *ifp;
123 
124 COUNT(IN_PCBCONNECT);
125 	if (sin->sin_family != AF_INET)
126 		return (EAFNOSUPPORT);
127 	if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
128 		return (EADDRNOTAVAIL);
129 	if (inp->inp_laddr.s_addr == 0) {
130 		ifp = if_ifonnetof(sin->sin_addr.s_addr);
131 		if (ifp == 0)
132 			ifp = ifnet;
133 		inp->inp_laddr = ifp->if_addr;
134 	}
135 	if (in_pcblookup(inp->inp_head,
136 	    sin->sin_addr, sin->sin_port, inp->inp_laddr, inp->inp_lport, 0))
137 		return (EADDRINUSE);
138 	inp->inp_faddr = sin->sin_addr;
139 	inp->inp_fport = sin->sin_port;
140 	return (0);
141 }
142 
143 in_setsockaddr(inp)
144 	struct inpcb *inp;
145 {
146 	register struct sockaddr_in *sin =
147 	    (struct sockaddr_in *)&inp->inp_socket->so_addr;
148 
149 	sin->sin_family = AF_INET;
150 	sin->sin_addr = inp->inp_laddr;
151 	sin->sin_port = inp->inp_lport;
152 }
153 
154 in_pcbdisconnect(inp)
155 	struct inpcb *inp;
156 {
157 
158 COUNT(IN_PCBDISCONNECT);
159 	inp->inp_faddr.s_addr = 0;
160 	if (inp->inp_socket->so_state & SS_USERGONE)
161 		in_pcbdetach(inp);
162 }
163 
164 in_pcbdetach(inp)
165 	struct inpcb *inp;
166 {
167 	struct socket *so = inp->inp_socket;
168 
169 	so->so_pcb = 0;
170 	sofree(so);
171 	remque(inp);
172 	(void) m_free(dtom(inp));
173 }
174 
175 /*
176  * Look for a control block to accept a segment.
177  * First choice is an exact address match.
178  * Second choice is a match with either the foreign or the local
179  * address specified.
180  *
181  * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
182  */
183 struct inpcb *
184 in_pcblookup(head, faddr, fport, laddr, lport, enter)
185 	struct inpcb *head;
186 	struct in_addr faddr, laddr;
187 	u_short fport, lport;
188 	int enter;
189 {
190 	register struct inpcb *inp, *match = 0;
191 	int matchwild = 3, wildcard;
192 
193 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
194 		if (inp->inp_lport != lport)
195 			continue;
196 		wildcard = 0;
197 		if (inp->inp_laddr.s_addr != 0) {
198 			if (inp->inp_laddr.s_addr != laddr.s_addr)
199 				continue;
200 		} else {
201 			if (laddr.s_addr != 0)
202 				wildcard++;
203 		}
204 		if (inp->inp_faddr.s_addr != 0) {
205 			if (inp->inp_faddr.s_addr != faddr.s_addr)
206 				continue;
207 		} else {
208 			if (faddr.s_addr != 0)
209 				wildcard++;
210 		}
211 		if (enter == 0 && wildcard)
212 			continue;
213 		if (wildcard < matchwild) {
214 			match = inp;
215 			matchwild = wildcard;
216 			if (matchwild == 0)
217 				break;
218 		}
219 	}
220 	if (match && enter) {
221 		match->inp_laddr = laddr;
222 		in_setsockaddr(match);
223 	}
224 	return (match);
225 }
226