ipx_usrreq.c revision 157370
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 157370 2006-04-01 15:42:02Z 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 void 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 void 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 void 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} 445 446static int 447ipx_attach(so, proto, td) 448 struct socket *so; 449 int proto; 450 struct thread *td; 451{ 452 struct ipxpcb *ipxp = sotoipxpcb(so); 453 int error; 454 455 KASSERT(ipxp == NULL, ("ipx_attach: ipxp != NULL")); 456 error = soreserve(so, ipxsendspace, ipxrecvspace); 457 if (error != 0) 458 return (error); 459 IPX_LIST_LOCK(); 460 error = ipx_pcballoc(so, &ipxpcb_list, td); 461 IPX_LIST_UNLOCK(); 462 return (error); 463} 464 465static int 466ipx_bind(so, nam, td) 467 struct socket *so; 468 struct sockaddr *nam; 469 struct thread *td; 470{ 471 struct ipxpcb *ipxp = sotoipxpcb(so); 472 int error; 473 474 KASSERT(ipxp != NULL, ("ipx_bind: ipxp == NULL")); 475 IPX_LIST_LOCK(); 476 IPX_LOCK(ipxp); 477 error = ipx_pcbbind(ipxp, nam, td); 478 IPX_UNLOCK(ipxp); 479 IPX_LIST_UNLOCK(); 480 return (error); 481} 482 483static int 484ipx_connect(so, nam, td) 485 struct socket *so; 486 struct sockaddr *nam; 487 struct thread *td; 488{ 489 struct ipxpcb *ipxp = sotoipxpcb(so); 490 int error; 491 492 KASSERT(ipxp != NULL, ("ipx_connect: ipxp == NULL")); 493 IPX_LIST_LOCK(); 494 IPX_LOCK(ipxp); 495 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 496 error = EISCONN; 497 goto out; 498 } 499 error = ipx_pcbconnect(ipxp, nam, td); 500 if (error == 0) 501 soisconnected(so); 502out: 503 IPX_UNLOCK(ipxp); 504 IPX_LIST_UNLOCK(); 505 return (error); 506} 507 508static void 509ipx_detach(so) 510 struct socket *so; 511{ 512 struct ipxpcb *ipxp = sotoipxpcb(so); 513 514 KASSERT(ipxp != NULL, ("ipx_detach: ipxp == NULL")); 515 IPX_LIST_LOCK(); 516 IPX_LOCK(ipxp); 517 ipx_pcbdetach(ipxp); 518 ipx_pcbfree(ipxp); 519 IPX_LIST_UNLOCK(); 520} 521 522static int 523ipx_disconnect(so) 524 struct socket *so; 525{ 526 struct ipxpcb *ipxp = sotoipxpcb(so); 527 int error; 528 529 KASSERT(ipxp != NULL, ("ipx_disconnect: ipxp == NULL")); 530 IPX_LIST_LOCK(); 531 IPX_LOCK(ipxp); 532 error = 0; 533 if (ipx_nullhost(ipxp->ipxp_faddr)) { 534 error = ENOTCONN; 535 goto out; 536 } 537 ipx_pcbdisconnect(ipxp); 538 soisdisconnected(so); 539out: 540 IPX_UNLOCK(ipxp); 541 IPX_LIST_UNLOCK(); 542 return (0); 543} 544 545int 546ipx_peeraddr(so, nam) 547 struct socket *so; 548 struct sockaddr **nam; 549{ 550 struct ipxpcb *ipxp = sotoipxpcb(so); 551 552 KASSERT(ipxp != NULL, ("ipx_peeraddr: ipxp == NULL")); 553 ipx_setpeeraddr(ipxp, nam); 554 return (0); 555} 556 557static int 558ipx_send(so, flags, m, nam, control, td) 559 struct socket *so; 560 int flags; 561 struct mbuf *m; 562 struct sockaddr *nam; 563 struct mbuf *control; 564 struct thread *td; 565{ 566 int error; 567 struct ipxpcb *ipxp = sotoipxpcb(so); 568 struct ipx_addr laddr; 569 570 KASSERT(ipxp != NULL, ("ipxp_send: ipxp == NULL")); 571 /* 572 * Attempt to only acquire the necessary locks: if the socket is 573 * already connected, we don't need to hold the IPX list lock to be 574 * used by ipx_pcbconnect() and ipx_pcbdisconnect(), just the IPX 575 * pcb lock. 576 */ 577 if (nam != NULL) { 578 IPX_LIST_LOCK(); 579 IPX_LOCK(ipxp); 580 laddr = ipxp->ipxp_laddr; 581 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 582 IPX_UNLOCK(ipxp); 583 IPX_LIST_UNLOCK(); 584 error = EISCONN; 585 goto send_release; 586 } 587 /* 588 * Must block input while temporarily connected. 589 */ 590 error = ipx_pcbconnect(ipxp, nam, td); 591 if (error) { 592 IPX_UNLOCK(ipxp); 593 IPX_LIST_UNLOCK(); 594 goto send_release; 595 } 596 } else { 597 IPX_LOCK(ipxp); 598 if (ipx_nullhost(ipxp->ipxp_faddr)) { 599 IPX_UNLOCK(ipxp); 600 error = ENOTCONN; 601 goto send_release; 602 } 603 } 604 error = ipx_output(ipxp, m); 605 m = NULL; 606 if (nam != NULL) { 607 ipx_pcbdisconnect(ipxp); 608 ipxp->ipxp_laddr = laddr; 609 IPX_UNLOCK(ipxp); 610 IPX_LIST_UNLOCK(); 611 } else 612 IPX_UNLOCK(ipxp); 613 614send_release: 615 if (m != NULL) 616 m_freem(m); 617 return (error); 618} 619 620static int 621ipx_shutdown(so) 622 struct socket *so; 623{ 624 625 KASSERT(so->so_pcb != NULL, ("ipx_shutdown: so_pcb == NULL")); 626 socantsendmore(so); 627 return (0); 628} 629 630int 631ipx_sockaddr(so, nam) 632 struct socket *so; 633 struct sockaddr **nam; 634{ 635 struct ipxpcb *ipxp = sotoipxpcb(so); 636 637 KASSERT(ipxp != NULL, ("ipx_sockaddr: ipxp == NULL")); 638 ipx_setsockaddr(ipxp, nam); 639 return (0); 640} 641 642static int 643ripx_attach(so, proto, td) 644 struct socket *so; 645 int proto; 646 struct thread *td; 647{ 648 int error = 0; 649 struct ipxpcb *ipxp = sotoipxpcb(so); 650 651 KASSERT(ipxp == NULL, ("ripx_attach: ipxp != NULL")); 652 if (td != NULL && (error = suser(td)) != 0) 653 return (error); 654 /* 655 * We hold the IPX list lock for the duration as address parameters 656 * of the IPX pcb are changed. Since no one else holds a reference 657 * to the ipxpcb yet, we don't need the ipxpcb lock here. 658 */ 659 IPX_LIST_LOCK(); 660 error = ipx_pcballoc(so, &ipxrawpcb_list, td); 661 if (error) 662 goto out; 663 ipxp = sotoipxpcb(so); 664 error = soreserve(so, ipxsendspace, ipxrecvspace); 665 if (error) 666 goto out; 667 ipxp->ipxp_faddr.x_host = ipx_broadhost; 668 ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT; 669out: 670 IPX_LIST_UNLOCK(); 671 return (error); 672} 673