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