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