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