ether_at_pr_module.c [plain text]
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/route.h>
#include <net/if_llc.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <netinet/if_ether.h>
#include <sys/socketvar.h>
#include <net/dlil.h>
#include <netat/at_pat.h>
#if NETAT
extern struct ifqueue atalkintrq;
#endif
#if BRIDGE
#include <net/bridge.h>
#endif
#if NVLAN > 0
#include <net/if_vlan_var.h>
#endif
struct dl_es_at_entry
{
struct ifnet *ifp;
int ref_count;
};
int at_ether_input(struct mbuf *m, char *frame_header, struct ifnet *ifp,
u_long protocol_family, int sync_ok);
int ether_pre_output(struct ifnet *ifp, u_long protocol_family, struct mbuf **m0,
const struct sockaddr *dst_netaddr, caddr_t route, char *type, char *edst);
int ether_prmod_ioctl(u_long protocol_family, struct ifnet *ifp, u_long command,
caddr_t data);
int ether_attach_at(struct ifnet *ifp);
void ether_detach_at(struct ifnet *ifp);
#define MAX_EN_COUNT 30
static struct dl_es_at_entry en_at_array[MAX_EN_COUNT];
int
at_ether_input(
struct mbuf *m,
__unused char *frame_header,
__unused struct ifnet *ifp,
__unused u_long protocol_family,
__unused int sync_ok)
{
m->m_data -= sizeof(struct ether_header);
m->m_len += sizeof(struct ether_header);
m->m_pkthdr.len += sizeof(struct ether_header);
proto_input(PF_APPLETALK, m);
return 0;
}
int
ether_pre_output(
struct ifnet *ifp,
__unused u_long protocol_family,
struct mbuf **m0,
const struct sockaddr *dst_netaddr,
__unused caddr_t route,
char *type,
char *edst)
{
register struct mbuf *m = *m0;
register struct ether_header *eh;
int hlen;
if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
return ENETDOWN;
hlen = ETHER_HDR_LEN;
m->m_flags |= M_LOOP;
switch (dst_netaddr->sa_family) {
case AF_UNSPEC:
m->m_flags &= ~M_LOOP;
eh = (struct ether_header *)dst_netaddr->sa_data;
(void)memcpy(edst, eh->ether_dhost, 6);
*(u_short *)type = eh->ether_type;
break;
case AF_APPLETALK:
{
eh = (struct ether_header *)dst_netaddr->sa_data;
bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, 6);
*(u_short *)type = m->m_pkthdr.len;
}
break;
default:
kprintf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
dst_netaddr->sa_family);
return EAFNOSUPPORT;
}
return (0);
}
int
ether_prmod_ioctl(
__unused u_long protocol_family,
struct ifnet *ifp,
u_long command,
caddr_t data)
{
struct ifreq *ifr = (struct ifreq *) data;
int error = 0;
switch (command) {
case SIOCSIFADDR:
if ((ifp->if_flags & IFF_RUNNING) == 0) {
ifnet_set_flags(ifp, IFF_UP, IFF_UP);
dlil_ioctl(0, ifp, SIOCSIFFLAGS, (caddr_t) 0);
}
break;
case SIOCGIFADDR:
ifnet_lladdr_copy_bytes(ifp, ifr->ifr_addr.sa_data, ETHER_ADDR_LEN);
break;
case SIOCSIFMTU:
if (ifr->ifr_mtu > ETHERMTU) {
error = EINVAL;
} else {
ifp->if_mtu = ifr->ifr_mtu;
}
break;
default:
return EOPNOTSUPP;
}
return (error);
}
int
ether_attach_at(
struct ifnet *ifp)
{
struct dlil_proto_reg_str reg;
struct dlil_demux_desc desc;
struct dlil_demux_desc desc2;
int stat;
int first_empty;
int i;
u_int8_t atalk_snap[5] = {0x08, 0x00, 0x07, 0x80, 0x9b};
u_int8_t aarp_snap[5] = {0x00, 0x00, 0x00, 0x80, 0xf3};
first_empty = MAX_EN_COUNT;
for (i=0; i < MAX_EN_COUNT; i++) {
if (en_at_array[i].ifp == 0)
first_empty = i;
if (en_at_array[i].ifp == ifp) {
en_at_array[i].ref_count++;
return 0;
}
}
if (first_empty == MAX_EN_COUNT)
return ENOMEM;
bzero(®, sizeof(reg));
bzero(&desc, sizeof(desc));
bzero(&desc2, sizeof(desc2));
TAILQ_INIT(®.demux_desc_head);
reg.interface_family = ifp->if_family;
reg.unit_number = ifp->if_unit;
reg.input = at_ether_input;
reg.pre_output = ether_pre_output;
reg.ioctl = ether_prmod_ioctl;
reg.protocol_family = PF_APPLETALK;
desc.type = DLIL_DESC_SNAP;
desc.native_type = atalk_snap;
desc.variants.native_type_length = sizeof(atalk_snap);
TAILQ_INSERT_TAIL(®.demux_desc_head, &desc, next);
desc2.type = DLIL_DESC_SNAP;
desc2.native_type = aarp_snap;
desc2.variants.native_type_length = sizeof(aarp_snap);
TAILQ_INSERT_TAIL(®.demux_desc_head, &desc2, next);
stat = dlil_attach_protocol(®);
if (stat) {
printf("WARNING: ether_attach_at can't attach at to interface\n");
return stat;
}
en_at_array[first_empty].ifp = ifp;
en_at_array[first_empty].ref_count = 1;
return 0;
}
void
ether_detach_at(struct ifnet *ifp)
{
int i;
for (i=0; i < MAX_EN_COUNT; i++) {
if (en_at_array[i].ifp == ifp)
break;
}
if (i < MAX_EN_COUNT) {
if (en_at_array[i].ref_count > 1)
en_at_array[i].ref_count--;
else {
if (en_at_array[i].ref_count == 1) {
dlil_detach_protocol(ifp, PF_APPLETALK);
en_at_array[i].ifp = 0;
}
}
}
}