ipx_usrreq.c revision 96972
1/* 2 * Copyright (c) 1995, Mike Mitchell 3 * Copyright (c) 1984, 1985, 1986, 1987, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by the University of 17 * California, Berkeley and its contributors. 18 * 4. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * @(#)ipx_usrreq.c 35 * 36 * $FreeBSD: head/sys/netipx/ipx_usrreq.c 96972 2002-05-20 05:41:09Z tanimura $ 37 */ 38 39#include "opt_ipx.h" 40 41#include <sys/param.h> 42#include <sys/kernel.h> 43#include <sys/lock.h> 44#include <sys/mbuf.h> 45#include <sys/protosw.h> 46#include <sys/signalvar.h> 47#include <sys/socket.h> 48#include <sys/socketvar.h> 49#include <sys/sx.h> 50#include <sys/sysctl.h> 51#include <sys/systm.h> 52 53#include <net/if.h> 54#include <net/route.h> 55 56#include <netinet/in.h> 57 58#include <netipx/ipx.h> 59#include <netipx/ipx_if.h> 60#include <netipx/ipx_ip.h> 61#include <netipx/ipx_pcb.h> 62#include <netipx/ipx_var.h> 63 64/* 65 * IPX protocol implementation. 66 */ 67 68static int ipxsendspace = IPXSNDQ; 69SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxsendspace, CTLFLAG_RW, 70 &ipxsendspace, 0, ""); 71static int ipxrecvspace = IPXRCVQ; 72SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxrecvspace, CTLFLAG_RW, 73 &ipxrecvspace, 0, ""); 74 75static int ipx_usr_abort(struct socket *so); 76static int ipx_attach(struct socket *so, int proto, struct thread *td); 77static int ipx_bind(struct socket *so, struct sockaddr *nam, struct thread *td); 78static int ipx_connect(struct socket *so, struct sockaddr *nam, 79 struct thread *td); 80static int ipx_detach(struct socket *so); 81static int ipx_disconnect(struct socket *so); 82static int ipx_send(struct socket *so, int flags, struct mbuf *m, 83 struct sockaddr *addr, struct mbuf *control, 84 struct thread *td); 85static int ipx_shutdown(struct socket *so); 86static int ripx_attach(struct socket *so, int proto, struct thread *td); 87static int ipx_output(struct ipxpcb *ipxp, struct mbuf *m0); 88 89struct pr_usrreqs ipx_usrreqs = { 90 ipx_usr_abort, pru_accept_notsupp, ipx_attach, ipx_bind, 91 ipx_connect, pru_connect2_notsupp, ipx_control, ipx_detach, 92 ipx_disconnect, pru_listen_notsupp, ipx_peeraddr, pru_rcvd_notsupp, 93 pru_rcvoob_notsupp, ipx_send, pru_sense_null, ipx_shutdown, 94 ipx_sockaddr, sosend, soreceive, sopoll 95}; 96 97struct pr_usrreqs ripx_usrreqs = { 98 ipx_usr_abort, pru_accept_notsupp, ripx_attach, ipx_bind, 99 ipx_connect, pru_connect2_notsupp, ipx_control, ipx_detach, 100 ipx_disconnect, pru_listen_notsupp, ipx_peeraddr, pru_rcvd_notsupp, 101 pru_rcvoob_notsupp, ipx_send, pru_sense_null, ipx_shutdown, 102 ipx_sockaddr, sosend, soreceive, sopoll 103}; 104 105/* 106 * This may also be called for raw listeners. 107 */ 108void 109ipx_input(m, ipxp) 110 struct mbuf *m; 111 register struct ipxpcb *ipxp; 112{ 113 register struct ipx *ipx = mtod(m, struct ipx *); 114 struct ifnet *ifp = m->m_pkthdr.rcvif; 115 struct sockaddr_ipx ipx_ipx; 116 117 if (ipxp == NULL) 118 panic("No ipxpcb"); 119 /* 120 * Construct sockaddr format source address. 121 * Stuff source address and datagram in user buffer. 122 */ 123 ipx_ipx.sipx_len = sizeof(ipx_ipx); 124 ipx_ipx.sipx_family = AF_IPX; 125 ipx_ipx.sipx_addr = ipx->ipx_sna; 126 ipx_ipx.sipx_zero[0] = '\0'; 127 ipx_ipx.sipx_zero[1] = '\0'; 128 if (ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet) && ifp != NULL) { 129 register struct ifaddr *ifa; 130 131 for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa != NULL; 132 ifa = TAILQ_NEXT(ifa, ifa_link)) { 133 if (ifa->ifa_addr->sa_family == AF_IPX) { 134 ipx_ipx.sipx_addr.x_net = 135 IA_SIPX(ifa)->sipx_addr.x_net; 136 break; 137 } 138 } 139 } 140 ipxp->ipxp_rpt = ipx->ipx_pt; 141 if (!(ipxp->ipxp_flags & IPXP_RAWIN) ) { 142 m->m_len -= sizeof(struct ipx); 143 m->m_pkthdr.len -= sizeof(struct ipx); 144 m->m_data += sizeof(struct ipx); 145 } 146 if (sbappendaddr(&ipxp->ipxp_socket->so_rcv, (struct sockaddr *)&ipx_ipx, 147 m, (struct mbuf *)NULL) == 0) 148 goto bad; 149 SOCK_LOCK(ipxp->ipxp_socket); 150 sorwakeup(ipxp->ipxp_socket); 151 SOCK_UNLOCK(ipxp->ipxp_socket); 152 return; 153bad: 154 m_freem(m); 155} 156 157void 158ipx_abort(ipxp) 159 struct ipxpcb *ipxp; 160{ 161 struct socket *so = ipxp->ipxp_socket; 162 163 ipx_pcbdisconnect(ipxp); 164 SOCK_LOCK(so); 165 soisdisconnected(so); 166 SOCK_UNLOCK(so); 167} 168 169/* 170 * Drop connection, reporting 171 * the specified error. 172 */ 173void 174ipx_drop(ipxp, errno) 175 register struct ipxpcb *ipxp; 176 int errno; 177{ 178 struct socket *so = ipxp->ipxp_socket; 179 180 /* 181 * someday, in the IPX world 182 * we will generate error protocol packets 183 * announcing that the socket has gone away. 184 * 185 * XXX Probably never. IPX does not have error packets. 186 */ 187 /*if (TCPS_HAVERCVDSYN(tp->t_state)) { 188 tp->t_state = TCPS_CLOSED; 189 tcp_output(tp); 190 }*/ 191 so->so_error = errno; 192 ipx_pcbdisconnect(ipxp); 193 SOCK_LOCK(so); 194 soisdisconnected(so); 195 SOCK_UNLOCK(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 int soopts; 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 SOCK_LOCK(so); 272 if (so->so_options & SO_DONTROUTE) { 273 soopts = so->so_options & SO_BROADCAST; 274 SOCK_UNLOCK(so); 275 return (ipx_outputfl(m, (struct route *)NULL, 276 soopts | IPX_ROUTETOIF)); 277 } 278 SOCK_UNLOCK(so); 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 SOCK_LOCK(so); 322 soopts = so->so_options & SO_BROADCAST; 323 SOCK_UNLOCK(so); 324 return (ipx_outputfl(m, ro, soopts)); 325} 326 327int 328ipx_ctloutput(so, sopt) 329 struct socket *so; 330 struct sockopt *sopt; 331{ 332 struct ipxpcb *ipxp = sotoipxpcb(so); 333 int mask, error, optval; 334 short soptval; 335 struct ipx ioptval; 336 337 error = 0; 338 if (ipxp == NULL) 339 return (EINVAL); 340 341 switch (sopt->sopt_dir) { 342 case SOPT_GET: 343 switch (sopt->sopt_name) { 344 case SO_ALL_PACKETS: 345 mask = IPXP_ALL_PACKETS; 346 goto get_flags; 347 348 case SO_HEADERS_ON_INPUT: 349 mask = IPXP_RAWIN; 350 goto get_flags; 351 352 case SO_IPX_CHECKSUM: 353 mask = IPXP_CHECKSUM; 354 goto get_flags; 355 356 case SO_HEADERS_ON_OUTPUT: 357 mask = IPXP_RAWOUT; 358 get_flags: 359 soptval = ipxp->ipxp_flags & mask; 360 error = sooptcopyout(sopt, &soptval, sizeof soptval); 361 break; 362 363 case SO_DEFAULT_HEADERS: 364 ioptval.ipx_len = 0; 365 ioptval.ipx_sum = 0; 366 ioptval.ipx_tc = 0; 367 ioptval.ipx_pt = ipxp->ipxp_dpt; 368 ioptval.ipx_dna = ipxp->ipxp_faddr; 369 ioptval.ipx_sna = ipxp->ipxp_laddr; 370 error = sooptcopyout(sopt, &soptval, sizeof soptval); 371 break; 372 373 case SO_SEQNO: 374 error = sooptcopyout(sopt, &ipx_pexseq, 375 sizeof ipx_pexseq); 376 ipx_pexseq++; 377 break; 378 379 default: 380 error = EINVAL; 381 } 382 break; 383 384 case SOPT_SET: 385 switch (sopt->sopt_name) { 386 case SO_ALL_PACKETS: 387 mask = IPXP_ALL_PACKETS; 388 goto set_head; 389 390 case SO_HEADERS_ON_INPUT: 391 mask = IPXP_RAWIN; 392 goto set_head; 393 394 case SO_IPX_CHECKSUM: 395 mask = IPXP_CHECKSUM; 396 397 case SO_HEADERS_ON_OUTPUT: 398 mask = IPXP_RAWOUT; 399 set_head: 400 error = sooptcopyin(sopt, &optval, sizeof optval, 401 sizeof optval); 402 if (error) 403 break; 404 if (optval) 405 ipxp->ipxp_flags |= mask; 406 else 407 ipxp->ipxp_flags &= ~mask; 408 break; 409 410 case SO_DEFAULT_HEADERS: 411 error = sooptcopyin(sopt, &ioptval, sizeof ioptval, 412 sizeof ioptval); 413 if (error) 414 break; 415 ipxp->ipxp_dpt = ioptval.ipx_pt; 416 break; 417#ifdef IPXIP 418 case SO_IPXIP_ROUTE: 419 error = ipxip_route(so, sopt); 420 break; 421#endif /* IPXIP */ 422#ifdef IPTUNNEL 423#if 0 424 case SO_IPXTUNNEL_ROUTE: 425 error = ipxtun_route(so, sopt); 426 break; 427#endif 428#endif 429 default: 430 error = EINVAL; 431 } 432 break; 433 } 434 return (error); 435} 436 437static int 438ipx_usr_abort(so) 439 struct socket *so; 440{ 441 int s; 442 struct ipxpcb *ipxp = sotoipxpcb(so); 443 444 s = splnet(); 445 ipx_pcbdetach(ipxp); 446 splx(s); 447 SOCK_LOCK(so); 448 sotryfree(so); 449 SOCK_LOCK(so); 450 soisdisconnected(so); 451 SOCK_UNLOCK(so); 452 return (0); 453} 454 455static int 456ipx_attach(so, proto, td) 457 struct socket *so; 458 int proto; 459 struct thread *td; 460{ 461 int error; 462 int s; 463 struct ipxpcb *ipxp = sotoipxpcb(so); 464 465 if (ipxp != NULL) 466 return (EINVAL); 467 s = splnet(); 468 error = ipx_pcballoc(so, &ipxpcb, td); 469 splx(s); 470 if (error == 0) 471 error = soreserve(so, ipxsendspace, ipxrecvspace); 472 return (error); 473} 474 475static int 476ipx_bind(so, nam, td) 477 struct socket *so; 478 struct sockaddr *nam; 479 struct thread *td; 480{ 481 struct ipxpcb *ipxp = sotoipxpcb(so); 482 483 return (ipx_pcbbind(ipxp, nam, td)); 484} 485 486static int 487ipx_connect(so, nam, td) 488 struct socket *so; 489 struct sockaddr *nam; 490 struct thread *td; 491{ 492 int error; 493 int s; 494 struct ipxpcb *ipxp = sotoipxpcb(so); 495 496 if (!ipx_nullhost(ipxp->ipxp_faddr)) 497 return (EISCONN); 498 s = splnet(); 499 error = ipx_pcbconnect(ipxp, nam, td); 500 splx(s); 501 if (error == 0) { 502 SOCK_LOCK(so); 503 soisconnected(so); 504 SOCK_UNLOCK(so); 505 } 506 return (error); 507} 508 509static int 510ipx_detach(so) 511 struct socket *so; 512{ 513 int s; 514 struct ipxpcb *ipxp = sotoipxpcb(so); 515 516 if (ipxp == NULL) 517 return (ENOTCONN); 518 s = splnet(); 519 ipx_pcbdetach(ipxp); 520 splx(s); 521 return (0); 522} 523 524static int 525ipx_disconnect(so) 526 struct socket *so; 527{ 528 int s; 529 struct ipxpcb *ipxp = sotoipxpcb(so); 530 531 if (ipx_nullhost(ipxp->ipxp_faddr)) 532 return (ENOTCONN); 533 s = splnet(); 534 ipx_pcbdisconnect(ipxp); 535 splx(s); 536 SOCK_LOCK(so); 537 soisdisconnected(so); 538 SOCK_UNLOCK(so); 539 return (0); 540} 541 542int 543ipx_peeraddr(so, nam) 544 struct socket *so; 545 struct sockaddr **nam; 546{ 547 struct ipxpcb *ipxp = sotoipxpcb(so); 548 549 ipx_setpeeraddr(ipxp, nam); /* XXX what if alloc fails? */ 550 return (0); 551} 552 553static int 554ipx_send(so, flags, m, nam, control, td) 555 struct socket *so; 556 int flags; 557 struct mbuf *m; 558 struct sockaddr *nam; 559 struct mbuf *control; 560 struct thread *td; 561{ 562 int error; 563 struct ipxpcb *ipxp = sotoipxpcb(so); 564 struct ipx_addr laddr; 565 int s = 0; 566 567 if (nam != NULL) { 568 laddr = ipxp->ipxp_laddr; 569 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 570 error = EISCONN; 571 goto send_release; 572 } 573 /* 574 * Must block input while temporarily connected. 575 */ 576 s = splnet(); 577 error = ipx_pcbconnect(ipxp, nam, td); 578 if (error) { 579 splx(s); 580 goto send_release; 581 } 582 } else { 583 if (ipx_nullhost(ipxp->ipxp_faddr)) { 584 error = ENOTCONN; 585 goto send_release; 586 } 587 } 588 error = ipx_output(ipxp, m); 589 m = NULL; 590 if (nam != NULL) { 591 ipx_pcbdisconnect(ipxp); 592 splx(s); 593 ipxp->ipxp_laddr = laddr; 594 } 595 596send_release: 597 if (m != NULL) 598 m_freem(m); 599 return (error); 600} 601 602static int 603ipx_shutdown(so) 604 struct socket *so; 605{ 606 socantsendmore(so); 607 return (0); 608} 609 610int 611ipx_sockaddr(so, nam) 612 struct socket *so; 613 struct sockaddr **nam; 614{ 615 struct ipxpcb *ipxp = sotoipxpcb(so); 616 617 ipx_setsockaddr(ipxp, nam); /* XXX what if alloc fails? */ 618 return (0); 619} 620 621static int 622ripx_attach(so, proto, td) 623 struct socket *so; 624 int proto; 625 struct thread *td; 626{ 627 int error = 0; 628 int s; 629 struct ipxpcb *ipxp = sotoipxpcb(so); 630 631 if (td != NULL && (error = suser(td)) != 0) 632 return (error); 633 s = splnet(); 634 error = ipx_pcballoc(so, &ipxrawpcb, td); 635 splx(s); 636 if (error) 637 return (error); 638 error = soreserve(so, ipxsendspace, ipxrecvspace); 639 if (error) 640 return (error); 641 ipxp = sotoipxpcb(so); 642 ipxp->ipxp_faddr.x_host = ipx_broadhost; 643 ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT; 644 return (error); 645} 646