ipx_usrreq.c revision 50519
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 50519 1999-08-28 18:21:55Z jhay $ 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 mtod(m, char*)[m->m_len++] = 0; 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 (ipxp->ipxp_flags & IPXP_CHECKSUM) { 254 ipx->ipx_sum = ipx_cksum(m, len); 255 } else 256 ipx->ipx_sum = 0xffff; 257 258 /* 259 * Output datagram. 260 */ 261 so = ipxp->ipxp_socket; 262 if (so->so_options & SO_DONTROUTE) 263 return (ipx_outputfl(m, (struct route *)NULL, 264 (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF)); 265 /* 266 * Use cached route for previous datagram if 267 * possible. If the previous net was the same 268 * and the interface was a broadcast medium, or 269 * if the previous destination was identical, 270 * then we are ok. 271 * 272 * NB: We don't handle broadcasts because that 273 * would require 3 subroutine calls. 274 */ 275 ro = &ipxp->ipxp_route; 276#ifdef ancient_history 277 /* 278 * I think that this will all be handled in ipx_pcbconnect! 279 */ 280 if (ro->ro_rt != NULL) { 281 if(ipx_neteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) { 282 /* 283 * This assumes we have no GH type routes 284 */ 285 if (ro->ro_rt->rt_flags & RTF_HOST) { 286 if (!ipx_hosteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) 287 goto re_route; 288 289 } 290 if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) { 291 register struct ipx_addr *dst = 292 &satoipx_addr(ro->ro_dst); 293 dst->x_host = ipx->ipx_dna.x_host; 294 } 295 /* 296 * Otherwise, we go through the same gateway 297 * and dst is already set up. 298 */ 299 } else { 300 re_route: 301 RTFREE(ro->ro_rt); 302 ro->ro_rt = NULL; 303 } 304 } 305 ipxp->ipxp_lastdst = ipx->ipx_dna; 306#endif /* ancient_history */ 307 return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST)); 308} 309 310int 311ipx_ctloutput(so, sopt) 312 struct socket *so; 313 struct sockopt *sopt; 314{ 315 struct ipxpcb *ipxp = sotoipxpcb(so); 316 int mask, error, optval; 317 short soptval; 318 struct ipx ioptval; 319 320 error = 0; 321 if (ipxp == NULL) 322 return (EINVAL); 323 324 switch (sopt->sopt_dir) { 325 case SOPT_GET: 326 switch (sopt->sopt_name) { 327 case SO_ALL_PACKETS: 328 mask = IPXP_ALL_PACKETS; 329 goto get_flags; 330 331 case SO_HEADERS_ON_INPUT: 332 mask = IPXP_RAWIN; 333 goto get_flags; 334 335 case SO_IPX_CHECKSUM: 336 mask = IPXP_CHECKSUM; 337 goto get_flags; 338 339 case SO_HEADERS_ON_OUTPUT: 340 mask = IPXP_RAWOUT; 341 get_flags: 342 soptval = ipxp->ipxp_flags & mask; 343 error = sooptcopyout(sopt, &soptval, sizeof soptval); 344 break; 345 346 case SO_DEFAULT_HEADERS: 347 ioptval.ipx_len = 0; 348 ioptval.ipx_sum = 0; 349 ioptval.ipx_tc = 0; 350 ioptval.ipx_pt = ipxp->ipxp_dpt; 351 ioptval.ipx_dna = ipxp->ipxp_faddr; 352 ioptval.ipx_sna = ipxp->ipxp_laddr; 353 error = sooptcopyout(sopt, &soptval, sizeof soptval); 354 break; 355 356 case SO_SEQNO: 357 error = sooptcopyout(sopt, &ipx_pexseq, 358 sizeof ipx_pexseq); 359 ipx_pexseq++; 360 break; 361 362 default: 363 error = EINVAL; 364 } 365 break; 366 367 case SOPT_SET: 368 switch (sopt->sopt_name) { 369 case SO_ALL_PACKETS: 370 mask = IPXP_ALL_PACKETS; 371 goto set_head; 372 373 case SO_HEADERS_ON_INPUT: 374 mask = IPXP_RAWIN; 375 goto set_head; 376 377 case SO_IPX_CHECKSUM: 378 mask = IPXP_CHECKSUM; 379 380 case SO_HEADERS_ON_OUTPUT: 381 mask = IPXP_RAWOUT; 382 set_head: 383 error = sooptcopyin(sopt, &optval, sizeof optval, 384 sizeof optval); 385 if (error) 386 break; 387 if (optval) 388 ipxp->ipxp_flags |= mask; 389 else 390 ipxp->ipxp_flags &= ~mask; 391 break; 392 393 case SO_DEFAULT_HEADERS: 394 error = sooptcopyin(sopt, &ioptval, sizeof ioptval, 395 sizeof ioptval); 396 if (error) 397 break; 398 ipxp->ipxp_dpt = ioptval.ipx_pt; 399 break; 400#ifdef IPXIP 401 case SO_IPXIP_ROUTE: 402 error = ipxip_route(so, sopt); 403 break; 404#endif /* IPXIP */ 405#ifdef IPTUNNEL 406#if 0 407 case SO_IPXTUNNEL_ROUTE: 408 error = ipxtun_route(so, sopt); 409 break; 410#endif 411#endif 412 default: 413 error = EINVAL; 414 } 415 break; 416 } 417 return (error); 418} 419 420static int 421ipx_usr_abort(so) 422 struct socket *so; 423{ 424 int s; 425 struct ipxpcb *ipxp = sotoipxpcb(so); 426 427 s = splnet(); 428 ipx_pcbdetach(ipxp); 429 splx(s); 430 sofree(so); 431 soisdisconnected(so); 432 return (0); 433} 434 435static int 436ipx_attach(so, proto, p) 437 struct socket *so; 438 int proto; 439 struct proc *p; 440{ 441 int error; 442 int s; 443 struct ipxpcb *ipxp = sotoipxpcb(so); 444 445 if (ipxp != NULL) 446 return (EINVAL); 447 s = splnet(); 448 error = ipx_pcballoc(so, &ipxpcb, p); 449 splx(s); 450 if (error == 0) 451 error = soreserve(so, ipxsendspace, ipxrecvspace); 452 return (error); 453} 454 455static int 456ipx_bind(so, nam, p) 457 struct socket *so; 458 struct sockaddr *nam; 459 struct proc *p; 460{ 461 struct ipxpcb *ipxp = sotoipxpcb(so); 462 463 return (ipx_pcbbind(ipxp, nam, p)); 464} 465 466static int 467ipx_connect(so, nam, p) 468 struct socket *so; 469 struct sockaddr *nam; 470 struct proc *p; 471{ 472 int error; 473 int s; 474 struct ipxpcb *ipxp = sotoipxpcb(so); 475 476 if (!ipx_nullhost(ipxp->ipxp_faddr)) 477 return (EISCONN); 478 s = splnet(); 479 error = ipx_pcbconnect(ipxp, nam, p); 480 splx(s); 481 if (error == 0) 482 soisconnected(so); 483 return (error); 484} 485 486static int 487ipx_detach(so) 488 struct socket *so; 489{ 490 int s; 491 struct ipxpcb *ipxp = sotoipxpcb(so); 492 493 if (ipxp == NULL) 494 return (ENOTCONN); 495 s = splnet(); 496 ipx_pcbdetach(ipxp); 497 splx(s); 498 return (0); 499} 500 501static int 502ipx_disconnect(so) 503 struct socket *so; 504{ 505 int s; 506 struct ipxpcb *ipxp = sotoipxpcb(so); 507 508 if (ipx_nullhost(ipxp->ipxp_faddr)) 509 return (ENOTCONN); 510 s = splnet(); 511 ipx_pcbdisconnect(ipxp); 512 splx(s); 513 soisdisconnected(so); 514 return (0); 515} 516 517int 518ipx_peeraddr(so, nam) 519 struct socket *so; 520 struct sockaddr **nam; 521{ 522 struct ipxpcb *ipxp = sotoipxpcb(so); 523 524 ipx_setpeeraddr(ipxp, nam); /* XXX what if alloc fails? */ 525 return (0); 526} 527 528static int 529ipx_send(so, flags, m, nam, control, p) 530 struct socket *so; 531 int flags; 532 struct mbuf *m; 533 struct sockaddr *nam; 534 struct mbuf *control; 535 struct proc *p; 536{ 537 int error; 538 struct ipxpcb *ipxp = sotoipxpcb(so); 539 struct ipx_addr laddr; 540 int s = 0; 541 542 if (nam != NULL) { 543 laddr = ipxp->ipxp_laddr; 544 if (!ipx_nullhost(ipxp->ipxp_faddr)) { 545 error = EISCONN; 546 goto send_release; 547 } 548 /* 549 * Must block input while temporarily connected. 550 */ 551 s = splnet(); 552 error = ipx_pcbconnect(ipxp, nam, p); 553 if (error) { 554 splx(s); 555 goto send_release; 556 } 557 } else { 558 if (ipx_nullhost(ipxp->ipxp_faddr)) { 559 error = ENOTCONN; 560 goto send_release; 561 } 562 } 563 error = ipx_output(ipxp, m); 564 m = NULL; 565 if (nam != NULL) { 566 ipx_pcbdisconnect(ipxp); 567 splx(s); 568 ipxp->ipxp_laddr = laddr; 569 } 570 571send_release: 572 if (m != NULL) 573 m_freem(m); 574 return (error); 575} 576 577static int 578ipx_shutdown(so) 579 struct socket *so; 580{ 581 socantsendmore(so); 582 return (0); 583} 584 585int 586ipx_sockaddr(so, nam) 587 struct socket *so; 588 struct sockaddr **nam; 589{ 590 struct ipxpcb *ipxp = sotoipxpcb(so); 591 592 ipx_setsockaddr(ipxp, nam); /* XXX what if alloc fails? */ 593 return (0); 594} 595 596static int 597ripx_attach(so, proto, p) 598 struct socket *so; 599 int proto; 600 struct proc *p; 601{ 602 int error = 0; 603 int s; 604 struct ipxpcb *ipxp = sotoipxpcb(so); 605 606 if (p != NULL && (error = suser(p)) != 0) 607 return (error); 608 s = splnet(); 609 error = ipx_pcballoc(so, &ipxrawpcb, p); 610 splx(s); 611 if (error) 612 return (error); 613 error = soreserve(so, ipxsendspace, ipxrecvspace); 614 if (error) 615 return (error); 616 ipxp = sotoipxpcb(so); 617 ipxp->ipxp_faddr.x_host = ipx_broadhost; 618 ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT; 619 return (error); 620} 621