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.6 (Berkeley) 06/21/90 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 struct x25_packet *pk_template (); 33 34 /* 35 * 36 * X.25 Packet level protocol interface to socket abstraction. 37 * 38 * Process an X.25 user request on a logical channel. If this is a send 39 * request then m is the mbuf chain of the send data. If this is a timer 40 * expiration (called from the software clock routine) them timertype is 41 * the particular timer. 42 * 43 */ 44 45 pk_usrreq (so, req, m, nam, control) 46 struct socket *so; 47 int req; 48 register struct mbuf *m, *nam; 49 struct mbuf *control; 50 { 51 register struct pklcd *lcp = (struct pklcd *) so -> so_pcb; 52 register struct x25_packet *xp; 53 register int error = 0; 54 55 if (req == PRU_CONTROL) 56 return (pk_control(so, (int)m, (caddr_t)nam, 57 (struct ifnet *)control)); 58 if (control && control->m_len) { 59 error = EINVAL; 60 goto release; 61 } 62 if (lcp == NULL && req != PRU_ATTACH) { 63 error = EINVAL; 64 goto release; 65 } 66 67 /* 68 pk_trace (pkcbhead, TR_USER, (struct pklcd *)0, 69 req, (struct x25_packet *)0); 70 */ 71 72 return (EINVAL); 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 error = pk_attach (so); 87 break; 88 89 /* 90 * Detach a logical channel from the socket. If the state of the 91 * channel is embryonic, simply discard it. Otherwise we have to 92 * initiate a PRU_DISCONNECT which will finish later. 93 */ 94 case PRU_DETACH: 95 pk_disconnect (lcp); 96 break; 97 98 /* 99 * Give the socket an address. 100 */ 101 case PRU_BIND: 102 if (nam -> m_len == sizeof (struct x25_sockaddr)) 103 old_to_new (nam); 104 error = pk_bind (lcp, nam); 105 break; 106 107 /* 108 * Prepare to accept connections. 109 */ 110 case PRU_LISTEN: 111 if (lcp -> lcd_ceaddr == 0) { 112 error = EDESTADDRREQ; 113 break; 114 } 115 lcp -> lcd_state = LISTEN; 116 lcp -> lcd_listen = pk_listenhead; 117 pk_listenhead = lcp; 118 break; 119 120 /* 121 * Initiate a CALL REQUEST to peer entity. Enter state SENT_CALL 122 * and mark the socket as connecting. Set timer waiting for 123 * CALL ACCEPT or CLEAR. 124 */ 125 case PRU_CONNECT: 126 if (nam -> m_len == sizeof (struct x25_sockaddr)) 127 old_to_new (nam); 128 error = pk_connect (lcp, nam, (struct sockaddr_25 *)0); 129 break; 130 131 /* 132 * Initiate a disconnect to peer entity via a CLEAR REQUEST packet. 133 * The socket will be disconnected when we receive a confirmation 134 * or a clear collision. 135 */ 136 case PRU_DISCONNECT: 137 pk_disconnect (lcp); 138 break; 139 140 /* 141 * Accept an INCOMING CALL. Most of the work has already been done 142 * by pk_input. Just return the callers address to the user. 143 */ 144 case PRU_ACCEPT: 145 if (lcp -> lcd_craddr == NULL) 146 break; 147 bcopy ((caddr_t)lcp -> lcd_craddr, mtod (nam, caddr_t), 148 sizeof (struct sockaddr_x25)); 149 nam -> m_len = sizeof (struct sockaddr_x25); 150 if (lcp -> lcd_flags & X25_OLDSOCKADDR) 151 new_to_old (nam); 152 break; 153 154 /* 155 * After a receive, we should send a RR. 156 */ 157 case PRU_RCVD: 158 lcp -> lcd_rxcnt++; 159 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RR); 160 pk_output (lcp); 161 break; 162 163 /* 164 * Do send by placing data on the socket output queue. 165 * SHOULD WE USE m_cat HERE. 166 */ 167 case PRU_SEND: 168 error = pk_send (lcp, m); 169 break; 170 171 /* 172 * Abort a virtual circuit. For example all completed calls 173 * waiting acceptance. 174 */ 175 case PRU_ABORT: 176 pk_disconnect (lcp); 177 break; 178 179 /* Begin unimplemented hooks. */ 180 181 case PRU_SHUTDOWN: 182 error = EOPNOTSUPP; 183 break; 184 185 case PRU_CONTROL: 186 error = EOPNOTSUPP; 187 break; 188 189 case PRU_SENSE: 190 #ifdef BSD4_3 191 ((struct stat *)m) -> st_blksize = so -> so_snd.sb_hiwat; 192 #else 193 error = EOPNOTSUPP; 194 #endif 195 break; 196 197 /* End unimplemented hooks. */ 198 199 case PRU_SOCKADDR: 200 if (lcp -> lcd_ceaddr == 0) 201 return (EADDRNOTAVAIL); 202 nam -> m_len = sizeof (struct sockaddr_x25); 203 bcopy ((caddr_t)lcp -> lcd_ceaddr, mtod (nam, caddr_t), 204 sizeof (struct sockaddr_x25)); 205 if (lcp -> lcd_flags & X25_OLDSOCKADDR) 206 new_to_old (nam); 207 break; 208 209 case PRU_PEERADDR: 210 if (lcp -> lcd_state != DATA_TRANSFER) 211 return (ENOTCONN); 212 nam -> m_len = sizeof (struct sockaddr_x25); 213 bcopy (lcp -> lcd_craddr ? (caddr_t)lcp -> lcd_craddr : 214 (caddr_t)lcp -> lcd_ceaddr, 215 mtod (nam, caddr_t), sizeof (struct sockaddr_x25)); 216 if (lcp -> lcd_flags & X25_OLDSOCKADDR) 217 new_to_old (nam); 218 break; 219 220 /* 221 * Receive INTERRUPT packet. 222 */ 223 case PRU_RCVOOB: 224 m -> m_len = 1; 225 *mtod (m, char *) = lcp -> lcd_intrdata; 226 break; 227 228 /* 229 * Send INTERRUPT packet. 230 */ 231 case PRU_SENDOOB: 232 m_freem (m); 233 if (lcp -> lcd_intrconf_pending) { 234 error = ETOOMANYREFS; 235 break; 236 } 237 lcp -> lcd_intrcnt++; 238 xp = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_INTERRUPT); 239 xp -> packet_data = 0; 240 (dtom (xp)) -> m_len++; 241 pk_output (lcp); 242 break; 243 244 default: 245 panic ("pk_usrreq"); 246 } 247 release: 248 if (control != NULL) 249 m_freem(control); 250 if (m != NULL) 251 m_freem(m); 252 return (error); 253 } 254 255 /* 256 * If you want to use UBC X.25 level 3 in conjunction with some 257 * other X.25 level 2 driver, have the ifp->if_ioctl routine 258 * assign pk_start to pkp -> pk_start when called with SIOCSIFCONF_X25. 259 */ 260 /* ARGSUSED */ 261 pk_start (lcp) 262 register struct pklcd *lcp; 263 { 264 extern int pk_send(); 265 266 lcp -> lcp_send = pk_send; 267 return (pk_output(lcp)); 268 } 269 270 /*ARGSUSED*/ 271 pk_control (so, cmd, data, ifp) 272 struct socket *so; 273 int cmd; 274 caddr_t data; 275 register struct ifnet *ifp; 276 { 277 register struct ifreq_x25 *ifr = (struct ifreq_x25 *)data; 278 register struct ifaddr *ifa = 0; 279 register struct x25_ifaddr *ia = 0; 280 struct pklcd *dev_lcp = 0; 281 int error, s; 282 unsigned n; 283 284 /* 285 * Find address for this interface, if it exists. 286 */ 287 if (ifp) 288 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) 289 if (ifa->ifa_addr->sa_family == AF_CCITT) 290 break; 291 292 ia = (struct x25_ifaddr *)ifa; 293 switch (cmd) { 294 case SIOCGIFCONF_X25: 295 if (ifa == 0) 296 return (EADDRNOTAVAIL); 297 ifr->ifr_xc = *(struct sockaddr *)ia->ia_xc; 298 return (0); 299 300 case SIOCSIFCONF_X25: 301 if (!suser()) 302 return (u.u_error); 303 304 if (ifp == 0) 305 panic("pk_control"); 306 if (ifa == (struct ifaddr *)0) { 307 register struct mbuf *m; 308 309 MALLOC(ia, struct x25_ifaddr *, sizeof (*ia), 310 M_IFADDR, M_WAITOK); 311 if (ia == 0) 312 return (ENOBUFS); 313 if (ifa = ifp->if_addrlist) { 314 for ( ; ifa->ifa_next; ifa = ifa->ifa_next) 315 ; 316 ifa->ifa_next = &ia->ia_ifa; 317 } else 318 ifp->if_addrlist = &ia->ia_ifa; 319 ifa = &ia->ia_ifa; 320 ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; 321 ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; 322 ia->ia_ifp = ifp; 323 ia->ia_pkcb.pk_ia = ia; 324 ia->ia_pkcb.pk_next = pkcbhead; 325 pkcbhead = &ia->ia_pkcb; 326 } 327 ia->ia_xcp = &(ifr->ifr_xc); 328 if (ia->ia_chan && (ia->ia_maxlcn != ia->xcp->xc_maxlcn)) { 329 pk_restart(&ia->ia_pkp, X25_RESTART_NETWORK_CONGESTION); 330 dev_lcp = ia->ia_chan[0]; 331 free((caddr_t)ia->ia_chan, M_IFADDR); 332 ia->ia_chan = 0; 333 } 334 if (ia->ia_chan == 0) { 335 n = ia->ia_maxlcn * sizeof(struct pklcd *); 336 ia->ia_chan = (struct pklcd **) malloc(n, M_IFADDR); 337 if (ia->ia_chan) { 338 bzero((caddr_t)ia->ia_chan, n); 339 if (dev_lcp == 0) 340 dev_lcp = pk_attach((struct socket *)0); 341 ia->ia_chan = dev_lcp; 342 } else { 343 if (dev_lcp) 344 pk_close(dev_lcp); 345 ia->ia_xcp = &ia->ia_xc; 346 return (ENOBUFS); 347 } 348 } 349 /* 350 * Give the interface a chance to initialize if this 351 * is its first address, and to validate the address. 352 */ 353 s = splimp(); 354 if (ifp->if_ioctl) 355 error = (*ifp->if_ioctl)(ifp, SIOCSIFCONF_X25, ifa))); 356 splx(s); 357 if (error == 0) { 358 ia->ia_xc = *ia->ia_xcp; 359 #ifndef WATERLOO 360 (void) pk_accton (); 361 #endif 362 } 363 ia->ia_xcp = &ia->ia_xc; 364 return (error); 365 366 default: 367 if (ifp == 0 || ifp->if_ioctl == 0) 368 return (EOPNOTSUPP); 369 return ((*ifp->if_ioctl)(ifp, cmd, data)); 370 } 371 } 372 373 /* 374 * Do an in-place conversion of an "old style" 375 * socket address to the new style 376 */ 377 378 static 379 old_to_new (m) 380 register struct mbuf *m; 381 { 382 register struct x25_sockaddr *oldp; 383 register struct sockaddr_x25 *newp; 384 register char *ocp, *ncp; 385 struct sockaddr_x25 new; 386 387 oldp = mtod (m, struct x25_sockaddr *); 388 newp = &new; 389 bzero ((caddr_t)newp, sizeof (*newp)); 390 391 newp -> x25_family = AF_CCITT; 392 newp->x25_opts.op_flags = (oldp->xaddr_facilities & X25_REVERSE_CHARGE) 393 | X25_MQBIT | X25_OLDSOCKADDR; 394 if (oldp -> xaddr_facilities & XS_HIPRIO) /* Datapac specific */ 395 newp -> x25_opts.op_psize = X25_PS128; 396 bcopy ((caddr_t)oldp -> xaddr_addr, newp -> x25_addr, 397 (unsigned)min (oldp -> xaddr_len, sizeof (newp -> x25_addr) - 1)); 398 bcopy ((caddr_t)oldp -> xaddr_proto, newp -> x25_udata, 4); 399 newp -> x25_udlen = 4; 400 401 ocp = (caddr_t)oldp -> xaddr_userdata; 402 ncp = newp -> x25_udata + 4; 403 while (*ocp && ocp < (caddr_t)oldp -> xaddr_userdata + 12) { 404 *ncp++ = *ocp++; 405 newp -> x25_udlen++; 406 } 407 408 bcopy ((caddr_t)newp, mtod (m, char *), sizeof (*newp)); 409 m->m_len = sizeof (*newp); 410 } 411 412 /* 413 * Do an in-place conversion of a new style 414 * socket address to the old style 415 */ 416 417 static 418 new_to_old (m) 419 register struct mbuf *m; 420 { 421 register struct x25_sockaddr *oldp; 422 register struct sockaddr_x25 *newp; 423 register char *ocp, *ncp; 424 struct x25_sockaddr old; 425 426 oldp = &old; 427 newp = mtod (m, struct sockaddr_x25 *); 428 bzero ((caddr_t)oldp, sizeof (*oldp)); 429 430 oldp -> xaddr_facilities = newp -> x25_opts.op_flags & X25_REVERSE_CHARGE; 431 if (newp -> x25_opts.op_psize == X25_PS128) 432 oldp -> xaddr_facilities |= XS_HIPRIO; /* Datapac specific */ 433 ocp = (char *)oldp -> xaddr_addr; 434 ncp = newp -> x25_addr; 435 while (*ncp) { 436 *ocp++ = *ncp++; 437 oldp -> xaddr_len++; 438 } 439 440 bcopy (newp -> x25_udata, (caddr_t)oldp -> xaddr_proto, 4); 441 bcopy (newp -> x25_udata + 4, (caddr_t)oldp -> xaddr_userdata, 442 (unsigned)(newp -> x25_udlen - 4)); 443 444 bcopy ((caddr_t)oldp, mtod (m, char *), sizeof (*oldp)); 445 m -> m_len = sizeof (*oldp); 446 } 447 448 pk_send (lcp, m) 449 register struct pklcd *lcp; 450 register struct mbuf *m; 451 { 452 register struct x25_packet *xp; 453 register struct mbuf *m0; 454 register int len; 455 456 m0 = dtom ((xp = pk_template (lcp -> lcd_lcn, X25_DATA))); 457 m0 -> m_next = m; 458 /* 459 * Application has elected (at call setup time) to prepend 460 * a control byte to each packet written indicating m-bit 461 * and q-bit status. Examine and then discard this byte. 462 */ 463 if (lcp -> lcd_flags & X25_MQBIT) { 464 register octet *cp; 465 466 if (m -> m_len < 1) { 467 m_freem (m0); 468 return (EMSGSIZE); 469 } 470 cp = mtod (m, octet *); 471 if (*cp & 0x80) /* XXX */ 472 xp -> q_bit = 1; 473 xp -> packet_type |= (*cp & 0x40) >> 2; /* XXX */ 474 m -> m_len--; 475 m -> m_data++; 476 } 477 len = m -> m_len; 478 while (m -> m_next) { 479 m = m -> m_next; 480 len += m -> m_len; 481 } 482 if (len > (1 << lcp -> lcd_packetsize)) { 483 m_freem (m0); 484 return (EMSGSIZE); 485 } 486 if (lcp -> lcd_so) 487 sbappendrecord (&lcp -> lcd_so -> so_snd, m0); 488 else 489 sbappendrecord (&lcp -> lcd_sb, m0); 490 lcp -> lcd_template = 0; 491 lcp -> lcd_txcnt++; 492 pk_output (lcp); 493 return (0); 494 } 495