1 /*	udp_usrreq.c	4.45	83/02/16	*/
2 /*
3  * UDP protocol implementation.
4  * Per RFC 768, August, 1980.
5  */
6 
7 #include "../h/param.h"
8 #include "../h/dir.h"
9 #include "../h/user.h"
10 #include "../h/mbuf.h"
11 #include "../h/protosw.h"
12 #include "../h/socket.h"
13 #include "../h/socketvar.h"
14 #include "../h/errno.h"
15 
16 #include "../net/if.h"
17 #include "../net/route.h"
18 
19 #include "../bbnnet/in.h"
20 #include "../bbnnet/in_var.h"
21 #include "../bbnnet/net.h"
22 #include "../bbnnet/in_pcb.h"
23 #include "../bbnnet/ip.h"
24 #include "../bbnnet/udp.h"
25 #include "../bbnnet/fsm.h"
26 #include "../bbnnet/tcp.h"
27 #include "../bbnnet/icmp.h"
28 
29 extern udp_binding_used();
30 extern struct inpcb udp;
31 
32 struct pr_advice udp_advice =
33 {
34     UDP_RESERVED,		/* all basically the same as TCP */
35     UDP_USERRESERVED,
36     UDP_MAXPORT,
37     UDP_USERRESERVED+1,
38     sizeof(u_short),
39     udp_binding_used
40 } ;
41 
42 udp_init()
43 {
44     udp.inp_next = udp.inp_prev = &udp;
45     ipsw[IPPROTO_UDP].ipsw_hlen = sizeof(struct udp);
46 }
47 
48 udp_abort(inp)
49 struct inpcb *inp;
50 {
51     struct socket *so = inp->inp_socket;
52 
53     in_pcbdisconnect(inp, (int(*)())0);
54     soisdisconnected(so);
55 }
56 
57 /*
58  * Is a udp port/address pair already in use?
59  */
60 int udp_binding_used(inp, lport, lsaddr, reuselocal)
61 struct inpcb   *inp;
62 u_short	lport;
63 u_long	lsaddr;
64 {
65     register struct inpcb *i;
66 
67     if (reuselocal)
68 	/*
69 	 * But since UDP, unlike TCP, is not connection oriented,
70 	 * this allows for liars to exist.
71 	 */
72 	return (0);
73 
74     for (i = udp.inp_next; i != &udp; i = i->inp_next)
75     {
76 	if (i != inp)
77 	    if (i->inp_lport == lport)
78 		if ((i->inp_laddr.s_addr == lsaddr) ||
79 		    (i->inp_laddr.s_addr == INADDR_ANY) ||
80 		    (lsaddr == INADDR_ANY))
81 			break;
82     }
83     return (i != &udp);
84 }
85 
86 char *udp_conn_used(inp, lport, lsaddr, fport, fsaddr)
87 struct inpcb   *inp;
88 u_short	lport;
89 u_long	lsaddr;
90 u_short	fport;
91 u_long	fsaddr;
92 {
93     register struct inpcb *i;
94 
95     for (i = udp.inp_next; i != &udp; i = i->inp_next)
96     {
97 	/*
98 	 * Since our inpcb is in this linked list, don't want to know
99 	 * if we, ourselves, are already using this connetion.
100 	 */
101 	if (i != inp)
102 	    if ((i->inp_lport == lport) && (i->inp_fport == fport) &&
103 		(i->inp_laddr.s_addr == lsaddr) &&
104 		(i->inp_faddr.s_addr == fsaddr))
105 		    return((char *) i);
106     }
107 
108     return ((char *) NULL);
109 }
110 
111 
112 /*ARGSUSED*/
113 udp_usrreq(so, req, m, nam, rights)
114 struct socket *so;
115 register int req;
116 struct mbuf *m, *nam, *rights;
117 {
118     register int s;
119     struct inpcb *inp;
120     int error = 0;
121 
122     s = splnet();
123     inp = sotoinpcb(so);
124 
125     if (rights && req != PRU_CONTROL)
126     {
127 	if (rights->m_len)
128 	{
129 	    error = EINVAL;
130 	    goto release;
131 	}
132     }
133 
134     if (inp == NULL && req != PRU_ATTACH)
135     {
136 	error = EINVAL;
137 	goto release;
138     }
139 
140     switch (req)
141     {
142 
143       case PRU_ATTACH:
144 	if (inp != NULL)
145 	{
146 	    error = EINVAL;
147 	    break;
148 	}
149 	error = soreserve(so, 2048, 2048);
150 	if (error)
151 	    break;
152 	error = in_pcballoc(so, &udp);
153 	if (error)
154 	    break;
155 	break;
156 
157       case PRU_DETACH:
158 	if (inp == NULL)
159 	{
160 	    error = ENOTCONN;
161 	    break;
162 	}
163 	in_pcbdetach(inp, (int (*)())0);
164 	break;
165 
166       case PRU_BIND:
167 	error = in_pcbbind(inp, nam, &udp_advice);
168 	break;
169 
170       case PRU_LISTEN:
171 	error = EOPNOTSUPP;
172 	break;
173 
174       case PRU_CONNECT:
175 	if (inp->inp_faddr.s_addr != INADDR_ANY)
176 	{
177 	    error = EISCONN;
178 	    break;
179 	}
180 	if (inp->inp_lport == 0)
181 	{
182 	    error = in_pcbbind(inp, (struct mbuf *)0, &udp_advice);
183 	    if (error)
184 		break;
185 	}
186 	error = in_pcbconnect(inp, nam, udp_conn_used);
187 	if (error == 0)
188 	    soisconnected(so);
189 	break;
190 
191       case PRU_ACCEPT:
192 	error = EOPNOTSUPP;
193 	break;
194 
195       case PRU_DISCONNECT:
196 	if (inp->inp_faddr.s_addr == INADDR_ANY)
197 	{
198 	    error = ENOTCONN;
199 	    break;
200 	}
201 	in_pcbdisconnect(inp, (int(*)())0);
202 	soisdisconnected(so);
203 	break;
204 
205       case PRU_SHUTDOWN:
206 	socantsendmore(so);
207 	break;
208 
209       case PRU_SEND:
210 	{
211 	    struct in_addr laddr;
212 
213 	    if (nam)
214 	    {
215 		laddr = inp->inp_laddr;
216 		if (inp->inp_faddr.s_addr != INADDR_ANY)
217 		{
218 		    error = EISCONN;
219 		    break;
220 		}
221 		if (inp->inp_lport == 0)
222 		{
223 		    if (error = in_pcbbind(inp, (struct mbuf *)0, &udp_advice))
224 			break;
225 		}
226 		error = in_pcbconnect(inp, nam, udp_conn_used);
227 		if (error)
228 		    break;
229 	    }
230 	    else
231 	    {
232 		if (inp->inp_faddr.s_addr == INADDR_ANY)
233 		{
234 		    error = ENOTCONN;
235 		    break;
236 		}
237 	    }
238 	    error = udp_output(inp, m);
239 	    m = NULL;
240 	    if (nam)
241 	    {
242 		in_pcbdisconnect(inp, (int(*))0);
243 		inp->inp_laddr = laddr;
244 	    }
245 	}
246 	break;
247 
248       case PRU_ABORT:
249 	in_pcbdetach(inp, (int (*)())0);
250 	break;
251 
252       case PRU_CONTROL:
253 	/* not our ioctl, let lower level try ioctl */
254 	error = ip_ioctl (inp, (int) m, (caddr_t) nam);
255 	goto dontfree;
256 
257       case PRU_SOCKADDR:
258 	in_setsockaddr(inp, nam);
259 	break;
260 
261       default:
262 	panic("udp_usrreq");
263     }
264 
265 release :
266     if (m != NULL)
267 	m_freem(m);
268 dontfree:
269     splx(s);
270     return (error);
271 }
272 
273 udp_ctlinput (prc_code, arg)
274 caddr_t arg;
275 {
276     int error;
277 
278     error = inetctlerrmap[prc_code];
279 
280     switch (prc_code)
281     {
282 	case PRC_UNREACH_PROTOCOL:	/* icmp message */
283 	case PRC_UNREACH_PORT:
284 	case PRC_MSGSIZE:
285 	    {
286 	    register struct udp	*up;
287 	    struct inpcb *inp;
288 
289 	    up = (struct udp *) (&((struct icmp *) arg)->ic_iphdr);
290 	    inp = (struct inpcb *)udp_conn_used ((struct inpcb *) 0,
291 		up->u_src, up->u_s.s_addr,
292 		up->u_dst, up->u_d.s_addr);
293 
294 	    if (inp)
295 	    {
296 		inp->inp_socket->so_error = error;
297 		udp_abort(inp);
298 	    }
299 	    }
300 	    break;
301 
302 	case PRC_UNREACH_NET:
303 	case PRC_UNREACH_HOST:
304 	    {
305 	    register struct udp	*up;
306 	    struct inpcb *inp;
307 
308 	    up = (struct udp *) (&((struct icmp *) arg)->ic_iphdr);
309 	    inp = (struct inpcb *)udp_conn_used ((struct inpcb *) 0,
310 		up->u_src, up->u_s.s_addr,
311 		up->u_dst, up->u_d.s_addr);
312 
313 	    if (inp)
314 	    {
315 		struct socket *so;
316 
317 		so = inp->inp_socket;
318 		if ((so->so_state & SS_NOFDREF) == 0)
319 		    advise_user(so, error);
320 		else
321 		{
322 		    so->so_error = error;
323 		    udp_abort(inp);
324 		}
325 	    }
326 	    }
327 	    break;
328 
329 	case PRC_GWDOWN:
330 	    in_gdown (&udp, (u_long) arg);
331 	    break;
332 
333 	case PRC_REDIRECT_NET:	/* icmp message */
334 	case PRC_REDIRECT_HOST:
335 	    {
336 	    register struct udp	*up;
337 	    struct inpcb *inp;
338 
339 	    up = (struct udp *) (&((struct icmp *) arg)->ic_iphdr);
340 	    inp = (struct inpcb *)udp_conn_used ((struct inpcb *) 0,
341 		up->u_src, up->u_s.s_addr,
342 		up->u_dst, up->u_d.s_addr);
343 
344 	    if (inp)
345 		icmp_redirect_inp(inp, (struct icmp *) arg,
346 		 prc_code == PRC_REDIRECT_NET ? rtnet : rthost);
347 	    }
348 	    break;
349 
350 	case PRC_TIMXCEED_INTRANS:	/* icmp message */
351 	case PRC_TIMXCEED_REASS:
352 	case PRC_PARAMPROB:
353 	case PRC_QUENCH:
354 	    break;
355 
356 	case PRC_IFDOWN:
357 	    {
358 	    u_long addr;
359 
360 	    addr = ((struct sockaddr_in *)(arg))->sin_addr.s_addr;
361 	    inpcb_notify(&udp, addr, (u_long) 0, error);
362 	    inpcb_notify(&udp, (u_long) 0, addr, error);
363 	    }
364 	    break;
365 
366 	case PRC_HOSTDEAD:	/* from imp interface */
367 	case PRC_HOSTUNREACH:
368 	    /*
369 	     * get same message for destination hosts and gateways.
370 	     */
371 	    {
372 	    u_long addr;
373 
374 	    addr = ((struct sockaddr_in *)arg)->sin_addr.s_addr;
375 	    in_gdown (&udp, addr);
376 	    inpcb_notify(&udp, (u_long) 0, addr, error);
377 	    }
378 	    break;
379 
380 	default:
381 	    panic("udp_ctlinput");
382     }
383 }
384