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