225 lines
5.0 KiB
C
225 lines
5.0 KiB
C
|
/*
|
||
|
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||
|
*
|
||
|
* This document is the property of Apple Computer, Inc.
|
||
|
* It is considered confidential and proprietary.
|
||
|
*
|
||
|
* This document may not be reproduced or transmitted in any form,
|
||
|
* in whole or in part, without the express written permission of
|
||
|
* Apple Computer, Inc.
|
||
|
*/
|
||
|
#include <stdio.h>
|
||
|
#include <sys.h>
|
||
|
#include <sys/task.h>
|
||
|
#include <lib/net.h>
|
||
|
#include <lib/net/ethernet.h>
|
||
|
#include <lib/net/ipv4.h>
|
||
|
#include <lib/net/arp.h>
|
||
|
#include <lib/net/xp.h>
|
||
|
|
||
|
arp_cache_t arpcache;
|
||
|
|
||
|
static int arp_transmit(bool reply,char* tomacaddr,uint32_t toip)
|
||
|
{
|
||
|
//generate packet and send it down to the ethernet layer
|
||
|
mymbuf_t *packet;
|
||
|
char *data;
|
||
|
char dstmac[] = {0xff,0xff,0xff,0xff,0xff,0xff};
|
||
|
uint32_t srcip;
|
||
|
|
||
|
|
||
|
packet = mbuf_initialize(28,14,4); //28byte arp, 14 ethernet, 4 ethernet trailer
|
||
|
data = mbuf_tail(packet,28);
|
||
|
*(uint16_t *)&data[ARP_OFFSET_HTYPE] = htons(1); //ethernet
|
||
|
*(uint16_t *)&data[ARP_OFFSET_PTYPE] = htons(0x0800); //ipv4
|
||
|
data[ARP_OFFSET_HLEN] = 6; //macaddr is 6 bytes
|
||
|
data[ARP_OFFSET_PLEN] = 4; //ip is 4 bytes
|
||
|
*(uint16_t *)&data[ARP_OFFSET_OPER] = htons(reply ? 2 : 1);
|
||
|
|
||
|
xp_getmac((uint8_t*)&data[ARP_OFFSET_SHA]);
|
||
|
|
||
|
srcip = ipv4_get_ip();
|
||
|
*(uint32_t *)&data[ARP_OFFSET_SHA+6] = htonl(srcip);
|
||
|
|
||
|
if(tomacaddr == 0) {
|
||
|
memcpy(&data[ARP_OFFSET_SHA+10],"\0\0\0\0\0\0",6);
|
||
|
} else {
|
||
|
memcpy(&data[ARP_OFFSET_SHA+10],tomacaddr,6);
|
||
|
}
|
||
|
toip = htonl(toip);
|
||
|
memcpy(&data[ARP_OFFSET_SHA+16],&toip,4);
|
||
|
|
||
|
add_eth_and_transmit(packet,dstmac,ETHERTYPE_ARP);
|
||
|
mbuf_destroy(packet);
|
||
|
return 0;
|
||
|
}
|
||
|
int arp_hash(uint32_t ip)
|
||
|
{
|
||
|
return ip % ARP_CACHE_SIZE;
|
||
|
}
|
||
|
|
||
|
int arp_cache_add(uint32_t ip, const char *mac)
|
||
|
{
|
||
|
arp_cache_entry_t *ce = &arpcache.cache[arp_hash(ip)];
|
||
|
if (ce->ip == 0 || ce->ip == ip)
|
||
|
goto update;
|
||
|
while (ce->next) {
|
||
|
ce = ce->next;
|
||
|
if(ce->ip == ip)
|
||
|
goto update;
|
||
|
}
|
||
|
// Not in the list, add a new entry
|
||
|
ce->next = malloc(sizeof(arp_cache_entry_t));
|
||
|
ce = ce->next;
|
||
|
ce->next = 0;
|
||
|
update:
|
||
|
ce->ip = ip;
|
||
|
memcpy(ce->mac,mac,6);
|
||
|
// printf("arp: learned %#I at %M\n", &ce->ip, &ce->mac);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int arp_cache_get(uint32_t ip,char *mac)
|
||
|
{
|
||
|
arp_cache_entry_t *ce = &arpcache.cache[arp_hash(ip)];
|
||
|
|
||
|
if(ip == ce->ip)
|
||
|
goto found;
|
||
|
while(ce->next) {
|
||
|
ce = ce->next;
|
||
|
if(ip == ce->ip)
|
||
|
goto found;
|
||
|
}
|
||
|
return -1;
|
||
|
found:
|
||
|
// printf("arp: found %#I at %M\n", &ce->ip, &ce->mac);
|
||
|
if (mac)
|
||
|
memcpy(mac,ce->mac,6);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void arp_cache_delete(void)
|
||
|
{
|
||
|
arp_cache_entry_t *ce;
|
||
|
int i;
|
||
|
|
||
|
for(i=0;i<ARP_CACHE_SIZE;i++) {
|
||
|
ce = arpcache.cache[i].next;
|
||
|
while(ce) {
|
||
|
arp_cache_entry_t *next;
|
||
|
next = ce->next;
|
||
|
free(ce);
|
||
|
ce = next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void arp_dump_table(void)
|
||
|
{
|
||
|
arp_cache_entry_t *ce;
|
||
|
int i;
|
||
|
|
||
|
for(i=0;i<ARP_CACHE_SIZE;i++) {
|
||
|
ce = &arpcache.cache[i];
|
||
|
while (ce) {
|
||
|
if (ce->ip != 0)
|
||
|
printf("ip: %#I\tmac: %M\n", &ce->ip, &ce->mac);
|
||
|
ce = ce->next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int arp_get_macaddr(uint32_t ip, char *outmac)
|
||
|
{
|
||
|
if(arp_cache_get(ip,outmac) < 0) {
|
||
|
// printf("Sending request for 0x%x\n",ip);
|
||
|
arp_transmit(false,0,ip);
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* send arp requests until it is satisfied or timeout */
|
||
|
int arp_fill(uint32_t ip, utime_t timeout)
|
||
|
{
|
||
|
utime_t t;
|
||
|
|
||
|
t = system_time();
|
||
|
do {
|
||
|
if (arp_cache_get(ip, NULL) >= 0)
|
||
|
return 0;
|
||
|
|
||
|
arp_transmit(false, 0, ip);
|
||
|
task_sleep(100*1000);
|
||
|
} while (system_time() - t < timeout);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int arp_workloop(char *packet,int offset,int len,void *prevlayerdata)
|
||
|
{
|
||
|
uint16_t htype,ptype;
|
||
|
uint8_t hlen,plen;
|
||
|
uint16_t oper;
|
||
|
unsigned char *buffer = (unsigned char *)packet+offset;
|
||
|
uint32_t itoip,ifromip;
|
||
|
unsigned char *tmp;
|
||
|
|
||
|
htype = ntohs(*(uint16_t*)&buffer[ARP_OFFSET_HTYPE]);
|
||
|
ptype = ntohs(*(uint16_t*)&buffer[ARP_OFFSET_PTYPE]);
|
||
|
hlen = buffer[ARP_OFFSET_HLEN];
|
||
|
plen = buffer[ARP_OFFSET_PLEN];
|
||
|
oper = ntohs(*(uint16_t*)&buffer[ARP_OFFSET_OPER]);
|
||
|
|
||
|
// printf("arp: 0x%x 0x%x %d %d op %d\n", htype, ptype, hlen, plen, oper);
|
||
|
|
||
|
if ((htype != 1) || /* ethernet */
|
||
|
(ptype != 0x800) || /* IPv4 */
|
||
|
(hlen != 6) || /* 6-byte MAC */
|
||
|
(plen != 4)) { /* 4-byte protocol address */
|
||
|
// printf("Not ethernet. Bailing\n");
|
||
|
return -1;
|
||
|
}
|
||
|
tmp = &buffer[ARP_OFFSET_SHA+hlen];
|
||
|
ifromip = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3];
|
||
|
|
||
|
tmp = &buffer[ARP_OFFSET_SHA+(2*hlen)+plen];
|
||
|
itoip = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3];
|
||
|
|
||
|
// printf("arp: from %#I to %#I\n", &ifromip, &itoip);
|
||
|
|
||
|
//someone sent out their address
|
||
|
arp_cache_add(ifromip,(char *)&buffer[ARP_OFFSET_SHA]);
|
||
|
|
||
|
if (oper == 1) {
|
||
|
if (itoip == ipv4_get_ip()) {
|
||
|
// printf("arp: sending reply to %#I\n", &ifromip);
|
||
|
arp_transmit(true,(char *)&buffer[ARP_OFFSET_SHA],ifromip);
|
||
|
} else {
|
||
|
// printf("arp: dest %I not us\n", &itoip);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int arp_layer(bool on)
|
||
|
{
|
||
|
static int id = -1;
|
||
|
if(on) {
|
||
|
if(id == -1) {
|
||
|
memset(&arpcache,0,sizeof(arpcache));
|
||
|
id = registerEtherTypeHandler(ETHERTYPE_ARP,arp_workloop);
|
||
|
}
|
||
|
} else {
|
||
|
if(id >= 0) {
|
||
|
unregisterEtherTypeHandler(id);
|
||
|
arp_cache_delete();
|
||
|
id = -1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|