/* * 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 #include #include #include #include #include #include #include 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;inext; free(ce); ce = next; } } } void arp_dump_table(void) { arp_cache_entry_t *ce; int i; for(i=0;iip != 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; }