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