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