xref: /csrg-svn/sys/netinet/in_pcb.c (revision 8397)
1 /*	in_pcb.c	4.32	82/10/09	*/
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 "../netinet/in.h"
11 #include "../netinet/in_systm.h"
12 #include "../net/if.h"
13 #include "../net/route.h"
14 #include "../netinet/in_pcb.h"
15 #include "../h/protosw.h"
16 
17 struct	in_addr zeroin_addr;
18 
19 in_pcbreserve(so, sndcc, rcvcc)
20 	struct socket *so;
21 	int sndcc, rcvcc;
22 {
23 
24 	if (sbreserve(&so->so_snd, sndcc) == 0)
25 		goto bad;
26 	if (sbreserve(&so->so_rcv, rcvcc) == 0)
27 		goto bad2;
28 	return (0);
29 bad2:
30 	sbrelease(&so->so_snd);
31 bad:
32 	return (ENOBUFS);
33 }
34 
35 in_pcballoc(so, head)
36 	struct socket *so;
37 	struct inpcb *head;
38 {
39 	struct mbuf *m;
40 	register struct inpcb *inp;
41 
42 	m = m_getclr(M_DONTWAIT);
43 	if (m == 0)
44 		return (ENOBUFS);
45 	inp = mtod(m, struct inpcb *);
46 	inp->inp_head = head;
47 	inp->inp_socket = so;
48 	insque(inp, head);
49 	so->so_pcb = (caddr_t)inp;
50 	return (0);
51 }
52 
53 in_pcbbind(inp, nam)
54 	register struct inpcb *inp;
55 	struct mbuf *nam;
56 {
57 	register struct socket *so = inp->inp_socket;
58 	register struct inpcb *head = inp->inp_head;
59 	register struct sockaddr_in *sin;
60 	u_short lport = 0;
61 
62 	if (ifnet == 0)
63 		return (EADDRNOTAVAIL);
64 	if (inp->inp_lport || inp->inp_laddr.s_addr)
65 		return (EINVAL);
66 	if (nam == 0)
67 		goto noname;
68 	sin = mtod(nam, struct sockaddr_in *);
69 	if (nam->m_len != sizeof (*sin))
70 		return (EINVAL);
71 	if (sin->sin_addr.s_addr) {
72 		int tport = sin->sin_port;
73 
74 		sin->sin_port = 0;		/* yech... */
75 		if (if_ifwithaddr((struct sockaddr *)sin) == 0)
76 			return (EADDRNOTAVAIL);
77 		sin->sin_port = tport;
78 	}
79 	lport = sin->sin_port;
80 	if (lport) {
81 		u_short aport = lport;
82 		int wild = 0;
83 
84 #if vax
85 		aport = htons(aport);
86 #endif
87 		/* GROSS */
88 		if (aport < IPPORT_RESERVED && u.u_uid != 0)
89 			return (EACCES);
90 		if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0)
91 			wild = INPLOOKUP_WILDCARD;
92 		if (in_pcblookup(head,
93 		    zeroin_addr, 0, sin->sin_addr, lport, wild))
94 			return (EADDRINUSE);
95 	}
96 	inp->inp_laddr = sin->sin_addr;
97 noname:
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 	return (0);
107 }
108 
109 /*
110  * Connect from a socket to a specified address.
111  * Both address and port must be specified in argument sin.
112  * If don't have a local address for this socket yet,
113  * then pick one.
114  */
115 in_pcbconnect(inp, nam)
116 	struct inpcb *inp;
117 	struct mbuf *nam;
118 {
119 	struct ifnet *ifp;
120 	struct sockaddr_in *ifaddr;
121 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
122 
123 	if (nam->m_len != sizeof (*sin))
124 		return (EINVAL);
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(in_netof(sin->sin_addr));
131 		if (ifp == 0) {
132 			/*
133 			 * We should select the interface based on
134 			 * the route to be used, but for udp this would
135 			 * result in two calls to rtalloc for each packet
136 			 * sent; hardly worthwhile...
137 			 */
138 			ifp = if_ifwithaf(AF_INET);
139 			if (ifp == 0)
140 				return (EADDRNOTAVAIL);
141 		}
142 		ifaddr = (struct sockaddr_in *)&ifp->if_addr;
143 	}
144 	if (in_pcblookup(inp->inp_head,
145 	    sin->sin_addr,
146 	    sin->sin_port,
147 	    inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
148 	    inp->inp_lport,
149 	    0))
150 		return (EADDRINUSE);
151 	if (inp->inp_laddr.s_addr == 0)
152 		inp->inp_laddr = ifaddr->sin_addr;
153 	inp->inp_faddr = sin->sin_addr;
154 	inp->inp_fport = sin->sin_port;
155 	return (0);
156 }
157 
158 in_pcbdisconnect(inp)
159 	struct inpcb *inp;
160 {
161 
162 	inp->inp_faddr.s_addr = 0;
163 	inp->inp_fport = 0;
164 	if (inp->inp_socket->so_state & SS_NOFDREF)
165 		in_pcbdetach(inp);
166 }
167 
168 in_pcbdetach(inp)
169 	struct inpcb *inp;
170 {
171 	struct socket *so = inp->inp_socket;
172 
173 	so->so_pcb = 0;
174 	sofree(so);
175 	if (inp->inp_route.ro_rt)
176 		rtfree(inp->inp_route.ro_rt);
177 	remque(inp);
178 	(void) m_free(dtom(inp));
179 }
180 
181 in_setsockaddr(inp, nam)
182 	register struct inpcb *inp;
183 	struct mbuf *nam;
184 {
185 	register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
186 
187 	nam->m_len = sizeof (*sin);
188 	sin = mtod(nam, struct sockaddr_in *);
189 	bzero((caddr_t)sin, sizeof (*sin));
190 	sin->sin_family = AF_INET;
191 	sin->sin_port = inp->inp_lport;
192 	sin->sin_addr = inp->inp_laddr;
193 }
194 
195 /*
196  * Pass an error to all internet connections
197  * associated with address sin.  Call the
198  * protocol specific routine to clean up the
199  * mess afterwards.
200  */
201 in_pcbnotify(head, dst, errno, abort)
202 	struct inpcb *head;
203 	register struct in_addr *dst;
204 	int errno, (*abort)();
205 {
206 	register struct inpcb *inp, *oinp;
207 	int s = splimp();
208 
209 	for (inp = head->inp_next; inp != head;) {
210 		if (inp->inp_faddr.s_addr != dst->s_addr) {
211 	next:
212 			inp = inp->inp_next;
213 			continue;
214 		}
215 		if (inp->inp_socket == 0)
216 			goto next;
217 		inp->inp_socket->so_error = errno;
218 		oinp = inp;
219 		inp = inp->inp_next;
220 		(*abort)(oinp);
221 	}
222 	splx(s);
223 }
224 
225 struct inpcb *
226 in_pcblookup(head, faddr, fport, laddr, lport, flags)
227 	struct inpcb *head;
228 	struct in_addr faddr, laddr;
229 	u_short fport, lport;
230 	int flags;
231 {
232 	register struct inpcb *inp, *match = 0;
233 	int matchwild = 3, wildcard;
234 
235 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
236 		if (inp->inp_lport != lport)
237 			continue;
238 		wildcard = 0;
239 		if (inp->inp_laddr.s_addr != 0) {
240 			if (laddr.s_addr == 0)
241 				wildcard++;
242 			else if (inp->inp_laddr.s_addr != laddr.s_addr)
243 				continue;
244 		} else {
245 			if (laddr.s_addr != 0)
246 				wildcard++;
247 		}
248 		if (inp->inp_faddr.s_addr != 0) {
249 			if (faddr.s_addr == 0)
250 				wildcard++;
251 			else if (inp->inp_faddr.s_addr != faddr.s_addr ||
252 			    inp->inp_fport != fport)
253 				continue;
254 		} else {
255 			if (faddr.s_addr != 0)
256 				wildcard++;
257 		}
258 		if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
259 			continue;
260 		if (wildcard < matchwild) {
261 			match = inp;
262 			matchwild = wildcard;
263 			if (matchwild == 0)
264 				break;
265 		}
266 	}
267 	return (match);
268 }
269