/* * $Id: at_control.c,v 1.2 2001-06-29 14:14:47 rufustfirefly Exp $ * * Copyright (c) 1990,1991 Regents of The University of Michigan. * All Rights Reserved. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #ifdef ibm032 #include #endif /* ibm032 */ #include #include #include #include #include #ifndef _IBMR2 #include #endif /* ! _IBMR2 */ #include #include #include #include #include #include #undef s_net #include #ifdef _IBMR2 #include #endif /* _IBMR2 */ #include "at.h" #include "at_var.h" #include "aarp.h" #include "phase2.h" #ifdef BSD4_4 # define sateqaddr(a,b) ((a)->sat_len == (b)->sat_len && \ (a)->sat_family == (b)->sat_family && \ (a)->sat_addr.s_net == (b)->sat_addr.s_net && \ (a)->sat_addr.s_node == (b)->sat_addr.s_node ) #else /* BSD4_4 */ atalk_hash( sat, hp ) struct sockaddr_at *sat; struct afhash *hp; { hp->afh_nethash = sat->sat_addr.s_net; hp->afh_hosthash = ( sat->sat_addr.s_net << 8 ) + sat->sat_addr.s_node; } /* * Note the magic to get ifa_ifwithnet() to work without adding an * ifaddr entry for each net in our local range. */ atalk_netmatch( sat1, sat2 ) struct sockaddr_at *sat1, *sat2; { struct at_ifaddr *aa; for ( aa = at_ifaddr; aa; aa = aa->aa_next ) { if ( AA_SAT( aa ) == sat1 ) { break; } } if ( aa ) { return( ntohs( aa->aa_firstnet ) <= ntohs( sat2->sat_addr.s_net ) && ntohs( aa->aa_lastnet ) >= ntohs( sat2->sat_addr.s_net )); } return( sat1->sat_addr.s_net == sat2->sat_addr.s_net ); } #endif /* BSD4_4 */ at_control( cmd, data, ifp ) int cmd; caddr_t data; struct ifnet *ifp; { struct ifreq *ifr = (struct ifreq *)data; struct sockaddr_at *sat; struct netrange *nr; #ifdef BSD4_4 struct at_aliasreq *ifra = (struct at_aliasreq *)data; struct at_ifaddr *aa0; #endif /* BSD4_4 */ struct at_ifaddr *aa = 0; struct mbuf *m; struct ifaddr *ifa; if ( ifp ) { for ( aa = at_ifaddr; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp ) break; } } switch ( cmd ) { #ifdef BSD4_4 case SIOCAIFADDR: case SIOCDIFADDR: if ( ifra->ifra_addr.sat_family == AF_APPLETALK ) { for ( ; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp && sateqaddr( &aa->aa_addr, &ifra->ifra_addr )) { break; } } } if ( cmd == SIOCDIFADDR && aa == 0 ) { return( EADDRNOTAVAIL ); } /*FALLTHROUGH*/ #endif /* BSD4_4 */ case SIOCSIFADDR: #ifdef BSD4_4 /* * What a great idea this is: Let's reverse the meaning of * the return... */ if ( suser( u.u_cred, &u.u_acflag )) { return( EPERM ); } #else /* BSD4_4 */ if ( !suser()) { return( EPERM ); } #endif /* BSD4_4 */ sat = satosat( &ifr->ifr_addr ); nr = (struct netrange *)sat->sat_zero; if ( nr->nr_phase == 1 ) { for ( ; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 ) == 0 ) { break; } } } else { /* default to phase 2 */ for ( ; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) { break; } } } if ( ifp == 0 ) panic( "at_control" ); if ( aa == (struct at_ifaddr *) 0 ) { m = m_getclr( M_WAIT, MT_IFADDR ); if ( m == (struct mbuf *)NULL ) { return( ENOBUFS ); } if (( aa = at_ifaddr ) != NULL ) { /* * Don't let the loopback be first, since the first * address is the machine's default address for * binding. */ if ( at_ifaddr->aa_ifp->if_flags & IFF_LOOPBACK ) { aa = mtod( m, struct at_ifaddr *); aa->aa_next = at_ifaddr; at_ifaddr = aa; } else { for ( ; aa->aa_next; aa = aa->aa_next ) ; aa->aa_next = mtod( m, struct at_ifaddr *); } } else { at_ifaddr = mtod( m, struct at_ifaddr *); } aa = mtod( m, struct at_ifaddr *); if (( ifa = ifp->if_addrlist ) != NULL ) { for ( ; ifa->ifa_next; ifa = ifa->ifa_next ) ; ifa->ifa_next = (struct ifaddr *)aa; } else { ifp->if_addrlist = (struct ifaddr *)aa; } #ifdef BSD4_4 aa->aa_ifa.ifa_addr = (struct sockaddr *)&aa->aa_addr; aa->aa_ifa.ifa_dstaddr = (struct sockaddr *)&aa->aa_addr; aa->aa_ifa.ifa_netmask = (struct sockaddr *)&aa->aa_netmask; #endif /* BSD4_4 */ /* * Set/clear the phase 2 bit. */ if ( nr->nr_phase == 1 ) { aa->aa_flags &= ~AFA_PHASE2; } else { aa->aa_flags |= AFA_PHASE2; } aa->aa_ifp = ifp; } else { at_scrub( ifp, aa ); } break; case SIOCGIFADDR : sat = satosat( &ifr->ifr_addr ); nr = (struct netrange *)sat->sat_zero; if ( nr->nr_phase == 1 ) { for ( ; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 ) == 0 ) { break; } } } else { /* default to phase 2 */ for ( ; aa; aa = aa->aa_next ) { if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) { break; } } } if ( aa == (struct at_ifaddr *) 0 ) return( EADDRNOTAVAIL ); break; } switch ( cmd ) { case SIOCGIFADDR: #ifdef BSD4_4 *(struct sockaddr_at *)&ifr->ifr_addr = aa->aa_addr; #else /* BSD4_4 */ ifr->ifr_addr = aa->aa_addr; #endif /* BSD4_4 */ break; case SIOCSIFADDR: return( at_ifinit( ifp, aa, (struct sockaddr_at *)&ifr->ifr_addr )); #ifdef BSD4_4 case SIOCAIFADDR: if ( sateqaddr( &ifra->ifra_addr, &aa->aa_addr )) { return( 0 ); } return( at_ifinit( ifp, aa, (struct sockaddr_at *)&ifr->ifr_addr )); case SIOCDIFADDR: at_scrub( ifp, aa ); if (( ifa = ifp->if_addrlist ) == (struct ifaddr *)aa ) { ifp->if_addrlist = ifa->ifa_next; } else { while ( ifa->ifa_next && ( ifa->ifa_next != (struct ifaddr *)aa )) { ifa = ifa->ifa_next; } if ( ifa->ifa_next ) { ifa->ifa_next = ((struct ifaddr *)aa)->ifa_next; } else { panic( "at_control" ); } } aa0 = aa; if ( aa0 == ( aa = at_ifaddr )) { at_ifaddr = aa->aa_next; } else { while ( aa->aa_next && ( aa->aa_next != aa0 )) { aa = aa->aa_next; } if ( aa->aa_next ) { aa->aa_next = aa0->aa_next; } else { panic( "at_control" ); } } m_free( dtom( aa0 )); break; #endif /* BSD4_4 */ default: if ( ifp == 0 || ifp->if_ioctl == 0 ) return( EOPNOTSUPP ); return( (*ifp->if_ioctl)( ifp, cmd, data )); } return( 0 ); } at_scrub( ifp, aa ) struct ifnet *ifp; struct at_ifaddr *aa; { #ifndef BSD4_4 struct sockaddr_at netsat; int error; u_short net; #endif /* ! BSD4_4 */ if ( aa->aa_flags & AFA_ROUTE ) { #ifdef BSD4_4 if (( error = rtinit( &(aa->aa_ifa), RTM_DELETE, ( ifp->if_flags & IFF_LOOPBACK ) ? RTF_HOST : 0 )) != 0 ) { return( error ); } aa->aa_ifa.ifa_flags &= ~IFA_ROUTE; #else /* BSD4_4 */ if ( ifp->if_flags & IFF_LOOPBACK ) { rtinit( &aa->aa_addr, &aa->aa_addr, SIOCDELRT, RTF_HOST ); } else { bzero( &netsat, sizeof( struct sockaddr_at )); netsat.sat_family = AF_APPLETALK; netsat.sat_addr.s_node = ATADDR_ANYNODE; /* * If the range is the full 0-fffe range, just use * the default route. */ if ( aa->aa_firstnet == htons( 0x0000 ) && aa->aa_lastnet == htons( 0xfffe )) { netsat.sat_addr.s_net = 0; rtinit((struct sockaddr *)&netsat, &aa->aa_addr, (int)SIOCDELRT, 0 ); } else { for ( net = ntohs( aa->aa_firstnet ); net <= ntohs( aa->aa_lastnet ); net++ ) { netsat.sat_addr.s_net = htons( net ); rtinit((struct sockaddr *)&netsat, &aa->aa_addr, (int)SIOCDELRT, 0 ); } } } #endif /* BSD4_4 */ aa->aa_flags &= ~AFA_ROUTE; } return( 0 ); } extern struct timeval time; at_ifinit( ifp, aa, sat ) struct ifnet *ifp; struct at_ifaddr *aa; struct sockaddr_at *sat; { struct netrange nr, onr; #ifdef BSD4_4 struct sockaddr_at oldaddr; #else /* BSD4_4 */ struct sockaddr oldaddr; #endif /* BSD4_4 */ struct sockaddr_at netaddr; int s = splimp(), error = 0, i, j, netinc, nodeinc, nnets; u_short net; oldaddr = aa->aa_addr; bzero( AA_SAT( aa ), sizeof( struct sockaddr_at )); bcopy( sat->sat_zero, &nr, sizeof( struct netrange )); nnets = ntohs( nr.nr_lastnet ) - ntohs( nr.nr_firstnet ) + 1; onr.nr_firstnet = aa->aa_firstnet; onr.nr_lastnet = aa->aa_lastnet; aa->aa_firstnet = nr.nr_firstnet; aa->aa_lastnet = nr.nr_lastnet; /* * We could eliminate the need for a second phase 1 probe (post * autoconf) if we check whether we're resetting the node. Note * that phase 1 probes use only nodes, not net.node pairs. Under * phase 2, both the net and node must be the same. */ if ( ifp->if_flags & IFF_LOOPBACK ) { #ifdef BSD4_4 AA_SAT( aa )->sat_len = sat->sat_len; #endif /* BSD4_4 */ AA_SAT( aa )->sat_family = AF_APPLETALK; AA_SAT( aa )->sat_addr.s_net = sat->sat_addr.s_net; AA_SAT( aa )->sat_addr.s_node = sat->sat_addr.s_node; } else { aa->aa_flags |= AFA_PROBING; AA_SAT( aa )->sat_family = AF_APPLETALK; if ( aa->aa_flags & AFA_PHASE2 ) { if ( sat->sat_addr.s_net == ATADDR_ANYNET ) { if ( nnets != 1 ) { net = ntohs( nr.nr_firstnet ) + time.tv_sec % ( nnets - 1 ); } else { net = ntohs( nr.nr_firstnet ); } } else { if ( ntohs( sat->sat_addr.s_net ) < ntohs( nr.nr_firstnet ) || ntohs( sat->sat_addr.s_net ) > ntohs( nr.nr_lastnet )) { aa->aa_addr = oldaddr; aa->aa_firstnet = onr.nr_firstnet; aa->aa_lastnet = onr.nr_lastnet; return( EINVAL ); } net = ntohs( sat->sat_addr.s_net ); } } else { net = ntohs( sat->sat_addr.s_net ); } if ( sat->sat_addr.s_node == ATADDR_ANYNODE ) { AA_SAT( aa )->sat_addr.s_node = time.tv_sec; } else { AA_SAT( aa )->sat_addr.s_node = sat->sat_addr.s_node; } for ( i = nnets, netinc = 1; i > 0; net = ntohs( nr.nr_firstnet ) + (( net - ntohs( nr.nr_firstnet ) + netinc ) % nnets ), i-- ) { AA_SAT( aa )->sat_addr.s_net = htons( net ); for ( j = 0, nodeinc = time.tv_sec | 1; j < 256; j++, AA_SAT( aa )->sat_addr.s_node += nodeinc ) { if ( AA_SAT( aa )->sat_addr.s_node > 253 || AA_SAT( aa )->sat_addr.s_node < 1 ) { continue; } aa->aa_probcnt = 10; timeout( aarpprobe, (caddr_t)ifp, hz / 5 ); splx( s ); if ( sleep( aa, PSLEP|PCATCH )) { printf( "at_ifinit why did this happen?!\n" ); aa->aa_addr = oldaddr; aa->aa_firstnet = onr.nr_firstnet; aa->aa_lastnet = onr.nr_lastnet; return( EINTR ); } s = splimp(); if (( aa->aa_flags & AFA_PROBING ) == 0 ) { break; } } if (( aa->aa_flags & AFA_PROBING ) == 0 ) { break; } /* reset node for next network */ AA_SAT( aa )->sat_addr.s_node = time.tv_sec; } if ( aa->aa_flags & AFA_PROBING ) { aa->aa_addr = oldaddr; aa->aa_firstnet = onr.nr_firstnet; aa->aa_lastnet = onr.nr_lastnet; splx( s ); return( EADDRINUSE ); } } if ( ifp->if_ioctl && ( error = (*ifp->if_ioctl)( ifp, SIOCSIFADDR, aa ))) { splx( s ); aa->aa_addr = oldaddr; aa->aa_firstnet = onr.nr_firstnet; aa->aa_lastnet = onr.nr_lastnet; return( error ); } #ifdef BSD4_4 aa->aa_netmask.sat_len = 6; aa->aa_netmask.sat_family = AF_APPLETALK; aa->aa_netmask.sat_addr.s_net = 0xffff; aa->aa_netmask.sat_addr.s_node = 0; #endif /* BSD4_4 */ if ( ifp->if_flags & IFF_LOOPBACK ) { #ifndef BSD4_4 rtinit( &aa->aa_addr, &aa->aa_addr, (int)SIOCADDRT, RTF_HOST|RTF_UP ); #else /* ! BSD4_4 */ error = rtinit( &(aa->aa_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP ); #endif /* ! BSD4_4 */ } else { #ifndef BSD4_4 /* * rtrequest looks for point-to-point links first. The * broadaddr is in the same spot as the destaddr. So, if * ATADDR_ANYNET is 0, and we don't fill in the broadaddr, we * get 0.0 routed out the ether interface. So, initialize the * broadaddr, even tho we don't use it. * * We *could* use the broadaddr field to reduce some of the * sockaddr_at overloading that we've done. E.g. Just send * to INTERFACE-NET.255, and have the kernel reroute that * to broadaddr, which would be 0.255 for phase 2 interfaces, * and IFACE-NET.255 for phase 1 interfaces. */ ((struct sockaddr_at *)&aa->aa_broadaddr)->sat_addr.s_net = sat->sat_addr.s_net; ((struct sockaddr_at *)&aa->aa_broadaddr)->sat_addr.s_node = ATADDR_BCAST; bzero( &netaddr, sizeof( struct sockaddr_at )); netaddr.sat_family = AF_APPLETALK; netaddr.sat_addr.s_node = ATADDR_ANYNODE; if (( aa->aa_flags & AFA_PHASE2 ) == 0 ) { netaddr.sat_addr.s_net = AA_SAT( aa )->sat_addr.s_net; rtinit((struct sockaddr *)&netaddr, &aa->aa_addr, (int)SIOCADDRT, RTF_UP ); } else { /* * If the range is the full 0-fffe range, just use * the default route. */ if ( aa->aa_firstnet == htons( 0x0000 ) && aa->aa_lastnet == htons( 0xfffe )) { netaddr.sat_addr.s_net = 0; rtinit((struct sockaddr *)&netaddr, &aa->aa_addr, (int)SIOCADDRT, RTF_UP ); } else { for ( net = ntohs( aa->aa_firstnet ); net <= ntohs( aa->aa_lastnet ); net++ ) { netaddr.sat_addr.s_net = htons( net ); rtinit((struct sockaddr *)&netaddr, &aa->aa_addr, (int)SIOCADDRT, RTF_UP ); } } } #else /* ! BSD4_4 */ error = rtinit( &(aa->aa_ifa), (int)RTM_ADD, RTF_UP ); #endif /* ! BSD4_4 */ } if ( error ) { aa->aa_addr = oldaddr; aa->aa_firstnet = onr.nr_firstnet; aa->aa_lastnet = onr.nr_lastnet; splx( s ); return( error ); } #ifdef BSD4_4 aa->aa_ifa.ifa_flags |= IFA_ROUTE; #endif /* BSD4_4 */ aa->aa_flags |= AFA_ROUTE; splx( s ); return( 0 ); } at_broadcast( sat ) struct sockaddr_at *sat; { struct at_ifaddr *aa; if ( sat->sat_addr.s_node != ATADDR_BCAST ) { return( 0 ); } if ( sat->sat_addr.s_net == 0 ) { return( 1 ); } else { for ( aa = at_ifaddr; aa; aa = aa->aa_next ) { if (( aa->aa_ifp->if_flags & IFF_BROADCAST ) && ( ntohs( sat->sat_addr.s_net ) >= ntohs( aa->aa_firstnet ) && ntohs( sat->sat_addr.s_net ) <= ntohs( aa->aa_lastnet ))) { return( 1 ); } } } return( 0 ); } aa_clean() { struct at_ifaddr *aa; struct ifaddr *ifa; struct ifnet *ifp; while ( aa = at_ifaddr ) { ifp = aa->aa_ifp; at_scrub( ifp, aa ); at_ifaddr = aa->aa_next; if (( ifa = ifp->if_addrlist ) == (struct ifaddr *)aa ) { ifp->if_addrlist = ifa->ifa_next; } else { while ( ifa->ifa_next && ( ifa->ifa_next != (struct ifaddr *)aa )) { ifa = ifa->ifa_next; } if ( ifa->ifa_next ) { ifa->ifa_next = ((struct ifaddr *)aa)->ifa_next; } else { panic( "at_entry" ); } } } }