xref: /original-bsd/sys/netinet/in_pcb.c (revision a13d7ea1)
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  */
48 struct	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  */
55 in_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 
65 COUNT(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);
116 bad2:
117 	sbrelease(&so->so_snd);
118 bad:
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  */
129 in_pcbconnect(inp, sin)
130 	struct inpcb *inp;
131 	struct sockaddr_in *sin;
132 {
133 	struct ifnet *ifp;
134 
135 COUNT(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 
164 in_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 
175 in_pcbdisconnect(inp)
176 	struct inpcb *inp;
177 {
178 
179 COUNT(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 
186 in_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  */
200 struct inpcb *
201 in_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 }
244