xref: /csrg-svn/sys/deprecated/bbnnet/in_pcb.c (revision 25202)
1 #ifdef	RCSIDENT
2 static char rcsident[] = "$Header: in_pcb.c,v 1.12 84/11/29 17:02:13 walsh Exp $";
3 #endif	RCSIDENT
4 
5 #include "../h/param.h"
6 #include "../h/systm.h"
7 #include "../h/dir.h"
8 #include "../h/user.h"
9 #include "../h/mbuf.h"
10 #include "../h/socket.h"
11 #include "../h/socketvar.h"
12 #include "../h/protosw.h"
13 #include "../h/domain.h"
14 
15 #include "../net/if.h"
16 #include "../net/route.h"
17 
18 #include "../bbnnet/in.h"
19 #include "../bbnnet/net.h"
20 #include "../bbnnet/in_pcb.h"
21 #include "../bbnnet/in_var.h"
22 
23 extern struct rtentry *ip_route();
24 extern struct domain  inetdomain;
25 
26 in_pcballoc(so, head)
27 struct socket *so;
28 struct inpcb *head;
29 {
30     register struct mbuf *m;
31     register struct inpcb *inp;
32 
33     m = m_getclr(M_DONTWAIT, MT_PCB);
34     if (m == NULL)
35 	return (ENOBUFS);
36 
37     inp = mtod(m, struct inpcb *);
38     inp->inp_socket = so;
39 
40     insque(inp,head);
41 
42     so->so_pcb = (caddr_t)inp;
43 
44     return (0);
45 }
46 
47 /*
48  * changed from 4.2 to accept a structure which has protocol
49  * specific data like how to break down port allocation.
50  */
51 
52 in_pcbbind(inp, nam, advice)
53 register struct inpcb *inp;
54 struct mbuf *nam;
55 struct pr_advice *advice;
56 {
57     register struct socket *so = inp->inp_socket;
58     register struct sockaddr_in *sin;
59     register u_short lport = 0;
60 
61     if (in_ifaddr == NULL)
62 	return (EADDRNOTAVAIL);
63     /*
64      * socket must not already be bound
65      */
66 
67     if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
68 	return (EINVAL);
69 
70     if (nam == 0)
71 	goto noname;
72     sin = mtod(nam, struct sockaddr_in *);
73     if (nam->m_len != sizeof (*sin))
74 	return (EINVAL);
75     /*
76      * Since Berkeley left this out, some of their programs (ftpd)
77      * aren't ready for it
78      *
79     if (sin->sin_family != AF_INET)
80 	    return (EAFNOSUPPORT);
81      */
82 
83     if (sin->sin_addr.s_addr != INADDR_ANY)
84     {
85 	/* some code says ..withnet() */
86 	if (in_iawithaddr(sin->sin_addr, FALSE) == NULL)
87 	    return (EADDRNOTAVAIL);
88 
89     }
90 
91     /* user gives port to us in net order */
92     if (lport = sin->sin_port)
93     {
94 	u_short aport;
95 
96 	/* if portsize > 2 a major rewrite needed to
97 	 * accomodate longs.....
98 	 */
99 
100 	if (advice->portsize > 1)
101 	    aport = ntohs(lport);
102 	else
103 	    aport = lport;	/* a char is a char */
104 
105 	/*
106 	 *  really only a worry for byte size ports
107 	 */
108 
109 	if (aport > advice->maxport)
110 	    return(EADDRNOTAVAIL);
111 
112 	if (aport <= advice->rootport && u.u_uid != 0)
113 	    return (EACCES);
114 
115 	/*
116 	 * Check to see if the local address/port is in use.
117 	 * but, process may use this pair to communicate with
118 	 * several destinations (each with its own tcp) if he
119 	 * sets SO_REUSEADDR
120 	 */
121 	if (advice->bind_used &&
122 	    (*(advice->bind_used))(inp,		/* current binding */
123 	    lport,			/* desired port */
124 	    sin->sin_addr.s_addr,	/* desired address */
125 	    so->so_options & SO_REUSEADDR))
126 	{
127 	    return (EADDRINUSE);
128 	}
129     }
130     inp->inp_laddr = sin->sin_addr;
131 
132 noname :
133     /* any ports for random allocation by non-root users? */
134     if ((advice->maxport <= advice->resvport) && (u.u_uid))
135 	return(EADDRNOTAVAIL);
136 
137     if (lport == 0)
138     {
139 	/*
140 	 * Allow for reserved ports for non-super users
141 	 * so that don't interfere with some project's software.
142 	 */
143 	u_short possible = advice->nowport;
144 
145 	do
146 	{
147 	    if (advice->portsize > 1)
148 		lport = htons(possible);
149 	    else
150 		lport = possible;
151 
152 	    /*
153 	     * catch roll over.....
154 	     */
155 
156 	    if (possible >= advice->maxport)
157 		possible = advice->resvport + 1;
158 	    else
159 		possible++;
160 
161 	    /*
162 	     * no free ports??? RDP/HMP problem
163 	     */
164 
165 	    if (possible == advice->nowport)
166 		return(EADDRNOTAVAIL);
167 
168 	}
169 	while (advice->bind_used &&
170 	    (*(advice->bind_used))(inp, lport, inp->inp_laddr.s_addr, 0));
171 
172 	advice->nowport = possible;
173     }
174     inp->inp_lport = lport;
175     return (0);
176 }
177 
178 /*
179  * Connect from a socket to a specified address.
180  * Both address and port must be specified in argument sin.
181  * If don't have a local address for this socket yet,
182  * then pick one.
183  */
184 in_pcbconnect(inp, nam, conn_used)
185 struct inpcb *inp;
186 struct mbuf *nam;
187 char *(*conn_used)();
188 {
189     register struct ifnet *ifp = NULL;
190     register struct in_ifaddr *ia = NULL;
191     register struct sockaddr_in *ifaddr;
192     register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
193     register struct rtentry *rt;
194     struct sockaddr_in *inpsin;
195 
196     if (nam->m_len != sizeof (*sin))
197 	return (EINVAL);
198     if (sin->sin_family != AF_INET)
199 	return (EAFNOSUPPORT);
200     if (sin->sin_addr.s_addr == INADDR_ANY || sin->sin_port == 0)
201 	return (EADDRNOTAVAIL);
202 
203     /*
204      * Find route for connection.  For a tcp connecting to a server,
205      * this route will be used for the duration of the connection
206      * (unless redirected...).  For a UDP doing a connect, this route
207      * will also be used for the duration.  For a UDP unconnected send,
208      * this route will be used for the current packet.
209      *
210      * rtalloc cannot handle routing with both sides already bound
211      */
212     rt = (struct rtentry *) NULL;
213 
214     /*
215      * NOTE: programmers often forget to zero sin_zero[0-1].
216      * rtalloc does not want to know the port number for routes to hosts.
217      */
218     inpsin = (struct sockaddr_in *) &inp->inp_route.ro_dst;
219     bcopy((caddr_t)sin, (caddr_t)inpsin, sizeof (*sin));
220     inpsin->sin_port = 0;
221 
222     if (inp->inp_laddr.s_addr == INADDR_ANY)
223     {
224 	rtalloc(&inp->inp_route);
225 	if (rt = inp->inp_route.ro_rt)
226 	    ifp = rt->rt_ifp;
227     }
228     else
229     {
230 	if (rt = ip_route(&inp->inp_laddr, &sin->sin_addr))
231 	{
232 	    inp->inp_route.ro_rt = rt;
233 	    ifp = rt->rt_ifp;
234 	}
235     }
236 
237     if (ifp == NULL)
238 	return (ENETUNREACH);
239 
240     /*
241      * find Internet address structure for this interface.
242      */
243     ia = in_iafromif(ifp);
244 
245     if (ia == NULL)
246 	/* ??? */
247 	return (ENETUNREACH);
248 
249     ifaddr = (struct sockaddr_in *) &ia->ia_addr;
250 
251 #ifdef bsd42
252     /*
253      * 8.7.0.2 (on IMP net) can send to 128.11.0.0 (on Ethernet), but
254      * not to 8.0.0.0
255      */
256     if (in_broadcast(sin->sin_addr) &&
257 	iptonet(sin->sin_addr) == iptonet(ifaddr->sin_addr) &&
258 	!(ifp->if_flags & IFF_BROADCAST) )
259     {
260 	if (rt)
261 	{
262 	    rtfree(rt);
263 	    inp->inp_route.ro_rt = NULL;
264 	}
265 	return (EADDRNOTAVAIL);
266     }
267 #endif
268 
269     if ((*conn_used)(inp,
270 	inp->inp_lport,
271 	(inp->inp_laddr.s_addr ? inp->inp_laddr.s_addr : ifaddr->sin_addr.s_addr),
272 	sin->sin_port,
273 	sin->sin_addr.s_addr) != (char *)NULL)
274     {
275 
276 	if (rt)
277 	{
278 	    rtfree(rt);
279 	    inp->inp_route.ro_rt = NULL;
280 	}
281 	return (EADDRINUSE);
282     }
283 
284     if (inp->inp_laddr.s_addr == INADDR_ANY)
285 	inp->inp_laddr = ifaddr->sin_addr;
286     inp->inp_faddr = sin->sin_addr;
287     inp->inp_fport = sin->sin_port;
288     return (0);
289 }
290 
291 in_pcbdisconnect(inp, pcb_free_func)
292 struct inpcb *inp;
293 int (*pcb_free_func)();
294 {
295     inp->inp_faddr.s_addr = INADDR_ANY;
296     inp->inp_fport = 0;
297     /*
298      * may attach a route to an inpcb several times.  For example,
299      * when UDP does unconnected, but bound, sends.
300      */
301     if (inp->inp_route.ro_rt)
302     {
303 	rtfree(inp->inp_route.ro_rt);
304 	inp->inp_route.ro_rt = NULL;
305     }
306 
307     if (inp->inp_socket->so_state & SS_NOFDREF)
308 	in_pcbdetach(inp, pcb_free_func);
309 }
310 
311 /*
312  * Don't need to splnet while altering lists, since called from places
313  * where that has already been done.
314  */
315 in_pcbdetach(inp, pcb_free_func)
316 register struct inpcb *inp;
317 int (*pcb_free_func)();
318 {
319     register struct socket *so;
320 
321     if (so = inp->inp_socket)
322     {
323 	so->so_pcb = (caddr_t) NULL;
324 	/* inp->inp_socket = (struct socket *) NULL; */
325 	soisdisconnected(so);
326 	sofree(so);
327     }
328     else
329 	panic("in_pcbdetach");
330 
331     if (inp->inp_route.ro_rt)
332 	rtfree(inp->inp_route.ro_rt);
333 
334     if (inp->inp_ppcb)
335 	(*pcb_free_func)(inp); /* free per-protocol block */
336 
337     remque(inp);
338 
339     (void) m_free(dtom(inp));
340 }
341 
342 in_setsockaddr(inp, nam)
343 register struct inpcb *inp;
344 struct mbuf *nam;
345 {
346     register struct sockaddr_in *sin;
347 
348     nam->m_len = sizeof (*sin);
349     sin = mtod(nam, struct sockaddr_in *);
350     bzero((caddr_t)sin, sizeof (*sin));
351     sin->sin_family = AF_INET;
352     sin->sin_port = inp->inp_lport;
353     sin->sin_addr = inp->inp_laddr;
354 }
355 
356 in_setpeeraddr(inp, nam)
357 register struct inpcb *inp;
358 struct mbuf *nam;
359 {
360     register struct sockaddr_in *sin;
361 
362     nam->m_len = sizeof (*sin);
363     sin = mtod(nam, struct sockaddr_in *);
364     bzero((caddr_t)sin, sizeof (*sin));
365     sin->sin_family = AF_INET;
366     sin->sin_port = inp->inp_fport;
367     sin->sin_addr = inp->inp_faddr;
368 }
369 
370 /*
371  * somewhat different from the one in 4.2 and (I think) substantially
372  * easier to read, though a bit slower.
373  *
374  * fport == 0 if don't want/need match on remote port # (HMP and UDP)
375  */
376 struct inpcb *
377 in_pcblookup(head,faddr,fport,laddr,lport,wild)
378 struct inpcb *head;
379 u_long faddr, laddr;
380 u_short fport, lport;
381 int wild;
382 {
383     register struct inpcb *inp;
384 
385     /* try exact match */
386     for(inp = head->inp_next; inp != head; inp = inp->inp_next)
387     {
388 	/* ports check */
389 	if (inp->inp_lport != lport)
390 	    continue;
391 
392 	if (fport && (inp->inp_fport != fport))
393 	    continue;
394 
395 	if ((inp->inp_faddr.s_addr != faddr) || (inp->inp_laddr.s_addr != laddr))
396 	    continue;
397 
398 	/* keep it! */
399 	return(inp);
400     }
401 
402     /* try wildcard ? */
403     if (wild)
404     {
405 	for(inp = head->inp_next; inp != head; inp = inp->inp_next)
406 	{
407 	    /* ports again */
408 	    if (inp->inp_lport != lport)
409 		continue;
410 
411 	    if (fport && (inp->inp_fport != fport) && inp->inp_fport)
412 		continue;
413 
414 	    if ((inp->inp_faddr.s_addr) && (inp->inp_faddr.s_addr != faddr))
415 		continue;
416 
417 	    if ((inp->inp_laddr.s_addr) && (inp->inp_laddr.s_addr != laddr))
418 		continue;
419 
420 	    return(inp);
421 	}
422     }
423 
424     return((struct inpcb *) NULL);
425 }
426 
427 
428 /*
429  * This only advises process and does not internally close socket,
430  * not so much because the user can do much but close when he gets a
431  * HOSTDEAD/HOSTUNREACH indication, but because it is possible that
432  * the destination host has saved connection state information. (His IMP
433  * interface went down for PM, but the machine stayed up...)
434  *
435  * Also, this makes addition of new protocols easy, since we don't need to
436  * know the name and calling sequence of their close/abort routine.
437  *
438  * We do not close child sockets of listen(2)ers for connection oriented
439  * protocols.  We let the protocol do that by timing out connection
440  * establishment.
441  */
442 inpcb_notify(head, laddr, faddr, error)
443 register struct inpcb *head;
444 register u_long	laddr;
445 register u_long	faddr;
446 {
447     register struct inpcb   *inp;
448 
449     for(inp = head->inp_next; inp != head; inp = inp->inp_next)
450 	if (((inp->inp_faddr.s_addr == faddr) || (faddr == 0)) &&
451 	    ((inp->inp_laddr.s_addr == laddr) || (laddr == 0)))
452 		advise_user(inp->inp_socket, error);
453 }
454 
455 advise_user(so, error)
456 struct socket *so;
457 int error;
458 {
459     if (so == 0)
460 	return;
461 
462     so->so_error = error;
463 
464     wakeup((caddr_t) &so->so_timeo); /* in connect(2) */
465     sowwakeup(so);
466     sorwakeup(so);
467 }
468