fix carrier drop handling in ttread & try improving LCASE mode
[unix-history] / usr / src / sys / netinet / in_pcb.c
... / ...
CommitLineData
1/* in_pcb.c 6.7 85/04/18 */
2
3#include "param.h"
4#include "systm.h"
5#include "dir.h"
6#include "user.h"
7#include "mbuf.h"
8#include "socket.h"
9#include "socketvar.h"
10#include "in.h"
11#include "in_systm.h"
12#include "../net/if.h"
13#include "../net/route.h"
14#include "in_pcb.h"
15#include "in_var.h"
16#include "protosw.h"
17
18struct in_addr zeroin_addr;
19
20in_pcballoc(so, head)
21 struct socket *so;
22 struct inpcb *head;
23{
24 struct mbuf *m;
25 register struct inpcb *inp;
26
27 m = m_getclr(M_DONTWAIT, MT_PCB);
28 if (m == NULL)
29 return (ENOBUFS);
30 inp = mtod(m, struct inpcb *);
31 inp->inp_head = head;
32 inp->inp_socket = so;
33 insque(inp, head);
34 so->so_pcb = (caddr_t)inp;
35 return (0);
36}
37
38in_pcbbind(inp, nam)
39 register struct inpcb *inp;
40 struct mbuf *nam;
41{
42 register struct socket *so = inp->inp_socket;
43 register struct inpcb *head = inp->inp_head;
44 register struct sockaddr_in *sin;
45 u_short lport = 0;
46
47 if (in_ifaddr == 0)
48 return (EADDRNOTAVAIL);
49 if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
50 return (EINVAL);
51 if (nam == 0)
52 goto noname;
53 sin = mtod(nam, struct sockaddr_in *);
54 if (nam->m_len != sizeof (*sin))
55 return (EINVAL);
56 if (sin->sin_addr.s_addr != INADDR_ANY) {
57 int tport = sin->sin_port;
58
59 sin->sin_port = 0; /* yech... */
60 if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
61 return (EADDRNOTAVAIL);
62 sin->sin_port = tport;
63 }
64 lport = sin->sin_port;
65 if (lport) {
66 u_short aport = ntohs(lport);
67 int wild = 0;
68
69 /* GROSS */
70 if (aport < IPPORT_RESERVED && u.u_uid != 0)
71 return (EACCES);
72 /* even GROSSER, but this is the Internet */
73 if ((so->so_options & SO_REUSEADDR) == 0 &&
74 ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
75 (so->so_options & SO_ACCEPTCONN) == 0))
76 wild = INPLOOKUP_WILDCARD;
77 if (in_pcblookup(head,
78 zeroin_addr, 0, sin->sin_addr, lport, wild))
79 return (EADDRINUSE);
80 }
81 inp->inp_laddr = sin->sin_addr;
82noname:
83 if (lport == 0)
84 do {
85 if (head->inp_lport++ < IPPORT_RESERVED)
86 head->inp_lport = IPPORT_RESERVED;
87 lport = htons(head->inp_lport);
88 } while (in_pcblookup(head,
89 zeroin_addr, 0, inp->inp_laddr, lport, 0));
90 inp->inp_lport = lport;
91 return (0);
92}
93
94/*
95 * Connect from a socket to a specified address.
96 * Both address and port must be specified in argument sin.
97 * If don't have a local address for this socket yet,
98 * then pick one.
99 */
100in_pcbconnect(inp, nam)
101 struct inpcb *inp;
102 struct mbuf *nam;
103{
104 struct in_ifaddr *ia;
105 struct sockaddr_in *ifaddr;
106 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
107
108 if (nam->m_len != sizeof (*sin))
109 return (EINVAL);
110 if (sin->sin_family != AF_INET)
111 return (EAFNOSUPPORT);
112 if (sin->sin_port == 0)
113 return (EADDRNOTAVAIL);
114 if (in_ifaddr) {
115 /*
116 * If the destination address is INADDR_ANY,
117 * use the primary local address.
118 * If the supplied address is INADDR_BROADCAST,
119 * and the primary interface supports broadcast,
120 * choose the broadcast address for that interface.
121 */
122#define satosin(sa) ((struct sockaddr_in *)(sa))
123 if (sin->sin_addr.s_addr == INADDR_ANY)
124 sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
125 else if (sin->sin_addr.s_addr == INADDR_BROADCAST &&
126 (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
127 sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
128 }
129 if (inp->inp_laddr.s_addr == INADDR_ANY) {
130 ia = (struct in_ifaddr *)ifa_ifwithnet((struct sockaddr *)sin);
131 if (ia == (struct in_ifaddr *)0) {
132 register struct route *ro;
133 struct ifnet *ifp;
134
135 /*
136 * If route is known or can be allocated now,
137 * our src addr is taken from the i/f, else punt.
138 */
139 ro = &inp->inp_route;
140 if (ro->ro_rt &&
141 satosin(&ro->ro_dst)->sin_addr.s_addr !=
142 sin->sin_addr.s_addr) {
143 RTFREE(ro->ro_rt);
144 ro->ro_rt = (struct rtentry *)0;
145 }
146 if ((ro->ro_rt == (struct rtentry *)0) ||
147 (ifp = ro->ro_rt->rt_ifp) == (struct ifnet *)0) {
148 /* No route yet, so try to acquire one */
149 ro->ro_dst.sa_family = AF_INET;
150 ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
151 sin->sin_addr;
152 rtalloc(ro);
153 if (ro->ro_rt == 0)
154 ifp = (struct ifnet *)0;
155 else
156 ifp = ro->ro_rt->rt_ifp;
157 }
158 if (ifp) {
159 for (ia = in_ifaddr; ia; ia = ia->ia_next)
160 if (ia->ia_ifp == ifp)
161 break;
162 } else
163 ia = (struct in_ifaddr *)0;
164 if (ia == 0)
165 ia = in_ifaddr;
166 if (ia == 0)
167 return (EADDRNOTAVAIL);
168 }
169 ifaddr = (struct sockaddr_in *)&ia->ia_addr;
170 }
171 if (in_pcblookup(inp->inp_head,
172 sin->sin_addr,
173 sin->sin_port,
174 inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
175 inp->inp_lport,
176 0))
177 return (EADDRINUSE);
178 if (inp->inp_laddr.s_addr == INADDR_ANY) {
179 if (inp->inp_lport == 0)
180 in_pcbbind(inp, (struct mbuf *)0);
181 inp->inp_laddr = ifaddr->sin_addr;
182 }
183 inp->inp_faddr = sin->sin_addr;
184 inp->inp_fport = sin->sin_port;
185 return (0);
186}
187
188in_pcbdisconnect(inp)
189 struct inpcb *inp;
190{
191
192 inp->inp_faddr.s_addr = INADDR_ANY;
193 inp->inp_fport = 0;
194 if (inp->inp_socket->so_state & SS_NOFDREF)
195 in_pcbdetach(inp);
196}
197
198in_pcbdetach(inp)
199 struct inpcb *inp;
200{
201 struct socket *so = inp->inp_socket;
202
203 so->so_pcb = 0;
204 sofree(so);
205 if (inp->inp_route.ro_rt)
206 rtfree(inp->inp_route.ro_rt);
207 remque(inp);
208 (void) m_free(dtom(inp));
209}
210
211in_setsockaddr(inp, nam)
212 register struct inpcb *inp;
213 struct mbuf *nam;
214{
215 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
216
217 nam->m_len = sizeof (*sin);
218 sin = mtod(nam, struct sockaddr_in *);
219 bzero((caddr_t)sin, sizeof (*sin));
220 sin->sin_family = AF_INET;
221 sin->sin_port = inp->inp_lport;
222 sin->sin_addr = inp->inp_laddr;
223}
224
225in_setpeeraddr(inp, nam)
226 register struct inpcb *inp;
227 struct mbuf *nam;
228{
229 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
230
231 nam->m_len = sizeof (*sin);
232 sin = mtod(nam, struct sockaddr_in *);
233 bzero((caddr_t)sin, sizeof (*sin));
234 sin->sin_family = AF_INET;
235 sin->sin_port = inp->inp_fport;
236 sin->sin_addr = inp->inp_faddr;
237}
238
239/*
240 * Pass some notification to all connections of a protocol
241 * associated with address dst. Call the
242 * protocol specific routine to handle each connection.
243 */
244in_pcbnotify(head, dst, errno, notify)
245 struct inpcb *head;
246 register struct in_addr *dst;
247 int errno, (*notify)();
248{
249 register struct inpcb *inp, *oinp;
250 int s = splimp();
251
252 for (inp = head->inp_next; inp != head;) {
253 if (inp->inp_faddr.s_addr != dst->s_addr) {
254 next:
255 inp = inp->inp_next;
256 continue;
257 }
258 if (inp->inp_socket == 0)
259 goto next;
260 if (errno)
261 inp->inp_socket->so_error = errno;
262 oinp = inp;
263 inp = inp->inp_next;
264 (*notify)(oinp);
265 }
266 splx(s);
267}
268
269/*
270 * After a routing change, flush old routing
271 * and allocate a (hopefully) better one.
272 */
273in_rtchange(inp)
274 struct inpcb *inp;
275{
276 if (inp->inp_route.ro_rt) {
277 rtfree(inp->inp_route.ro_rt);
278 inp->inp_route.ro_rt = 0;
279 /*
280 * A new route can be allocated the next time
281 * output is attempted.
282 */
283 }
284 /* SHOULD NOTIFY HIGHER-LEVEL PROTOCOLS */
285}
286
287struct inpcb *
288in_pcblookup(head, faddr, fport, laddr, lport, flags)
289 struct inpcb *head;
290 struct in_addr faddr, laddr;
291 u_short fport, lport;
292 int flags;
293{
294 register struct inpcb *inp, *match = 0;
295 int matchwild = 3, wildcard;
296
297 for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
298 if (inp->inp_lport != lport)
299 continue;
300 wildcard = 0;
301 if (inp->inp_laddr.s_addr != INADDR_ANY) {
302 if (laddr.s_addr == INADDR_ANY)
303 wildcard++;
304 else if (inp->inp_laddr.s_addr != laddr.s_addr)
305 continue;
306 } else {
307 if (laddr.s_addr != INADDR_ANY)
308 wildcard++;
309 }
310 if (inp->inp_faddr.s_addr != INADDR_ANY) {
311 if (faddr.s_addr == INADDR_ANY)
312 wildcard++;
313 else if (inp->inp_faddr.s_addr != faddr.s_addr ||
314 inp->inp_fport != fport)
315 continue;
316 } else {
317 if (faddr.s_addr != INADDR_ANY)
318 wildcard++;
319 }
320 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
321 continue;
322 if (wildcard < matchwild) {
323 match = inp;
324 matchwild = wildcard;
325 if (matchwild == 0)
326 break;
327 }
328 }
329 return (match);
330}