]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netipx/ipx_usrreq.c
This commit was generated by cvs2svn to compensate for changes in r158782,
[FreeBSD/FreeBSD.git] / sys / netipx / ipx_usrreq.c
1 /*-
2  * Copyright (c) 1984, 1985, 1986, 1987, 1993
3  *      The Regents of the University of California.
4  * Copyright (c) 1995, Mike Mitchell
5  * Copyright (c) 2004-2006 Robert N. M. Watson
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *      @(#)ipx_usrreq.c
37  */
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 #include "opt_ipx.h"
43
44 #include <sys/param.h>
45 #include <sys/kernel.h>
46 #include <sys/lock.h>
47 #include <sys/mbuf.h>
48 #include <sys/protosw.h>
49 #include <sys/signalvar.h>
50 #include <sys/socket.h>
51 #include <sys/socketvar.h>
52 #include <sys/sx.h>
53 #include <sys/sysctl.h>
54 #include <sys/systm.h>
55
56 #include <net/if.h>
57 #include <net/route.h>
58
59 #include <netinet/in.h>
60
61 #include <netipx/ipx.h>
62 #include <netipx/ipx_if.h>
63 #include <netipx/ipx_ip.h>
64 #include <netipx/ipx_pcb.h>
65 #include <netipx/ipx_var.h>
66
67 /*
68  * IPX protocol implementation.
69  */
70
71 static int ipxsendspace = IPXSNDQ;
72 SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxsendspace, CTLFLAG_RW,
73             &ipxsendspace, 0, "");
74 static int ipxrecvspace = IPXRCVQ;
75 SYSCTL_INT(_net_ipx_ipx, OID_AUTO, ipxrecvspace, CTLFLAG_RW,
76             &ipxrecvspace, 0, "");
77
78 static  void ipx_usr_abort(struct socket *so);
79 static  int ipx_attach(struct socket *so, int proto, struct thread *td);
80 static  int ipx_bind(struct socket *so, struct sockaddr *nam, struct thread *td);
81 static  int ipx_connect(struct socket *so, struct sockaddr *nam,
82                         struct thread *td);
83 static  void ipx_detach(struct socket *so);
84 static  int ipx_disconnect(struct socket *so);
85 static  int ipx_send(struct socket *so, int flags, struct mbuf *m,
86                      struct sockaddr *addr, struct mbuf *control,
87                      struct thread *td);
88 static  int ipx_shutdown(struct socket *so);
89 static  int ripx_attach(struct socket *so, int proto, struct thread *td);
90 static  int ipx_output(struct ipxpcb *ipxp, struct mbuf *m0);
91
92 struct  pr_usrreqs ipx_usrreqs = {
93         .pru_abort =            ipx_usr_abort,
94         .pru_attach =           ipx_attach,
95         .pru_bind =             ipx_bind,
96         .pru_connect =          ipx_connect,
97         .pru_control =          ipx_control,
98         .pru_detach =           ipx_detach,
99         .pru_disconnect =       ipx_disconnect,
100         .pru_peeraddr =         ipx_peeraddr,
101         .pru_send =             ipx_send,
102         .pru_shutdown =         ipx_shutdown,
103         .pru_sockaddr =         ipx_sockaddr,
104 };
105
106 struct  pr_usrreqs ripx_usrreqs = {
107         .pru_abort =            ipx_usr_abort,
108         .pru_attach =           ripx_attach,
109         .pru_bind =             ipx_bind,
110         .pru_connect =          ipx_connect,
111         .pru_control =          ipx_control,
112         .pru_detach =           ipx_detach,
113         .pru_disconnect =       ipx_disconnect,
114         .pru_peeraddr =         ipx_peeraddr,
115         .pru_send =             ipx_send,
116         .pru_shutdown =         ipx_shutdown,
117         .pru_sockaddr =         ipx_sockaddr,
118 };
119
120 /*
121  *  This may also be called for raw listeners.
122  */
123 void
124 ipx_input(m, ipxp)
125         struct mbuf *m;
126         register struct ipxpcb *ipxp;
127 {
128         register struct ipx *ipx = mtod(m, struct ipx *);
129         struct ifnet *ifp = m->m_pkthdr.rcvif;
130         struct sockaddr_ipx ipx_ipx;
131
132         KASSERT(ipxp != NULL, ("ipx_input: NULL ipxpcb"));
133         IPX_LOCK_ASSERT(ipxp);
134         /*
135          * Construct sockaddr format source address.
136          * Stuff source address and datagram in user buffer.
137          */
138         ipx_ipx.sipx_len = sizeof(ipx_ipx);
139         ipx_ipx.sipx_family = AF_IPX;
140         ipx_ipx.sipx_addr = ipx->ipx_sna;
141         ipx_ipx.sipx_zero[0] = '\0';
142         ipx_ipx.sipx_zero[1] = '\0';
143         if (ipx_neteqnn(ipx->ipx_sna.x_net, ipx_zeronet) && ifp != NULL) {
144                 register struct ifaddr *ifa;
145
146                 for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa != NULL;
147                      ifa = TAILQ_NEXT(ifa, ifa_link)) {
148                         if (ifa->ifa_addr->sa_family == AF_IPX) {
149                                 ipx_ipx.sipx_addr.x_net =
150                                         IA_SIPX(ifa)->sipx_addr.x_net;
151                                 break;
152                         }
153                 }
154         }
155         ipxp->ipxp_rpt = ipx->ipx_pt;
156         if ((ipxp->ipxp_flags & IPXP_RAWIN) == 0) {
157                 m->m_len -= sizeof(struct ipx);
158                 m->m_pkthdr.len -= sizeof(struct ipx);
159                 m->m_data += sizeof(struct ipx);
160         }
161         if (sbappendaddr(&ipxp->ipxp_socket->so_rcv,
162             (struct sockaddr *)&ipx_ipx, m, NULL) == 0)
163                 m_freem(m);
164         else
165                 sorwakeup(ipxp->ipxp_socket);
166 }
167
168 /*
169  * Drop connection, reporting
170  * the specified error.
171  */
172 void
173 ipx_drop(ipxp, errno)
174         register struct ipxpcb *ipxp;
175         int errno;
176 {
177         struct socket *so = ipxp->ipxp_socket;
178
179         IPX_LIST_LOCK_ASSERT();
180         IPX_LOCK_ASSERT(ipxp);
181
182         /*
183          * someday, in the IPX world
184          * we will generate error protocol packets
185          * announcing that the socket has gone away.
186          *
187          * XXX Probably never. IPX does not have error packets.
188          */
189         /*if (TCPS_HAVERCVDSYN(tp->t_state)) {
190                 tp->t_state = TCPS_CLOSED;
191                 tcp_output(tp);
192         }*/
193         so->so_error = errno;
194         ipx_pcbdisconnect(ipxp);
195         soisdisconnected(so);
196 }
197
198 static int
199 ipx_output(ipxp, m0)
200         struct ipxpcb *ipxp;
201         struct mbuf *m0;
202 {
203         register struct ipx *ipx;
204         register struct socket *so;
205         register int len = 0;
206         register struct route *ro;
207         struct mbuf *m;
208         struct mbuf *mprev = NULL;
209
210         IPX_LOCK_ASSERT(ipxp);
211
212         /*
213          * Calculate data length.
214          */
215         for (m = m0; m != NULL; m = m->m_next) {
216                 mprev = m;
217                 len += m->m_len;
218         }
219         /*
220          * Make sure packet is actually of even length.
221          */
222
223         if (len & 1) {
224                 m = mprev;
225                 if ((m->m_flags & M_EXT) == 0 &&
226                         (m->m_len + m->m_data < &m->m_dat[MLEN])) {
227                         mtod(m, char*)[m->m_len++] = 0;
228                 } else {
229                         struct mbuf *m1 = m_get(M_DONTWAIT, MT_DATA);
230
231                         if (m1 == NULL) {
232                                 m_freem(m0);
233                                 return (ENOBUFS);
234                         }
235                         m1->m_len = 1;
236                         * mtod(m1, char *) = 0;
237                         m->m_next = m1;
238                 }
239                 m0->m_pkthdr.len++;
240         }
241
242         /*
243          * Fill in mbuf with extended IPX header
244          * and addresses and length put into network format.
245          */
246         m = m0;
247         if (ipxp->ipxp_flags & IPXP_RAWOUT) {
248                 ipx = mtod(m, struct ipx *);
249         } else {
250                 M_PREPEND(m, sizeof(struct ipx), M_DONTWAIT);
251                 if (m == NULL)
252                         return (ENOBUFS);
253                 ipx = mtod(m, struct ipx *);
254                 ipx->ipx_tc = 0;
255                 ipx->ipx_pt = ipxp->ipxp_dpt;
256                 ipx->ipx_sna = ipxp->ipxp_laddr;
257                 ipx->ipx_dna = ipxp->ipxp_faddr;
258                 len += sizeof(struct ipx);
259         }
260
261         ipx->ipx_len = htons((u_short)len);
262
263         if (ipxp->ipxp_flags & IPXP_CHECKSUM) {
264                 ipx->ipx_sum = ipx_cksum(m, len);
265         } else
266                 ipx->ipx_sum = 0xffff;
267
268         /*
269          * Output datagram.
270          */
271         so = ipxp->ipxp_socket;
272         if (so->so_options & SO_DONTROUTE)
273                 return (ipx_outputfl(m, (struct route *)NULL,
274                     (so->so_options & SO_BROADCAST) | IPX_ROUTETOIF));
275         /*
276          * Use cached route for previous datagram if
277          * possible.  If the previous net was the same
278          * and the interface was a broadcast medium, or
279          * if the previous destination was identical,
280          * then we are ok.
281          *
282          * NB: We don't handle broadcasts because that
283          *     would require 3 subroutine calls.
284          */
285         ro = &ipxp->ipxp_route;
286 #ifdef ancient_history
287         /*
288          * I think that this will all be handled in ipx_pcbconnect!
289          */
290         if (ro->ro_rt != NULL) {
291                 if(ipx_neteq(ipxp->ipxp_lastdst, ipx->ipx_dna)) {
292                         /*
293                          * This assumes we have no GH type routes
294                          */
295                         if (ro->ro_rt->rt_flags & RTF_HOST) {
296                                 if (!ipx_hosteq(ipxp->ipxp_lastdst, ipx->ipx_dna))
297                                         goto re_route;
298
299                         }
300                         if ((ro->ro_rt->rt_flags & RTF_GATEWAY) == 0) {
301                                 register struct ipx_addr *dst =
302                                                 &satoipx_addr(ro->ro_dst);
303                                 dst->x_host = ipx->ipx_dna.x_host;
304                         }
305                         /*
306                          * Otherwise, we go through the same gateway
307                          * and dst is already set up.
308                          */
309                 } else {
310                 re_route:
311                         RTFREE(ro->ro_rt);
312                         ro->ro_rt = NULL;
313                 }
314         }
315         ipxp->ipxp_lastdst = ipx->ipx_dna;
316 #endif /* ancient_history */
317         return (ipx_outputfl(m, ro, so->so_options & SO_BROADCAST));
318 }
319
320 int
321 ipx_ctloutput(so, sopt)
322         struct socket *so;
323         struct sockopt *sopt;
324 {
325         struct ipxpcb *ipxp = sotoipxpcb(so);
326         int mask, error, optval;
327         short soptval;
328         struct ipx ioptval;
329         long seq;
330
331         KASSERT(ipxp != NULL, ("ipx_ctloutput: ipxp == NULL"));
332         error = 0;
333
334         switch (sopt->sopt_dir) {
335         case SOPT_GET:
336                 switch (sopt->sopt_name) {
337                 case SO_ALL_PACKETS:
338                         mask = IPXP_ALL_PACKETS;
339                         goto get_flags;
340
341                 case SO_HEADERS_ON_INPUT:
342                         mask = IPXP_RAWIN;
343                         goto get_flags;
344
345                 case SO_IPX_CHECKSUM:
346                         mask = IPXP_CHECKSUM;
347                         goto get_flags;
348
349                 case SO_HEADERS_ON_OUTPUT:
350                         mask = IPXP_RAWOUT;
351                 get_flags:
352                         /* Unlocked read. */
353                         soptval = ipxp->ipxp_flags & mask;
354                         error = sooptcopyout(sopt, &soptval, sizeof soptval);
355                         break;
356
357                 case SO_DEFAULT_HEADERS:
358                         ioptval.ipx_len = 0;
359                         ioptval.ipx_sum = 0;
360                         ioptval.ipx_tc = 0;
361                         IPX_LOCK(ipxp);
362                         ioptval.ipx_pt = ipxp->ipxp_dpt;
363                         ioptval.ipx_dna = ipxp->ipxp_faddr;
364                         ioptval.ipx_sna = ipxp->ipxp_laddr;
365                         IPX_UNLOCK(ipxp);
366                         error = sooptcopyout(sopt, &soptval, sizeof soptval);
367                         break;
368
369                 case SO_SEQNO:
370                         IPX_LIST_LOCK();
371                         seq = ipx_pexseq;
372                         ipx_pexseq++;
373                         IPX_LIST_UNLOCK();
374                         error = sooptcopyout(sopt, &seq, sizeof seq);
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                         IPX_LOCK(ipxp);
403                         if (optval)
404                                 ipxp->ipxp_flags |= mask;
405                         else
406                                 ipxp->ipxp_flags &= ~mask;
407                         IPX_UNLOCK(ipxp);
408                         break;
409
410                 case SO_DEFAULT_HEADERS:
411                         error = sooptcopyin(sopt, &ioptval, sizeof ioptval,
412                                             sizeof ioptval);
413                         if (error)
414                                 break;
415                         /* Unlocked write. */
416                         ipxp->ipxp_dpt = ioptval.ipx_pt;
417                         break;
418 #ifdef IPXIP
419                 case SO_IPXIP_ROUTE:
420                         error = ipxip_route(so, sopt);
421                         break;
422 #endif /* IPXIP */
423                 default:
424                         error = EINVAL;
425                 }
426                 break;
427         }
428         return (error);
429 }
430
431 static void
432 ipx_usr_abort(so)
433         struct socket *so;
434 {
435         struct ipxpcb *ipxp = sotoipxpcb(so);
436
437         KASSERT(ipxp != NULL, ("ipx_usr_abort: ipxp == NULL"));
438         IPX_LIST_LOCK();
439         IPX_LOCK(ipxp);
440         ipx_pcbdetach(ipxp);
441         ipx_pcbfree(ipxp);
442         IPX_LIST_UNLOCK();
443         soisdisconnected(so);
444 }
445
446 static int
447 ipx_attach(so, proto, td)
448         struct socket *so;
449         int proto;
450         struct thread *td;
451 {
452 #ifdef INVARIANTS
453         struct ipxpcb *ipxp = sotoipxpcb(so);
454 #endif
455         int error;
456
457         KASSERT(ipxp == NULL, ("ipx_attach: ipxp != NULL"));
458         error = soreserve(so, ipxsendspace, ipxrecvspace);
459         if (error != 0)
460                 return (error);
461         IPX_LIST_LOCK();
462         error = ipx_pcballoc(so, &ipxpcb_list, td);
463         IPX_LIST_UNLOCK();
464         return (error);
465 }
466
467 static int
468 ipx_bind(so, nam, td)
469         struct socket *so;
470         struct sockaddr *nam;
471         struct thread *td;
472 {
473         struct ipxpcb *ipxp = sotoipxpcb(so);
474         int error;
475
476         KASSERT(ipxp != NULL, ("ipx_bind: ipxp == NULL"));
477         IPX_LIST_LOCK();
478         IPX_LOCK(ipxp);
479         error = ipx_pcbbind(ipxp, nam, td);
480         IPX_UNLOCK(ipxp);
481         IPX_LIST_UNLOCK();
482         return (error);
483 }
484
485 static int
486 ipx_connect(so, nam, td)
487         struct socket *so;
488         struct sockaddr *nam;
489         struct thread *td;
490 {
491         struct ipxpcb *ipxp = sotoipxpcb(so);
492         int error;
493
494         KASSERT(ipxp != NULL, ("ipx_connect: ipxp == NULL"));
495         IPX_LIST_LOCK();
496         IPX_LOCK(ipxp);
497         if (!ipx_nullhost(ipxp->ipxp_faddr)) {
498                 error = EISCONN;
499                 goto out;
500         }
501         error = ipx_pcbconnect(ipxp, nam, td);
502         if (error == 0)
503                 soisconnected(so);
504 out:
505         IPX_UNLOCK(ipxp);
506         IPX_LIST_UNLOCK();
507         return (error);
508 }
509
510 static void
511 ipx_detach(so)
512         struct socket *so;
513 {
514         struct ipxpcb *ipxp = sotoipxpcb(so);
515
516         KASSERT(ipxp != NULL, ("ipx_detach: ipxp == NULL"));
517         IPX_LIST_LOCK();
518         IPX_LOCK(ipxp);
519         ipx_pcbdetach(ipxp);
520         ipx_pcbfree(ipxp);
521         IPX_LIST_UNLOCK();
522 }
523
524 static int
525 ipx_disconnect(so)
526         struct socket *so;
527 {
528         struct ipxpcb *ipxp = sotoipxpcb(so);
529         int error;
530
531         KASSERT(ipxp != NULL, ("ipx_disconnect: ipxp == NULL"));
532         IPX_LIST_LOCK();
533         IPX_LOCK(ipxp);
534         error = 0;
535         if (ipx_nullhost(ipxp->ipxp_faddr)) {
536                 error = ENOTCONN;
537                 goto out;
538         }
539         ipx_pcbdisconnect(ipxp);
540         soisdisconnected(so);
541 out:
542         IPX_UNLOCK(ipxp);
543         IPX_LIST_UNLOCK();
544         return (0);
545 }
546
547 int
548 ipx_peeraddr(so, nam)
549         struct socket *so;
550         struct sockaddr **nam;
551 {
552         struct ipxpcb *ipxp = sotoipxpcb(so);
553
554         KASSERT(ipxp != NULL, ("ipx_peeraddr: ipxp == NULL"));
555         ipx_setpeeraddr(ipxp, nam);
556         return (0);
557 }
558
559 static int
560 ipx_send(so, flags, m, nam, control, td)
561         struct socket *so;
562         int flags;
563         struct mbuf *m;
564         struct sockaddr *nam;
565         struct mbuf *control;
566         struct thread *td;
567 {
568         int error;
569         struct ipxpcb *ipxp = sotoipxpcb(so);
570         struct ipx_addr laddr;
571
572         KASSERT(ipxp != NULL, ("ipxp_send: ipxp == NULL"));
573         /*
574          * Attempt to only acquire the necessary locks: if the socket is
575          * already connected, we don't need to hold the IPX list lock to be
576          * used by ipx_pcbconnect() and ipx_pcbdisconnect(), just the IPX
577          * pcb lock.
578          */
579         if (nam != NULL) {
580                 IPX_LIST_LOCK();
581                 IPX_LOCK(ipxp);
582                 laddr = ipxp->ipxp_laddr;
583                 if (!ipx_nullhost(ipxp->ipxp_faddr)) {
584                         IPX_UNLOCK(ipxp);
585                         IPX_LIST_UNLOCK();
586                         error = EISCONN;
587                         goto send_release;
588                 }
589                 /*
590                  * Must block input while temporarily connected.
591                  */
592                 error = ipx_pcbconnect(ipxp, nam, td);
593                 if (error) {
594                         IPX_UNLOCK(ipxp);
595                         IPX_LIST_UNLOCK();
596                         goto send_release;
597                 }
598         } else {
599                 IPX_LOCK(ipxp);
600                 if (ipx_nullhost(ipxp->ipxp_faddr)) {
601                         IPX_UNLOCK(ipxp);
602                         error = ENOTCONN;
603                         goto send_release;
604                 }
605         }
606         error = ipx_output(ipxp, m);
607         m = NULL;
608         if (nam != NULL) {
609                 ipx_pcbdisconnect(ipxp);
610                 ipxp->ipxp_laddr = laddr;
611                 IPX_UNLOCK(ipxp);
612                 IPX_LIST_UNLOCK();
613         } else
614                 IPX_UNLOCK(ipxp);
615
616 send_release:
617         if (m != NULL)
618                 m_freem(m);
619         return (error);
620 }
621
622 static int
623 ipx_shutdown(so)
624         struct socket *so;
625 {
626
627         KASSERT(so->so_pcb != NULL, ("ipx_shutdown: so_pcb == NULL"));
628         socantsendmore(so);
629         return (0);
630 }
631
632 int
633 ipx_sockaddr(so, nam)
634         struct socket *so;
635         struct sockaddr **nam;
636 {
637         struct ipxpcb *ipxp = sotoipxpcb(so);
638
639         KASSERT(ipxp != NULL, ("ipx_sockaddr: ipxp == NULL"));
640         ipx_setsockaddr(ipxp, nam);
641         return (0);
642 }
643
644 static int
645 ripx_attach(so, proto, td)
646         struct socket *so;
647         int proto;
648         struct thread *td;
649 {
650         int error = 0;
651         struct ipxpcb *ipxp = sotoipxpcb(so);
652
653         KASSERT(ipxp == NULL, ("ripx_attach: ipxp != NULL"));
654         if (td != NULL && (error = suser(td)) != 0)
655                 return (error);
656         /*
657          * We hold the IPX list lock for the duration as address parameters
658          * of the IPX pcb are changed.  Since no one else holds a reference
659          * to the ipxpcb yet, we don't need the ipxpcb lock here.
660          */
661         IPX_LIST_LOCK();
662         error = ipx_pcballoc(so, &ipxrawpcb_list, td);
663         if (error)
664                 goto out;
665         ipxp = sotoipxpcb(so);
666         error = soreserve(so, ipxsendspace, ipxrecvspace);
667         if (error)
668                 goto out;
669         ipxp->ipxp_faddr.x_host = ipx_broadhost;
670         ipxp->ipxp_flags = IPXP_RAWIN | IPXP_RAWOUT;
671 out:
672         IPX_LIST_UNLOCK();
673         return (error);
674 }