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