xref: /original-bsd/sys/netccitt/pk_usrreq.c (revision 9052d23a)
1 /*
2  * Copyright (c) University of British Columbia, 1984
3  * Copyright (c) 1990 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Laboratory for Computation Vision and the Computer Science Department
8  * of the University of British Columbia.
9  *
10  * %sccs.include.redist.c%
11  *
12  *	@(#)pk_usrreq.c	7.7 (Berkeley) 08/30/90
13  */
14 
15 #include "param.h"
16 #include "systm.h"
17 #include "mbuf.h"
18 #include "socket.h"
19 #include "protosw.h"
20 #include "socketvar.h"
21 #include "errno.h"
22 #include "ioctl.h"
23 #include "user.h"
24 #include "stat.h"
25 
26 #include "../net/if.h"
27 
28 #include "x25.h"
29 #include "pk.h"
30 #include "pk_var.h"
31 
32 struct	x25_packet *pk_template ();
33 
34 /*
35  *
36  *  X.25 Packet level protocol interface to socket abstraction.
37  *
38  *  Process an X.25 user request on a logical channel.  If this is a send
39  *  request then m is the mbuf chain of the send data. If this is a timer
40  *  expiration (called from the software clock routine) them timertype is
41  *  the particular timer.
42  *
43  */
44 
45 pk_usrreq (so, req, m, nam, control)
46 struct socket *so;
47 int req;
48 register struct mbuf *m, *nam;
49 struct mbuf *control;
50 {
51 	register struct pklcd *lcp = (struct pklcd *) so -> so_pcb;
52 	register struct x25_packet *xp;
53 	register int error = 0;
54 
55 	if (req == PRU_CONTROL)
56 		return (pk_control(so, (int)m, (caddr_t)nam,
57 			(struct ifnet *)control));
58 	if (control && control->m_len) {
59 		error = EINVAL;
60 		goto release;
61 	}
62 	if (lcp == NULL && req != PRU_ATTACH) {
63 		error = EINVAL;
64 		goto release;
65 	}
66 
67 /*
68 	pk_trace (pkcbhead, TR_USER, (struct pklcd *)0,
69 		req, (struct x25_packet *)0);
70 */
71 
72 	switch (req) {
73 	/*
74 	 *  X.25 attaches to socket via PRU_ATTACH and allocates a logical
75 	 *  channel descriptor.  If the socket is to  receive connections,
76 	 *  then the LISTEN state is entered.
77 	 */
78 	case PRU_ATTACH:
79 		if (lcp) {
80 			error = EISCONN;
81 			/* Socket already connected. */
82 			break;
83 		}
84 		lcp = pk_attach (so);
85 		if (lcp == 0)
86 			error = ENOBUFS;
87 		break;
88 
89 	/*
90 	 *  Detach a logical channel from the socket. If the state of the
91 	 *  channel is embryonic, simply discard it. Otherwise we have to
92 	 *  initiate a PRU_DISCONNECT which will finish later.
93 	 */
94 	case PRU_DETACH:
95 		pk_disconnect (lcp);
96 		break;
97 
98 	/*
99 	 *  Give the socket an address.
100 	 */
101 	case PRU_BIND:
102 		if (nam -> m_len == sizeof (struct x25_sockaddr))
103 			old_to_new (nam);
104 		error = pk_bind (lcp, nam);
105 		break;
106 
107 	/*
108 	 *  Prepare to accept connections.
109 	 */
110 	case PRU_LISTEN:
111 		if (lcp -> lcd_ceaddr == 0) {
112 			error = EDESTADDRREQ;
113 			break;
114 		}
115 		lcp -> lcd_state = LISTEN;
116 		lcp -> lcd_listen = pk_listenhead;
117 		pk_listenhead = lcp;
118 		break;
119 
120 	/*
121 	 *  Initiate a CALL REQUEST to peer entity. Enter state SENT_CALL
122 	 *  and mark the socket as connecting. Set timer waiting for
123 	 *  CALL ACCEPT or CLEAR.
124 	 */
125 	case PRU_CONNECT:
126 		if (nam -> m_len == sizeof (struct x25_sockaddr))
127 			old_to_new (nam);
128 		error = pk_connect (lcp, nam, (struct sockaddr_25 *)0);
129 		break;
130 
131 	/*
132 	 *  Initiate a disconnect to peer entity via a CLEAR REQUEST packet.
133 	 *  The socket will be disconnected when we receive a confirmation
134 	 *  or a clear collision.
135 	 */
136 	case PRU_DISCONNECT:
137 		pk_disconnect (lcp);
138 		break;
139 
140 	/*
141 	 *  Accept an INCOMING CALL. Most of the work has already been done
142 	 *  by pk_input. Just return the callers address to the user.
143 	 */
144 	case PRU_ACCEPT:
145 		if (lcp -> lcd_craddr == NULL)
146 			break;
147 		bcopy ((caddr_t)lcp -> lcd_craddr, mtod (nam, caddr_t),
148 			sizeof (struct sockaddr_x25));
149 		nam -> m_len = sizeof (struct sockaddr_x25);
150 		if (lcp -> lcd_flags & X25_OLDSOCKADDR)
151 			new_to_old (nam);
152 		break;
153 
154 	/*
155 	 *  After a receive, we should send a RR.
156 	 */
157 	case PRU_RCVD:
158 		lcp -> lcd_rxcnt++;
159 		lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RR);
160 		pk_output (lcp);
161 		break;
162 
163 	/*
164 	 *  Do send by placing data on the socket output queue.
165 	 *  SHOULD WE USE m_cat HERE.
166 	 */
167 	case PRU_SEND:
168 		error = pk_send (lcp, m);
169 		break;
170 
171 	/*
172 	 *  Abort a virtual circuit. For example all completed calls
173 	 *  waiting acceptance.
174 	 */
175 	case PRU_ABORT:
176 		pk_disconnect (lcp);
177 		break;
178 
179 	/* Begin unimplemented hooks. */
180 
181 	case PRU_SHUTDOWN:
182 		error = EOPNOTSUPP;
183 		break;
184 
185 	case PRU_CONTROL:
186 		error = EOPNOTSUPP;
187 		break;
188 
189 	case PRU_SENSE:
190 #ifdef BSD4_3
191 		((struct stat *)m) -> st_blksize = so -> so_snd.sb_hiwat;
192 #else
193 		error = EOPNOTSUPP;
194 #endif
195 		break;
196 
197 	/* End unimplemented hooks. */
198 
199 	case PRU_SOCKADDR:
200 		if (lcp -> lcd_ceaddr == 0)
201 			return (EADDRNOTAVAIL);
202 		nam -> m_len = sizeof (struct sockaddr_x25);
203 		bcopy ((caddr_t)lcp -> lcd_ceaddr, mtod (nam, caddr_t),
204 			sizeof (struct sockaddr_x25));
205 		if (lcp -> lcd_flags & X25_OLDSOCKADDR)
206 			new_to_old (nam);
207 		break;
208 
209 	case PRU_PEERADDR:
210 		if (lcp -> lcd_state != DATA_TRANSFER)
211 			return (ENOTCONN);
212 		nam -> m_len = sizeof (struct sockaddr_x25);
213 		bcopy (lcp -> lcd_craddr ? (caddr_t)lcp -> lcd_craddr :
214 			(caddr_t)lcp -> lcd_ceaddr,
215 			mtod (nam, caddr_t), sizeof (struct sockaddr_x25));
216 		if (lcp -> lcd_flags & X25_OLDSOCKADDR)
217 			new_to_old (nam);
218 		break;
219 
220 	/*
221 	 *  Receive INTERRUPT packet.
222 	 */
223 	case PRU_RCVOOB:
224 		m -> m_len = 1;
225 		*mtod (m, char *) = lcp -> lcd_intrdata;
226 		break;
227 
228 	/*
229 	 *  Send INTERRUPT packet.
230 	 */
231 	case PRU_SENDOOB:
232 		m_freem (m);
233 		if (lcp -> lcd_intrconf_pending) {
234 			error = ETOOMANYREFS;
235 			break;
236 		}
237 		lcp -> lcd_intrcnt++;
238 		xp = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_INTERRUPT);
239 		xp -> packet_data = 0;
240 		(dtom (xp)) -> m_len++;
241 		pk_output (lcp);
242 		break;
243 
244 	default:
245 		panic ("pk_usrreq");
246 	}
247 release:
248 	if (control != NULL)
249 		m_freem(control);
250 	if (m != NULL)
251 		m_freem(m);
252 	return (error);
253 }
254 
255 /*
256  * If you want to use UBC X.25 level 3 in conjunction with some
257  * other X.25 level 2 driver, have the ifp->if_ioctl routine
258  * assign pk_start to pkp -> pk_start when called with SIOCSIFCONF_X25.
259  */
260 /* ARGSUSED */
261 pk_start (lcp)
262 register struct pklcd *lcp;
263 {
264 	extern int pk_send();
265 
266 	lcp -> lcd_send = pk_send;
267 	return (pk_output(lcp));
268 }
269 
270 /*ARGSUSED*/
271 pk_control (so, cmd, data, ifp)
272 struct socket *so;
273 int cmd;
274 caddr_t data;
275 register struct ifnet *ifp;
276 {
277 	register struct ifreq_x25 *ifr = (struct ifreq_x25 *)data;
278 	register struct ifaddr *ifa = 0;
279 	register struct x25_ifaddr *ia = 0;
280 	struct pklcd *dev_lcp = 0;
281 	int error, s;
282 	unsigned n;
283 
284 	/*
285 	 * Find address for this interface, if it exists.
286 	 */
287 	if (ifp)
288 		for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
289 			if (ifa->ifa_addr->sa_family == AF_CCITT)
290 				break;
291 
292 	ia = (struct x25_ifaddr *)ifa;
293 	switch (cmd) {
294 	case SIOCGIFCONF_X25:
295 		if (ifa == 0)
296 			return (EADDRNOTAVAIL);
297 		ifr->ifr_xc = ia->ia_xc;
298 		return (0);
299 
300 	case SIOCSIFCONF_X25:
301 		if (error = suser(u.u_cred, &u.u_acflag))
302 			return (error);
303 		if (ifp == 0)
304 			panic("pk_control");
305 		if (ifa == (struct ifaddr *)0) {
306 			register struct mbuf *m;
307 
308 			MALLOC(ia, struct x25_ifaddr *, sizeof (*ia),
309 				M_IFADDR, M_WAITOK);
310 			if (ia == 0)
311 				return (ENOBUFS);
312 			if (ifa = ifp->if_addrlist) {
313 				for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
314 					;
315 				ifa->ifa_next = &ia->ia_ifa;
316 			} else
317 				ifp->if_addrlist = &ia->ia_ifa;
318 			ifa = &ia->ia_ifa;
319 			ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask;
320 			ifa->ifa_addr = (struct sockaddr *)&ia->ia_xc.xc_addr;
321 			ia->ia_ifp = ifp;
322 			ia->ia_pkcb.pk_ia = ia;
323 			ia->ia_pkcb.pk_next = pkcbhead;
324 			pkcbhead = &ia->ia_pkcb;
325 		}
326 		ia->ia_xcp = &(ifr->ifr_xc);
327 		if (ia->ia_chan && (ia->ia_maxlcn != ia->ia_xcp->xc_maxlcn)) {
328 			pk_restart(&ia->ia_pkcb, X25_RESTART_NETWORK_CONGESTION);
329 			dev_lcp = ia->ia_chan[0];
330 			free((caddr_t)ia->ia_chan, M_IFADDR);
331 			ia->ia_chan = 0;
332 		}
333 		if (ia->ia_chan == 0) {
334 			n = ia->ia_maxlcn * sizeof(struct pklcd *);
335 			ia->ia_chan = (struct pklcd **) malloc(n, M_IFADDR);
336 			if (ia->ia_chan) {
337 				bzero((caddr_t)ia->ia_chan, n);
338 				if (dev_lcp == 0)
339 					dev_lcp = pk_attach((struct socket *)0);
340 				ia->ia_chan[0] = dev_lcp;
341 			} else {
342 				if (dev_lcp)
343 					pk_close(dev_lcp);
344 				ia->ia_xcp = &ia->ia_xc;
345 				return (ENOBUFS);
346 			}
347 		}
348 		/*
349 		 * Give the interface a chance to initialize if this
350 		 * is its first address, and to validate the address.
351 		 */
352 		s = splimp();
353 		if (ifp->if_ioctl)
354 			error = (*ifp->if_ioctl)(ifp, SIOCSIFCONF_X25, ifa);
355 		splx(s);
356 		if (error == 0) {
357 			ia->ia_xc = *ia->ia_xcp;
358 		}
359 		ia->ia_xcp = &ia->ia_xc;
360 		return (error);
361 
362 	default:
363 		if (ifp == 0 || ifp->if_ioctl == 0)
364 			return (EOPNOTSUPP);
365 		return ((*ifp->if_ioctl)(ifp, cmd, data));
366 	}
367 }
368 
369 pk_ctloutput(cmd, so, level, optname, mp)
370 struct socket *so;
371 struct mbuf **mp;
372 int cmd, level, optname;
373 {
374 	register struct mbuf *m = *mp;
375 	int error;
376 
377 	if (cmd == PRCO_SETOPT) switch (optname) {
378 	case PK_ACCTFILE:
379 		if (m == 0)
380 			return (EINVAL);
381 		if (m->m_len)
382 			error = pk_accton(mtod(m, char *));
383 		else
384 			error = pk_accton((char *)0);
385 		(void) m_freem(m);
386 		*mp = 0;
387 		return (error);
388 	}
389 	if (*mp) {
390 		(void) m_freem(*mp);
391 		*mp = 0;
392 	}
393 	return (EOPNOTSUPP);
394 
395 }
396 
397 /*
398  * Do an in-place conversion of an "old style"
399  * socket address to the new style
400  */
401 
402 static
403 old_to_new (m)
404 register struct mbuf *m;
405 {
406 	register struct x25_sockaddr *oldp;
407 	register struct sockaddr_x25 *newp;
408 	register char *ocp, *ncp;
409 	struct sockaddr_x25 new;
410 
411 	oldp = mtod (m, struct x25_sockaddr *);
412 	newp = &new;
413 	bzero ((caddr_t)newp, sizeof (*newp));
414 
415 	newp -> x25_family = AF_CCITT;
416 	newp->x25_opts.op_flags = (oldp->xaddr_facilities & X25_REVERSE_CHARGE)
417 		| X25_MQBIT | X25_OLDSOCKADDR;
418 	if (oldp -> xaddr_facilities & XS_HIPRIO)	/* Datapac specific */
419 		newp -> x25_opts.op_psize = X25_PS128;
420 	bcopy ((caddr_t)oldp -> xaddr_addr, newp -> x25_addr,
421 		(unsigned)min (oldp -> xaddr_len, sizeof (newp -> x25_addr) - 1));
422 	bcopy ((caddr_t)oldp -> xaddr_proto, newp -> x25_udata, 4);
423 	newp -> x25_udlen = 4;
424 
425 	ocp = (caddr_t)oldp -> xaddr_userdata;
426 	ncp = newp -> x25_udata + 4;
427 	while (*ocp && ocp < (caddr_t)oldp -> xaddr_userdata + 12) {
428 		*ncp++ = *ocp++;
429 		newp -> x25_udlen++;
430 	}
431 
432 	bcopy ((caddr_t)newp, mtod (m, char *), sizeof (*newp));
433 	m->m_len = sizeof (*newp);
434 }
435 
436 /*
437  * Do an in-place conversion of a new style
438  * socket address to the old style
439  */
440 
441 static
442 new_to_old (m)
443 register struct mbuf *m;
444 {
445 	register struct x25_sockaddr *oldp;
446 	register struct sockaddr_x25 *newp;
447 	register char *ocp, *ncp;
448 	struct x25_sockaddr old;
449 
450 	oldp = &old;
451 	newp = mtod (m, struct sockaddr_x25 *);
452 	bzero ((caddr_t)oldp, sizeof (*oldp));
453 
454 	oldp -> xaddr_facilities = newp -> x25_opts.op_flags & X25_REVERSE_CHARGE;
455 	if (newp -> x25_opts.op_psize == X25_PS128)
456 		oldp -> xaddr_facilities |= XS_HIPRIO;	/* Datapac specific */
457 	ocp = (char *)oldp -> xaddr_addr;
458 	ncp = newp -> x25_addr;
459 	while (*ncp) {
460 		*ocp++ = *ncp++;
461 		oldp -> xaddr_len++;
462 	}
463 
464 	bcopy (newp -> x25_udata, (caddr_t)oldp -> xaddr_proto, 4);
465 	bcopy (newp -> x25_udata + 4, (caddr_t)oldp -> xaddr_userdata,
466 		(unsigned)(newp -> x25_udlen - 4));
467 
468 	bcopy ((caddr_t)oldp, mtod (m, char *), sizeof (*oldp));
469 	m -> m_len = sizeof (*oldp);
470 }
471 
472 pk_send (lcp, m)
473 register struct pklcd *lcp;
474 register struct mbuf *m;
475 {
476 	register struct x25_packet *xp;
477 	register struct mbuf *m0;
478 	register int len;
479 
480 	m0 = dtom ((xp = pk_template (lcp -> lcd_lcn, X25_DATA)));
481 	m0 -> m_next = m;
482 	/*
483 	 * Application has elected (at call setup time) to prepend
484 	 * a control byte to each packet written indicating m-bit
485 	 * and q-bit status.  Examine and then discard this byte.
486 	 */
487 	if (lcp -> lcd_flags & X25_MQBIT) {
488 		register octet *cp;
489 
490 		if (m -> m_len < 1) {
491 			m_freem (m0);
492 			return (EMSGSIZE);
493 		}
494 		cp = mtod (m, octet *);
495 		if (*cp & 0x80)					/* XXX */
496 			xp -> q_bit = 1;
497 		xp -> packet_type |= (*cp & 0x40) >> 2;		/* XXX */
498 		m -> m_len--;
499 		m -> m_data++;
500 	}
501 	len = m -> m_len;
502 	while (m -> m_next) {
503 		m = m -> m_next;
504 		len += m -> m_len;
505 	}
506 	if (len > (1 << lcp -> lcd_packetsize)) {
507 		m_freem (m0);
508 		return (EMSGSIZE);
509 	}
510 	if (lcp -> lcd_so)
511 		sbappendrecord (&lcp -> lcd_so -> so_snd, m0);
512 	else
513 		sbappendrecord (&lcp -> lcd_sb, m0);
514 	lcp -> lcd_template = 0;
515 	lcp -> lcd_txcnt++;
516 	pk_output (lcp);
517 	return (0);
518 }
519