BlinkenArea - GitList
Repositories
Blog
Wiki
mips_sys
Code
Commits
Branches
Tags
Search
Tree:
c2b0401
Branches
Tags
master
mips_sys
fw
ip.c
added file headers
Stefan Schuermans
commited
c2b0401
at 2012-04-08 11:54:40
ip.c
Blame
History
Raw
/* MIPS I system * Copyright 2011-2012 Stefan Schuermans <stefan@schuermans.info> * Copyleft GNU public license V2 or later * http://www.gnu.org/copyleft/gpl.html */ #include "arp.h" #include "checksum.h" #include "config.h" #include "ethernet.h" #include "icmp.h" #include "ip.h" #include "macros.h" #include "memcpy.h" #include "nethelp.h" #include "udp.h" // timing parameters #define IP_BUFFER_TICKS_MAX 50 // maximum age of buffered IP packet (in 200ms) /** * buffers for IP packets to transmit * - used if MAC is unknown when packet shall be transmitted * - packet is sent when MAC becomes known * - some buffers with different length (packets have different lengths) */ //@{ unsigned int ip_buffer0[20]; unsigned int ip_buffer1[20]; unsigned int ip_buffer2[40]; unsigned int ip_buffer3[80]; //@} /// table with buffers struct ip_buffer_table { void *ptr; ///< pointer to buffer for packet unsigned int buf_sz; ///< size of buffer unsigned int pkg_sz; ///< size of packet in buffer, 0 for packet unsigned int ticks; ///< age of entry (in 200ms) } ip_buffer_tab[] = { // put smaller buffers in front of larger buffers // - then short packets will use smaller buffers more often { ip_buffer0, sizeof(ip_buffer0), 0, 0 }, { ip_buffer1, sizeof(ip_buffer1), 0, 0 }, { ip_buffer2, sizeof(ip_buffer2), 0, 0 }, { ip_buffer3, sizeof(ip_buffer3), 0, 0 }, }; /// initialize void ip_init(void) { unsigned int i; for (i = 0; i < count(ip_buffer_tab); i++) ip_buffer_tab[i].pkg_sz = 0; } /// tick procedure - call every 200ms void ip_tick200(void) { unsigned int i; // increase age of buffered IP packets and remove timed out ones for (i = 0; i < count(ip_buffer_tab); i++) { if (ip_buffer_tab[i].pkg_sz > 0) { // buffer in use ip_buffer_tab[i].ticks++; // increase age if (ip_buffer_tab[i].ticks > IP_BUFFER_TICKS_MAX) // too old ip_buffer_tab[i].pkg_sz = 0; // discard packet } } } /** * @brief process a received IP packet * @param[in] ptr pointer to data of packet * @param[in] sz size of packet */ void ip_recv(void *ptr, unsigned int sz) { struct ip_packet *ip_pack; unsigned int len; // packet too short if (sz < sizeof(struct ip_packet)) return; ip_pack = ptr; // not IPv4 -> drop if (ip_pack->ip_hdr.ver__hdr_len != 0x45) // IPv4 with no options present return; // not to own IP / broadcast -> drop while (1) { // own IP -> accept if (ip_eq(ip_pack->ip_hdr.dest, config_ip.ip)) break; // local network's broadcast address -> accept if ((ip_pack->ip_hdr.dest[0] & config_ip.mask[0]) == (config_ip.ip[0] & config_ip.mask[0]) && (ip_pack->ip_hdr.dest[1] & config_ip.mask[1]) == (config_ip.ip[1] & config_ip.mask[1]) && (ip_pack->ip_hdr.dest[2] & config_ip.mask[2]) == (config_ip.ip[2] & config_ip.mask[2]) && (ip_pack->ip_hdr.dest[3] & config_ip.mask[3]) == (config_ip.ip[3] & config_ip.mask[3]) && (ip_pack->ip_hdr.dest[0] & ~config_ip.mask[0]) == ~config_ip.mask[0] && (ip_pack->ip_hdr.dest[1] & ~config_ip.mask[1]) == ~config_ip.mask[1] && (ip_pack->ip_hdr.dest[2] & ~config_ip.mask[2]) == ~config_ip.mask[2] && (ip_pack->ip_hdr.dest[3] & ~config_ip.mask[3]) == ~config_ip.mask[3]) break; // link local broadcast address -> accept if (ip_pack->ip_hdr.dest[0] == 0xFF && ip_pack->ip_hdr.dest[1] == 0xFF && ip_pack->ip_hdr.dest[2] == 0xFF && ip_pack->ip_hdr.dest[3] == 0xFF) break; // drop return; } // ignore packets sent from invalid source adresses // - this might be some attack or some router fault if (ip_pack->ip_hdr.src[0] >= 0xE0 || // broadcast, reserved or multicast ip_pack->ip_hdr.src[0] == 0x7F || // loopback network ip_eq(ip_pack->ip_hdr.src, "\x00\x00\x00\x00")) // IP 0.0.0.0 return; // ignore packets sent from local network or broadcast address if ((ip_pack->ip_hdr.src[0] & config_ip.mask[0]) == (config_ip.ip[0] & config_ip.mask[0]) && // source IP is in own subnet (ip_pack->ip_hdr.src[1] & config_ip.mask[1]) == (config_ip.ip[1] & config_ip.mask[1]) && (ip_pack->ip_hdr.src[2] & config_ip.mask[2]) == (config_ip.ip[2] & config_ip.mask[2]) && (ip_pack->ip_hdr.src[3] & config_ip.mask[3]) == (config_ip.ip[3] & config_ip.mask[3])) { // local network address if ((ip_pack->ip_hdr.src[0] & ~config_ip.mask[0]) == 0x00 && (ip_pack->ip_hdr.src[1] & ~config_ip.mask[1]) == 0x00 && (ip_pack->ip_hdr.src[2] & ~config_ip.mask[2]) == 0x00 && (ip_pack->ip_hdr.src[3] & ~config_ip.mask[3]) == 0x00) return; // local broadcast address if ((ip_pack->ip_hdr.src[0] & ~config_ip.mask[0]) == 0xFF && (ip_pack->ip_hdr.src[1] & ~config_ip.mask[1]) == 0xFF && (ip_pack->ip_hdr.src[2] & ~config_ip.mask[2]) == 0xFF && (ip_pack->ip_hdr.src[3] & ~config_ip.mask[3]) == 0xFF) return; } // ignore packets sent from own IP address if (ip_eq(ip_pack->ip_hdr.src, config_ip.ip)) return; // ignore fragmented packets // BUG: fragmentation must be supported according to RFC781 // but there is not enough RAM for assembling packets with up to 64kB // fragment offset 0, more_frags=0, dont_frag=x, reserved_flag=0 if ((ntohs(ip_pack->ip_hdr.frag_ofs) & 0xBFFF) != 0x0000) return; // check total length len = sizeof(struct ethernet_header) + ntohs(ip_pack->ip_hdr.total_len); if (sz < len) // packet is truncated return; sz = len; // remove ethernet padding from packet (maybe sz > len) // test header checksum if (checksum(&ip_pack->ip_hdr, sizeof(struct ip_header), 0x0000, 0x0000)) return; // save MAC/IP information in ARP table arp_store(ip_pack->ip_hdr.src, ip_pack->eth_hdr.src); // branch according to protocol switch (ip_pack->ip_hdr.proto) { // ICMP case 0x01: icmp_recv(ptr, sz); break; // UDP case 0x11: udp_recv(ptr, sz); break; } } /** * @brief send an IP packet * @param[in] ptr pointer to data of packet * @param[in] sz size of packet * * ptr must point to a ip_packet * with ip_hdr.proto and ip_hdr.dest already initialized */ void ip_send(void *ptr, unsigned int sz) { struct ip_packet *ip_pack; unsigned short chk; unsigned int i; // packet too short if (sz < sizeof(struct ip_packet)) return; ip_pack = ptr; // fill in header values ip_pack->ip_hdr.ver__hdr_len = 0x45; ip_pack->ip_hdr.tos = 0x00; ip_pack->ip_hdr.total_len = htons(sz - sizeof(struct ethernet_header)); ip_pack->ip_hdr.id = 0x0000; ip_pack->ip_hdr.frag_ofs = 0x0000; ip_pack->ip_hdr.ttl = 0x40; ip_pack->ip_hdr.hdr_chk = 0x0000; ip_cpy(ip_pack->ip_hdr.src, config_ip.ip); // generate header checksum chk = checksum(&ip_pack->ip_hdr, sizeof(struct ip_header), 0x0000, 0x0000); ip_pack->ip_hdr.hdr_chk = htons(chk); // destination is in own subnet if ((ip_pack->ip_hdr.dest[0] & config_ip.mask[0]) == (config_ip.ip[0] & config_ip.mask[0]) && (ip_pack->ip_hdr.dest[1] & config_ip.mask[1]) == (config_ip.ip[1] & config_ip.mask[1]) && (ip_pack->ip_hdr.dest[2] & config_ip.mask[2]) == (config_ip.ip[2] & config_ip.mask[2]) && (ip_pack->ip_hdr.dest[3] & config_ip.mask[3]) == (config_ip.ip[3] & config_ip.mask[3])) { // broadcast if ((ip_pack->ip_hdr.dest[0] | config_ip.mask[0]) == 0xFF && (ip_pack->ip_hdr.dest[1] | config_ip.mask[1]) == 0xFF && (ip_pack->ip_hdr.dest[2] | config_ip.mask[2]) == 0xFF && (ip_pack->ip_hdr.dest[3] | config_ip.mask[3]) == 0xFF) { mac_cpy(ip_pack->eth_hdr.dest, "\xFF\xFF\xFF\xFF\xFF\xFF"); i = 0; // MAC is available } // unicast else { // lookup MAC address of destination i = arp_lookup(ip_pack->ip_hdr.dest, ip_pack->eth_hdr.dest); } } // destination is not in own subnet else { // broadcast if (ip_pack->ip_hdr.dest[0] == 0xFF && ip_pack->ip_hdr.dest[1] == 0xFF && ip_pack->ip_hdr.dest[2] == 0xFF && ip_pack->ip_hdr.dest[3] == 0xFF) { mac_cpy(ip_pack->eth_hdr.dest, "\xFF\xFF\xFF\xFF\xFF\xFF"); i = 0; // MAC is available } // unicast else { // lookup MAC address of default gateway i = arp_lookup(config_ip.gw, ip_pack->eth_hdr.dest); } } // MAC available if (i == 0x00) { // send IP packet ip_pack->eth_hdr.type = htons(0x0800); // ethernet packet type: IP ethernet_send(ip_pack, sz); return; } // find a buffer to store the packet in for (i = 0; i < count(ip_buffer_tab); i++) { if (ip_buffer_tab[i].pkg_sz == 0 && // buffer not in use sz <= ip_buffer_tab[i].buf_sz) { // buffer long enough // put packet into buffer memcpy(ip_buffer_tab[i].ptr, ptr, sz); ip_buffer_tab[i].pkg_sz = sz; ip_buffer_tab[i].ticks = 0; break; } } /* if no buffer was found, we cannnot do anything about it and must discard the packet (i.e. do nothing here) */ } /** * @brief a MAC address was discovered * @param[in] ip the IP address the MAC was discovered for * @param[in] mac the MAC address corresponding to the IP address * * called by ARP to notify IP */ void ip_got_mac(unsigned char ip[4], unsigned char mac[6]) { unsigned char i; struct ip_packet *ip_pack; // search for buffered packets that can be sent now for (i = 0; i < count(ip_buffer_tab); i++) { if (ip_buffer_tab[i].pkg_sz > 0) { // buffer in use ip_pack = ip_buffer_tab[i].ptr; // destination is in own subnet if ((ip_pack->ip_hdr.dest[0] & config_ip.mask[0]) == (config_ip.ip[0] & config_ip.mask[0]) && (ip_pack->ip_hdr.dest[1] & config_ip.mask[1]) == (config_ip.ip[1] & config_ip.mask[1]) && (ip_pack->ip_hdr.dest[2] & config_ip.mask[2]) == (config_ip.ip[2] & config_ip.mask[2]) && (ip_pack->ip_hdr.dest[3] & config_ip.mask[3]) == (config_ip.ip[3] & config_ip.mask[3])) { // packet can be sent to destination if (ip_eq(ip_pack->ip_hdr.dest, ip)) { // send IP packet ip_pack->eth_hdr.type = htons(0x0800); // ethernet packet type: IP mac_cpy(ip_pack->eth_hdr.dest, mac); ethernet_send(ip_buffer_tab[i].ptr, ip_buffer_tab[i].pkg_sz); // buffer is now free ip_buffer_tab[i].pkg_sz = 0; } } // destination is not in own subnet else { // packet can be sent to gateway if (ip_eq(config_ip.gw, ip)) { // send IP packet ip_pack->eth_hdr.type = htons(0x0800); // ethernet packet type: IP mac_cpy(ip_pack->eth_hdr.dest, mac); ethernet_send(ip_buffer_tab[i].ptr, ip_buffer_tab[i].pkg_sz); // buffer is now free ip_buffer_tab[i].pkg_sz = 0; } } } } // for (i ... }