xref: /original-bsd/sys/netccitt/pk_usrreq.c (revision b5ed8b48)
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.16 (Berkeley) 06/27/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 "stat.h"
24 
25 #include "../net/if.h"
26 #include "../net/route.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 		pk_flowcontrol (lcp, /*sbspace (&so -> so_rcv) <= */ 0, 1);
152 		break;
153 
154 	/*
155 	 *  Send INTERRUPT packet.
156 	 */
157 	case PRU_SENDOOB:
158 		if (m == 0) {
159 			MGETHDR(m, M_WAITOK, MT_OOBDATA);
160 			m -> m_pkthdr.len = m -> m_len = 1;
161 			*mtod (m, octet *) = 0;
162 		}
163 		if (m -> m_pkthdr.len > 32) {
164 			m_freem (m);
165 			error = EMSGSIZE;
166 			break;
167 		}
168 		MCHTYPE(m, MT_OOBDATA);
169 		/* FALLTHROUGH */
170 
171 	/*
172 	 *  Do send by placing data on the socket output queue.
173 	 */
174 	case PRU_SEND:
175 		if (control) {
176 			register struct cmsghdr *ch = mtod (m, struct cmsghdr *);
177 			control -> m_len -= sizeof (*ch);
178 			control -> m_data += sizeof (*ch);
179 			error = pk_ctloutput (PRCO_SETOPT, so, ch -> cmsg_level,
180 					ch -> cmsg_type, &control);
181 		}
182 		if (error == 0 && m)
183 			error = pk_send (lcp, m);
184 		break;
185 
186 	/*
187 	 *  Abort a virtual circuit. For example all completed calls
188 	 *  waiting acceptance.
189 	 */
190 	case PRU_ABORT:
191 		pk_disconnect (lcp);
192 		break;
193 
194 	/* Begin unimplemented hooks. */
195 
196 	case PRU_SHUTDOWN:
197 		error = EOPNOTSUPP;
198 		break;
199 
200 	case PRU_CONTROL:
201 		error = EOPNOTSUPP;
202 		break;
203 
204 	case PRU_SENSE:
205 #ifdef BSD4_3
206 		((struct stat *)m) -> st_blksize = so -> so_snd.sb_hiwat;
207 #else
208 		error = EOPNOTSUPP;
209 #endif
210 		break;
211 
212 	/* End unimplemented hooks. */
213 
214 	case PRU_SOCKADDR:
215 		if (lcp -> lcd_ceaddr == 0)
216 			return (EADDRNOTAVAIL);
217 		nam -> m_len = sizeof (struct sockaddr_x25);
218 		bcopy ((caddr_t)lcp -> lcd_ceaddr, mtod (nam, caddr_t),
219 			sizeof (struct sockaddr_x25));
220 		if (lcp -> lcd_flags & X25_OLDSOCKADDR)
221 			new_to_old (nam);
222 		break;
223 
224 	case PRU_PEERADDR:
225 		if (lcp -> lcd_state != DATA_TRANSFER)
226 			return (ENOTCONN);
227 		nam -> m_len = sizeof (struct sockaddr_x25);
228 		bcopy (lcp -> lcd_craddr ? (caddr_t)lcp -> lcd_craddr :
229 			(caddr_t)lcp -> lcd_ceaddr,
230 			mtod (nam, caddr_t), sizeof (struct sockaddr_x25));
231 		if (lcp -> lcd_flags & X25_OLDSOCKADDR)
232 			new_to_old (nam);
233 		break;
234 
235 	/*
236 	 *  Receive INTERRUPT packet.
237 	 */
238 	case PRU_RCVOOB:
239 		if (so -> so_options & SO_OOBINLINE) {
240 			register struct mbuf *n  = so -> so_rcv.sb_mb;
241 			if (n && n -> m_type == MT_OOBDATA) {
242 				unsigned len =  n -> m_pkthdr.len;
243 				so -> so_rcv.sb_mb = n -> m_nextpkt;
244 				if (len !=  n -> m_len &&
245 				    (n = m_pullup (n, len)) == 0)
246 					break;
247 				m -> m_len = len;
248 				bcopy (mtod (m, caddr_t), mtod (n, caddr_t), len);
249 				m_freem (n);
250 			}
251 			break;
252 		}
253 		m -> m_len = 1;
254 		*mtod (m, char *) = lcp -> lcd_intrdata;
255 		break;
256 
257 	default:
258 		panic ("pk_usrreq");
259 	}
260 release:
261 	if (control != NULL)
262 		m_freem (control);
263 	return (error);
264 }
265 
266 /*
267  * If you want to use UBC X.25 level 3 in conjunction with some
268  * other X.25 level 2 driver, have the ifp -> if_ioctl routine
269  * assign pk_start to ia -> ia_start when called with SIOCSIFCONF_X25.
270  */
271 /* ARGSUSED */
272 pk_start (lcp)
273 register struct pklcd *lcp;
274 {
275 	pk_output (lcp);
276 	return (0); /* XXX pk_output should return a value */
277 }
278 
279 #ifndef _offsetof
280 #define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
281 #endif
282 struct sockaddr_x25 pk_sockmask = {
283 _offsetof(struct sockaddr_x25, x25_addr[0]),
284 0, -1};
285 
286 /*ARGSUSED*/
287 pk_control (so, cmd, data, ifp)
288 struct socket *so;
289 int cmd;
290 caddr_t data;
291 register struct ifnet *ifp;
292 {
293 	register struct ifreq_x25 *ifr = (struct ifreq_x25 *)data;
294 	register struct ifaddr *ifa = 0;
295 	register struct x25_ifaddr *ia = 0;
296 	struct pklcd *dev_lcp = 0;
297 	int error, s, old_maxlcn;
298 	unsigned n;
299 
300 	/*
301 	 * Find address for this interface, if it exists.
302 	 */
303 	if (ifp)
304 		for (ifa = ifp -> if_addrlist; ifa; ifa = ifa -> ifa_next)
305 			if (ifa -> ifa_addr -> sa_family == AF_CCITT)
306 				break;
307 
308 	ia = (struct x25_ifaddr *)ifa;
309 	switch (cmd) {
310 	case SIOCGIFCONF_X25:
311 		if (ifa == 0)
312 			return (EADDRNOTAVAIL);
313 		ifr -> ifr_xc = ia -> ia_xc;
314 		return (0);
315 
316 	case SIOCSIFCONF_X25:
317 		if ((so->so_state & SS_PRIV) == 0)
318 			return (EPERM);
319 		if (ifp == 0)
320 			panic ("pk_control");
321 		if (ifa == (struct ifaddr *)0) {
322 			register struct mbuf *m;
323 
324 			MALLOC(ia, struct x25_ifaddr *, sizeof (*ia),
325 				M_IFADDR, M_WAITOK);
326 			if (ia == 0)
327 				return (ENOBUFS);
328 			bzero ((caddr_t)ia, sizeof (*ia));
329 			if (ifa = ifp -> if_addrlist) {
330 				for ( ; ifa -> ifa_next; ifa = ifa -> ifa_next)
331 					;
332 				ifa -> ifa_next = &ia -> ia_ifa;
333 			} else
334 				ifp -> if_addrlist = &ia -> ia_ifa;
335 			ifa = &ia -> ia_ifa;
336 			ifa -> ifa_netmask = (struct sockaddr *)&pk_sockmask;
337 			ifa -> ifa_addr = (struct sockaddr *)&ia -> ia_xc.xc_addr;
338 			ifa -> ifa_dstaddr = (struct sockaddr *)&ia -> ia_dstaddr; /* XXX */
339 			ia -> ia_ifp = ifp;
340 			ia -> ia_dstaddr.x25_family = AF_CCITT;
341 			ia -> ia_dstaddr.x25_len = pk_sockmask.x25_len;
342 		} else {
343 			rtinit (ifa, (int)RTM_DELETE, 0);
344 		}
345 		old_maxlcn = ia -> ia_maxlcn;
346 		ia -> ia_xc = ifr -> ifr_xc;
347 		ia -> ia_dstaddr.x25_net = ia -> ia_xc.xc_addr.x25_net;
348 		if (ia -> ia_maxlcn != old_maxlcn && old_maxlcn != 0) {
349 			/* VERY messy XXX */
350 			register struct pkcb *pkp;
351 			for (pkp = pkcbhead; pkp; pkp = pkp -> pk_next)
352 				if (pkp -> pk_ia == ia)
353 					pk_resize (pkp);
354 		}
355 		/*
356 		 * Give the interface a chance to initialize if this
357 		 * is its first address, and to validate the address.
358 		 */
359 		ia -> ia_start = pk_start;
360 		s = splimp();
361 		if (ifp -> if_ioctl)
362 			error = (*ifp -> if_ioctl)(ifp, SIOCSIFCONF_X25, ifa);
363 		if (error)
364 			ifp -> if_flags &= ~IFF_UP;
365 		else
366 			error = rtinit (ifa, (int)RTM_ADD, RTF_UP);
367 		splx (s);
368 		return (error);
369 
370 	default:
371 		if (ifp == 0 || ifp -> if_ioctl == 0)
372 			return (EOPNOTSUPP);
373 		return ((*ifp -> if_ioctl)(ifp, cmd, data));
374 	}
375 }
376 
377 pk_ctloutput (cmd, so, level, optname, mp)
378 struct socket *so;
379 struct mbuf **mp;
380 int cmd, level, optname;
381 {
382 	register struct mbuf *m = *mp;
383 	register struct pklcd *lcp = (struct pklcd *) so -> so_pcb;
384 	int error = EOPNOTSUPP;
385 
386 	if (m == 0)
387 		return (EINVAL);
388 	if (cmd == PRCO_SETOPT) switch (optname) {
389 	case PK_FACILITIES:
390 		if (m == 0)
391 			return (EINVAL);
392 		lcp -> lcd_facilities = m;
393 		*mp = 0;
394 		return (0);
395 
396 	case PK_ACCTFILE:
397 		if ((so->so_state & SS_PRIV) == 0)
398 			error = EPERM;
399 		else if (m -> m_len)
400 			error = pk_accton (mtod (m, char *));
401 		else
402 			error = pk_accton ((char *)0);
403 		break;
404 
405 	case PK_RTATTACH:
406 		error = pk_rtattach (so, m);
407 		break;
408 
409 	case PK_PRLISTEN:
410 		error = pk_user_protolisten (mtod (m, u_char *));
411 	}
412 	if (*mp) {
413 		(void) m_freem (*mp);
414 		*mp = 0;
415 	}
416 	return (error);
417 
418 }
419 
420 
421 /*
422  * Do an in-place conversion of an "old style"
423  * socket address to the new style
424  */
425 
426 static
427 old_to_new (m)
428 register struct mbuf *m;
429 {
430 	register struct x25_sockaddr *oldp;
431 	register struct sockaddr_x25 *newp;
432 	register char *ocp, *ncp;
433 	struct sockaddr_x25 new;
434 
435 	oldp = mtod (m, struct x25_sockaddr *);
436 	newp = &new;
437 	bzero ((caddr_t)newp, sizeof (*newp));
438 
439 	newp -> x25_family = AF_CCITT;
440 	newp -> x25_len = sizeof(*newp);
441 	newp -> x25_opts.op_flags = (oldp -> xaddr_facilities & X25_REVERSE_CHARGE)
442 		| X25_MQBIT | X25_OLDSOCKADDR;
443 	if (oldp -> xaddr_facilities & XS_HIPRIO)	/* Datapac specific */
444 		newp -> x25_opts.op_psize = X25_PS128;
445 	bcopy ((caddr_t)oldp -> xaddr_addr, newp -> x25_addr,
446 	       (unsigned)min (oldp -> xaddr_len, sizeof (newp -> x25_addr) - 1));
447 	if (bcmp ((caddr_t)oldp -> xaddr_proto, newp -> x25_udata, 4) != 0) {
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 		if (newp -> x25_udlen == 0)
455 			newp -> x25_udlen = 4;
456 		*ncp++ = *ocp++;
457 		newp -> x25_udlen++;
458 	}
459 	bcopy ((caddr_t)newp, mtod (m, char *), sizeof (*newp));
460 	m -> m_len = sizeof (*newp);
461 }
462 
463 /*
464  * Do an in-place conversion of a new style
465  * socket address to the old style
466  */
467 
468 static
469 new_to_old (m)
470 register struct mbuf *m;
471 {
472 	register struct x25_sockaddr *oldp;
473 	register struct sockaddr_x25 *newp;
474 	register char *ocp, *ncp;
475 	struct x25_sockaddr old;
476 
477 	oldp = &old;
478 	newp = mtod (m, struct sockaddr_x25 *);
479 	bzero ((caddr_t)oldp, sizeof (*oldp));
480 
481 	oldp -> xaddr_facilities = newp -> x25_opts.op_flags & X25_REVERSE_CHARGE;
482 	if (newp -> x25_opts.op_psize == X25_PS128)
483 		oldp -> xaddr_facilities |= XS_HIPRIO;	/* Datapac specific */
484 	ocp = (char *)oldp -> xaddr_addr;
485 	ncp = newp -> x25_addr;
486 	while (*ncp) {
487 		*ocp++ = *ncp++;
488 		oldp -> xaddr_len++;
489 	}
490 
491 	bcopy (newp -> x25_udata, (caddr_t)oldp -> xaddr_proto, 4);
492 	if (newp -> x25_udlen > 4)
493 		bcopy (newp -> x25_udata + 4, (caddr_t)oldp -> xaddr_userdata,
494 			(unsigned)(newp -> x25_udlen - 4));
495 
496 	bcopy ((caddr_t)oldp, mtod (m, char *), sizeof (*oldp));
497 	m -> m_len = sizeof (*oldp);
498 }
499 
500 
501 pk_checksockaddr (m)
502 struct mbuf *m;
503 {
504 	register struct sockaddr_x25 *sa = mtod (m, struct sockaddr_x25 *);
505 	register char *cp;
506 
507 	if (m -> m_len != sizeof (struct sockaddr_x25))
508 		return (1);
509 	if (sa -> x25_family != AF_CCITT ||
510 		sa -> x25_udlen > sizeof (sa -> x25_udata))
511 		return (1);
512 	for (cp = sa -> x25_addr; *cp; cp++) {
513 		if (*cp < '0' || *cp > '9' ||
514 			cp >= &sa -> x25_addr[sizeof (sa -> x25_addr) - 1])
515 			return (1);
516 	}
517 	return (0);
518 }
519 
520 pk_send (lcp, m)
521 struct pklcd *lcp;
522 register struct mbuf *m;
523 {
524 	int mqbit = 0, error = 0;
525 	register struct x25_packet *xp;
526 	register struct socket *so;
527 
528 	if (m -> m_type == MT_OOBDATA) {
529 		if (lcp -> lcd_intrconf_pending)
530 			error = ETOOMANYREFS;
531 		if (m -> m_pkthdr.len > 32)
532 			error = EMSGSIZE;
533 		M_PREPEND(m, PKHEADERLN, M_WAITOK);
534 		if (m == 0 || error)
535 			goto bad;
536 		*(mtod (m, octet *)) = 0;
537 		xp = mtod (m, struct x25_packet *);
538 		xp -> fmt_identifier = 1;
539 		xp -> packet_type = X25_INTERRUPT;
540 		SET_LCN(xp, lcp -> lcd_lcn);
541 		sbinsertoob ( (so = lcp -> lcd_so) ?
542 			&so -> so_snd : &lcp -> lcd_sb, m);
543 		goto send;
544 	}
545 	/*
546 	 * Application has elected (at call setup time) to prepend
547 	 * a control byte to each packet written indicating m-bit
548 	 * and q-bit status.  Examine and then discard this byte.
549 	 */
550 	if (lcp -> lcd_flags & X25_MQBIT) {
551 		if (m -> m_len < 1) {
552 			m_freem (m);
553 			return (EMSGSIZE);
554 		}
555 		mqbit = *(mtod (m, u_char *));
556 		m -> m_len--;
557 		m -> m_data++;
558 		m -> m_pkthdr.len--;
559 	}
560 	error = pk_fragment (lcp, m, mqbit & 0x80, mqbit & 0x40, 1);
561 send:
562 	if (error == 0 && lcp -> lcd_state == DATA_TRANSFER)
563 		lcp -> lcd_send (lcp); /* XXXXXXXXX fix pk_output!!! */
564 	return (error);
565 bad:
566 	if (m)
567 		m_freem (m);
568 	return (error);
569 }
570