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