ipx_usrreq.c revision 43712
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 * $Id: ipx_usrreq.c,v 1.21 1998/12/07 21:58:42 archie Exp $ 37 */ 38 39#include "opt_ipx.h" 40 41#include <sys/param.h> 42#include <sys/systm.h> 43#include <sys/kernel.h> 44#include <sys/mbuf.h> 45#include <sys/proc.h> 46#include <sys/protosw.h> 47#include <sys/socket.h> 48#include <sys/socketvar.h> 49#include <sys/sysctl.h> 50 51#include <net/if.h> 52#include <net/route.h> 53 54#include <netinet/in.h> 55 56#include <netipx/ipx.h> 57#include <netipx/ipx_pcb.h> 58#include <netipx/ipx_if.h> 59#include <netipx/ipx_var.h> 60#include <netipx/ipx_ip.h> 61 62/* 63 * IPX protocol implementation. 64 */ 65 66static int ipxsendspace = IPXSNDQ; 67SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxsendspace, CTLFLAG_RW, 68 &ipxsendspace, 0, ""); 69static int ipxrecvspace = IPXRCVQ; 70SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxrecvspace, CTLFLAG_RW, 71 &ipxrecvspace, 0, ""); 72 73static int ipx_usr_abort(struct socket *so); 74static int ipx_attach(struct socket *so, int proto, struct proc *p); 75static int ipx_bind(struct socket *so, struct sockaddr *nam, struct proc *p); 76static int ipx_connect(struct socket *so, struct sockaddr *nam, 77 struct proc *p); 78static int ipx_detach(struct socket *so); 79static int ipx_disconnect(struct socket *so); 80static int ipx_send(struct socket *so, int flags, struct mbuf *m, 81 struct sockaddr *addr, struct mbuf *control, 82 struct proc *p); 83static int ipx_shutdown(struct socket *so); 84static int ripx_attach(struct socket *so, int proto, struct proc *p); 85static int ipx_output(struct ipxpcb *ipxp, struct mbuf *m0); 86 87struct pr_usrreqs ipx_usrreqs = { 88 ipx_usr_abort, pru_accept_notsupp, ipx_attach, ipx_bind, 89 ipx_connect, pru_connect2_notsupp, ipx_control, ipx_detach, 90 ipx_disconnect, pru_listen_notsupp, ipx_peeraddr, pru_rcvd_notsupp, 91 pru_rcvoob_notsupp, ipx_send, pru_sense_null, ipx_shutdown, 92 ipx_sockaddr, sosend, soreceive, sopoll 93}; 94 95struct pr_usrreqs ripx_usrreqs = { 96 ipx_usr_abort, pru_accept_notsupp, ripx_attach, ipx_bind, 97 ipx_connect, pru_connect2_notsupp, ipx_control, ipx_detach, 98 ipx_disconnect, pru_listen_notsupp, ipx_peeraddr, pru_rcvd_notsupp, 99 pru_rcvoob_notsupp, ipx_send, pru_sense_null, ipx_shutdown, 100 ipx_sockaddr, sosend, soreceive, sopoll 101}; 102 103/* 104 * This may also be called for raw listeners. 105 */ 106void 107ipx_input(m, ipxp) 108 struct mbuf *m; 109 register struct ipxpcb *ipxp; 110{ 111 register struct ipx *ipx = mtod(m, struct ipx *); 112 struct ifnet *ifp = m->m_pkthdr.rcvif; 113 struct sockaddr_ipx ipx_ipx; 114 115 if (ipxp == NULL) 116 panic("No ipxpcb"); 117 /* 118 * Construct sockaddr format source address. 119 * Stuff source address and datagram in user buffer. 120 */ 121 ipx_ipx.sipx_len = sizeof(ipx_ipx); 122 ipx_ipx.sipx_family = AF_IPX; 123 ipx_ipx.sipx_addr = ipx->ipx_sna; 124 ipx_ipx.sipx_zero[0] = '\0'; 125 ipx_ipx.sipx_zero[1] = '\0'; 126 if (ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet) && ifp != NULL) { 127 register struct ifaddr *ifa; 128 129 for (ifa = ifp->if_addrhead.tqh_first; ifa != NULL; 130 ifa = ifa->ifa_link.tqe_next) { 131 if (ifa->ifa_addr->sa_family == AF_IPX) { 132 ipx_ipx.sipx_addr.x_net = 133 IA_SIPX(ifa)->sipx_addr.x_net; 134 break; 135 } 136 } 137 } 138 ipxp->ipxp_rpt = ipx->ipx_pt; 139 if (!(ipxp->ipxp_flags & IPXP_RAWIN) ) { 140 m->m_len -= sizeof(struct ipx); 141 m->m_pkthdr.len -= sizeof(struct ipx); 142 m->m_data += sizeof(struct ipx); 143 } 144 if (sbappendaddr(&ipxp->ipxp_socket->so_rcv, (struct sockaddr *)&ipx_ipx, 145 m, (struct mbuf *)NULL) == 0) 146 goto bad; 147 sorwakeup(ipxp->ipxp_socket); 148 return; 149bad: 150 m_freem(m); 151} 152 153void 154ipx_abort(ipxp) 155 struct ipxpcb *ipxp; 156{ 157 struct socket *so = ipxp->ipxp_socket; 158 159 ipx_pcbdisconnect(ipxp); 160 soisdisconnected(so); 161} 162 163/* 164 * Drop connection, reporting 165 * the specified error. 166 */ 167void 168ipx_drop(ipxp, errno) 169 register struct ipxpcb *ipxp; 170 int errno; 171{ 172 struct socket *so = ipxp->ipxp_socket; 173 174 /* 175 * someday, in the IPX world 176 * we will generate error protocol packets 177 * announcing that the socket has gone away. 178 * 179 * XXX Probably never. IPX does not have error packets. 180 */ 181 /*if (TCPS_HAVERCVDSYN(tp->t_state)) { 182 tp->t_state = TCPS_CLOSED; 183 tcp_output(tp); 184 }*/ 185 so->so_error = errno; 186 ipx_pcbdisconnect(ipxp); 187 soisdisconnected(so); 188} 189 190static int 191ipx_output(ipxp, m0) 192 struct ipxpcb *ipxp; 193 struct mbuf *m0; 194{ 195 register struct mbuf *m; 196 register struct ipx *ipx; 197 register struct socket *so; 198 register int len = 0; 199 register struct route *ro; 200 struct mbuf *mprev = NULL; 201 202 /* 203 * Calculate data length. 204 */ 205 for (m = m0; m != NULL; m = m->m_next) { 206 mprev = m; 207 len += m->m_len; 208 } 209 /* 210 * Make sure packet is actually of even length. 211 */ 212 213 if (len & 1) { 214 m = mprev; 215 if ((m->m_flags & M_EXT) == 0 && 216 (m->m_len + m->m_data < &m->m_dat[MLEN])) { 217 m->m_len++; 218 } else { 219 struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA); 220 221 if (m1 == NULL) { 222 m_freem(m0); 223 return (ENOBUFS); 224 } 225 m1->m_len = 1; 226 * mtod(m1, char *) = 0; 227 m->m_next = m1; 228 } 229 m0->m_pkthdr.len++; 230 } 231 232 /* 233 * Fill in mbuf with extended IPX header 234 * and addresses and length put into network format. 235 */ 236 m = m0; 237 if (ipxp->ipxp_flags & IPXP_RAWOUT) { 238 ipx = mtod(m, struct ipx *); 239 } else { 240 M_PREPEND(m, sizeof(struct ipx), M_DONTWAIT); 241 if (m == NULL) 242 return (ENOBUFS); 243 ipx = mtod(m, struct ipx *); 244 ipx->ipx_tc = 0; 245 ipx->ipx_pt = ipxp->ipxp_dpt; 246 ipx->ipx_sna = ipxp->ipxp_laddr; 247 ipx->ipx_dna = ipxp->ipxp_faddr; 248 len += sizeof(struct ipx); 249 } 250 251 ipx->ipx_len = htons((u_short)len); 252 253 if (ipxcksum) { 254 ipx->ipx_sum = 0; 255 len = ((len - 1) | 1) + 1; 256 ipx->ipx_sum = ipx_cksum(m, len); 257 } else 258 ipx->ipx_sum = 0xffff; 259 260 /* 261 * Output datagram. 262 */ 263 so = ipxp->ipxp_socket; 264 if (so->so_options & SO_DONTROUTE) 265 return (ipx_outputfl(m, (struct route *)NULL, 266 (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF)); 267 /* 268 * Use cached route for previous datagram if 269 * possible. If the previous net was the same 270 * and the interface was a broadcast medium, or 271 * if the previous destination was identical, 272 * then we are ok. 273 * 274 * NB: We don't handle broadcasts because that 275 * would require 3 subroutine calls. 276 */ 277 ro = &ipxp->ipxp_route; 278#ifdef ancient_history 279 /* 280 * I think that this will all be handled in ipx_pcbconnect! 281 */ 282 if (ro->ro_rt != NULL) { 283 if(ipx_neteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) { 284 /* 285 * This assumes we have no GH type routes 286 */ 287 if (ro->ro_rt->rt_flags & RTF_HOST) { 288 if (!ipx_hosteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) 289 goto re_route; 290 291 } 292 if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) { 293 register struct ipx_addr *dst = 294 &satoipx_addr(ro->ro_dst); 295 dst->x_host = ipx->ipx_dna.x_host; 296 } 297 /* 298 * Otherwise, we go through the same gateway 299 * and dst is already set up. 300 */ 301 } else { 302 re_route: 303 RTFREE(ro->ro_rt); 304 ro->ro_rt = NULL; 305 } 306 } 307 ipxp->ipxp_lastdst = ipx->ipx_dna; 308#endif /* ancient_history */ 309 return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST)); 310} 311 312int 313ipx_ctloutput(so, sopt) 314 struct socket *so; 315 struct sockopt *sopt; 316{ 317 struct ipxpcb *ipxp = sotoipxpcb(so); 318 int mask, error, optval; 319 short soptval; 320 struct ipx ioptval; 321 322 error = 0; 323 if (ipxp == NULL) 324 return (EINVAL); 325 326 switch (sopt->sopt_dir) { 327 case SOPT_GET: 328 switch (sopt->sopt_name) { 329 case SO_ALL_PACKETS: 330 mask = IPXP_ALL_PACKETS; 331 goto get_flags; 332 333 case SO_HEADERS_ON_INPUT: 334 mask = IPXP_RAWIN; 335 goto get_flags; 336 337 case SO_HEADERS_ON_OUTPUT: 338 mask = IPXP_RAWOUT; 339 get_flags: 340 soptval = ipxp->ipxp_flags & mask; 341 error = sooptcopyout(sopt, &soptval, sizeof soptval); 342 break; 343 344 case SO_DEFAULT_HEADERS: 345 ioptval.ipx_len = 0; 346 ioptval.ipx_sum = 0; 347 ioptval.ipx_tc = 0; 348 ioptval.ipx_pt = ipxp->ipxp_dpt; 349 ioptval.ipx_dna = ipxp->ipxp_faddr; 350 ioptval.ipx_sna = ipxp->ipxp_laddr; 351 error = sooptcopyout(sopt, &soptval, sizeof soptval); 352 break; 353 354 case SO_SEQNO: 355 error = sooptcopyout(sopt, &ipx_pexseq, 356 sizeof ipx_pexseq); 357 ipx_pexseq++; 358 break; 359 360 default: 361 error = EINVAL; 362 } 363 break; 364 365 case SOPT_SET: 366 switch (sopt->sopt_name) { 367 case SO_ALL_PACKETS: 368 mask = IPXP_ALL_PACKETS; 369 goto set_head; 370 371 case SO_HEADERS_ON_INPUT: 372 mask = IPXP_RAWIN; 373 goto set_head; 374 375 case SO_HEADERS_ON_OUTPUT: 376 mask = IPXP_RAWOUT; 377 set_head: 378 error = sooptcopyin(sopt, &optval, sizeof optval, 379 sizeof optval); 380 if (error) 381 break; 382 if (optval) 383 ipxp->ipxp_flags |= mask; 384 else 385 ipxp->ipxp_flags &= ~mask; 386 break; 387 388 case SO_DEFAULT_HEADERS: 389 error = sooptcopyin(sopt, &ioptval, sizeof ioptval, 390 sizeof ioptval); 391 if (error) 392 break; 393 ipxp->ipxp_dpt = ioptval.ipx_pt; 394 break; 395#ifdef IPXIP 396 case SO_IPXIP_ROUTE: 397 error = ipxip_route(so, sopt); 398 break; 399#endif /* IPXIP */ 400#ifdef IPTUNNEL 401#if 0 402 case SO_IPXTUNNEL_ROUTE: 403 error = ipxtun_route(so, sopt); 404 break; 405#endif 406#endif 407 default: 408 error = EINVAL; 409 } 410 break; 411 } 412 return (error); 413} 414 415static int 416ipx_usr_abort(so) 417 struct socket *so; 418{ 419 int s; 420 struct ipxpcb *ipxp = sotoipxpcb(so); 421 422 s = splnet(); 423 ipx_pcbdetach(ipxp); 424 splx(s); 425 sofree(so); 426 soisdisconnected(so); 427 return (0); 428} 429 430static int 431ipx_attach(so, proto, p) 432 struct socket *so; 433 int proto; 434 struct proc *p; 435{ 436 int error; 437 int s; 438 struct ipxpcb *ipxp = sotoipxpcb(so); 439 440 if (ipxp != NULL) 441 return (EINVAL); 442 s = splnet(); 443 error = ipx_pcballoc(so, &ipxpcb, p); 444 splx(s); 445 if (error == 0) 446 error = soreserve(so, ipxsendspace, ipxrecvspace); 447 return (error); 448} 449 450static int 451ipx_bind(so, nam, p) 452 struct socket *so; 453 struct sockaddr *nam; 454 struct proc *p; 455{ 456 struct ipxpcb *ipxp = sotoipxpcb(so); 457 458 return (ipx_pcbbind(ipxp, nam, p)); 459} 460 461static int 462ipx_connect(so, nam, p) 463 struct socket *so; 464 struct sockaddr *nam; 465 struct proc *p; 466{ 467 int error; 468 int s; 469 struct ipxpcb *ipxp = sotoipxpcb(so); 470 471 if (!ipx_nullhost(ipxp->ipxp_faddr)) 472 return (EISCONN); 473 s = splnet(); 474 error = ipx_pcbconnect(ipxp, nam, p); 475 splx(s); 476 if (error == 0) 477 soisconnected(so); 478 return (error); 479} 480 481static int 482ipx_detach(so) 483 struct socket *so; 484{ 485 int s; 486 struct ipxpcb *ipxp = sotoipxpcb(so); 487 488 if (ipxp == NULL) 489 return (ENOTCONN); 490 s = splnet(); 491 ipx_pcbdetach(ipxp); 492 splx(s); 493 return (0); 494} 495 496static int 497ipx_disconnect(so) 498 struct socket *so; 499{ 500 int s; 501 struct ipxpcb *ipxp = sotoipxpcb(so); 502 503 if (ipx_nullhost(ipxp->ipxp_faddr)) 504 return (ENOTCONN); 505 s = splnet(); 506 ipx_pcbdisconnect(ipxp); 507 splx(s); 508 soisdisconnected(so); 509 return (0); 510} 511 512int 513ipx_peeraddr(so, nam) 514 struct socket *so; 515 struct sockaddr **nam; 516{ 517 struct ipxpcb *ipxp = sotoipxpcb(so); 518 519 ipx_setpeeraddr(ipxp, nam); /* XXX what if alloc fails? */ 520 return (0); 521} 522 523static int 524ipx_send(so, flags, m, nam, control, p) 525 struct socket *so; 526 int flags; 527 struct mbuf *m; 528 struct sockaddr *nam; 529 struct mbuf *control; 530 struct proc *p; 531{ 532 int error; 533 struct ipxpcb *ipxp = sotoipxpcb(so); 534 struct ipx_addr laddr; 535 int s = 0; 536 537 if (nam != NULL) { 538 laddr = ipxp->ipxp_laddr; 539 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 540 error = EISCONN; 541 goto send_release; 542 } 543 /* 544 * Must block input while temporarily connected. 545 */ 546 s = splnet(); 547 error = ipx_pcbconnect(ipxp, nam, p); 548 if (error) { 549 splx(s); 550 goto send_release; 551 } 552 } else { 553 if (ipx_nullhost(ipxp->ipxp_faddr)) { 554 error = ENOTCONN; 555 goto send_release; 556 } 557 } 558 error = ipx_output(ipxp, m); 559 m = NULL; 560 if (nam != NULL) { 561 ipx_pcbdisconnect(ipxp); 562 splx(s); 563 ipxp->ipxp_laddr = laddr; 564 } 565 566send_release: 567 if (m != NULL) 568 m_freem(m); 569 return (error); 570} 571 572static int 573ipx_shutdown(so) 574 struct socket *so; 575{ 576 socantsendmore(so); 577 return (0); 578} 579 580int 581ipx_sockaddr(so, nam) 582 struct socket *so; 583 struct sockaddr **nam; 584{ 585 struct ipxpcb *ipxp = sotoipxpcb(so); 586 587 ipx_setsockaddr(ipxp, nam); /* XXX what if alloc fails? */ 588 return (0); 589} 590 591static int 592ripx_attach(so, proto, p) 593 struct socket *so; 594 int proto; 595 struct proc *p; 596{ 597 int error = 0; 598 int s; 599 struct ipxpcb *ipxp = sotoipxpcb(so); 600 601 if (p != NULL && (error = suser(p->p_ucred, &p->p_acflag)) != 0) 602 return (error); 603 s = splnet(); 604 error = ipx_pcballoc(so, &ipxrawpcb, p); 605 splx(s); 606 if (error) 607 return (error); 608 error = soreserve(so, ipxsendspace, ipxrecvspace); 609 if (error) 610 return (error); 611 ipxp = sotoipxpcb(so); 612 ipxp->ipxp_faddr.x_host = ipx_broadhost; 613 ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT; 614 return (error); 615} 616