ipx_usrreq.c revision 139588
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 139588 2005-01-02 15:29:29Z 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) == 0) { 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, 160 (struct sockaddr *)&ipx_ipx, m, NULL) == 0) 161 m_freem(m); 162 else 163 sorwakeup(ipxp->ipxp_socket); 164} 165 166void 167ipx_abort(ipxp) 168 struct ipxpcb *ipxp; 169{ 170 struct socket *so = ipxp->ipxp_socket; 171 172 ipx_pcbdisconnect(ipxp); 173 soisdisconnected(so); 174} 175 176/* 177 * Drop connection, reporting 178 * the specified error. 179 */ 180void 181ipx_drop(ipxp, errno) 182 register struct ipxpcb *ipxp; 183 int errno; 184{ 185 struct socket *so = ipxp->ipxp_socket; 186 187 /* 188 * someday, in the IPX world 189 * we will generate error protocol packets 190 * announcing that the socket has gone away. 191 * 192 * XXX Probably never. IPX does not have error packets. 193 */ 194 /*if (TCPS_HAVERCVDSYN(tp->t_state)) { 195 tp->t_state = TCPS_CLOSED; 196 tcp_output(tp); 197 }*/ 198 so->so_error = errno; 199 ipx_pcbdisconnect(ipxp); 200 soisdisconnected(so); 201} 202 203static int 204ipx_output(ipxp, m0) 205 struct ipxpcb *ipxp; 206 struct mbuf *m0; 207{ 208 register struct ipx *ipx; 209 register struct socket *so; 210 register int len = 0; 211 register struct route *ro; 212 struct mbuf *m; 213 struct mbuf *mprev = NULL; 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 333 error = 0; 334 if (ipxp == NULL) 335 return (EINVAL); 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 soptval = ipxp->ipxp_flags & mask; 356 error = sooptcopyout(sopt, &soptval, sizeof soptval); 357 break; 358 359 case SO_DEFAULT_HEADERS: 360 ioptval.ipx_len = 0; 361 ioptval.ipx_sum = 0; 362 ioptval.ipx_tc = 0; 363 ioptval.ipx_pt = ipxp->ipxp_dpt; 364 ioptval.ipx_dna = ipxp->ipxp_faddr; 365 ioptval.ipx_sna = ipxp->ipxp_laddr; 366 error = sooptcopyout(sopt, &soptval, sizeof soptval); 367 break; 368 369 case SO_SEQNO: 370 error = sooptcopyout(sopt, &ipx_pexseq, 371 sizeof ipx_pexseq); 372 ipx_pexseq++; 373 break; 374 375 default: 376 error = EINVAL; 377 } 378 break; 379 380 case SOPT_SET: 381 switch (sopt->sopt_name) { 382 case SO_ALL_PACKETS: 383 mask = IPXP_ALL_PACKETS; 384 goto set_head; 385 386 case SO_HEADERS_ON_INPUT: 387 mask = IPXP_RAWIN; 388 goto set_head; 389 390 case SO_IPX_CHECKSUM: 391 mask = IPXP_CHECKSUM; 392 393 case SO_HEADERS_ON_OUTPUT: 394 mask = IPXP_RAWOUT; 395 set_head: 396 error = sooptcopyin(sopt, &optval, sizeof optval, 397 sizeof optval); 398 if (error) 399 break; 400 if (optval) 401 ipxp->ipxp_flags |= mask; 402 else 403 ipxp->ipxp_flags &= ~mask; 404 break; 405 406 case SO_DEFAULT_HEADERS: 407 error = sooptcopyin(sopt, &ioptval, sizeof ioptval, 408 sizeof ioptval); 409 if (error) 410 break; 411 ipxp->ipxp_dpt = ioptval.ipx_pt; 412 break; 413#ifdef IPXIP 414 case SO_IPXIP_ROUTE: 415 error = ipxip_route(so, sopt); 416 break; 417#endif /* IPXIP */ 418 default: 419 error = EINVAL; 420 } 421 break; 422 } 423 return (error); 424} 425 426static int 427ipx_usr_abort(so) 428 struct socket *so; 429{ 430 int s; 431 struct ipxpcb *ipxp = sotoipxpcb(so); 432 433 s = splnet(); 434 ipx_pcbdetach(ipxp); 435 splx(s); 436 soisdisconnected(so); 437 ACCEPT_LOCK(); 438 SOCK_LOCK(so); 439 sotryfree(so); 440 return (0); 441} 442 443static int 444ipx_attach(so, proto, td) 445 struct socket *so; 446 int proto; 447 struct thread *td; 448{ 449 int error; 450 int s; 451 struct ipxpcb *ipxp = sotoipxpcb(so); 452 453 if (ipxp != NULL) 454 return (EINVAL); 455 s = splnet(); 456 error = ipx_pcballoc(so, &ipxpcb_list, td); 457 splx(s); 458 if (error == 0) 459 error = soreserve(so, ipxsendspace, ipxrecvspace); 460 return (error); 461} 462 463static int 464ipx_bind(so, nam, td) 465 struct socket *so; 466 struct sockaddr *nam; 467 struct thread *td; 468{ 469 struct ipxpcb *ipxp = sotoipxpcb(so); 470 471 return (ipx_pcbbind(ipxp, nam, td)); 472} 473 474static int 475ipx_connect(so, nam, td) 476 struct socket *so; 477 struct sockaddr *nam; 478 struct thread *td; 479{ 480 int error; 481 int s; 482 struct ipxpcb *ipxp = sotoipxpcb(so); 483 484 if (!ipx_nullhost(ipxp->ipxp_faddr)) 485 return (EISCONN); 486 s = splnet(); 487 error = ipx_pcbconnect(ipxp, nam, td); 488 splx(s); 489 if (error == 0) 490 soisconnected(so); 491 return (error); 492} 493 494static int 495ipx_detach(so) 496 struct socket *so; 497{ 498 int s; 499 struct ipxpcb *ipxp = sotoipxpcb(so); 500 501 if (ipxp == NULL) 502 return (ENOTCONN); 503 s = splnet(); 504 ipx_pcbdetach(ipxp); 505 splx(s); 506 return (0); 507} 508 509static int 510ipx_disconnect(so) 511 struct socket *so; 512{ 513 int s; 514 struct ipxpcb *ipxp = sotoipxpcb(so); 515 516 if (ipx_nullhost(ipxp->ipxp_faddr)) 517 return (ENOTCONN); 518 s = splnet(); 519 ipx_pcbdisconnect(ipxp); 520 splx(s); 521 soisdisconnected(so); 522 return (0); 523} 524 525int 526ipx_peeraddr(so, nam) 527 struct socket *so; 528 struct sockaddr **nam; 529{ 530 struct ipxpcb *ipxp = sotoipxpcb(so); 531 532 ipx_setpeeraddr(ipxp, nam); 533 return (0); 534} 535 536static int 537ipx_send(so, flags, m, nam, control, td) 538 struct socket *so; 539 int flags; 540 struct mbuf *m; 541 struct sockaddr *nam; 542 struct mbuf *control; 543 struct thread *td; 544{ 545 int error; 546 struct ipxpcb *ipxp = sotoipxpcb(so); 547 struct ipx_addr laddr; 548 int s = 0; 549 550 if (nam != NULL) { 551 laddr = ipxp->ipxp_laddr; 552 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 553 error = EISCONN; 554 goto send_release; 555 } 556 /* 557 * Must block input while temporarily connected. 558 */ 559 s = splnet(); 560 error = ipx_pcbconnect(ipxp, nam, td); 561 if (error) { 562 splx(s); 563 goto send_release; 564 } 565 } else { 566 if (ipx_nullhost(ipxp->ipxp_faddr)) { 567 error = ENOTCONN; 568 goto send_release; 569 } 570 } 571 error = ipx_output(ipxp, m); 572 m = NULL; 573 if (nam != NULL) { 574 ipx_pcbdisconnect(ipxp); 575 splx(s); 576 ipxp->ipxp_laddr = laddr; 577 } 578 579send_release: 580 if (m != NULL) 581 m_freem(m); 582 return (error); 583} 584 585static int 586ipx_shutdown(so) 587 struct socket *so; 588{ 589 socantsendmore(so); 590 return (0); 591} 592 593int 594ipx_sockaddr(so, nam) 595 struct socket *so; 596 struct sockaddr **nam; 597{ 598 struct ipxpcb *ipxp = sotoipxpcb(so); 599 600 ipx_setsockaddr(ipxp, nam); 601 return (0); 602} 603 604static int 605ripx_attach(so, proto, td) 606 struct socket *so; 607 int proto; 608 struct thread *td; 609{ 610 int error = 0; 611 int s; 612 struct ipxpcb *ipxp = sotoipxpcb(so); 613 614 if (td != NULL && (error = suser(td)) != 0) 615 return (error); 616 s = splnet(); 617 error = ipx_pcballoc(so, &ipxrawpcb_list, td); 618 splx(s); 619 if (error) 620 return (error); 621 error = soreserve(so, ipxsendspace, ipxrecvspace); 622 if (error) 623 return (error); 624 ipxp = sotoipxpcb(so); 625 ipxp->ipxp_faddr.x_host = ipx_broadhost; 626 ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT; 627 return (error); 628} 629