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