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