ipx_usrreq.c revision 164033
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 164033 2006-11-06 13:42:10Z 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/priv.h> 49#include <sys/protosw.h> 50#include <sys/signalvar.h> 51#include <sys/socket.h> 52#include <sys/socketvar.h> 53#include <sys/sx.h> 54#include <sys/sysctl.h> 55#include <sys/systm.h> 56 57#include <net/if.h> 58#include <net/route.h> 59 60#include <netinet/in.h> 61 62#include <netipx/ipx.h> 63#include <netipx/ipx_if.h> 64#include <netipx/ipx_ip.h> 65#include <netipx/ipx_pcb.h> 66#include <netipx/ipx_var.h> 67 68/* 69 * IPX protocol implementation. 70 */ 71 72static int ipxsendspace = IPXSNDQ; 73SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxsendspace, CTLFLAG_RW, 74 &ipxsendspace, 0, ""); 75static int ipxrecvspace = IPXRCVQ; 76SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxrecvspace, CTLFLAG_RW, 77 &ipxrecvspace, 0, ""); 78 79static void ipx_usr_abort(struct socket *so); 80static int ipx_attach(struct socket *so, int proto, struct thread *td); 81static int ipx_bind(struct socket *so, struct sockaddr *nam, struct thread *td); 82static int ipx_connect(struct socket *so, struct sockaddr *nam, 83 struct thread *td); 84static void ipx_detach(struct socket *so); 85static int ipx_disconnect(struct socket *so); 86static int ipx_send(struct socket *so, int flags, struct mbuf *m, 87 struct sockaddr *addr, struct mbuf *control, 88 struct thread *td); 89static int ipx_shutdown(struct socket *so); 90static int ripx_attach(struct socket *so, int proto, struct thread *td); 91static int ipx_output(struct ipxpcb *ipxp, struct mbuf *m0); 92static void ipx_usr_close(struct socket *so); 93 94struct pr_usrreqs ipx_usrreqs = { 95 .pru_abort = ipx_usr_abort, 96 .pru_attach = ipx_attach, 97 .pru_bind = ipx_bind, 98 .pru_connect = ipx_connect, 99 .pru_control = ipx_control, 100 .pru_detach = ipx_detach, 101 .pru_disconnect = ipx_disconnect, 102 .pru_peeraddr = ipx_peeraddr, 103 .pru_send = ipx_send, 104 .pru_shutdown = ipx_shutdown, 105 .pru_sockaddr = ipx_sockaddr, 106 .pru_close = ipx_usr_close, 107}; 108 109struct pr_usrreqs ripx_usrreqs = { 110 .pru_abort = ipx_usr_abort, 111 .pru_attach = ripx_attach, 112 .pru_bind = ipx_bind, 113 .pru_connect = ipx_connect, 114 .pru_control = ipx_control, 115 .pru_detach = ipx_detach, 116 .pru_disconnect = ipx_disconnect, 117 .pru_peeraddr = ipx_peeraddr, 118 .pru_send = ipx_send, 119 .pru_shutdown = ipx_shutdown, 120 .pru_sockaddr = ipx_sockaddr, 121 .pru_close = ipx_usr_close, 122}; 123 124/* 125 * This may also be called for raw listeners. 126 */ 127void 128ipx_input(m, ipxp) 129 struct mbuf *m; 130 register struct ipxpcb *ipxp; 131{ 132 register struct ipx *ipx = mtod(m, struct ipx *); 133 struct ifnet *ifp = m->m_pkthdr.rcvif; 134 struct sockaddr_ipx ipx_ipx; 135 136 KASSERT(ipxp != NULL, ("ipx_input: NULL ipxpcb")); 137 IPX_LOCK_ASSERT(ipxp); 138 /* 139 * Construct sockaddr format source address. 140 * Stuff source address and datagram in user buffer. 141 */ 142 ipx_ipx.sipx_len = sizeof(ipx_ipx); 143 ipx_ipx.sipx_family = AF_IPX; 144 ipx_ipx.sipx_addr = ipx->ipx_sna; 145 ipx_ipx.sipx_zero[0] = '\0'; 146 ipx_ipx.sipx_zero[1] = '\0'; 147 if (ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet) && ifp != NULL) { 148 register struct ifaddr *ifa; 149 150 for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa != NULL; 151 ifa = TAILQ_NEXT(ifa, ifa_link)) { 152 if (ifa->ifa_addr->sa_family == AF_IPX) { 153 ipx_ipx.sipx_addr.x_net = 154 IA_SIPX(ifa)->sipx_addr.x_net; 155 break; 156 } 157 } 158 } 159 ipxp->ipxp_rpt = ipx->ipx_pt; 160 if ((ipxp->ipxp_flags & IPXP_RAWIN) == 0) { 161 m->m_len -= sizeof(struct ipx); 162 m->m_pkthdr.len -= sizeof(struct ipx); 163 m->m_data += sizeof(struct ipx); 164 } 165 if (sbappendaddr(&ipxp->ipxp_socket->so_rcv, 166 (struct sockaddr *)&ipx_ipx, m, NULL) == 0) 167 m_freem(m); 168 else 169 sorwakeup(ipxp->ipxp_socket); 170} 171 172/* 173 * Drop connection, reporting 174 * the specified error. 175 */ 176void 177ipx_drop(ipxp, errno) 178 register struct ipxpcb *ipxp; 179 int errno; 180{ 181 struct socket *so = ipxp->ipxp_socket; 182 183 IPX_LIST_LOCK_ASSERT(); 184 IPX_LOCK_ASSERT(ipxp); 185 186 /* 187 * someday, in the IPX world 188 * we will generate error protocol packets 189 * announcing that the socket has gone away. 190 * 191 * XXX Probably never. IPX does not have error packets. 192 */ 193 /*if (TCPS_HAVERCVDSYN(tp->t_state)) { 194 tp->t_state = TCPS_CLOSED; 195 tcp_output(tp); 196 }*/ 197 so->so_error = errno; 198 ipx_pcbdisconnect(ipxp); 199 soisdisconnected(so); 200} 201 202static int 203ipx_output(ipxp, m0) 204 struct ipxpcb *ipxp; 205 struct mbuf *m0; 206{ 207 register struct ipx *ipx; 208 register struct socket *so; 209 register int len = 0; 210 register struct route *ro; 211 struct mbuf *m; 212 struct mbuf *mprev = NULL; 213 214 IPX_LOCK_ASSERT(ipxp); 215 216 /* 217 * Calculate data length. 218 */ 219 for (m = m0; m != NULL; m = m->m_next) { 220 mprev = m; 221 len += m->m_len; 222 } 223 /* 224 * Make sure packet is actually of even length. 225 */ 226 227 if (len & 1) { 228 m = mprev; 229 if ((m->m_flags & M_EXT) == 0 && 230 (m->m_len + m->m_data < &m->m_dat[MLEN])) { 231 mtod(m, char*)[m->m_len++] = 0; 232 } else { 233 struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA); 234 235 if (m1 == NULL) { 236 m_freem(m0); 237 return (ENOBUFS); 238 } 239 m1->m_len = 1; 240 * mtod(m1, char *) = 0; 241 m->m_next = m1; 242 } 243 m0->m_pkthdr.len++; 244 } 245 246 /* 247 * Fill in mbuf with extended IPX header 248 * and addresses and length put into network format. 249 */ 250 m = m0; 251 if (ipxp->ipxp_flags & IPXP_RAWOUT) { 252 ipx = mtod(m, struct ipx *); 253 } else { 254 M_PREPEND(m, sizeof(struct ipx), M_DONTWAIT); 255 if (m == NULL) 256 return (ENOBUFS); 257 ipx = mtod(m, struct ipx *); 258 ipx->ipx_tc = 0; 259 ipx->ipx_pt = ipxp->ipxp_dpt; 260 ipx->ipx_sna = ipxp->ipxp_laddr; 261 ipx->ipx_dna = ipxp->ipxp_faddr; 262 len += sizeof(struct ipx); 263 } 264 265 ipx->ipx_len = htons((u_short)len); 266 267 if (ipxp->ipxp_flags & IPXP_CHECKSUM) { 268 ipx->ipx_sum = ipx_cksum(m, len); 269 } else 270 ipx->ipx_sum = 0xffff; 271 272 /* 273 * Output datagram. 274 */ 275 so = ipxp->ipxp_socket; 276 if (so->so_options & SO_DONTROUTE) 277 return (ipx_outputfl(m, (struct route *)NULL, 278 (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF)); 279 /* 280 * Use cached route for previous datagram if 281 * possible. If the previous net was the same 282 * and the interface was a broadcast medium, or 283 * if the previous destination was identical, 284 * then we are ok. 285 * 286 * NB: We don't handle broadcasts because that 287 * would require 3 subroutine calls. 288 */ 289 ro = &ipxp->ipxp_route; 290#ifdef ancient_history 291 /* 292 * I think that this will all be handled in ipx_pcbconnect! 293 */ 294 if (ro->ro_rt != NULL) { 295 if(ipx_neteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) { 296 /* 297 * This assumes we have no GH type routes 298 */ 299 if (ro->ro_rt->rt_flags & RTF_HOST) { 300 if (!ipx_hosteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) 301 goto re_route; 302 303 } 304 if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) { 305 register struct ipx_addr *dst = 306 &satoipx_addr(ro->ro_dst); 307 dst->x_host = ipx->ipx_dna.x_host; 308 } 309 /* 310 * Otherwise, we go through the same gateway 311 * and dst is already set up. 312 */ 313 } else { 314 re_route: 315 RTFREE(ro->ro_rt); 316 ro->ro_rt = NULL; 317 } 318 } 319 ipxp->ipxp_lastdst = ipx->ipx_dna; 320#endif /* ancient_history */ 321 return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST)); 322} 323 324int 325ipx_ctloutput(so, sopt) 326 struct socket *so; 327 struct sockopt *sopt; 328{ 329 struct ipxpcb *ipxp = sotoipxpcb(so); 330 int mask, error, optval; 331 short soptval; 332 struct ipx ioptval; 333 long seq; 334 335 KASSERT(ipxp != NULL, ("ipx_ctloutput: ipxp == NULL")); 336 error = 0; 337 338 switch (sopt->sopt_dir) { 339 case SOPT_GET: 340 switch (sopt->sopt_name) { 341 case SO_ALL_PACKETS: 342 mask = IPXP_ALL_PACKETS; 343 goto get_flags; 344 345 case SO_HEADERS_ON_INPUT: 346 mask = IPXP_RAWIN; 347 goto get_flags; 348 349 case SO_IPX_CHECKSUM: 350 mask = IPXP_CHECKSUM; 351 goto get_flags; 352 353 case SO_HEADERS_ON_OUTPUT: 354 mask = IPXP_RAWOUT; 355 get_flags: 356 /* Unlocked read. */ 357 soptval = ipxp->ipxp_flags & mask; 358 error = sooptcopyout(sopt, &soptval, sizeof soptval); 359 break; 360 361 case SO_DEFAULT_HEADERS: 362 ioptval.ipx_len = 0; 363 ioptval.ipx_sum = 0; 364 ioptval.ipx_tc = 0; 365 IPX_LOCK(ipxp); 366 ioptval.ipx_pt = ipxp->ipxp_dpt; 367 ioptval.ipx_dna = ipxp->ipxp_faddr; 368 ioptval.ipx_sna = ipxp->ipxp_laddr; 369 IPX_UNLOCK(ipxp); 370 error = sooptcopyout(sopt, &soptval, sizeof soptval); 371 break; 372 373 case SO_SEQNO: 374 IPX_LIST_LOCK(); 375 seq = ipx_pexseq; 376 ipx_pexseq++; 377 IPX_LIST_UNLOCK(); 378 error = sooptcopyout(sopt, &seq, sizeof seq); 379 break; 380 381 default: 382 error = EINVAL; 383 } 384 break; 385 386 case SOPT_SET: 387 switch (sopt->sopt_name) { 388 case SO_ALL_PACKETS: 389 mask = IPXP_ALL_PACKETS; 390 goto set_head; 391 392 case SO_HEADERS_ON_INPUT: 393 mask = IPXP_RAWIN; 394 goto set_head; 395 396 case SO_IPX_CHECKSUM: 397 mask = IPXP_CHECKSUM; 398 399 case SO_HEADERS_ON_OUTPUT: 400 mask = IPXP_RAWOUT; 401 set_head: 402 error = sooptcopyin(sopt, &optval, sizeof optval, 403 sizeof optval); 404 if (error) 405 break; 406 IPX_LOCK(ipxp); 407 if (optval) 408 ipxp->ipxp_flags |= mask; 409 else 410 ipxp->ipxp_flags &= ~mask; 411 IPX_UNLOCK(ipxp); 412 break; 413 414 case SO_DEFAULT_HEADERS: 415 error = sooptcopyin(sopt, &ioptval, sizeof ioptval, 416 sizeof ioptval); 417 if (error) 418 break; 419 /* Unlocked write. */ 420 ipxp->ipxp_dpt = ioptval.ipx_pt; 421 break; 422#ifdef IPXIP 423 case SO_IPXIP_ROUTE: 424 error = ipxip_route(so, sopt); 425 break; 426#endif /* IPXIP */ 427 default: 428 error = EINVAL; 429 } 430 break; 431 } 432 return (error); 433} 434 435static void 436ipx_usr_abort(so) 437 struct socket *so; 438{ 439 440 /* XXXRW: Possibly ipx_disconnect() here? */ 441 soisdisconnected(so); 442} 443 444static int 445ipx_attach(so, proto, td) 446 struct socket *so; 447 int proto; 448 struct thread *td; 449{ 450#ifdef INVARIANTS 451 struct ipxpcb *ipxp = sotoipxpcb(so); 452#endif 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 void 484ipx_usr_close(so) 485 struct socket *so; 486{ 487 488 /* XXXRW: Possibly ipx_disconnect() here? */ 489 soisdisconnected(so); 490} 491 492static int 493ipx_connect(so, nam, td) 494 struct socket *so; 495 struct sockaddr *nam; 496 struct thread *td; 497{ 498 struct ipxpcb *ipxp = sotoipxpcb(so); 499 int error; 500 501 KASSERT(ipxp != NULL, ("ipx_connect: ipxp == NULL")); 502 IPX_LIST_LOCK(); 503 IPX_LOCK(ipxp); 504 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 505 error = EISCONN; 506 goto out; 507 } 508 error = ipx_pcbconnect(ipxp, nam, td); 509 if (error == 0) 510 soisconnected(so); 511out: 512 IPX_UNLOCK(ipxp); 513 IPX_LIST_UNLOCK(); 514 return (error); 515} 516 517static void 518ipx_detach(so) 519 struct socket *so; 520{ 521 struct ipxpcb *ipxp = sotoipxpcb(so); 522 523 /* XXXRW: Should assert detached. */ 524 KASSERT(ipxp != NULL, ("ipx_detach: ipxp == NULL")); 525 IPX_LIST_LOCK(); 526 IPX_LOCK(ipxp); 527 ipx_pcbdetach(ipxp); 528 ipx_pcbfree(ipxp); 529 IPX_LIST_UNLOCK(); 530} 531 532static int 533ipx_disconnect(so) 534 struct socket *so; 535{ 536 struct ipxpcb *ipxp = sotoipxpcb(so); 537 int error; 538 539 KASSERT(ipxp != NULL, ("ipx_disconnect: ipxp == NULL")); 540 IPX_LIST_LOCK(); 541 IPX_LOCK(ipxp); 542 error = 0; 543 if (ipx_nullhost(ipxp->ipxp_faddr)) { 544 error = ENOTCONN; 545 goto out; 546 } 547 ipx_pcbdisconnect(ipxp); 548 soisdisconnected(so); 549out: 550 IPX_UNLOCK(ipxp); 551 IPX_LIST_UNLOCK(); 552 return (0); 553} 554 555int 556ipx_peeraddr(so, nam) 557 struct socket *so; 558 struct sockaddr **nam; 559{ 560 struct ipxpcb *ipxp = sotoipxpcb(so); 561 562 KASSERT(ipxp != NULL, ("ipx_peeraddr: ipxp == NULL")); 563 ipx_setpeeraddr(ipxp, nam); 564 return (0); 565} 566 567static int 568ipx_send(so, flags, m, nam, control, td) 569 struct socket *so; 570 int flags; 571 struct mbuf *m; 572 struct sockaddr *nam; 573 struct mbuf *control; 574 struct thread *td; 575{ 576 int error; 577 struct ipxpcb *ipxp = sotoipxpcb(so); 578 struct ipx_addr laddr; 579 580 KASSERT(ipxp != NULL, ("ipxp_send: ipxp == NULL")); 581 /* 582 * Attempt to only acquire the necessary locks: if the socket is 583 * already connected, we don't need to hold the IPX list lock to be 584 * used by ipx_pcbconnect() and ipx_pcbdisconnect(), just the IPX 585 * pcb lock. 586 */ 587 if (nam != NULL) { 588 IPX_LIST_LOCK(); 589 IPX_LOCK(ipxp); 590 laddr = ipxp->ipxp_laddr; 591 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 592 IPX_UNLOCK(ipxp); 593 IPX_LIST_UNLOCK(); 594 error = EISCONN; 595 goto send_release; 596 } 597 /* 598 * Must block input while temporarily connected. 599 */ 600 error = ipx_pcbconnect(ipxp, nam, td); 601 if (error) { 602 IPX_UNLOCK(ipxp); 603 IPX_LIST_UNLOCK(); 604 goto send_release; 605 } 606 } else { 607 IPX_LOCK(ipxp); 608 if (ipx_nullhost(ipxp->ipxp_faddr)) { 609 IPX_UNLOCK(ipxp); 610 error = ENOTCONN; 611 goto send_release; 612 } 613 } 614 error = ipx_output(ipxp, m); 615 m = NULL; 616 if (nam != NULL) { 617 ipx_pcbdisconnect(ipxp); 618 ipxp->ipxp_laddr = laddr; 619 IPX_UNLOCK(ipxp); 620 IPX_LIST_UNLOCK(); 621 } else 622 IPX_UNLOCK(ipxp); 623 624send_release: 625 if (m != NULL) 626 m_freem(m); 627 return (error); 628} 629 630static int 631ipx_shutdown(so) 632 struct socket *so; 633{ 634 635 KASSERT(so->so_pcb != NULL, ("ipx_shutdown: so_pcb == NULL")); 636 socantsendmore(so); 637 return (0); 638} 639 640int 641ipx_sockaddr(so, nam) 642 struct socket *so; 643 struct sockaddr **nam; 644{ 645 struct ipxpcb *ipxp = sotoipxpcb(so); 646 647 KASSERT(ipxp != NULL, ("ipx_sockaddr: ipxp == NULL")); 648 ipx_setsockaddr(ipxp, nam); 649 return (0); 650} 651 652static int 653ripx_attach(so, proto, td) 654 struct socket *so; 655 int proto; 656 struct thread *td; 657{ 658 int error = 0; 659 struct ipxpcb *ipxp = sotoipxpcb(so); 660 661 KASSERT(ipxp == NULL, ("ripx_attach: ipxp != NULL")); 662 663 if (td != NULL) { 664 error = priv_check(td, PRIV_NETIPX_RAW); 665 if (error) 666 return (error); 667 } 668 669 /* 670 * We hold the IPX list lock for the duration as address parameters 671 * of the IPX pcb are changed. Since no one else holds a reference 672 * to the ipxpcb yet, we don't need the ipxpcb lock here. 673 */ 674 IPX_LIST_LOCK(); 675 error = ipx_pcballoc(so, &ipxrawpcb_list, td); 676 if (error) 677 goto out; 678 ipxp = sotoipxpcb(so); 679 error = soreserve(so, ipxsendspace, ipxrecvspace); 680 if (error) 681 goto out; 682 ipxp->ipxp_faddr.x_host = ipx_broadhost; 683 ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT; 684out: 685 IPX_LIST_UNLOCK(); 686 return (error); 687} 688