xref: /csrg-svn/sys/netinet/in_pcb.c (revision 5161)
1 /* in_pcb.c 4.11 81/12/02 */
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 
45 /*
46  * Allocate a protocol control block, space
47  * for send and receive data, and local host information.
48  * Return error.  If no error make socket point at pcb.
49  */
50 in_pcbattach(so, head, sndcc, rcvcc, sin)
51 	struct socket *so;
52 	struct inpcb *head;
53 	int sndcc, rcvcc;
54 	struct sockaddr_in *sin;
55 {
56 	struct mbuf *m;
57 	register struct inpcb *inp, *xp;
58 	struct ifnet *ifp;
59 	u_long lport;
60 
61 COUNT(IN_PCBATTACH);
62 	if (sin) {
63 		if (sin->sin_family != AF_INET)
64 			return (EAFNOSUPPORT);
65 		ifp = if_ifwithaddr(sin->sin_addr);
66 		if (ifp == 0)
67 			return (EADDRNOTAVAIL);
68 		lport = sin->sin_port;
69 		if (lport) {
70 			xp = head->inp_next;
71 			for (; xp != head; xp = xp->inp_next)
72 				if (xp->inp_laddr.s_addr ==
73 				    sin->sin_addr.s_addr &&
74 				    xp->inp_lport == lport &&
75 				    xp->inp_faddr.s_addr == 0)
76 					return (EADDRINUSE);
77 		}
78 	} else {
79 		ifp = if_ifwithaddr(ifnet->if_addr);
80 		lport = 0;
81 	}
82 	m = m_getclr(M_WAIT);
83 	if (m == 0)
84 		return (ENOBUFS);
85 	if (sbreserve(&so->so_snd, sndcc) == 0)
86 		goto bad;
87 	if (sbreserve(&so->so_rcv, rcvcc) == 0)
88 		goto bad2;
89 	inp = mtod(m, struct inpcb *);
90 	inp->inp_laddr = ifp->if_addr;
91 	if (lport)
92 		goto gotport;
93 again:
94 	if (head->inp_lport++ < 1024)
95 		head->inp_lport = 1024;
96 	for (xp = head->inp_next; xp != head; xp = xp->inp_next)
97 		if (xp->inp_lport == head->inp_lport)
98 			goto again;
99 	lport = htons(head->inp_lport);
100 gotport:
101 	inp->inp_socket = so;
102 	inp->inp_lport = lport;
103 	insque(inp, head);
104 	so->so_pcb = (caddr_t)inp;
105 	sin = (struct sockaddr_in *)&so->so_addr;
106 	sin->sin_family = AF_INET;
107 	sin->sin_addr = inp->inp_laddr;
108 	sin->sin_port = inp->inp_lport;
109 	return (0);
110 bad2:
111 	sbrelease(&so->so_snd);
112 bad:
113 	(void) m_free(m);
114 	return (ENOBUFS);
115 }
116 
117 in_pcbconnect(inp, sin)
118 	struct inpcb *inp;
119 	struct sockaddr_in *sin;
120 {
121 
122 COUNT(IN_PCBCONNECT);
123 	if (sin->sin_family != AF_INET)
124 		return (EAFNOSUPPORT);
125 	if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
126 		return (EADDRNOTAVAIL);
127 	/* should check not already in use... */
128 	inp->inp_faddr = sin->sin_addr;
129 	inp->inp_fport = sin->sin_port;
130 	return (0);
131 }
132 
133 in_pcbdisconnect(inp)
134 	struct inpcb *inp;
135 {
136 
137 COUNT(IN_PCBDISCONNECT);
138 	inp->inp_faddr.s_addr = 0;
139 	if (inp->inp_socket->so_state & SS_USERGONE)
140 		in_pcbdetach(inp);
141 }
142 
143 in_pcbdetach(inp)
144 	struct inpcb *inp;
145 {
146 	struct socket *so = inp->inp_socket;
147 
148 	so->so_pcb = 0;
149 	sofree(so);
150 	remque(inp);
151 	(void) m_free(dtom(inp));
152 }
153 
154 /*
155  * Look for a control block to accept a segment.
156  * First choice is an exact address match.
157  * Second choice is a match of local address, with
158  * unspecified foreign address.
159  */
160 struct inpcb *
161 in_pcblookup(head, faddr, fport, laddr, lport)
162 	struct inpcb *head;
163 	struct in_addr faddr, laddr;
164 	u_short fport, lport;
165 {
166 	register struct inpcb *inp;
167 	struct inpcb *match = 0;
168 
169 	for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
170 		if (inp->inp_laddr.s_addr != laddr.s_addr ||
171 		    inp->inp_lport != lport)
172 			continue;
173 		if (inp->inp_faddr.s_addr == 0) {
174 			match = inp;
175 			continue;
176 		}
177 		if (inp->inp_faddr.s_addr == faddr.s_addr &&
178 		    inp->inp_fport == fport)
179 			return (inp);
180 	}
181 	return (match);
182 }
183