ipx_usrreq.c revision 139587
1/* 2 * Copyright (c) 2004 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 139587 2005-01-02 15:25:59Z 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: NUL ipxpcb")); 132 /* 133 * Construct sockaddr format source address. 134 * Stuff source address and datagram in user buffer. 135 */ 136 ipx_ipx.sipx_len = sizeof(ipx_ipx); 137 ipx_ipx.sipx_family = AF_IPX; 138 ipx_ipx.sipx_addr = ipx->ipx_sna; 139 ipx_ipx.sipx_zero[0] = '\0'; 140 ipx_ipx.sipx_zero[1] = '\0'; 141 if (ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet) && ifp != NULL) { 142 register struct ifaddr *ifa; 143 144 for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa != NULL; 145 ifa = TAILQ_NEXT(ifa, ifa_link)) { 146 if (ifa->ifa_addr->sa_family == AF_IPX) { 147 ipx_ipx.sipx_addr.x_net = 148 IA_SIPX(ifa)->sipx_addr.x_net; 149 break; 150 } 151 } 152 } 153 ipxp->ipxp_rpt = ipx->ipx_pt; 154 if (!(ipxp->ipxp_flags & IPXP_RAWIN) ) { 155 m->m_len -= sizeof(struct ipx); 156 m->m_pkthdr.len -= sizeof(struct ipx); 157 m->m_data += sizeof(struct ipx); 158 } 159 if (sbappendaddr(&ipxp->ipxp_socket->so_rcv, (struct sockaddr *)&ipx_ipx, 160 m, (struct mbuf *)NULL) == 0) 161 goto bad; 162 sorwakeup(ipxp->ipxp_socket); 163 return; 164bad: 165 m_freem(m); 166} 167 168void 169ipx_abort(ipxp) 170 struct ipxpcb *ipxp; 171{ 172 struct socket *so = ipxp->ipxp_socket; 173 174 ipx_pcbdisconnect(ipxp); 175 soisdisconnected(so); 176} 177 178/* 179 * Drop connection, reporting 180 * the specified error. 181 */ 182void 183ipx_drop(ipxp, errno) 184 register struct ipxpcb *ipxp; 185 int errno; 186{ 187 struct socket *so = ipxp->ipxp_socket; 188 189 /* 190 * someday, in the IPX world 191 * we will generate error protocol packets 192 * announcing that the socket has gone away. 193 * 194 * XXX Probably never. IPX does not have error packets. 195 */ 196 /*if (TCPS_HAVERCVDSYN(tp->t_state)) { 197 tp->t_state = TCPS_CLOSED; 198 tcp_output(tp); 199 }*/ 200 so->so_error = errno; 201 ipx_pcbdisconnect(ipxp); 202 soisdisconnected(so); 203} 204 205static int 206ipx_output(ipxp, m0) 207 struct ipxpcb *ipxp; 208 struct mbuf *m0; 209{ 210 register struct ipx *ipx; 211 register struct socket *so; 212 register int len = 0; 213 register struct route *ro; 214 struct mbuf *m; 215 struct mbuf *mprev = NULL; 216 217 /* 218 * Calculate data length. 219 */ 220 for (m = m0; m != NULL; m = m->m_next) { 221 mprev = m; 222 len += m->m_len; 223 } 224 /* 225 * Make sure packet is actually of even length. 226 */ 227 228 if (len & 1) { 229 m = mprev; 230 if ((m->m_flags & M_EXT) == 0 && 231 (m->m_len + m->m_data < &m->m_dat[MLEN])) { 232 mtod(m, char*)[m->m_len++] = 0; 233 } else { 234 struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA); 235 236 if (m1 == NULL) { 237 m_freem(m0); 238 return (ENOBUFS); 239 } 240 m1->m_len = 1; 241 * mtod(m1, char *) = 0; 242 m->m_next = m1; 243 } 244 m0->m_pkthdr.len++; 245 } 246 247 /* 248 * Fill in mbuf with extended IPX header 249 * and addresses and length put into network format. 250 */ 251 m = m0; 252 if (ipxp->ipxp_flags & IPXP_RAWOUT) { 253 ipx = mtod(m, struct ipx *); 254 } else { 255 M_PREPEND(m, sizeof(struct ipx), M_DONTWAIT); 256 if (m == NULL) 257 return (ENOBUFS); 258 ipx = mtod(m, struct ipx *); 259 ipx->ipx_tc = 0; 260 ipx->ipx_pt = ipxp->ipxp_dpt; 261 ipx->ipx_sna = ipxp->ipxp_laddr; 262 ipx->ipx_dna = ipxp->ipxp_faddr; 263 len += sizeof(struct ipx); 264 } 265 266 ipx->ipx_len = htons((u_short)len); 267 268 if (ipxp->ipxp_flags & IPXP_CHECKSUM) { 269 ipx->ipx_sum = ipx_cksum(m, len); 270 } else 271 ipx->ipx_sum = 0xffff; 272 273 /* 274 * Output datagram. 275 */ 276 so = ipxp->ipxp_socket; 277 if (so->so_options & SO_DONTROUTE) 278 return (ipx_outputfl(m, (struct route *)NULL, 279 (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF)); 280 /* 281 * Use cached route for previous datagram if 282 * possible. If the previous net was the same 283 * and the interface was a broadcast medium, or 284 * if the previous destination was identical, 285 * then we are ok. 286 * 287 * NB: We don't handle broadcasts because that 288 * would require 3 subroutine calls. 289 */ 290 ro = &ipxp->ipxp_route; 291#ifdef ancient_history 292 /* 293 * I think that this will all be handled in ipx_pcbconnect! 294 */ 295 if (ro->ro_rt != NULL) { 296 if(ipx_neteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) { 297 /* 298 * This assumes we have no GH type routes 299 */ 300 if (ro->ro_rt->rt_flags & RTF_HOST) { 301 if (!ipx_hosteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) 302 goto re_route; 303 304 } 305 if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) { 306 register struct ipx_addr *dst = 307 &satoipx_addr(ro->ro_dst); 308 dst->x_host = ipx->ipx_dna.x_host; 309 } 310 /* 311 * Otherwise, we go through the same gateway 312 * and dst is already set up. 313 */ 314 } else { 315 re_route: 316 RTFREE(ro->ro_rt); 317 ro->ro_rt = NULL; 318 } 319 } 320 ipxp->ipxp_lastdst = ipx->ipx_dna; 321#endif /* ancient_history */ 322 return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST)); 323} 324 325int 326ipx_ctloutput(so, sopt) 327 struct socket *so; 328 struct sockopt *sopt; 329{ 330 struct ipxpcb *ipxp = sotoipxpcb(so); 331 int mask, error, optval; 332 short soptval; 333 struct ipx ioptval; 334 335 error = 0; 336 if (ipxp == NULL) 337 return (EINVAL); 338 339 switch (sopt->sopt_dir) { 340 case SOPT_GET: 341 switch (sopt->sopt_name) { 342 case SO_ALL_PACKETS: 343 mask = IPXP_ALL_PACKETS; 344 goto get_flags; 345 346 case SO_HEADERS_ON_INPUT: 347 mask = IPXP_RAWIN; 348 goto get_flags; 349 350 case SO_IPX_CHECKSUM: 351 mask = IPXP_CHECKSUM; 352 goto get_flags; 353 354 case SO_HEADERS_ON_OUTPUT: 355 mask = IPXP_RAWOUT; 356 get_flags: 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 ioptval.ipx_pt = ipxp->ipxp_dpt; 366 ioptval.ipx_dna = ipxp->ipxp_faddr; 367 ioptval.ipx_sna = ipxp->ipxp_laddr; 368 error = sooptcopyout(sopt, &soptval, sizeof soptval); 369 break; 370 371 case SO_SEQNO: 372 error = sooptcopyout(sopt, &ipx_pexseq, 373 sizeof ipx_pexseq); 374 ipx_pexseq++; 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 if (optval) 403 ipxp->ipxp_flags |= mask; 404 else 405 ipxp->ipxp_flags &= ~mask; 406 break; 407 408 case SO_DEFAULT_HEADERS: 409 error = sooptcopyin(sopt, &ioptval, sizeof ioptval, 410 sizeof ioptval); 411 if (error) 412 break; 413 ipxp->ipxp_dpt = ioptval.ipx_pt; 414 break; 415#ifdef IPXIP 416 case SO_IPXIP_ROUTE: 417 error = ipxip_route(so, sopt); 418 break; 419#endif /* IPXIP */ 420 default: 421 error = EINVAL; 422 } 423 break; 424 } 425 return (error); 426} 427 428static int 429ipx_usr_abort(so) 430 struct socket *so; 431{ 432 int s; 433 struct ipxpcb *ipxp = sotoipxpcb(so); 434 435 s = splnet(); 436 ipx_pcbdetach(ipxp); 437 splx(s); 438 soisdisconnected(so); 439 ACCEPT_LOCK(); 440 SOCK_LOCK(so); 441 sotryfree(so); 442 return (0); 443} 444 445static int 446ipx_attach(so, proto, td) 447 struct socket *so; 448 int proto; 449 struct thread *td; 450{ 451 int error; 452 int s; 453 struct ipxpcb *ipxp = sotoipxpcb(so); 454 455 if (ipxp != NULL) 456 return (EINVAL); 457 s = splnet(); 458 error = ipx_pcballoc(so, &ipxpcb_list, td); 459 splx(s); 460 if (error == 0) 461 error = soreserve(so, ipxsendspace, ipxrecvspace); 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 473 return (ipx_pcbbind(ipxp, nam, td)); 474} 475 476static int 477ipx_connect(so, nam, td) 478 struct socket *so; 479 struct sockaddr *nam; 480 struct thread *td; 481{ 482 int error; 483 int s; 484 struct ipxpcb *ipxp = sotoipxpcb(so); 485 486 if (!ipx_nullhost(ipxp->ipxp_faddr)) 487 return (EISCONN); 488 s = splnet(); 489 error = ipx_pcbconnect(ipxp, nam, td); 490 splx(s); 491 if (error == 0) 492 soisconnected(so); 493 return (error); 494} 495 496static int 497ipx_detach(so) 498 struct socket *so; 499{ 500 int s; 501 struct ipxpcb *ipxp = sotoipxpcb(so); 502 503 if (ipxp == NULL) 504 return (ENOTCONN); 505 s = splnet(); 506 ipx_pcbdetach(ipxp); 507 splx(s); 508 return (0); 509} 510 511static int 512ipx_disconnect(so) 513 struct socket *so; 514{ 515 int s; 516 struct ipxpcb *ipxp = sotoipxpcb(so); 517 518 if (ipx_nullhost(ipxp->ipxp_faddr)) 519 return (ENOTCONN); 520 s = splnet(); 521 ipx_pcbdisconnect(ipxp); 522 splx(s); 523 soisdisconnected(so); 524 return (0); 525} 526 527int 528ipx_peeraddr(so, nam) 529 struct socket *so; 530 struct sockaddr **nam; 531{ 532 struct ipxpcb *ipxp = sotoipxpcb(so); 533 534 ipx_setpeeraddr(ipxp, nam); 535 return (0); 536} 537 538static int 539ipx_send(so, flags, m, nam, control, td) 540 struct socket *so; 541 int flags; 542 struct mbuf *m; 543 struct sockaddr *nam; 544 struct mbuf *control; 545 struct thread *td; 546{ 547 int error; 548 struct ipxpcb *ipxp = sotoipxpcb(so); 549 struct ipx_addr laddr; 550 int s = 0; 551 552 if (nam != NULL) { 553 laddr = ipxp->ipxp_laddr; 554 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 555 error = EISCONN; 556 goto send_release; 557 } 558 /* 559 * Must block input while temporarily connected. 560 */ 561 s = splnet(); 562 error = ipx_pcbconnect(ipxp, nam, td); 563 if (error) { 564 splx(s); 565 goto send_release; 566 } 567 } else { 568 if (ipx_nullhost(ipxp->ipxp_faddr)) { 569 error = ENOTCONN; 570 goto send_release; 571 } 572 } 573 error = ipx_output(ipxp, m); 574 m = NULL; 575 if (nam != NULL) { 576 ipx_pcbdisconnect(ipxp); 577 splx(s); 578 ipxp->ipxp_laddr = laddr; 579 } 580 581send_release: 582 if (m != NULL) 583 m_freem(m); 584 return (error); 585} 586 587static int 588ipx_shutdown(so) 589 struct socket *so; 590{ 591 socantsendmore(so); 592 return (0); 593} 594 595int 596ipx_sockaddr(so, nam) 597 struct socket *so; 598 struct sockaddr **nam; 599{ 600 struct ipxpcb *ipxp = sotoipxpcb(so); 601 602 ipx_setsockaddr(ipxp, nam); 603 return (0); 604} 605 606static int 607ripx_attach(so, proto, td) 608 struct socket *so; 609 int proto; 610 struct thread *td; 611{ 612 int error = 0; 613 int s; 614 struct ipxpcb *ipxp = sotoipxpcb(so); 615 616 if (td != NULL && (error = suser(td)) != 0) 617 return (error); 618 s = splnet(); 619 error = ipx_pcballoc(so, &ipxrawpcb_list, td); 620 splx(s); 621 if (error) 622 return (error); 623 error = soreserve(so, ipxsendspace, ipxrecvspace); 624 if (error) 625 return (error); 626 ipxp = sotoipxpcb(so); 627 ipxp->ipxp_faddr.x_host = ipx_broadhost; 628 ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT; 629 return (error); 630} 631