Annotation of sys/netatalk/at_control.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: at_control.c,v 1.10 2007/04/10 17:47:55 miod Exp $ */
2:
3: /*
4: * Copyright (c) 1990,1991 Regents of The University of Michigan.
5: * All Rights Reserved.
6: */
7:
8: /*
9: * The following is the contents of the COPYRIGHT file from the
10: * netatalk-1.4a2 distribution, from which this file is derived.
11: */
12: /*
13: * Copyright (c) 1990,1996 Regents of The University of Michigan.
14: *
15: * All Rights Reserved.
16: *
17: * Permission to use, copy, modify, and distribute this software and
18: * its documentation for any purpose and without fee is hereby granted,
19: * provided that the above copyright notice appears in all copies and
20: * that both that copyright notice and this permission notice appear
21: * in supporting documentation, and that the name of The University
22: * of Michigan not be used in advertising or publicity pertaining to
23: * distribution of the software without specific, written prior
24: * permission. This software is supplied as is without expressed or
25: * implied warranties of any kind.
26: *
27: * This product includes software developed by the University of
28: * California, Berkeley and its contributors.
29: *
30: * Solaris code is encumbered by the following:
31: *
32: * Copyright (C) 1996 by Sun Microsystems Computer Co.
33: *
34: * Permission to use, copy, modify, and distribute this software and
35: * its documentation for any purpose and without fee is hereby
36: * granted, provided that the above copyright notice appear in all
37: * copies and that both that copyright notice and this permission
38: * notice appear in supporting documentation. This software is
39: * provided "as is" without express or implied warranty.
40: *
41: * Research Systems Unix Group
42: * The University of Michigan
43: * c/o Wesley Craig
44: * 535 W. William Street
45: * Ann Arbor, Michigan
46: * +1-313-764-2278
47: * netatalk@umich.edu
48: */
49: /*
50: * None of the Solaris code mentioned is included in OpenBSD.
51: * This code also relies heavily on previous effort in FreeBSD and NetBSD.
52: */
53:
54: #include <sys/param.h>
55: #include <sys/systm.h>
56: #include <sys/kernel.h>
57: #include <sys/malloc.h>
58: #include <sys/mbuf.h>
59: #include <sys/protosw.h>
60: #include <sys/socket.h>
61: #include <sys/ioctl.h>
62: #include <sys/errno.h>
63: #include <sys/syslog.h>
64: #include <sys/proc.h>
65: #include <sys/timeout.h>
66:
67: #include <net/if.h>
68: #include <net/route.h>
69: #include <netinet/in.h>
70: #undef s_net
71: #include <netinet/if_ether.h>
72: #include <net/if_llc.h>
73:
74: #include <netatalk/at.h>
75: #include <netatalk/at_var.h>
76: #include <netatalk/aarp.h>
77: #include <netatalk/phase2.h>
78: #include <netatalk/at_extern.h>
79:
80: #include <dev/rndvar.h>
81:
82: int at_control( u_long, caddr_t, struct ifnet *, struct proc * );
83: static int at_scrub( struct ifnet *, struct at_ifaddr * );
84: static int at_ifinit( struct ifnet *, struct at_ifaddr *,
85: struct sockaddr_at * );
86: int at_broadcast( struct sockaddr_at * );
87:
88: static int aa_dorangeroute(struct ifaddr *, u_int, u_int, int);
89: static int aa_addsingleroute(struct ifaddr *, struct at_addr *,
90: struct at_addr *);
91: static int aa_delsingleroute(struct ifaddr *, struct at_addr *,
92: struct at_addr *);
93: static int aa_dosingleroute(struct ifaddr *, struct at_addr *,
94: struct at_addr *, int, int );
95:
96: # define sateqaddr(a,b) ((a)->sat_len == (b)->sat_len && \
97: (a)->sat_family == (b)->sat_family && \
98: (a)->sat_addr.s_net == (b)->sat_addr.s_net && \
99: (a)->sat_addr.s_node == (b)->sat_addr.s_node )
100:
101: extern struct timeout aarpprobe_timeout;
102:
103: int
104: at_control( cmd, data, ifp, p )
105: u_long cmd;
106: caddr_t data;
107: struct ifnet *ifp;
108: struct proc *p;
109: {
110: struct ifreq *ifr = (struct ifreq *)data;
111: struct sockaddr_at *sat;
112: struct netrange *nr;
113: struct at_aliasreq *ifra = (struct at_aliasreq *)data;
114: struct at_ifaddr *aa0;
115: struct at_ifaddr *aa = 0;
116: struct ifaddr *ifa, *ifa0;
117:
118: if ( ifp ) {
119: for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
120: if ( aa->aa_ifp == ifp ) break;
121: }
122: }
123:
124: switch ( cmd ) {
125: case SIOCAIFADDR:
126: case SIOCDIFADDR:
127: if ( ifra->ifra_addr.sat_family == AF_APPLETALK ) {
128: for ( ; aa; aa = aa->aa_next ) {
129: if ( aa->aa_ifp == ifp &&
130: sateqaddr( &aa->aa_addr, &ifra->ifra_addr )) {
131: break;
132: }
133: }
134: }
135: if ( cmd == SIOCDIFADDR && aa == 0 ) {
136: return( EADDRNOTAVAIL );
137: }
138: /*FALLTHROUGH*/
139:
140: case SIOCSIFADDR:
141: /*
142: * What a great idea this is: Let's reverse the meaning of
143: * the return...
144: */
145: if ( suser( p, 0 )) {
146: return( EPERM );
147: }
148:
149: sat = satosat( &ifr->ifr_addr );
150: nr = (struct netrange *)sat->sat_zero;
151: if ( nr->nr_phase == 1 ) {
152: for ( ; aa; aa = aa->aa_next ) {
153: if ( aa->aa_ifp == ifp &&
154: ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
155: break;
156: }
157: }
158: } else { /* default to phase 2 */
159: for ( ; aa; aa = aa->aa_next ) {
160: if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) {
161: break;
162: }
163: }
164: }
165:
166: if ( ifp == 0 )
167: panic( "at_control" );
168:
169: if ( aa == (struct at_ifaddr *) 0 ) {
170: aa0 = malloc(sizeof(struct at_ifaddr), M_IFADDR, M_WAITOK);
171: bzero(aa0, sizeof(struct at_ifaddr));
172:
173: if (( aa = at_ifaddr ) != NULL ) {
174: /*
175: * Don't let the loopback be first, since the first
176: * address is the machine's default address for
177: * binding.
178: */
179: if ( at_ifaddr->aa_ifp->if_flags & IFF_LOOPBACK ) {
180: aa = aa0;
181: aa->aa_next = at_ifaddr;
182: at_ifaddr = aa;
183: } else {
184: for ( ; aa->aa_next; aa = aa->aa_next )
185: ;
186: aa->aa_next = aa0;
187: }
188: } else {
189: at_ifaddr = aa0;
190: }
191:
192: aa = aa0;
193:
194: if (( ifa = ifp->if_addrlist.tqh_first ) != NULL ) {
195: for ( ; ifa->ifa_list.tqe_next; ifa = ifa->ifa_list.tqe_next )
196: ;
197: ifa->ifa_list.tqe_next = (struct ifaddr *)aa;
198: } else {
199: ifp->if_addrlist.tqh_first = (struct ifaddr *)aa;
200: }
201:
202: /* FreeBSD found this. Whew */
203: aa->aa_ifa.ifa_refcnt++;
204:
205: aa->aa_ifa.ifa_addr = (struct sockaddr *)&aa->aa_addr;
206: aa->aa_ifa.ifa_dstaddr = (struct sockaddr *)&aa->aa_addr;
207: aa->aa_ifa.ifa_netmask = (struct sockaddr *)&aa->aa_netmask;
208:
209: /*
210: * Set/clear the phase 2 bit.
211: */
212: if ( nr->nr_phase == 1 ) {
213: aa->aa_flags &= ~AFA_PHASE2;
214: } else {
215: aa->aa_flags |= AFA_PHASE2;
216: }
217: aa->aa_ifp = ifp;
218: } else {
219: at_scrub( ifp, aa );
220: }
221: break;
222:
223: case SIOCGIFADDR :
224: sat = satosat( &ifr->ifr_addr );
225: nr = (struct netrange *)sat->sat_zero;
226: if ( nr->nr_phase == 1 ) {
227: for ( ; aa; aa = aa->aa_next ) {
228: if ( aa->aa_ifp == ifp &&
229: ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
230: break;
231: }
232: }
233: } else { /* default to phase 2 */
234: for ( ; aa; aa = aa->aa_next ) {
235: if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) {
236: break;
237: }
238: }
239: }
240:
241: if ( aa == (struct at_ifaddr *) 0 )
242: return( EADDRNOTAVAIL );
243: break;
244: }
245:
246: switch ( cmd ) {
247: case SIOCGIFADDR:
248: *(struct sockaddr_at *)&ifr->ifr_addr = aa->aa_addr;
249:
250: /* from FreeBSD : some cleanups about netranges */
251: ((struct netrange *)&sat->sat_zero)->nr_phase
252: = (aa->aa_flags & AFA_PHASE2) ? 2 : 1;
253: ((struct netrange *)&sat->sat_zero)->nr_firstnet = aa->aa_firstnet;
254: ((struct netrange *)&sat->sat_zero)->nr_lastnet = aa->aa_lastnet;
255: break;
256:
257: case SIOCSIFADDR:
258: return( at_ifinit( ifp, aa, (struct sockaddr_at *)&ifr->ifr_addr ));
259:
260: case SIOCAIFADDR:
261: if ( sateqaddr( &ifra->ifra_addr, &aa->aa_addr )) {
262: return( 0 );
263: }
264: return( at_ifinit( ifp, aa, (struct sockaddr_at *)&ifr->ifr_addr ));
265:
266: case SIOCDIFADDR:
267: at_scrub( ifp, aa );
268: ifa0 = (struct ifaddr *)aa;
269: if (( ifa = ifp->if_addrlist.tqh_first ) == ifa0 ) {
270: ifp->if_addrlist.tqh_first = ifa->ifa_list.tqe_next;
271: } else {
272: while ( ifa->ifa_list.tqe_next &&
273: ( ifa->ifa_list.tqe_next != ifa0 )) {
274: ifa = ifa->ifa_list.tqe_next;
275: }
276: if ( ifa->ifa_list.tqe_next ) {
277: ifa->ifa_list.tqe_next = ifa0->ifa_list.tqe_next;
278: } else {
279: panic( "at_control" );
280: }
281: }
282:
283: /* FreeBSD */
284: IFAFREE(ifa0);
285:
286: aa0 = aa;
287: if ( aa0 == ( aa = at_ifaddr )) {
288: at_ifaddr = aa->aa_next;
289: } else {
290: while ( aa->aa_next && ( aa->aa_next != aa0 )) {
291: aa = aa->aa_next;
292: }
293: if ( aa->aa_next ) {
294: aa->aa_next = aa0->aa_next;
295: } else {
296: panic( "at_control" );
297: }
298: }
299:
300: /* FreeBSD */
301: IFAFREE(ifa0);
302: break;
303:
304: default:
305: if ( ifp == 0 || ifp->if_ioctl == 0 )
306: return( EOPNOTSUPP );
307: return( (*ifp->if_ioctl)( ifp, cmd, data ));
308: }
309: return( 0 );
310: }
311:
312: /* replaced this routine with the one from FreeBSD */
313: static int
314: at_scrub( ifp, aa )
315: struct ifnet *ifp;
316: struct at_ifaddr *aa;
317: {
318: int error;
319:
320: if ( aa->aa_flags & AFA_ROUTE ) {
321: if (ifp->if_flags & IFF_LOOPBACK) {
322: if ((error = aa_delsingleroute(&aa->aa_ifa,
323: &aa->aa_addr.sat_addr,
324: &aa->aa_netmask.sat_addr))) {
325: return( error );
326: }
327: } else if (ifp->if_flags & IFF_POINTOPOINT) {
328: if ((error = rtinit( &aa->aa_ifa, RTM_DELETE, RTF_HOST)) != 0)
329: return( error );
330: } else if (ifp->if_flags & IFF_BROADCAST) {
331: error = aa_dorangeroute(&aa->aa_ifa,
332: ntohs(aa->aa_firstnet),
333: ntohs(aa->aa_lastnet),
334: RTM_DELETE );
335: }
336: aa->aa_ifa.ifa_flags &= ~IFA_ROUTE;
337: aa->aa_flags &= ~AFA_ROUTE;
338: }
339: return( 0 );
340: }
341:
342: static int
343: at_ifinit( ifp, aa, sat )
344: struct ifnet *ifp;
345: struct at_ifaddr *aa;
346: struct sockaddr_at *sat;
347: {
348: struct netrange nr, onr;
349: struct sockaddr_at oldaddr;
350: int s = splnet(), error = 0, i, j, netinc, nodeinc, nnets;
351: u_int16_t net;
352:
353: oldaddr = aa->aa_addr;
354: bzero( AA_SAT( aa ), sizeof( struct sockaddr_at ));
355: bcopy( sat->sat_zero, &nr, sizeof( struct netrange ));
356: bcopy( sat->sat_zero, AA_SAT( aa )->sat_zero, sizeof( struct netrange ));
357: nnets = ntohs( nr.nr_lastnet ) - ntohs( nr.nr_firstnet ) + 1;
358:
359: onr.nr_firstnet = aa->aa_firstnet;
360: onr.nr_lastnet = aa->aa_lastnet;
361: aa->aa_firstnet = nr.nr_firstnet;
362: aa->aa_lastnet = nr.nr_lastnet;
363:
364: /*
365: * We could eliminate the need for a second phase 1 probe (post
366: * autoconf) if we check whether we're resetting the node. Note
367: * that phase 1 probes use only nodes, not net.node pairs. Under
368: * phase 2, both the net and node must be the same.
369: */
370: if ( ifp->if_flags & IFF_LOOPBACK ) {
371: AA_SAT( aa )->sat_len = sat->sat_len;
372: AA_SAT( aa )->sat_family = AF_APPLETALK;
373: AA_SAT( aa )->sat_addr.s_net = sat->sat_addr.s_net;
374: AA_SAT( aa )->sat_addr.s_node = sat->sat_addr.s_node;
375: } else {
376: aa->aa_flags |= AFA_PROBING;
377: AA_SAT( aa )->sat_len = sizeof(struct sockaddr_at);
378: AA_SAT( aa )->sat_family = AF_APPLETALK;
379: if ( aa->aa_flags & AFA_PHASE2 ) {
380: if ( sat->sat_addr.s_net == ATADDR_ANYNET ) {
381: if ( nnets != 1 ) {
382: net = ntohs( nr.nr_firstnet ) +
383: arc4random() % ( nnets - 1 );
384: } else {
385: net = ntohs( nr.nr_firstnet );
386: }
387: } else {
388: if ( ntohs( sat->sat_addr.s_net ) < ntohs( nr.nr_firstnet ) ||
389: ntohs( sat->sat_addr.s_net ) > ntohs( nr.nr_lastnet )) {
390: aa->aa_addr = oldaddr;
391: aa->aa_firstnet = onr.nr_firstnet;
392: aa->aa_lastnet = onr.nr_lastnet;
393: splx(s);
394: return( EINVAL );
395: }
396: net = ntohs( sat->sat_addr.s_net );
397: }
398: } else {
399: net = ntohs( sat->sat_addr.s_net );
400: }
401:
402: if ( sat->sat_addr.s_node == ATADDR_ANYNODE ) {
403: AA_SAT( aa )->sat_addr.s_node = arc4random();
404: } else {
405: AA_SAT( aa )->sat_addr.s_node = sat->sat_addr.s_node;
406: }
407:
408: for ( i = nnets, netinc = 1; i > 0; net = ntohs( nr.nr_firstnet ) +
409: (( net - ntohs( nr.nr_firstnet ) + netinc ) % nnets ), i-- ) {
410: AA_SAT( aa )->sat_addr.s_net = htons( net );
411:
412: for ( j = 0, nodeinc = arc4random() | 1; j < 256;
413: j++, AA_SAT( aa )->sat_addr.s_node += nodeinc ) {
414: if ( AA_SAT( aa )->sat_addr.s_node > 253 ||
415: AA_SAT( aa )->sat_addr.s_node < 1 ) {
416: continue;
417: }
418: aa->aa_probcnt = 10;
419: timeout_set(&aarpprobe_timeout, aarpprobe, ifp);
420: /* XXX don't use hz so badly */
421: timeout_add(&aarpprobe_timeout, hz / 5);
422: if ( tsleep( aa, PPAUSE|PCATCH, "at_ifinit", 0 )) {
423: printf( "at_ifinit why did this happen?!\n" );
424: aa->aa_addr = oldaddr;
425: aa->aa_firstnet = onr.nr_firstnet;
426: aa->aa_lastnet = onr.nr_lastnet;
427: splx( s );
428: return( EINTR );
429: }
430: if (( aa->aa_flags & AFA_PROBING ) == 0 ) {
431: break;
432: }
433: }
434: if (( aa->aa_flags & AFA_PROBING ) == 0 ) {
435: break;
436: }
437: /* reset node for next network */
438: AA_SAT( aa )->sat_addr.s_node = arc4random();
439: }
440:
441: if ( aa->aa_flags & AFA_PROBING ) {
442: aa->aa_addr = oldaddr;
443: aa->aa_firstnet = onr.nr_firstnet;
444: aa->aa_lastnet = onr.nr_lastnet;
445: splx( s );
446: return( EADDRINUSE );
447: }
448: }
449:
450: if ( ifp->if_ioctl &&
451: ( error = (*ifp->if_ioctl)( ifp, SIOCSIFADDR, (caddr_t) aa ))) {
452: aa->aa_addr = oldaddr;
453: aa->aa_firstnet = onr.nr_firstnet;
454: aa->aa_lastnet = onr.nr_lastnet;
455: splx( s );
456: return( error );
457: }
458:
459: bzero(&aa->aa_netmask, sizeof(aa->aa_netmask));
460: aa->aa_netmask.sat_len = sizeof(struct sockaddr_at);
461: aa->aa_netmask.sat_family = AF_APPLETALK;
462: aa->aa_netmask.sat_addr.s_net = 0xffff;
463: aa->aa_netmask.sat_addr.s_node = 0;
464: /* XXX From FreeBSD. Why does it do this? */
465: aa->aa_ifa.ifa_netmask =(struct sockaddr *) &(aa->aa_netmask);
466:
467: /* This block came from FreeBSD too */
468: /*
469: * Initialize broadcast (or remote p2p) address
470: */
471: bzero(&aa->aa_broadaddr, sizeof(aa->aa_broadaddr));
472: aa->aa_broadaddr.sat_len = sizeof(struct sockaddr_at);
473: aa->aa_broadaddr.sat_family = AF_APPLETALK;
474:
475: aa->aa_ifa.ifa_metric = ifp->if_metric;
476: if (ifp->if_flags & IFF_BROADCAST) {
477: aa->aa_broadaddr.sat_addr.s_net = htons(0);
478: aa->aa_broadaddr.sat_addr.s_node = 0xff;
479: aa->aa_ifa.ifa_broadaddr = (struct sockaddr *) &aa->aa_broadaddr;
480: /* add the range of routes needed */
481: error = aa_dorangeroute(&aa->aa_ifa,
482: ntohs(aa->aa_firstnet), ntohs(aa->aa_lastnet), RTM_ADD );
483: }
484: else if (ifp->if_flags & IFF_POINTOPOINT) {
485: struct at_addr rtaddr, rtmask;
486:
487: bzero(&rtaddr, sizeof(rtaddr));
488: bzero(&rtmask, sizeof(rtmask));
489: /* fill in the far end if we know it here XXX */
490: aa->aa_ifa.ifa_dstaddr = (struct sockaddr *) &aa->aa_broadaddr;
491: error = aa_addsingleroute(&aa->aa_ifa, &rtaddr, &rtmask);
492: }
493: else if ( ifp->if_flags & IFF_LOOPBACK ) {
494: struct at_addr rtaddr, rtmask;
495:
496: bzero(&rtaddr, sizeof(rtaddr));
497: bzero(&rtmask, sizeof(rtmask));
498: rtaddr.s_net = AA_SAT( aa )->sat_addr.s_net;
499: rtaddr.s_node = AA_SAT( aa )->sat_addr.s_node;
500: rtmask.s_net = 0xffff;
501: rtmask.s_node = 0x0; /* XXX should not be so.. should be HOST route */
502: error = aa_addsingleroute(&aa->aa_ifa, &rtaddr, &rtmask);
503: }
504:
505: if ( error ) {
506: at_scrub( ifp, aa );
507: aa->aa_addr = oldaddr;
508: aa->aa_firstnet = onr.nr_firstnet;
509: aa->aa_lastnet = onr.nr_lastnet;
510: splx( s );
511: return( error );
512: }
513:
514: aa->aa_ifa.ifa_flags |= IFA_ROUTE;
515: aa->aa_flags |= AFA_ROUTE;
516: splx( s );
517: return( 0 );
518: }
519:
520: int
521: at_broadcast( sat )
522: struct sockaddr_at *sat;
523: {
524: struct at_ifaddr *aa;
525:
526: if ( sat->sat_addr.s_node != ATADDR_BCAST ) {
527: return( 0 );
528: }
529: if ( sat->sat_addr.s_net == ATADDR_ANYNET ) {
530: return( 1 );
531: } else {
532: for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
533: if (( aa->aa_ifp->if_flags & IFF_BROADCAST ) &&
534: ( ntohs( sat->sat_addr.s_net ) >= ntohs( aa->aa_firstnet ) &&
535: ntohs( sat->sat_addr.s_net ) <= ntohs( aa->aa_lastnet ))) {
536: return( 1 );
537: }
538: }
539: }
540: return( 0 );
541: }
542:
543: /* Yet another bunch of routines from FreeBSD. Those guys are good */
544: /*
545: * aa_dorangeroute()
546: *
547: * Add a route for a range of networks from bot to top - 1.
548: * Algorithm:
549: *
550: * Split the range into two subranges such that the middle
551: * of the two ranges is the point where the highest bit of difference
552: * between the two addresses, makes its transition
553: * Each of the upper and lower ranges might not exist, or might be
554: * representable by 1 or more netmasks. In addition, if both
555: * ranges can be represented by the same netmask, then they can be merged
556: * by using the next higher netmask..
557: */
558:
559: static int
560: aa_dorangeroute(struct ifaddr *ifa, u_int bot, u_int top, int cmd)
561: {
562: u_int mask1;
563: struct at_addr addr;
564: struct at_addr mask;
565: int error;
566:
567: /*
568: * slight sanity check
569: */
570: if (bot > top) return (EINVAL);
571:
572: addr.s_node = 0;
573: mask.s_node = 0;
574: /*
575: * just start out with the lowest boundary
576: * and keep extending the mask till it's too big.
577: */
578:
579: while (bot <= top) {
580: mask1 = 1;
581: while ((( bot & ~mask1) >= bot)
582: && (( bot | mask1) <= top)) {
583: mask1 <<= 1;
584: mask1 |= 1;
585: }
586: mask1 >>= 1;
587: mask.s_net = htons(~mask1);
588: addr.s_net = htons(bot);
589: if(cmd == RTM_ADD) {
590: error = aa_addsingleroute(ifa,&addr,&mask);
591: if (error) {
592: /* XXX clean up? */
593: return (error);
594: }
595: } else {
596: error = aa_delsingleroute(ifa,&addr,&mask);
597: }
598: bot = (bot | mask1) + 1;
599: }
600: return 0;
601: }
602:
603: static int
604: aa_addsingleroute(struct ifaddr *ifa,
605: struct at_addr *addr, struct at_addr *mask)
606: {
607: int error;
608:
609: #if 0
610: printf("aa_addsingleroute: %x.%x mask %x.%x ...\n",
611: ntohs(addr->s_net), addr->s_node,
612: ntohs(mask->s_net), mask->s_node);
613: #endif
614:
615: error = aa_dosingleroute(ifa, addr, mask, RTM_ADD, RTF_UP);
616: if (error)
617: printf("aa_addsingleroute: error %d\n", error);
618: return(error);
619: }
620:
621: static int
622: aa_delsingleroute(struct ifaddr *ifa,
623: struct at_addr *addr, struct at_addr *mask)
624: {
625: int error;
626:
627: error = aa_dosingleroute(ifa, addr, mask, RTM_DELETE, 0);
628: if (error)
629: printf("aa_delsingleroute: error %d\n", error);
630: return(error);
631: }
632:
633: static int
634: aa_dosingleroute(struct ifaddr *ifa,
635: struct at_addr *at_addr, struct at_addr *at_mask, int cmd, int flags)
636: {
637: struct sockaddr_at addr, mask;
638:
639: bzero(&addr, sizeof(addr));
640: bzero(&mask, sizeof(mask));
641: addr.sat_family = AF_APPLETALK;
642: addr.sat_len = sizeof(struct sockaddr_at);
643: addr.sat_addr.s_net = at_addr->s_net;
644: addr.sat_addr.s_node = at_addr->s_node;
645: mask.sat_family = AF_APPLETALK;
646: mask.sat_len = sizeof(struct sockaddr_at);
647: mask.sat_addr.s_net = at_mask->s_net;
648: mask.sat_addr.s_node = at_mask->s_node;
649: if (at_mask->s_node)
650: flags |= RTF_HOST;
651: return(rtrequest(cmd, (struct sockaddr *) &addr,
652: (flags & RTF_HOST)?(ifa->ifa_dstaddr):(ifa->ifa_addr),
653: (struct sockaddr *) &mask, flags, NULL, 0));
654: }
CVSweb