ipx_usrreq.c revision 157128
1/*- 2 * Copyright (c) 1984, 1985, 1986, 1987, 1993 3 * The Regents of the University of California. 4 * Copyright (c) 1995, Mike Mitchell 5 * Copyright (c) 2004-2006 Robert N. M. Watson 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)ipx_usrreq.c 37 */ 38 39#include <sys/cdefs.h> 40__FBSDID("$FreeBSD: head/sys/netipx/ipx_usrreq.c 157128 2006-03-25 17:28:42Z rwatson $"); 41 42#include "opt_ipx.h" 43 44#include <sys/param.h> 45#include <sys/kernel.h> 46#include <sys/lock.h> 47#include <sys/mbuf.h> 48#include <sys/protosw.h> 49#include <sys/signalvar.h> 50#include <sys/socket.h> 51#include <sys/socketvar.h> 52#include <sys/sx.h> 53#include <sys/sysctl.h> 54#include <sys/systm.h> 55 56#include <net/if.h> 57#include <net/route.h> 58 59#include <netinet/in.h> 60 61#include <netipx/ipx.h> 62#include <netipx/ipx_if.h> 63#include <netipx/ipx_ip.h> 64#include <netipx/ipx_pcb.h> 65#include <netipx/ipx_var.h> 66 67/* 68 * IPX protocol implementation. 69 */ 70 71static int ipxsendspace = IPXSNDQ; 72SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxsendspace, CTLFLAG_RW, 73 &ipxsendspace, 0, ""); 74static int ipxrecvspace = IPXRCVQ; 75SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxrecvspace, CTLFLAG_RW, 76 &ipxrecvspace, 0, ""); 77 78static int ipx_usr_abort(struct socket *so); 79static int ipx_attach(struct socket *so, int proto, struct thread *td); 80static int ipx_bind(struct socket *so, struct sockaddr *nam, struct thread *td); 81static int ipx_connect(struct socket *so, struct sockaddr *nam, 82 struct thread *td); 83static int ipx_detach(struct socket *so); 84static int ipx_disconnect(struct socket *so); 85static int ipx_send(struct socket *so, int flags, struct mbuf *m, 86 struct sockaddr *addr, struct mbuf *control, 87 struct thread *td); 88static int ipx_shutdown(struct socket *so); 89static int ripx_attach(struct socket *so, int proto, struct thread *td); 90static int ipx_output(struct ipxpcb *ipxp, struct mbuf *m0); 91 92struct pr_usrreqs ipx_usrreqs = { 93 .pru_abort = ipx_usr_abort, 94 .pru_attach = ipx_attach, 95 .pru_bind = ipx_bind, 96 .pru_connect = ipx_connect, 97 .pru_control = ipx_control, 98 .pru_detach = ipx_detach, 99 .pru_disconnect = ipx_disconnect, 100 .pru_peeraddr = ipx_peeraddr, 101 .pru_send = ipx_send, 102 .pru_shutdown = ipx_shutdown, 103 .pru_sockaddr = ipx_sockaddr, 104}; 105 106struct pr_usrreqs ripx_usrreqs = { 107 .pru_abort = ipx_usr_abort, 108 .pru_attach = ripx_attach, 109 .pru_bind = ipx_bind, 110 .pru_connect = ipx_connect, 111 .pru_control = ipx_control, 112 .pru_detach = ipx_detach, 113 .pru_disconnect = ipx_disconnect, 114 .pru_peeraddr = ipx_peeraddr, 115 .pru_send = ipx_send, 116 .pru_shutdown = ipx_shutdown, 117 .pru_sockaddr = ipx_sockaddr, 118}; 119 120/* 121 * This may also be called for raw listeners. 122 */ 123void 124ipx_input(m, ipxp) 125 struct mbuf *m; 126 register struct ipxpcb *ipxp; 127{ 128 register struct ipx *ipx = mtod(m, struct ipx *); 129 struct ifnet *ifp = m->m_pkthdr.rcvif; 130 struct sockaddr_ipx ipx_ipx; 131 132 KASSERT(ipxp != NULL, ("ipx_input: NULL ipxpcb")); 133 IPX_LOCK_ASSERT(ipxp); 134 /* 135 * Construct sockaddr format source address. 136 * Stuff source address and datagram in user buffer. 137 */ 138 ipx_ipx.sipx_len = sizeof(ipx_ipx); 139 ipx_ipx.sipx_family = AF_IPX; 140 ipx_ipx.sipx_addr = ipx->ipx_sna; 141 ipx_ipx.sipx_zero[0] = '\0'; 142 ipx_ipx.sipx_zero[1] = '\0'; 143 if (ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet) && ifp != NULL) { 144 register struct ifaddr *ifa; 145 146 for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa != NULL; 147 ifa = TAILQ_NEXT(ifa, ifa_link)) { 148 if (ifa->ifa_addr->sa_family == AF_IPX) { 149 ipx_ipx.sipx_addr.x_net = 150 IA_SIPX(ifa)->sipx_addr.x_net; 151 break; 152 } 153 } 154 } 155 ipxp->ipxp_rpt = ipx->ipx_pt; 156 if ((ipxp->ipxp_flags & IPXP_RAWIN) == 0) { 157 m->m_len -= sizeof(struct ipx); 158 m->m_pkthdr.len -= sizeof(struct ipx); 159 m->m_data += sizeof(struct ipx); 160 } 161 if (sbappendaddr(&ipxp->ipxp_socket->so_rcv, 162 (struct sockaddr *)&ipx_ipx, m, NULL) == 0) 163 m_freem(m); 164 else 165 sorwakeup(ipxp->ipxp_socket); 166} 167 168/* 169 * Drop connection, reporting 170 * the specified error. 171 */ 172void 173ipx_drop(ipxp, errno) 174 register struct ipxpcb *ipxp; 175 int errno; 176{ 177 struct socket *so = ipxp->ipxp_socket; 178 179 IPX_LIST_LOCK_ASSERT(); 180 IPX_LOCK_ASSERT(ipxp); 181 182 /* 183 * someday, in the IPX world 184 * we will generate error protocol packets 185 * announcing that the socket has gone away. 186 * 187 * XXX Probably never. IPX does not have error packets. 188 */ 189 /*if (TCPS_HAVERCVDSYN(tp->t_state)) { 190 tp->t_state = TCPS_CLOSED; 191 tcp_output(tp); 192 }*/ 193 so->so_error = errno; 194 ipx_pcbdisconnect(ipxp); 195 soisdisconnected(so); 196} 197 198static int 199ipx_output(ipxp, m0) 200 struct ipxpcb *ipxp; 201 struct mbuf *m0; 202{ 203 register struct ipx *ipx; 204 register struct socket *so; 205 register int len = 0; 206 register struct route *ro; 207 struct mbuf *m; 208 struct mbuf *mprev = NULL; 209 210 IPX_LOCK_ASSERT(ipxp); 211 212 /* 213 * Calculate data length. 214 */ 215 for (m = m0; m != NULL; m = m->m_next) { 216 mprev = m; 217 len += m->m_len; 218 } 219 /* 220 * Make sure packet is actually of even length. 221 */ 222 223 if (len & 1) { 224 m = mprev; 225 if ((m->m_flags & M_EXT) == 0 && 226 (m->m_len + m->m_data < &m->m_dat[MLEN])) { 227 mtod(m, char*)[m->m_len++] = 0; 228 } else { 229 struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA); 230 231 if (m1 == NULL) { 232 m_freem(m0); 233 return (ENOBUFS); 234 } 235 m1->m_len = 1; 236 * mtod(m1, char *) = 0; 237 m->m_next = m1; 238 } 239 m0->m_pkthdr.len++; 240 } 241 242 /* 243 * Fill in mbuf with extended IPX header 244 * and addresses and length put into network format. 245 */ 246 m = m0; 247 if (ipxp->ipxp_flags & IPXP_RAWOUT) { 248 ipx = mtod(m, struct ipx *); 249 } else { 250 M_PREPEND(m, sizeof(struct ipx), M_DONTWAIT); 251 if (m == NULL) 252 return (ENOBUFS); 253 ipx = mtod(m, struct ipx *); 254 ipx->ipx_tc = 0; 255 ipx->ipx_pt = ipxp->ipxp_dpt; 256 ipx->ipx_sna = ipxp->ipxp_laddr; 257 ipx->ipx_dna = ipxp->ipxp_faddr; 258 len += sizeof(struct ipx); 259 } 260 261 ipx->ipx_len = htons((u_short)len); 262 263 if (ipxp->ipxp_flags & IPXP_CHECKSUM) { 264 ipx->ipx_sum = ipx_cksum(m, len); 265 } else 266 ipx->ipx_sum = 0xffff; 267 268 /* 269 * Output datagram. 270 */ 271 so = ipxp->ipxp_socket; 272 if (so->so_options & SO_DONTROUTE) 273 return (ipx_outputfl(m, (struct route *)NULL, 274 (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF)); 275 /* 276 * Use cached route for previous datagram if 277 * possible. If the previous net was the same 278 * and the interface was a broadcast medium, or 279 * if the previous destination was identical, 280 * then we are ok. 281 * 282 * NB: We don't handle broadcasts because that 283 * would require 3 subroutine calls. 284 */ 285 ro = &ipxp->ipxp_route; 286#ifdef ancient_history 287 /* 288 * I think that this will all be handled in ipx_pcbconnect! 289 */ 290 if (ro->ro_rt != NULL) { 291 if(ipx_neteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) { 292 /* 293 * This assumes we have no GH type routes 294 */ 295 if (ro->ro_rt->rt_flags & RTF_HOST) { 296 if (!ipx_hosteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) 297 goto re_route; 298 299 } 300 if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) { 301 register struct ipx_addr *dst = 302 &satoipx_addr(ro->ro_dst); 303 dst->x_host = ipx->ipx_dna.x_host; 304 } 305 /* 306 * Otherwise, we go through the same gateway 307 * and dst is already set up. 308 */ 309 } else { 310 re_route: 311 RTFREE(ro->ro_rt); 312 ro->ro_rt = NULL; 313 } 314 } 315 ipxp->ipxp_lastdst = ipx->ipx_dna; 316#endif /* ancient_history */ 317 return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST)); 318} 319 320int 321ipx_ctloutput(so, sopt) 322 struct socket *so; 323 struct sockopt *sopt; 324{ 325 struct ipxpcb *ipxp = sotoipxpcb(so); 326 int mask, error, optval; 327 short soptval; 328 struct ipx ioptval; 329 long seq; 330 331 KASSERT(ipxp != NULL, ("ipx_ctloutput: ipxp == NULL")); 332 error = 0; 333 334 switch (sopt->sopt_dir) { 335 case SOPT_GET: 336 switch (sopt->sopt_name) { 337 case SO_ALL_PACKETS: 338 mask = IPXP_ALL_PACKETS; 339 goto get_flags; 340 341 case SO_HEADERS_ON_INPUT: 342 mask = IPXP_RAWIN; 343 goto get_flags; 344 345 case SO_IPX_CHECKSUM: 346 mask = IPXP_CHECKSUM; 347 goto get_flags; 348 349 case SO_HEADERS_ON_OUTPUT: 350 mask = IPXP_RAWOUT; 351 get_flags: 352 /* Unlocked read. */ 353 soptval = ipxp->ipxp_flags & mask; 354 error = sooptcopyout(sopt, &soptval, sizeof soptval); 355 break; 356 357 case SO_DEFAULT_HEADERS: 358 ioptval.ipx_len = 0; 359 ioptval.ipx_sum = 0; 360 ioptval.ipx_tc = 0; 361 IPX_LOCK(ipxp); 362 ioptval.ipx_pt = ipxp->ipxp_dpt; 363 ioptval.ipx_dna = ipxp->ipxp_faddr; 364 ioptval.ipx_sna = ipxp->ipxp_laddr; 365 IPX_UNLOCK(ipxp); 366 error = sooptcopyout(sopt, &soptval, sizeof soptval); 367 break; 368 369 case SO_SEQNO: 370 IPX_LIST_LOCK(); 371 seq = ipx_pexseq; 372 ipx_pexseq++; 373 IPX_LIST_UNLOCK(); 374 error = sooptcopyout(sopt, &seq, sizeof seq); 375 break; 376 377 default: 378 error = EINVAL; 379 } 380 break; 381 382 case SOPT_SET: 383 switch (sopt->sopt_name) { 384 case SO_ALL_PACKETS: 385 mask = IPXP_ALL_PACKETS; 386 goto set_head; 387 388 case SO_HEADERS_ON_INPUT: 389 mask = IPXP_RAWIN; 390 goto set_head; 391 392 case SO_IPX_CHECKSUM: 393 mask = IPXP_CHECKSUM; 394 395 case SO_HEADERS_ON_OUTPUT: 396 mask = IPXP_RAWOUT; 397 set_head: 398 error = sooptcopyin(sopt, &optval, sizeof optval, 399 sizeof optval); 400 if (error) 401 break; 402 IPX_LOCK(ipxp); 403 if (optval) 404 ipxp->ipxp_flags |= mask; 405 else 406 ipxp->ipxp_flags &= ~mask; 407 IPX_UNLOCK(ipxp); 408 break; 409 410 case SO_DEFAULT_HEADERS: 411 error = sooptcopyin(sopt, &ioptval, sizeof ioptval, 412 sizeof ioptval); 413 if (error) 414 break; 415 /* Unlocked write. */ 416 ipxp->ipxp_dpt = ioptval.ipx_pt; 417 break; 418#ifdef IPXIP 419 case SO_IPXIP_ROUTE: 420 error = ipxip_route(so, sopt); 421 break; 422#endif /* IPXIP */ 423 default: 424 error = EINVAL; 425 } 426 break; 427 } 428 return (error); 429} 430 431static int 432ipx_usr_abort(so) 433 struct socket *so; 434{ 435 struct ipxpcb *ipxp = sotoipxpcb(so); 436 437 KASSERT(ipxp != NULL, ("ipx_usr_abort: ipxp == NULL")); 438 IPX_LIST_LOCK(); 439 IPX_LOCK(ipxp); 440 ipx_pcbdetach(ipxp); 441 ipx_pcbfree(ipxp); 442 IPX_LIST_UNLOCK(); 443 soisdisconnected(so); 444 ACCEPT_LOCK(); 445 SOCK_LOCK(so); 446 sotryfree(so); 447 return (0); 448} 449 450static int 451ipx_attach(so, proto, td) 452 struct socket *so; 453 int proto; 454 struct thread *td; 455{ 456 struct ipxpcb *ipxp = sotoipxpcb(so); 457 int error; 458 459 KASSERT(ipxp == NULL, ("ipx_attach: ipxp != NULL")); 460 error = soreserve(so, ipxsendspace, ipxrecvspace); 461 if (error != 0) 462 return (error); 463 IPX_LIST_LOCK(); 464 error = ipx_pcballoc(so, &ipxpcb_list, td); 465 IPX_LIST_UNLOCK(); 466 return (error); 467} 468 469static int 470ipx_bind(so, nam, td) 471 struct socket *so; 472 struct sockaddr *nam; 473 struct thread *td; 474{ 475 struct ipxpcb *ipxp = sotoipxpcb(so); 476 int error; 477 478 KASSERT(ipxp != NULL, ("ipx_bind: ipxp == NULL")); 479 IPX_LIST_LOCK(); 480 IPX_LOCK(ipxp); 481 error = ipx_pcbbind(ipxp, nam, td); 482 IPX_UNLOCK(ipxp); 483 IPX_LIST_UNLOCK(); 484 return (error); 485} 486 487static int 488ipx_connect(so, nam, td) 489 struct socket *so; 490 struct sockaddr *nam; 491 struct thread *td; 492{ 493 struct ipxpcb *ipxp = sotoipxpcb(so); 494 int error; 495 496 KASSERT(ipxp != NULL, ("ipx_connect: ipxp == NULL")); 497 IPX_LIST_LOCK(); 498 IPX_LOCK(ipxp); 499 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 500 error = EISCONN; 501 goto out; 502 } 503 error = ipx_pcbconnect(ipxp, nam, td); 504 if (error == 0) 505 soisconnected(so); 506out: 507 IPX_UNLOCK(ipxp); 508 IPX_LIST_UNLOCK(); 509 return (error); 510} 511 512static int 513ipx_detach(so) 514 struct socket *so; 515{ 516 struct ipxpcb *ipxp = sotoipxpcb(so); 517 518 KASSERT(ipxp != NULL, ("ipx_detach: ipxp == NULL")); 519 IPX_LIST_LOCK(); 520 IPX_LOCK(ipxp); 521 ipx_pcbdetach(ipxp); 522 ipx_pcbfree(ipxp); 523 IPX_LIST_UNLOCK(); 524 return (0); 525} 526 527static int 528ipx_disconnect(so) 529 struct socket *so; 530{ 531 struct ipxpcb *ipxp = sotoipxpcb(so); 532 int error; 533 534 KASSERT(ipxp != NULL, ("ipx_disconnect: ipxp == NULL")); 535 IPX_LIST_LOCK(); 536 IPX_LOCK(ipxp); 537 error = 0; 538 if (ipx_nullhost(ipxp->ipxp_faddr)) { 539 error = ENOTCONN; 540 goto out; 541 } 542 ipx_pcbdisconnect(ipxp); 543 soisdisconnected(so); 544out: 545 IPX_UNLOCK(ipxp); 546 IPX_LIST_UNLOCK(); 547 return (0); 548} 549 550int 551ipx_peeraddr(so, nam) 552 struct socket *so; 553 struct sockaddr **nam; 554{ 555 struct ipxpcb *ipxp = sotoipxpcb(so); 556 557 KASSERT(ipxp != NULL, ("ipx_peeraddr: ipxp == NULL")); 558 ipx_setpeeraddr(ipxp, nam); 559 return (0); 560} 561 562static int 563ipx_send(so, flags, m, nam, control, td) 564 struct socket *so; 565 int flags; 566 struct mbuf *m; 567 struct sockaddr *nam; 568 struct mbuf *control; 569 struct thread *td; 570{ 571 int error; 572 struct ipxpcb *ipxp = sotoipxpcb(so); 573 struct ipx_addr laddr; 574 575 KASSERT(ipxp != NULL, ("ipxp_send: ipxp == NULL")); 576 /* 577 * Attempt to only acquire the necessary locks: if the socket is 578 * already connected, we don't need to hold the IPX list lock to be 579 * used by ipx_pcbconnect() and ipx_pcbdisconnect(), just the IPX 580 * pcb lock. 581 */ 582 if (nam != NULL) { 583 IPX_LIST_LOCK(); 584 IPX_LOCK(ipxp); 585 laddr = ipxp->ipxp_laddr; 586 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 587 IPX_UNLOCK(ipxp); 588 IPX_LIST_UNLOCK(); 589 error = EISCONN; 590 goto send_release; 591 } 592 /* 593 * Must block input while temporarily connected. 594 */ 595 error = ipx_pcbconnect(ipxp, nam, td); 596 if (error) { 597 IPX_UNLOCK(ipxp); 598 IPX_LIST_UNLOCK(); 599 goto send_release; 600 } 601 } else { 602 IPX_LOCK(ipxp); 603 if (ipx_nullhost(ipxp->ipxp_faddr)) { 604 IPX_UNLOCK(ipxp); 605 error = ENOTCONN; 606 goto send_release; 607 } 608 } 609 error = ipx_output(ipxp, m); 610 m = NULL; 611 if (nam != NULL) { 612 ipx_pcbdisconnect(ipxp); 613 ipxp->ipxp_laddr = laddr; 614 IPX_UNLOCK(ipxp); 615 IPX_LIST_UNLOCK(); 616 } else 617 IPX_UNLOCK(ipxp); 618 619send_release: 620 if (m != NULL) 621 m_freem(m); 622 return (error); 623} 624 625static int 626ipx_shutdown(so) 627 struct socket *so; 628{ 629 630 KASSERT(so->so_pcb != NULL, ("ipx_shutdown: so_pcb == NULL")); 631 socantsendmore(so); 632 return (0); 633} 634 635int 636ipx_sockaddr(so, nam) 637 struct socket *so; 638 struct sockaddr **nam; 639{ 640 struct ipxpcb *ipxp = sotoipxpcb(so); 641 642 KASSERT(ipxp != NULL, ("ipx_sockaddr: ipxp == NULL")); 643 ipx_setsockaddr(ipxp, nam); 644 return (0); 645} 646 647static int 648ripx_attach(so, proto, td) 649 struct socket *so; 650 int proto; 651 struct thread *td; 652{ 653 int error = 0; 654 struct ipxpcb *ipxp = sotoipxpcb(so); 655 656 KASSERT(ipxp == NULL, ("ripx_attach: ipxp != NULL")); 657 if (td != NULL && (error = suser(td)) != 0) 658 return (error); 659 /* 660 * We hold the IPX list lock for the duration as address parameters 661 * of the IPX pcb are changed. Since no one else holds a reference 662 * to the ipxpcb yet, we don't need the ipxpcb lock here. 663 */ 664 IPX_LIST_LOCK(); 665 error = ipx_pcballoc(so, &ipxrawpcb_list, td); 666 if (error) 667 goto out; 668 ipxp = sotoipxpcb(so); 669 error = soreserve(so, ipxsendspace, ipxrecvspace); 670 if (error) 671 goto out; 672 ipxp->ipxp_faddr.x_host = ipx_broadhost; 673 ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT; 674out: 675 IPX_LIST_UNLOCK(); 676 return (error); 677} 678