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