;
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <machine/spl.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/filedesc.h>
#include <sys/fcntl.h>
#include <sys/mbuf.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <net/if.h>
#include <net/if_types.h>
#include <netat/sysglue.h>
#include <netat/appletalk.h>
#include <netat/ddp.h>
#include <netat/at_snmp.h>
#include <netat/at_pcb.h>
#include <netat/at_var.h>
#include <netat/at_aarp.h>
#include <netat/at_pat.h>
#include <netat/debug.h>
#include <sys/kern_event.h>
static int probing;
int no_of_nodes_tried;
int no_of_nets_tried;
struct etalk_addr et_zeroaddr = {
{0, 0, 0, 0, 0, 0}};
aarp_amt_t probe_cb;
aarp_amt_array *aarp_table[IF_TOTAL_MAX];
StaticProc int aarp_req_cmd_in(aarp_pkt_t *, at_ifaddr_t*);
StaticProc int aarp_resp_cmd_in(aarp_pkt_t *, at_ifaddr_t*);
StaticProc int aarp_probe_cmd_in(aarp_pkt_t *, at_ifaddr_t*);
StaticProc int aarp_send_resp(at_ifaddr_t *, aarp_pkt_t *);
StaticProc int aarp_send_req(aarp_amt_t *);
StaticProc int aarp_send_probe(void);
StaticProc aarp_amt_t *aarp_lru_entry(aarp_amt_t *);
StaticProc int aarp_glean_info(aarp_pkt_t *, at_ifaddr_t*);
StaticProc int aarp_delete_amt_info(aarp_amt_t *);
StaticProc void aarp_build_pkt(aarp_pkt_t *, at_ifaddr_t*);
StaticProc void aarp_sched_req(void *);
StaticProc int aarp_get_rand_node(at_ifaddr_t *);
StaticProc int aarp_get_next_node(at_ifaddr_t *);
StaticProc int aarp_get_rand_net(at_ifaddr_t *);
int aarp_init1(elapp)
register at_ifaddr_t *elapp;
{
elapp->ifThisNode.s_net = 0;
elapp->ifThisNode.s_node = 0;
if (probing != PROBE_TENTATIVE)
probing = PROBE_IDLE;
else {
dPrintf(D_M_AARP,D_L_ERROR,
("aarp_init: error :probing == PROBE_TENTATIVE\n"));
return(-1);
}
if (elapp->initial_addr.s_net == 0 && elapp->initial_addr.s_node == 0) {
dPrintf(D_M_AARP, D_L_INFO,
("aarp_init: pick up a new node number\n"));
aarp_get_rand_node(elapp);
aarp_get_rand_net(elapp);
}
probe_cb.elapp = elapp;
probe_cb.no_of_retries = 0;
probe_cb.error = 0;
no_of_nodes_tried = 0;
no_of_nets_tried = 0;
if (aarp_send_probe() == -1) {
probing = PROBE_IDLE;
dPrintf(D_M_AARP, D_L_ERROR,
("aarp_init: aarp_send_probe returns error\n"));
return(-1);
}
return(ENOTREADY);
}
int aarp_init2(elapp)
register at_ifaddr_t *elapp;
{
if (probe_cb.error != 0) {
probing = PROBE_IDLE;
dPrintf(D_M_AARP, D_L_ERROR,
("aarp_init: probe_cb.error creates error =%d\n",
probe_cb.error));
return(-1);
}
if (aarp_table[elapp->ifPort])
bzero ((caddr_t)&aarp_table[elapp->ifPort]->et_aarp_amt[0],
sizeof(aarp_amt_array));
else
return(-1);
elapp->ifThisNode = elapp->initial_addr;
probing = PROBE_DONE;
atalk_post_msg(elapp->aa_ifp, KEV_ATALK_ENABLED, &(elapp->ifThisNode), 0);
at_state.flags |= AT_ST_STARTED;
return(0);
}
int aarp_rcv_pkt(pkt, elapp)
aarp_pkt_t *pkt;
at_ifaddr_t *elapp;
{
switch (ntohs(pkt->aarp_cmd)) {
case AARP_REQ_CMD:
return (aarp_req_cmd_in (pkt, elapp));
case AARP_RESP_CMD:
return (aarp_resp_cmd_in (pkt, elapp));
case AARP_PROBE_CMD:
return (aarp_probe_cmd_in (pkt, elapp));
default:
return (-1);
}
}
StaticProc int aarp_req_cmd_in (pkt, elapp)
aarp_pkt_t *pkt;
at_ifaddr_t *elapp;
{
if ((probing == PROBE_DONE) &&
(NET_VALUE(pkt->dest_at_addr.atalk_net) == elapp->ifThisNode.s_net) &&
(pkt->dest_at_addr.atalk_node == elapp->ifThisNode.s_node)) {
if (aarp_send_resp(elapp, pkt) == -1)
return(-1);
}
aarp_glean_info(pkt, elapp);
return (0);
}
StaticProc int aarp_resp_cmd_in (pkt, elapp)
aarp_pkt_t *pkt;
at_ifaddr_t *elapp;
{
register aarp_amt_t *amt_ptr;
gbuf_t *m;
switch (probing) {
case PROBE_TENTATIVE :
if ((NET_VALUE(pkt->src_at_addr.atalk_net) ==
probe_cb.elapp->initial_addr.s_net) &&
(pkt->src_at_addr.atalk_node ==
probe_cb.elapp->initial_addr.s_node)) {
untimeout(aarp_sched_probe, 0);
probe_cb.no_of_retries = 0;
aarp_get_next_node(probe_cb.elapp);
no_of_nodes_tried++;
if (no_of_nodes_tried == AARP_MAX_NODES_TRIED) {
aarp_get_rand_net(probe_cb.elapp);
aarp_get_rand_node(probe_cb.elapp);
no_of_nodes_tried = 0;
no_of_nets_tried++;
}
if (no_of_nets_tried == AARP_MAX_NETS_TRIED) {
probe_cb.error = EADDRNOTAVAIL;
AARPwakeup(&probe_cb);
return(0);
}
if (aarp_send_probe() == -1) {
AARPwakeup(&probe_cb);
return(-1);
}
} else {
return(-1);
}
break;
case PROBE_DONE :
AMT_LOOK(amt_ptr, pkt->src_at_addr, elapp)
if (amt_ptr == NULL)
return(-1);
if (amt_ptr->tmo) {
untimeout(aarp_sched_req, amt_ptr);
amt_ptr->tmo = 0;
}
if (amt_ptr->m == NULL) {
return(0);
}
amt_ptr->dest_addr = pkt->src_addr;
if (FDDI_OR_TOKENRING(elapp->aa_ifp->if_type))
ddp_bit_reverse((unsigned char *)&amt_ptr->dest_addr);
m = amt_ptr->m;
amt_ptr->m = NULL;
pat_output(amt_ptr->elapp, m,
(unsigned char *)&amt_ptr->dest_addr, 0);
break;
default :
return(-1);
}
return(0);
}
StaticProc int aarp_probe_cmd_in (pkt, elapp)
register aarp_pkt_t *pkt;
at_ifaddr_t *elapp;
{
register aarp_amt_t *amt_ptr;
switch (probing) {
case PROBE_TENTATIVE :
if ((elapp == probe_cb.elapp) &&
(NET_VALUE(pkt->src_at_addr.atalk_net) ==
probe_cb.elapp->initial_addr.s_net) &&
(pkt->src_at_addr.atalk_node ==
probe_cb.elapp->initial_addr.s_node)) {
untimeout(aarp_sched_probe, 0);
probe_cb.no_of_retries = 0;
aarp_get_next_node(probe_cb.elapp);
no_of_nodes_tried++;
if (no_of_nodes_tried == AARP_MAX_NODES_TRIED) {
aarp_get_rand_net(probe_cb.elapp);
aarp_get_rand_node(probe_cb.elapp);
no_of_nodes_tried = 0;
no_of_nets_tried++;
}
if (no_of_nets_tried == AARP_MAX_NETS_TRIED) {
probe_cb.error = EADDRNOTAVAIL;
AARPwakeup(&probe_cb);
return(0);
}
if (aarp_send_probe() == -1) {
AARPwakeup(&probe_cb);
return(-1);
}
} else {
return (0);
}
break;
case PROBE_DONE :
if ((NET_VALUE(pkt->src_at_addr.atalk_net) == elapp->ifThisNode.s_net) &&
(pkt->src_at_addr.atalk_node == elapp->ifThisNode.s_node)) {
if (aarp_send_resp(elapp, pkt) == -1)
return (-1);
return (0);
}
AMT_LOOK(amt_ptr, pkt->src_at_addr, elapp);
if (amt_ptr)
aarp_delete_amt_info(amt_ptr);
break;
default :
return (-1);
}
return (0);
}
int aarp_chk_addr(ddp_hdrp, elapp)
at_ddp_t *ddp_hdrp;
at_ifaddr_t *elapp;
{
if ((ddp_hdrp->dst_node == elapp->ifThisNode.s_node) &&
(NET_VALUE(ddp_hdrp->dst_net) == elapp->ifThisNode.s_net)) {
return(0);
}
if (AARP_BROADCAST(ddp_hdrp, elapp)) {
return(0);
}
return (AARP_ERR_NOT_OURS);
}
int aarp_send_data(m, elapp, dest_at_addr, loop)
register gbuf_t *m;
register at_ifaddr_t *elapp;
struct atalk_addr *dest_at_addr;
int loop;
{
register aarp_amt_t *amt_ptr;
register at_ddp_t *ddp_hdrp;
int error;
struct timeval timenow;
getmicrouptime(&timenow);
if (gbuf_len(m) <= 0)
ddp_hdrp = (at_ddp_t *)gbuf_rptr(gbuf_cont(m));
else
ddp_hdrp = (at_ddp_t *)gbuf_rptr(m);
if ((ddp_hdrp->dst_node == ddp_hdrp->src_node) &&
(NET_VALUE(ddp_hdrp->dst_net) == NET_VALUE(ddp_hdrp->src_net))) {
ddp_input(m, elapp);
return(0);
}
AMT_LOOK(amt_ptr, *dest_at_addr, elapp);
if (amt_ptr) {
if (amt_ptr->m) {
gbuf_freel(m);
return (0);
}
return (pat_output(elapp, m,
(unsigned char *)&amt_ptr->dest_addr, 0));
}
if (AARP_BROADCAST(ddp_hdrp, elapp)) {
gbuf_t *newm = 0;
struct etalk_addr *dest_addr;
dest_addr = &elapp->cable_multicast_addr;
if (loop)
newm = (gbuf_t *)gbuf_dupm(m);
if ( !(error = pat_output(elapp, m,
(unsigned char *)dest_addr, 0))) {
if (loop) {
if (newm == NULL)
return (error);
ddp_input(newm, elapp);
}
} else {
if (newm)
gbuf_freem(newm);
}
return (error);
}
NEW_AMT(amt_ptr, *dest_at_addr, elapp)
if (amt_ptr->m) {
gbuf_freel(m);
return (0);
}
amt_ptr->dest_at_addr = *dest_at_addr;
amt_ptr->dest_at_addr.atalk_unused = 0;
getmicrouptime(&timenow);
amt_ptr->last_time = timenow.tv_sec;
amt_ptr->m = m;
amt_ptr->elapp = elapp;
amt_ptr->no_of_retries = 0;
if ((error = aarp_send_req(amt_ptr))) {
aarp_delete_amt_info(amt_ptr);
return(error);
}
return(0);
}
StaticProc int aarp_send_resp(elapp, pkt)
register at_ifaddr_t *elapp;
aarp_pkt_t *pkt;
{
register aarp_pkt_t *new_pkt;
register gbuf_t *m;
if ((m = gbuf_alloc(AT_WR_OFFSET+sizeof(aarp_pkt_t), PRI_MED)) == NULL) {
return (-1);
}
gbuf_rinc(m,AT_WR_OFFSET);
gbuf_wset(m,0);
new_pkt = (aarp_pkt_t *)gbuf_rptr(m);
aarp_build_pkt(new_pkt, elapp);
new_pkt->aarp_cmd = htons(AARP_RESP_CMD);
new_pkt->dest_addr = pkt->src_addr;
new_pkt->dest_at_addr = pkt->src_at_addr;
new_pkt->dest_at_addr.atalk_unused = 0;
ATALK_ASSIGN(new_pkt->src_at_addr, elapp->ifThisNode.s_net,
elapp->ifThisNode.s_node, 0);
gbuf_winc(m,sizeof(aarp_pkt_t));
if (FDDI_OR_TOKENRING(elapp->aa_ifp->if_type))
ddp_bit_reverse((unsigned char *)&new_pkt->dest_addr);
if (pat_output(elapp, m, (unsigned char *)&new_pkt->dest_addr,
AARP_AT_TYPE))
return(-1);
return(0);
}
StaticProc int aarp_send_req (amt_ptr)
register aarp_amt_t *amt_ptr;
{
register aarp_pkt_t *pkt;
register gbuf_t *m;
int error;
if ((m = gbuf_alloc(AT_WR_OFFSET+sizeof(aarp_pkt_t), PRI_MED)) == NULL) {
return (ENOBUFS);
}
gbuf_rinc(m,AT_WR_OFFSET);
gbuf_wset(m,0);
pkt = (aarp_pkt_t *)gbuf_rptr(m);
aarp_build_pkt(pkt, amt_ptr->elapp);
pkt->aarp_cmd = htons(AARP_REQ_CMD);
pkt->dest_addr = et_zeroaddr;
pkt->dest_at_addr = amt_ptr->dest_at_addr;
pkt->dest_at_addr.atalk_unused = 0;
ATALK_ASSIGN(pkt->src_at_addr, amt_ptr->elapp->ifThisNode.s_net,
amt_ptr->elapp->ifThisNode.s_node, 0);
gbuf_winc(m,sizeof(aarp_pkt_t));
amt_ptr->no_of_retries++;
timeout(aarp_sched_req, amt_ptr, AARP_REQ_TIMER_INT);
amt_ptr->tmo = 1;
error = pat_output(amt_ptr->elapp, m,
(unsigned char *)&amt_ptr->elapp->cable_multicast_addr, AARP_AT_TYPE);
if (error)
{
untimeout(aarp_sched_req, amt_ptr);
amt_ptr->tmo = 0;
return(error);
}
return(0);
}
StaticProc int aarp_send_probe(void)
{
register aarp_pkt_t *pkt;
register gbuf_t *m;
if ((m = gbuf_alloc(AT_WR_OFFSET+sizeof(aarp_pkt_t), PRI_MED)) == NULL) {
probe_cb.error = ENOBUFS;
return (-1);
}
gbuf_rinc(m,AT_WR_OFFSET);
gbuf_wset(m,0);
pkt = (aarp_pkt_t *)gbuf_rptr(m);
aarp_build_pkt(pkt, probe_cb.elapp);
pkt->aarp_cmd = htons(AARP_PROBE_CMD);
pkt->dest_addr = et_zeroaddr;
ATALK_ASSIGN(pkt->src_at_addr, probe_cb.elapp->initial_addr.s_net,
probe_cb.elapp->initial_addr.s_node, 0);
ATALK_ASSIGN(pkt->dest_at_addr, probe_cb.elapp->initial_addr.s_net,
probe_cb.elapp->initial_addr.s_node, 0);
gbuf_winc(m,sizeof(aarp_pkt_t));
probe_cb.error = pat_output(probe_cb.elapp, m,
(unsigned char *)&probe_cb.elapp->cable_multicast_addr, AARP_AT_TYPE);
if (probe_cb.error) {
return(-1);
}
probing = PROBE_TENTATIVE;
probe_cb.no_of_retries++;
timeout(aarp_sched_probe, 0, AARP_PROBE_TIMER_INT);
return(0);
}
StaticProc aarp_amt_t *aarp_lru_entry(at)
register aarp_amt_t *at;
{
register aarp_amt_t *at_ret;
register int i;
at_ret = at;
for (i = 1, at++; i < AMT_BSIZ; i++, at++) {
if (at->last_time < at_ret->last_time && (at->m == NULL))
at_ret = at;
}
return(at_ret);
}
StaticProc int aarp_glean_info(pkt, elapp)
register aarp_pkt_t *pkt;
at_ifaddr_t *elapp;
{
register aarp_amt_t *amt_ptr;
AMT_LOOK(amt_ptr, pkt->src_at_addr, elapp);
if (amt_ptr == NULL) {
NEW_AMT(amt_ptr, pkt->src_at_addr,elapp);
if (amt_ptr->m)
return(0);
amt_ptr->dest_at_addr = pkt->src_at_addr;
amt_ptr->dest_at_addr.atalk_unused = 0;
amt_ptr->last_time = (int)random();
}
amt_ptr->dest_addr = pkt->src_addr;
if (FDDI_OR_TOKENRING(elapp->aa_ifp->if_type))
ddp_bit_reverse((unsigned char *)&amt_ptr->dest_addr);
return(1);
}
StaticProc int aarp_delete_amt_info(amt_ptr)
register aarp_amt_t *amt_ptr;
{
register gbuf_t *m;
amt_ptr->last_time = 0;
ATALK_ASSIGN(amt_ptr->dest_at_addr, 0, 0, 0);
amt_ptr->no_of_retries = 0;
if (amt_ptr->m) {
m = amt_ptr->m;
amt_ptr->m = NULL;
gbuf_freel(m);
}
return(0);
}
void aarp_sched_probe(__unused void *arg)
{
atalk_lock();
if (probe_cb.elapp->aa_ifp != 0 &&
probe_cb.no_of_retries != AARP_MAX_PROBE_RETRIES) {
if (aarp_send_probe() == -1)
AARPwakeup(&probe_cb);
} else {
probe_cb.error = 0;
AARPwakeup(&probe_cb);
}
atalk_unlock();
}
StaticProc void aarp_build_pkt(pkt, elapp)
register aarp_pkt_t *pkt;
at_ifaddr_t *elapp;
{
pkt->hardware_type = htons(AARP_ETHER_HW_TYPE);
pkt->stack_type = htons(AARP_AT_PROTO);
pkt->hw_addr_len = ETHERNET_ADDR_LEN;
pkt->stack_addr_len = AARP_AT_ADDR_LEN;
bcopy(elapp->xaddr, pkt->src_addr.etalk_addr_octet, sizeof(elapp->xaddr));
if (FDDI_OR_TOKENRING(elapp->aa_ifp->if_type))
ddp_bit_reverse(pkt->src_addr.etalk_addr_octet);
}
StaticProc void aarp_sched_req(arg)
void *arg;
{
int i;
aarp_amt_t *amt_ptr = (aarp_amt_t *)arg;
atalk_lock();
for (i = 0; i < IF_TOTAL_MAX; i++) {
if (aarp_table[i] == NULL || (void *)amt_ptr < (void *)aarp_table[i] ||
(void *)amt_ptr >= (void *)(aarp_table[i] + 1))
continue;
if (amt_ptr->tmo == 0) {
atalk_unlock();
return;
}
if (amt_ptr->no_of_retries < AARP_MAX_REQ_RETRIES) {
if (aarp_send_req(amt_ptr) == 0) {
atalk_unlock();
return;
}
}
aarp_delete_amt_info(amt_ptr);
break;
}
atalk_unlock();
return;
}
StaticProc int aarp_get_rand_node(elapp)
at_ifaddr_t *elapp;
{
register u_char node;
node = ((u_char)(random() & 0xff)) % 0xfd + 2;
elapp->initial_addr.s_node = node;
return(0);
}
StaticProc int aarp_get_next_node(elapp)
at_ifaddr_t *elapp;
{
register u_char node = elapp->initial_addr.s_node;
node = (node == 0xfd) ? (1) : (node+1);
elapp->initial_addr.s_node = node;
return(0);
}
StaticProc int aarp_get_rand_net(elapp)
register at_ifaddr_t *elapp;
{
register at_net_al last_net, new_net;
if (elapp->ifThisCableStart) {
last_net = elapp->initial_addr.s_net;
new_net= ((at_net_al)random() & 0xffff);
new_net = new_net % (unsigned) (elapp->ifThisCableEnd -
elapp->ifThisCableStart + 1)
+ elapp->ifThisCableStart;
if (new_net == last_net) {
if (new_net == elapp->ifThisCableEnd)
new_net = elapp->ifThisCableStart;
else
new_net++;
}
elapp->initial_addr.s_net = new_net;
} else {
last_net = (elapp->initial_addr.s_net & 0x00ff);
new_net = (at_net_al)random() & 0x00ff;
if (new_net == last_net)
new_net++;
if (new_net == 0xff)
new_net = 0;
elapp->initial_addr.s_net = (DDP_STARTUP_LOW | new_net);
}
return(0);
}
int getAarpTableSize(__unused int elapId)
{
return(AMTSIZE);
}
int getPhysAddrSize(__unused int elapId)
{
return(ETHERNET_ADDR_LEN);
}
#define ENTRY_SIZE sizeof(struct atalk_addr) + sizeof(struct etalk_addr)
snmpAarpEnt_t *getAarp(elapId)
int *elapId;
{
int i, cnt=0;
aarp_amt_t *amtp;
static snmpAarpEnt_t snmp[AMTSIZE];
snmpAarpEnt_t *snmpp;
struct atalk_addr addr;
u_short tmp_net;
if (*elapId <0 || *elapId >= IF_TOTAL_MAX)
return NULL;
for (i=0, amtp = &(aarp_table[*elapId]->et_aarp_amt[0]), snmpp = snmp;
i < AMTSIZE; i++,amtp++) {
if (amtp->last_time) {
addr.atalk_unused = 0;
tmp_net = UAS_VALUE(amtp->dest_at_addr.atalk_net);
NET_ASSIGN(addr.atalk_net, tmp_net);
addr.atalk_node = amtp->dest_at_addr.atalk_node;
bcopy(&addr, &snmpp->ap_ddpAddr, ENTRY_SIZE);
snmpp++;
cnt++;
}
}
*elapId = cnt;
return(snmp);
}