#include <sys/param.h>
#include <sys/systm.h>
#if ISO
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <sys/malloc.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netiso/iso.h>
#include <netiso/iso_var.h>
#include <netiso/iso_snpac.h>
#include <netiso/clnp.h>
#include <netiso/clnp_stat.h>
#include <netiso/esis.h>
#include <netiso/argo_debug.h>
int iso_systype = SNPA_ES;
extern short esis_holding_time, esis_config_time, esis_esconfig_time;
extern struct timeval time;
extern void esis_config();
extern int hz;
static void snpac_fixdstandmask();
struct sockaddr_iso blank_siso = {sizeof(blank_siso), AF_ISO};
extern u_long iso_hashchar();
static struct sockaddr_iso
dst = {sizeof(dst), AF_ISO},
gte = {sizeof(dst), AF_ISO},
src = {sizeof(dst), AF_ISO},
msk = {sizeof(dst), AF_ISO},
zmk = {0};
#define zsi blank_siso
#define zero_isoa zsi.siso_addr
#define zap_isoaddr(a, b) {Bzero(&a.siso_addr, sizeof(*r)); r = b; \
Bcopy(r, &a.siso_addr, 1 + (r)->isoa_len);}
#define S(x) ((struct sockaddr *)&(x))
static struct sockaddr_dl blank_dl = {sizeof(blank_dl), AF_LINK};
static struct sockaddr_dl gte_dl;
#define zap_linkaddr(a, b, c, i) \
(*a = blank_dl, bcopy(b, a->sdl_data, a->sdl_alen = c), a->sdl_index = i)
struct rtentry *known_is;
char all_es_snpa[] = { 0x09, 0x00, 0x2b, 0x00, 0x00, 0x04 };
char all_is_snpa[] = { 0x09, 0x00, 0x2b, 0x00, 0x00, 0x05 };
char all_l1is_snpa[] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x14};
char all_l2is_snpa[] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x15};
union sockunion {
struct sockaddr_iso siso;
struct sockaddr_dl sdl;
struct sockaddr sa;
};
llc_rtrequest(req, rt, sa)
int req;
register struct rtentry *rt;
struct sockaddr *sa;
{
register union sockunion *gate = (union sockunion *)rt->rt_gateway;
register struct llinfo_llc *lc = (struct llinfo_llc *)rt->rt_llinfo, *lc2;
struct rtentry *rt2;
struct ifnet *ifp = rt->rt_ifp;
int addrlen = ifp->if_addrlen;
#define LLC_SIZE 3
IFDEBUG (D_SNPA)
printf("llc_rtrequest(%d, %x, %x)\n", req, rt, sa);
ENDDEBUG
if (rt->rt_flags & RTF_GATEWAY)
return;
else switch (req) {
case RTM_ADD:
if (rt->rt_flags & RTF_CLONING) {
iso_setmcasts(ifp, req);
rt_setgate(rt, rt_key(rt), &blank_dl);
return;
}
if (lc != 0)
return;
case RTM_RESOLVE:
if (gate->sdl.sdl_family != AF_LINK) {
log(LOG_DEBUG, "llc_rtrequest: got non-link non-gateway route\n");
break;
}
R_Malloc(lc, struct llinfo_llc *, sizeof (*lc));
rt->rt_llinfo = (caddr_t)lc;
if (lc == 0) {
log(LOG_DEBUG, "llc_rtrequest: malloc failed\n");
break;
}
Bzero(lc, sizeof(*lc));
lc->lc_rt = rt;
rt->rt_flags |= RTF_LLINFO;
insque(lc, &llinfo_llc);
if (gate->sdl.sdl_alen == sizeof(struct esis_req) + addrlen) {
gate->sdl.sdl_alen -= sizeof(struct esis_req);
bcopy(addrlen + LLADDR(&gate->sdl),
(caddr_t)&lc->lc_er, sizeof(lc->lc_er));
} else if (gate->sdl.sdl_alen == addrlen)
lc->lc_flags = (SNPA_ES | SNPA_VALID | SNPA_PERM);
break;
case RTM_DELETE:
if (rt->rt_flags & RTF_CLONING)
iso_setmcasts(ifp, req);
if (lc == 0)
return;
remque(lc);
Free(lc);
rt->rt_llinfo = 0;
rt->rt_flags &= ~RTF_LLINFO;
break;
}
if (rt->rt_rmx.rmx_mtu == 0) {
rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu - LLC_SIZE;
}
}
iso_setmcasts(ifp, req)
struct ifnet *ifp;
int req;
{
static char *addrlist[] =
{ all_es_snpa, all_is_snpa, all_l1is_snpa, all_l2is_snpa, 0};
struct ifreq ifr;
register caddr_t *cpp;
int doreset = 0;
bzero((caddr_t)&ifr, sizeof(ifr));
for (cpp = (caddr_t *)addrlist; *cpp; cpp++) {
bcopy(*cpp, (caddr_t)ifr.ifr_addr.sa_data, 6);
if (req == RTM_ADD)
if (ether_addmulti(&ifr, (struct arpcom *)ifp) == ENETRESET)
doreset++;
else
if (ether_delmulti(&ifr, (struct arpcom *)ifp) == ENETRESET)
doreset++;
}
if (doreset) {
if (ifp->if_reset)
(*ifp->if_reset)(ifp->if_unit);
else
printf("iso_setmcasts: %s%d needs reseting to receive iso mcasts\n",
ifp->if_name, ifp->if_unit);
}
}
iso_snparesolve(ifp, dest, snpa, snpa_len)
struct ifnet *ifp;
struct sockaddr_iso *dest;
caddr_t snpa;
int *snpa_len;
{
struct llinfo_llc *sc;
caddr_t found_snpa;
int addrlen;
if (dest->siso_data[0] == AFI_SNA) {
IFDEBUG(D_SNPA)
printf("iso_snparesolve: return SN address\n");
ENDDEBUG
addrlen = dest->siso_nlen - 1;
found_snpa = (caddr_t) dest->siso_data + 1;
} else if (iso_systype != SNPA_IS && known_is != 0 &&
(sc = (struct llinfo_llc *)known_is->rt_llinfo) &&
(sc->lc_flags & SNPA_VALID)) {
register struct sockaddr_dl *sdl =
(struct sockaddr_dl *)(known_is->rt_gateway);
found_snpa = LLADDR(sdl);
addrlen = sdl->sdl_alen;
} else if (ifp->if_flags & IFF_BROADCAST) {
addrlen = ifp->if_addrlen;
found_snpa = (caddr_t)all_es_snpa;
} else
return (ENETUNREACH);
bcopy(found_snpa, snpa, *snpa_len = addrlen);
return (0);
}
snpac_free(lc)
register struct llinfo_llc *lc;
{
register struct rtentry *rt = lc->lc_rt;
register struct iso_addr *r;
if (known_is == rt)
known_is = 0;
if (rt && (rt->rt_flags & RTF_UP) &&
(rt->rt_flags & (RTF_DYNAMIC | RTF_MODIFIED))) {
RTFREE(rt);
rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt),
rt->rt_flags, (struct rtentry **)0);
RTFREE(rt);
}
}
snpac_add(ifp, nsap, snpa, type, ht, nsellength)
struct ifnet *ifp;
struct iso_addr *nsap;
caddr_t snpa;
char type;
u_short ht;
int nsellength;
{
register struct llinfo_llc *lc;
register struct rtentry *rt;
struct rtentry *mrt = 0;
register struct iso_addr *r;
int snpalen = min(ifp->if_addrlen, MAX_SNPALEN);
int new_entry = 0, index = ifp->if_index, iftype = ifp->if_type;
IFDEBUG(D_SNPA)
printf("snpac_add(%x, %x, %x, %x, %x, %x)\n",
ifp, nsap, snpa, type, ht, nsellength);
ENDDEBUG
zap_isoaddr(dst, nsap);
rt = rtalloc1(S(dst), 0);
IFDEBUG(D_SNPA)
printf("snpac_add: rtalloc1 returns %x\n", rt);
ENDDEBUG
if (rt == 0) {
struct sockaddr *netmask;
int flags;
add:
if (nsellength) {
netmask = S(msk); flags = RTF_UP;
snpac_fixdstandmask(nsellength);
} else {
netmask = 0; flags = RTF_UP | RTF_HOST;
}
new_entry = 1;
zap_linkaddr((>e_dl), snpa, snpalen, index);
gte_dl.sdl_type = iftype;
if (rtrequest(RTM_ADD, S(dst), S(gte_dl), netmask, flags, &mrt) ||
mrt == 0)
return (0);
rt = mrt;
rt->rt_refcnt--;
} else {
register struct sockaddr_dl *sdl = (struct sockaddr_dl *)rt->rt_gateway;
rt->rt_refcnt--;
if ((rt->rt_flags & RTF_LLINFO) == 0)
goto add;
if (nsellength && (rt->rt_flags & RTF_HOST)) {
if (rt->rt_refcnt == 0) {
rtrequest(RTM_DELETE, S(dst), (struct sockaddr *)0,
(struct sockaddr *)0, 0, (struct rtentry *)0);
rt = 0;
goto add;
} else {
static struct iso_addr nsap2; register char *cp;
nsap2 = *nsap;
cp = nsap2.isoa_genaddr + nsap->isoa_len - nsellength;
while (cp < (char *)(1 + &nsap2))
*cp++ = 0;
(void) snpac_add(ifp, &nsap2, snpa, type, ht, nsellength);
}
}
if (sdl->sdl_family != AF_LINK || sdl->sdl_alen == 0) {
int old_sdl_len = sdl->sdl_len;
if (old_sdl_len < sizeof(*sdl)) {
log(LOG_DEBUG, "snpac_add: cant make room for lladdr\n");
return (0);
}
zap_linkaddr(sdl, snpa, snpalen, index);
sdl->sdl_len = old_sdl_len;
sdl->sdl_type = iftype;
new_entry = 1;
}
}
if ((lc = (struct llinfo_llc *)rt->rt_llinfo) == 0)
panic("snpac_rtrequest");
rt->rt_rmx.rmx_expire = ht + time.tv_sec;
lc->lc_flags = SNPA_VALID | type;
if ((type & SNPA_IS) && !(iso_systype & SNPA_IS))
snpac_logdefis(rt);
return (new_entry);
}
static void
snpac_fixdstandmask(nsellength)
{
register char *cp = msk.siso_data, *cplim;
cplim = cp + (dst.siso_nlen -= nsellength);
msk.siso_len = cplim - (char *)&msk;
msk.siso_nlen = 0;
while (cp < cplim)
*cp++ = -1;
while (cp < (char *)msk.siso_pad)
*cp++ = 0;
for (cp = dst.siso_data + dst.siso_nlen; cp < (char *)dst.siso_pad; )
*cp++ = 0;
}
snpac_ioctl (so, cmd, data)
struct socket *so;
int cmd;
caddr_t data;
{
register struct systype_req *rq = (struct systype_req *)data;
IFDEBUG(D_IOCTL)
if (cmd == SIOCSSTYPE)
printf("snpac_ioctl: cmd set, type x%x, ht %d, ct %d\n",
rq->sr_type, rq->sr_holdt, rq->sr_configt);
else
printf("snpac_ioctl: cmd get\n");
ENDDEBUG
if (cmd == SIOCSSTYPE) {
if ((so->so_state & SS_PRIV) == 0)
return (EPERM);
if ((rq->sr_type & (SNPA_ES|SNPA_IS)) == (SNPA_ES|SNPA_IS))
return(EINVAL);
if (rq->sr_type & SNPA_ES) {
iso_systype = SNPA_ES;
} else if (rq->sr_type & SNPA_IS) {
iso_systype = SNPA_IS;
} else {
return(EINVAL);
}
esis_holding_time = rq->sr_holdt;
esis_config_time = rq->sr_configt;
if (esis_esconfig_time != rq->sr_esconfigt) {
untimeout(esis_config, (caddr_t)0);
esis_esconfig_time = rq->sr_esconfigt;
esis_config();
}
} else if (cmd == SIOCGSTYPE) {
rq->sr_type = iso_systype;
rq->sr_holdt = esis_holding_time;
rq->sr_configt = esis_config_time;
rq->sr_esconfigt = esis_esconfig_time;
} else {
return (EINVAL);
}
return (0);
}
snpac_logdefis(sc)
register struct rtentry *sc;
{
register struct iso_addr *r;
register struct sockaddr_dl *sdl = (struct sockaddr_dl *)sc->rt_gateway;
register struct rtentry *rt;
if (known_is == sc || !(sc->rt_flags & RTF_HOST))
return;
if (known_is) {
RTFREE(known_is);
}
known_is = sc;
RTHOLD(sc);
rt = rtalloc1((struct sockaddr *)&zsi, 0);
if (rt == 0)
rtrequest(RTM_ADD, S(zsi), rt_key(sc), S(zmk),
RTF_DYNAMIC|RTF_GATEWAY, 0);
else {
if ((rt->rt_flags & RTF_DYNAMIC) &&
(rt->rt_flags & RTF_GATEWAY) && rt_mask(rt)->sa_len == 0)
rt_setgate(rt, rt_key(rt), rt_key(sc));
}
}
void
snpac_age()
{
register struct llinfo_llc *lc, *nlc;
register struct rtentry *rt;
timeout(snpac_age, (caddr_t)0, SNPAC_AGE * hz);
for (lc = llinfo_llc.lc_next; lc != & llinfo_llc; lc = nlc) {
nlc = lc->lc_next;
if (lc->lc_flags & SNPA_VALID) {
rt = lc->lc_rt;
if (rt->rt_rmx.rmx_expire && rt->rt_rmx.rmx_expire < time.tv_sec)
snpac_free(lc);
}
}
}
snpac_ownmulti(snpa, len)
caddr_t snpa;
u_int len;
{
return (((iso_systype & SNPA_ES) &&
(!bcmp(snpa, (caddr_t)all_es_snpa, len))) ||
((iso_systype & SNPA_IS) &&
(!bcmp(snpa, (caddr_t)all_is_snpa, len))));
}
snpac_flushifp(ifp)
struct ifnet *ifp;
{
register struct llinfo_llc *lc;
for (lc = llinfo_llc.lc_next; lc != & llinfo_llc; lc = lc->lc_next) {
if (lc->lc_rt->rt_ifp == ifp && (lc->lc_flags & SNPA_VALID))
snpac_free(lc);
}
}
snpac_rtrequest(req, host, gateway, netmask, flags, ret_nrt)
int req;
struct iso_addr *host;
struct iso_addr *gateway;
struct iso_addr *netmask;
short flags;
struct rtentry **ret_nrt;
{
register struct iso_addr *r;
IFDEBUG(D_SNPA)
printf("snpac_rtrequest: ");
if (req == RTM_ADD)
printf("add");
else if (req == RTM_DELETE)
printf("delete");
else
printf("unknown command");
printf(" dst: %s\n", clnp_iso_addrp(host));
printf("\tgateway: %s\n", clnp_iso_addrp(gateway));
ENDDEBUG
zap_isoaddr(dst, host);
zap_isoaddr(gte, gateway);
if (netmask) {
zap_isoaddr(msk, netmask);
msk.siso_nlen = 0;
msk.siso_len = msk.siso_pad - (u_char *)&msk;
}
rtrequest(req, S(dst), S(gte), (netmask ? S(msk) : (struct sockaddr *)0),
flags, ret_nrt);
}
snpac_addrt(ifp, host, gateway, netmask)
struct ifnet *ifp;
struct iso_addr *host, *gateway, *netmask;
{
register struct iso_addr *r;
zap_isoaddr(dst, host);
zap_isoaddr(gte, gateway);
if (netmask) {
zap_isoaddr(msk, netmask);
msk.siso_nlen = 0;
msk.siso_len = msk.siso_pad - (u_char *)&msk;
rtredirect(S(dst), S(gte), S(msk), RTF_DONE, S(gte), 0);
} else
rtredirect(S(dst), S(gte), (struct sockaddr *)0,
RTF_DONE | RTF_HOST, S(gte), 0);
}
#endif