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