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