| /****************************************************************************** |
| * Copyright (c) 2004, 2008 IBM Corporation |
| * All rights reserved. |
| * This program and the accompanying materials |
| * are made available under the terms of the BSD License |
| * which accompanies this distribution, and is available at |
| * http://www.opensource.org/licenses/bsd-license.php |
| * |
| * Contributors: |
| * IBM Corporation - initial implementation |
| *****************************************************************************/ |
| |
| |
| /********************** DEFINITIONS & DECLARATIONS ***********************/ |
| |
| #include <ipv4.h> |
| #include <udp.h> |
| #include <tcp.h> |
| #include <ethernet.h> |
| #include <time.h> |
| #include <sys/socket.h> |
| #include <string.h> |
| |
| /* ARP Message types */ |
| #define ARP_REQUEST 1 |
| #define ARP_REPLY 2 |
| |
| /* ARP table size (+1) */ |
| #define ARP_ENTRIES 10 |
| |
| /* ICMP Message types */ |
| #define ICMP_ECHO_REPLY 0 |
| #define ICMP_DST_UNREACHABLE 3 |
| #define ICMP_SRC_QUENCH 4 |
| #define ICMP_REDIRECT 5 |
| #define ICMP_ECHO_REQUEST 8 |
| #define ICMP_TIME_EXCEEDED 11 |
| #define ICMP_PARAMETER_PROBLEM 12 |
| #define ICMP_TIMESTAMP_REQUEST 13 |
| #define ICMP_TIMESTAMP_REPLY 14 |
| #define ICMP_INFORMATION_REQUEST 15 |
| #define ICMP_INFORMATION_REPLY 16 |
| |
| /** \struct arp_entry |
| * A entry that describes a mapping between IPv4- and MAC-address. |
| */ |
| typedef struct arp_entry arp_entry_t; |
| struct arp_entry { |
| uint32_t ipv4_addr; |
| uint8_t mac_addr[6]; |
| uint8_t eth_frame[ETH_MTU_SIZE]; |
| int eth_len; |
| int pkt_pending; |
| }; |
| |
| /** \struct icmphdr |
| * ICMP packet |
| */ |
| struct icmphdr { |
| unsigned char type; |
| unsigned char code; |
| unsigned short int checksum; |
| union { |
| /* for type 3 "Destination Unreachable" */ |
| unsigned int unused; |
| /* for type 0 and 8 */ |
| struct echo { |
| unsigned short int id; |
| unsigned short int seq; |
| } echo; |
| } options; |
| union { |
| /* payload for destination unreachable */ |
| struct dun { |
| unsigned char iphdr[20]; |
| unsigned char data[64]; |
| } dun; |
| /* payload for echo or echo reply */ |
| /* maximum size supported is 84 */ |
| unsigned char data[84]; |
| } payload; |
| }; |
| |
| /****************************** PROTOTYPES *******************************/ |
| |
| static unsigned short checksum(unsigned short *packet, int words); |
| |
| static void arp_send_request(int fd, uint32_t dest_ip); |
| |
| static void arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac); |
| |
| static void fill_arphdr(uint8_t * packet, uint8_t opcode, |
| const uint8_t * src_mac, uint32_t src_ip, |
| const uint8_t * dest_mac, uint32_t dest_ip); |
| |
| static arp_entry_t *lookup_mac_addr(uint32_t ipv4_addr); |
| |
| static void fill_udp_checksum(struct iphdr *ipv4_hdr); |
| |
| static int8_t handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, |
| int32_t packetsize); |
| |
| /****************************** LOCAL VARIABLES **************************/ |
| |
| /* Routing parameters */ |
| static uint32_t own_ip = 0; |
| static uint32_t multicast_ip = 0; |
| static uint32_t router_ip = 0; |
| static uint32_t subnet_mask = 0; |
| |
| /* helper variables */ |
| static uint32_t ping_dst_ip; |
| static const uint8_t null_mac_addr[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
| static const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; |
| static uint8_t multicast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; |
| |
| /* There are only (ARP_ENTRIES-1) effective entries because |
| * the entry that is pointed by arp_producer is never used. |
| */ |
| static unsigned int arp_consumer = 0; |
| static unsigned int arp_producer = 0; |
| static arp_entry_t arp_table[ARP_ENTRIES]; |
| |
| static uint8_t pending_pkt_frame[ETH_MTU_SIZE]; |
| static int pending_pkt_len; |
| |
| /* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */ |
| int (*send_ip) (int fd, void *, int); |
| |
| /***************************** IMPLEMENTATION ****************************/ |
| |
| /** |
| * IPv4: Initialize the environment for the IPv4 layer. |
| */ |
| static void ipv4_init(void) |
| { |
| int i; |
| |
| ping_dst_ip = 0; |
| |
| // clear ARP table |
| arp_consumer = 0; |
| arp_producer = 0; |
| for(i=0; i<ARP_ENTRIES; ++i) { |
| arp_table[i].ipv4_addr = 0; |
| memset(arp_table[i].mac_addr, 0, 6); |
| arp_table[i].eth_len = 0; |
| arp_table[i].pkt_pending = 0; |
| } |
| |
| /* Set IP send function to send_ipv4() */ |
| send_ip = &send_ipv4; |
| } |
| |
| /** |
| * IPv4: Set the own IPv4 address. |
| * |
| * @param _own_ip client IPv4 address (e.g. 127.0.0.1) |
| */ |
| void set_ipv4_address(uint32_t _own_ip) |
| { |
| own_ip = _own_ip; |
| ipv4_init(); |
| } |
| |
| /** |
| * IPv4: Get the own IPv4 address. |
| * |
| * @return client IPv4 address (e.g. 127.0.0.1) |
| */ |
| uint32_t get_ipv4_address(void) |
| { |
| return own_ip; |
| } |
| |
| /** |
| * IPv4: Set the IPv4 multicast address. |
| * |
| * @param _own_ip multicast IPv4 address (224.0.0.0 - 239.255.255.255) |
| */ |
| void set_ipv4_multicast(uint32_t _multicast_ip) |
| { |
| // is this IP Multicast out of range (224.0.0.0 - 239.255.255.255) |
| if((htonl(_multicast_ip) < 0xE0000000) |
| || (htonl(_multicast_ip) > 0xEFFFFFFF)) { |
| multicast_ip = 0; |
| memset(multicast_mac, 0xFF, 6); |
| return; |
| } |
| |
| multicast_ip = _multicast_ip; |
| multicast_mac[0] = 0x01; |
| multicast_mac[1] = 0x00; |
| multicast_mac[2] = 0x5E; |
| multicast_mac[3] = (uint8_t) 0x7F & (multicast_ip >> 16); |
| multicast_mac[4] = (uint8_t) 0xFF & (multicast_ip >> 8); |
| multicast_mac[5] = (uint8_t) 0xFF & (multicast_ip >> 0); |
| } |
| |
| /** |
| * IPv4: Get the IPv4 multicast address. |
| * |
| * @return multicast IPv4 address (224.0.0.0 - 239.255.255.255 or 0 if not set) |
| */ |
| uint32_t get_ipv4_multicast(void) |
| { |
| return multicast_ip; |
| } |
| |
| /** |
| * IPv4: Set the routers IPv4 address. |
| * |
| * @param _router_ip router IPv4 address |
| */ |
| void set_ipv4_router(uint32_t _router_ip) |
| { |
| router_ip = _router_ip; |
| ipv4_init(); |
| } |
| |
| /** |
| * IPv4: Get the routers IPv4 address. |
| * |
| * @return router IPv4 address |
| */ |
| uint32_t get_ipv4_router(void) |
| { |
| return router_ip; |
| } |
| |
| /** |
| * IPv4: Set the subnet mask. |
| * |
| * @param _subnet_mask netmask of the own IPv4 address |
| */ |
| void set_ipv4_netmask(uint32_t _subnet_mask) |
| { |
| subnet_mask = _subnet_mask; |
| ipv4_init(); |
| } |
| |
| /** |
| * IPv4: Get the subnet mask. |
| * |
| * @return netmask of the own IPv4 address |
| */ |
| uint32_t get_ipv4_netmask(void) |
| { |
| return subnet_mask; |
| } |
| |
| /** |
| * IPv4: Get the default subnet mask according to the IP class |
| * |
| * @param ip_addr IPv4 address |
| * @return default netmask according to the IP class |
| */ |
| uint32_t get_default_ipv4_netmask(char *ip_addr) |
| { |
| unsigned char top; |
| |
| top = ip_addr[0]; |
| if (top > 0 && top < 128) |
| return 0xFF000000; /* Class A: 255.0.0.0 */ |
| else if (top >= 128 && top < 192) |
| return 0xFFFF0000; /* Class B: 255.255.0.0 */ |
| else if (top >= 192 && top < 224) |
| return 0xFFFFFF00; /* Class C: 255.255.255.0 */ |
| else |
| return 0; |
| } |
| |
| /** |
| * IPv4: Creates IP-packet. Places IP-header in a packet and fills it |
| * with corresponding information. |
| * <p> |
| * Use this function with similar functions for other network layers |
| * (fill_ethhdr, fill_udphdr, fill_dnshdr, fill_btphdr). |
| * |
| * @param packet Points to the place where IP-header must be placed. |
| * @param packetsize Size of the packet in bytes incl. this hdr and data. |
| * @param ip_proto Type of the next level protocol (e.g. UDP). |
| * @param ip_src Sender IP address |
| * @param ip_dst Receiver IP address |
| * @see iphdr |
| * @see fill_ethhdr |
| * @see fill_udphdr |
| * @see fill_dnshdr |
| * @see fill_btphdr |
| */ |
| void fill_iphdr(uint8_t * packet, uint16_t packetsize, |
| uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst) |
| { |
| struct iphdr * iph = (struct iphdr *) packet; |
| |
| iph -> ip_hlv = 0x45; |
| iph -> ip_tos = 0x10; |
| iph -> ip_len = htons(packetsize); |
| iph -> ip_id = htons(0); |
| iph -> ip_off = 0; |
| iph -> ip_ttl = 0xFF; |
| iph -> ip_p = ip_proto; |
| iph -> ip_src = htonl(ip_src); |
| iph -> ip_dst = htonl(ip_dst); |
| iph -> ip_sum = 0; |
| } |
| |
| /** |
| * IPv4: Handles IPv4-packets according to Receive-handle diagram. |
| * |
| * @param fd socket fd |
| * @param ip_packet IP-packet to be handled |
| * @param packetsize Length of the packet |
| * @return ZERO - packet handled successfully; |
| * NON ZERO - packet was not handled (e.g. bad format) |
| * @see receive_ether |
| * @see iphdr |
| */ |
| int8_t handle_ipv4(int fd, uint8_t * ip_packet, uint32_t packetsize) |
| { |
| struct iphdr * iph; |
| int32_t old_sum; |
| static uint8_t ip_heap[65536 + ETH_MTU_SIZE]; |
| |
| if (packetsize < sizeof(struct iphdr)) |
| return -1; // packet is too small |
| |
| iph = (struct iphdr * ) ip_packet; |
| |
| /* Drop it if destination IPv4 address is no IPv4 Broadcast, no |
| * registered IPv4 Multicast and not our Unicast address |
| */ |
| if((multicast_ip == 0 && iph->ip_dst >= 0xE0000000 && iph->ip_dst <= 0xEFFFFFFF) |
| || (multicast_ip != iph->ip_dst && iph->ip_dst != 0xFFFFFFFF && |
| own_ip != 0 && iph->ip_dst != own_ip)) { |
| return -1; |
| } |
| |
| old_sum = iph -> ip_sum; |
| iph -> ip_sum = 0; |
| if (old_sum != checksum((uint16_t *) iph, sizeof (struct iphdr) >> 1)) |
| return -1; // Wrong IP checksum |
| |
| // is it the first fragment in a packet? |
| if (((iph -> ip_off) & 0x1FFF) == 0) { |
| // is it part of more fragments? |
| if (((iph -> ip_off) & 0x2000) == 0x2000) { |
| memcpy(ip_heap, ip_packet, iph->ip_len); |
| return 0; |
| } |
| } |
| // it's not the first fragment |
| else { |
| // get the first fragment |
| struct iphdr * iph_first = (struct iphdr * ) ip_heap; |
| |
| // is this fragment not part of the first one, then exit |
| if ((iph_first->ip_id != iph->ip_id ) || |
| (iph_first->ip_p != iph->ip_p ) || |
| (iph_first->ip_src != iph->ip_src) || |
| (iph_first->ip_dst != iph->ip_dst)) { |
| return 0; |
| } |
| |
| // this fragment is part of the first one! |
| memcpy(ip_heap + sizeof(struct iphdr) + |
| ((iph -> ip_off) & 0x1FFF) * 8, |
| ip_packet + sizeof(struct iphdr), |
| iph -> ip_len - sizeof(struct iphdr)); |
| |
| // is it part of more fragments? Then return. |
| if (((iph -> ip_off) & 0x2000) == 0x2000) { |
| return 0; |
| } |
| |
| // packet is completely reassembled now! |
| |
| // recalculate ip_len and set iph and ip_packet to the |
| iph_first->ip_len = iph->ip_len + ((iph->ip_off) & 0x1FFF) * 8; |
| |
| // set iph and ip_packet to the resulting packet. |
| ip_packet = ip_heap; |
| iph = (struct iphdr * ) ip_packet; |
| } |
| |
| switch (iph -> ip_p) { |
| case IPTYPE_ICMP: |
| return handle_icmp(fd, iph, ip_packet + sizeof(struct iphdr), |
| iph -> ip_len - sizeof(struct iphdr)); |
| case IPTYPE_UDP: |
| return handle_udp(fd, ip_packet + sizeof(struct iphdr), |
| iph -> ip_len - sizeof(struct iphdr)); |
| case IPTYPE_TCP: |
| return handle_tcp(ip_packet + sizeof(struct iphdr), |
| iph -> ip_len - sizeof(struct iphdr)); |
| default: |
| break; |
| } |
| return -1; // Unknown protocol |
| } |
| |
| /** |
| * IPv4: Send IPv4-packets. |
| * |
| * Before the packet is sent there are some patches performed: |
| * - IPv4 source address is replaced by our unicast IPV4 address |
| * if it is set to 0 or 1 |
| * - IPv4 destination address is replaced by our multicast IPV4 address |
| * if it is set to 1 |
| * - IPv4 checksum is calculated. |
| * - If payload type is UDP, then the UDP checksum is calculated also. |
| * |
| * We send an ARP request first, if this is the first packet sent to |
| * the declared IPv4 destination address. In this case we store |
| * the packet and send it later if we receive the ARP response. |
| * If the MAC address is known already, then we send the packet immediately. |
| * If there is already an ARP request pending, then we drop this packet |
| * and send again an ARP request. |
| * |
| * @param fd socket fd |
| * @param ip_packet IP-packet to be handled |
| * @param packetsize Length of the packet |
| * @return -2 - packet dropped (MAC address not resolved - ARP request pending) |
| * -1 - packet dropped (bad format) |
| * 0 - packet stored (ARP request sent - packet will be sent if |
| * ARP response is received) |
| * >0 - packet send (number of transmitted bytes is returned) |
| * |
| * @see receive_ether |
| * @see iphdr |
| */ |
| int send_ipv4(int fd, void* buffer, int len) |
| { |
| arp_entry_t *arp_entry = 0; |
| struct iphdr *ip; |
| const uint8_t *mac_addr = 0; |
| uint32_t ip_dst = 0; |
| |
| if(len + sizeof(struct ethhdr) > ETH_MTU_SIZE) |
| return -1; |
| |
| ip = (struct iphdr *) buffer; |
| |
| /* Replace source IPv4 address with our own unicast IPv4 address |
| * if it's 0 (= own unicast source address not specified). |
| */ |
| if(ip->ip_src == 0) { |
| ip->ip_src = htonl( own_ip ); |
| } |
| /* Replace source IPv4 address with our unicast IPv4 address and |
| * replace destination IPv4 address with our multicast IPv4 address |
| * if source address is set to 1. |
| */ |
| else if(ip->ip_src == 1) { |
| ip->ip_src = htonl( own_ip ); |
| ip->ip_dst = htonl( multicast_ip ); |
| } |
| |
| // Calculate the IPv4 checksum |
| ip->ip_sum = 0; |
| ip->ip_sum = checksum((uint16_t *) ip, sizeof (struct iphdr) >> 1); |
| |
| // if payload type is UDP, then we need to calculate the |
| // UDP checksum that depends on the IP header |
| if(ip->ip_p == IPTYPE_UDP) { |
| fill_udp_checksum(ip); |
| } |
| |
| ip_dst = ip->ip_dst; |
| // Check if the MAC address is already cached |
| if(~ip->ip_dst == 0 |
| || ( ((~subnet_mask) & ip->ip_dst) == ~subnet_mask && |
| ( subnet_mask & ip->ip_dst) == (subnet_mask & own_ip))) { |
| arp_entry = &arp_table[arp_producer]; |
| mac_addr = broadcast_mac; |
| } |
| else if(ip->ip_dst == multicast_ip) { |
| arp_entry = &arp_table[arp_producer]; |
| mac_addr = multicast_mac; |
| } |
| else { |
| // Check if IP address is in the same subnet as we are |
| if((subnet_mask & own_ip) == (subnet_mask & ip->ip_dst)) |
| arp_entry = lookup_mac_addr(ip->ip_dst); |
| // if not then we need to know the router's IP address |
| else { |
| ip_dst = router_ip; |
| arp_entry = lookup_mac_addr(router_ip); |
| } |
| if(arp_entry && memcmp(arp_entry->mac_addr, null_mac_addr, 6) != 0) |
| mac_addr = arp_entry->mac_addr; |
| } |
| |
| // If we could not resolv the MAC address by our own... |
| if(!mac_addr) { |
| // send the ARP request |
| arp_send_request(fd, ip_dst); |
| |
| // drop the current packet if there is already a ARP request pending |
| if(arp_entry) |
| return -2; |
| |
| // take the next entry in the ARP table to prepare the new ARP entry. |
| arp_entry = &arp_table[arp_producer]; |
| arp_producer = (arp_producer+1)%ARP_ENTRIES; |
| |
| // if ARP table is full then we must drop the oldest entry. |
| if(arp_consumer == arp_producer) |
| arp_consumer = (arp_consumer+1)%ARP_ENTRIES; |
| |
| // store the packet to be send if the ARP reply is received |
| arp_entry->pkt_pending = 1; |
| arp_entry->ipv4_addr = ip_dst; |
| memset(arp_entry->mac_addr, 0, 6); |
| fill_ethhdr (pending_pkt_frame, htons(ETHERTYPE_IP), |
| get_mac_address(), null_mac_addr); |
| memcpy(&pending_pkt_frame[sizeof(struct ethhdr)], |
| buffer, len); |
| pending_pkt_len = len + sizeof(struct ethhdr); |
| |
| set_timer(TICKS_SEC); |
| do { |
| receive_ether(fd); |
| if (!arp_entry->eth_len) |
| break; |
| } while (get_timer() > 0); |
| |
| return 0; |
| } |
| |
| // Send the packet with the known MAC address |
| fill_ethhdr(arp_entry->eth_frame, htons(ETHERTYPE_IP), |
| get_mac_address(), mac_addr); |
| memcpy(&arp_entry->eth_frame[sizeof(struct ethhdr)], buffer, len); |
| return send_ether(fd, arp_entry->eth_frame, len + sizeof(struct ethhdr)); |
| } |
| |
| /** |
| * IPv4: Calculate UDP checksum. Places the result into the UDP-header. |
| * <p> |
| * Use this function after filling the UDP payload. |
| * |
| * @param ipv4_hdr Points to the place where IPv4-header starts. |
| */ |
| static void fill_udp_checksum(struct iphdr *ipv4_hdr) |
| { |
| unsigned i; |
| unsigned long checksum = 0; |
| struct iphdr ip_hdr; |
| char *ptr; |
| udp_hdr_t *udp_hdr; |
| |
| udp_hdr = (udp_hdr_t *) (ipv4_hdr + 1); |
| udp_hdr->uh_sum = 0; |
| |
| memset(&ip_hdr, 0, sizeof(struct iphdr)); |
| ip_hdr.ip_src = ipv4_hdr->ip_src; |
| ip_hdr.ip_dst = ipv4_hdr->ip_dst; |
| ip_hdr.ip_len = udp_hdr->uh_ulen; |
| ip_hdr.ip_p = ipv4_hdr->ip_p; |
| |
| ptr = (char*) udp_hdr; |
| for (i = 0; i < udp_hdr->uh_ulen; i+=2) |
| checksum += *((uint16_t*) &ptr[i]); |
| |
| ptr = (char*) &ip_hdr; |
| for (i = 0; i < sizeof(struct iphdr); i+=2) |
| checksum += *((uint16_t*) &ptr[i]); |
| |
| checksum = (checksum >> 16) + (checksum & 0xffff); |
| checksum += (checksum >> 16); |
| udp_hdr->uh_sum = ~checksum; |
| |
| /* As per RFC 768, if the computed checksum is zero, |
| * it is transmitted as all ones (the equivalent in |
| * one's complement arithmetic). |
| */ |
| if (udp_hdr->uh_sum == 0) |
| udp_hdr->uh_sum = ~udp_hdr->uh_sum; |
| } |
| |
| /** |
| * IPv4: Calculates checksum for IP header. |
| * |
| * @param packet Points to the IP-header |
| * @param words Size of the packet in words incl. IP-header and data. |
| * @return Checksum |
| * @see iphdr |
| */ |
| static unsigned short checksum(unsigned short * packet, int words) |
| { |
| unsigned long checksum; |
| |
| for (checksum = 0; words > 0; words--) |
| checksum += *packet++; |
| checksum = (checksum >> 16) + (checksum & 0xffff); |
| checksum += (checksum >> 16); |
| |
| return ~checksum; |
| } |
| |
| static arp_entry_t* lookup_mac_addr(uint32_t ipv4_addr) |
| { |
| unsigned int i; |
| |
| for(i=arp_consumer; i != arp_producer; i = ((i+1)%ARP_ENTRIES) ) { |
| if(arp_table[i].ipv4_addr == ipv4_addr) |
| return &arp_table[i]; |
| } |
| return 0; |
| } |
| |
| |
| /** |
| * ARP: Sends an ARP-request package. |
| * For given IPv4 retrieves MAC via ARP (makes several attempts) |
| * |
| * @param fd socket fd |
| * @param dest_ip IP of the host which MAC should be obtained |
| */ |
| static void arp_send_request(int fd, uint32_t dest_ip) |
| { |
| arp_entry_t *arp_entry = &arp_table[arp_producer]; |
| |
| memset(arp_entry->eth_frame, 0, sizeof(struct ethhdr) + sizeof(struct arphdr)); |
| fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REQUEST, |
| get_mac_address(), own_ip, broadcast_mac, dest_ip); |
| fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP, |
| get_mac_address(), broadcast_mac); |
| |
| send_ether(fd, arp_entry->eth_frame, |
| sizeof(struct ethhdr) + sizeof(struct arphdr)); |
| } |
| |
| /** |
| * ARP: Sends an ARP-reply package. |
| * This package is used to serve foreign requests (in case IP in |
| * foreign request matches our host IP). |
| * |
| * @param fd socket fd |
| * @param src_ip requester IP address (foreign IP) |
| * @param src_mac requester MAC address (foreign MAC) |
| */ |
| static void arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac) |
| { |
| arp_entry_t *arp_entry = &arp_table[arp_producer]; |
| |
| memset(arp_entry->eth_frame, 0, sizeof(struct ethhdr) + sizeof(struct arphdr)); |
| fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP, |
| get_mac_address(), src_mac); |
| fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REPLY, |
| get_mac_address(), own_ip, src_mac, src_ip); |
| |
| send_ether(fd, arp_entry->eth_frame, |
| sizeof(struct ethhdr) + sizeof(struct arphdr)); |
| } |
| |
| /** |
| * ARP: Creates ARP package. Places ARP-header in a packet and fills it |
| * with corresponding information. |
| * <p> |
| * Use this function with similar functions for other network layers |
| * (fill_ethhdr). |
| * |
| * @param packet Points to the place where ARP-header must be placed. |
| * @param opcode Identifies is it request (ARP_REQUEST) |
| * or reply (ARP_REPLY) package. |
| * @param src_mac sender MAC address |
| * @param src_ip sender IP address |
| * @param dest_mac receiver MAC address |
| * @param dest_ip receiver IP address |
| * @see arphdr |
| * @see fill_ethhdr |
| */ |
| static void fill_arphdr(uint8_t * packet, uint8_t opcode, |
| const uint8_t * src_mac, uint32_t src_ip, |
| const uint8_t * dest_mac, uint32_t dest_ip) |
| { |
| struct arphdr * arph = (struct arphdr *) packet; |
| |
| arph -> hw_type = htons(1); |
| arph -> proto_type = htons(ETHERTYPE_IP); |
| arph -> hw_len = 6; |
| arph -> proto_len = 4; |
| arph -> opcode = htons(opcode); |
| |
| memcpy(arph->src_mac, src_mac, 6); |
| arph->src_ip = htonl(src_ip); |
| memcpy(arph->dest_mac, dest_mac, 6); |
| arph->dest_ip = htonl(dest_ip); |
| } |
| |
| /** |
| * ARP: Handles ARP-messages according to Receive-handle diagram. |
| * Updates arp_table for outstanding ARP requests (see arp_getmac). |
| * |
| * @param fd socket fd |
| * @param packet ARP-packet to be handled |
| * @param packetsize length of the packet |
| * @return ZERO - packet handled successfully; |
| * NON ZERO - packet was not handled (e.g. bad format) |
| * @see arp_getmac |
| * @see receive_ether |
| * @see arphdr |
| */ |
| int8_t handle_arp(int fd, uint8_t * packet, uint32_t packetsize) |
| { |
| struct arphdr * arph = (struct arphdr *) packet; |
| |
| if (packetsize < sizeof(struct arphdr)) |
| return -1; // Packet is too small |
| |
| if (arph -> hw_type != htons(1) || arph -> proto_type != htons(ETHERTYPE_IP)) |
| return -1; // Unknown hardware or unsupported protocol |
| |
| if (arph -> dest_ip != htonl(own_ip)) |
| return -1; // receiver IP doesn't match our IP |
| |
| switch(htons(arph -> opcode)) { |
| case ARP_REQUEST: |
| // foreign request |
| if(own_ip != 0) |
| arp_send_reply(fd, htonl(arph->src_ip), arph -> src_mac); |
| return 0; // no error |
| case ARP_REPLY: { |
| unsigned int i; |
| // if it is not for us -> return immediately |
| if(memcmp(get_mac_address(), arph->dest_mac, 6)) { |
| return 0; // no error |
| } |
| |
| if(arph->src_ip == 0) { |
| // we are not interested for a MAC address if |
| // the IPv4 address is 0.0.0.0 or ff.ff.ff.ff |
| return -1; |
| } |
| |
| // now let's find the corresponding entry in the ARP table |
| |
| for(i=arp_consumer; i != arp_producer; i = ((i+1)%ARP_ENTRIES) ) { |
| if(arp_table[i].ipv4_addr == arph->src_ip) |
| break; |
| } |
| if(i == arp_producer || memcmp(arp_table[i].mac_addr, null_mac_addr, 6) != 0) { |
| // we have not asked to resolve this IPv4 address ! |
| return -1; |
| } |
| |
| memcpy(arp_table[i].mac_addr, arph->src_mac, 6); |
| |
| // do we have something to send |
| if (arp_table[i].pkt_pending) { |
| struct ethhdr * ethh = (struct ethhdr *) pending_pkt_frame; |
| memcpy(ethh -> dest_mac, arp_table[i].mac_addr, 6); |
| |
| send_ether(fd, pending_pkt_frame, pending_pkt_len); |
| arp_table[i].pkt_pending = 0; |
| arp_table[i].eth_len = 0; |
| } |
| return 0; // no error |
| } |
| default: |
| break; |
| } |
| return -1; // Invalid message type |
| } |
| |
| /** |
| * ICMP: Send an ICMP Echo request to destination IPv4 address. |
| * This function does also set a global variable to the |
| * destination IPv4 address. If there is an ICMP Echo Reply |
| * received later then the variable is set back to 0. |
| * In other words, reading a value of 0 form this variable |
| * means that an answer to the request has been arrived. |
| * |
| * @param fd socket descriptor |
| * @param _ping_dst_ip destination IPv4 address |
| */ |
| void ping_ipv4(int fd, uint32_t _ping_dst_ip) |
| { |
| unsigned char packet[sizeof(struct iphdr) + sizeof(struct icmphdr)]; |
| struct icmphdr *icmp; |
| |
| ping_dst_ip = _ping_dst_ip; |
| |
| if(ping_dst_ip == 0) |
| return; |
| |
| fill_iphdr(packet, sizeof(struct iphdr) + sizeof(struct icmphdr), IPTYPE_ICMP, |
| 0, ping_dst_ip); |
| icmp = (struct icmphdr *) (packet + sizeof(struct iphdr)); |
| icmp->type = ICMP_ECHO_REQUEST; |
| icmp->code = 0; |
| icmp->checksum = 0; |
| icmp->options.echo.id = 0xd476; |
| icmp->options.echo.seq = 1; |
| |
| memset(icmp->payload.data, '*', sizeof(icmp->payload.data)); |
| |
| icmp->checksum = |
| checksum((unsigned short *) icmp, sizeof(struct icmphdr) >> 1); |
| send_ipv4(fd, packet, sizeof(struct iphdr) + sizeof(struct icmphdr)); |
| } |
| |
| /** |
| * ICMP: Return host IPv4 address that we are waiting for a |
| * ICMP Echo reply message. If this value is 0 then we have |
| * received an reply. |
| * |
| * @return ping_dst_ip host IPv4 address |
| */ |
| uint32_t pong_ipv4(void) |
| { |
| return ping_dst_ip; |
| } |
| |
| /** |
| * ICMP: Handles ICMP-packets according to Receive-handle diagram. |
| * |
| * @param fd socket fd |
| * @param icmp_packet ICMP-packet to be handled |
| * @param packetsize Length of the packet |
| * @return ZERO - packet handled successfully; |
| * NON ZERO - packet was not handled (e.g. bad format) |
| * @see handle_ipv4 |
| */ |
| static int8_t handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, |
| int32_t packetsize) |
| { |
| struct icmphdr *icmp = (struct icmphdr *) packet; |
| |
| switch(icmp->type) { |
| case ICMP_ECHO_REPLY: |
| if (icmp->options.echo.id != 0xd476) |
| return -1; |
| if (icmp->options.echo.seq != 1) |
| return -1; |
| if(ping_dst_ip != iph->ip_src |
| || ping_dst_ip == 0) |
| return -1; |
| ping_dst_ip = 0; |
| break; |
| case ICMP_DST_UNREACHABLE: { |
| // We've got Destination Unreachable msg |
| // Inform corresponding upper network layers |
| struct iphdr * bad_iph = (struct iphdr * ) &icmp->payload; |
| |
| switch(bad_iph->ip_p) { |
| case IPTYPE_TCP: |
| handle_tcp_dun((uint8_t *) (bad_iph + 1), packetsize |
| - sizeof(struct icmphdr) |
| - sizeof(struct iphdr), icmp->code); |
| break; |
| case IPTYPE_UDP: |
| handle_udp_dun((uint8_t *) (bad_iph + 1), packetsize |
| - sizeof(struct icmphdr) |
| - sizeof(struct iphdr), icmp->code); |
| break; |
| } |
| break; |
| } |
| case ICMP_SRC_QUENCH: |
| break; |
| case ICMP_REDIRECT: |
| break; |
| case ICMP_ECHO_REQUEST: { |
| // We've got an Echo Request - answer with Echo Replay msg |
| unsigned char reply_packet[sizeof(struct iphdr) + packetsize]; |
| struct icmphdr *reply_icmph; |
| |
| fill_iphdr(reply_packet, sizeof(struct iphdr) + packetsize, |
| IPTYPE_ICMP, 0, iph->ip_src); |
| |
| reply_icmph = (struct icmphdr *) &reply_packet[sizeof(struct iphdr)]; |
| memcpy(reply_icmph, packet, packetsize); |
| reply_icmph -> type = ICMP_ECHO_REPLY; |
| reply_icmph -> checksum = 0; |
| reply_icmph->checksum = checksum((unsigned short *) reply_icmph, |
| sizeof(struct icmphdr) >> 1); |
| |
| send_ipv4(fd, reply_packet, sizeof(struct iphdr) + packetsize); |
| break; |
| } |
| case ICMP_TIME_EXCEEDED: |
| break; |
| case ICMP_PARAMETER_PROBLEM: |
| break; |
| case ICMP_TIMESTAMP_REQUEST: |
| break; |
| case ICMP_TIMESTAMP_REPLY: |
| break; |
| case ICMP_INFORMATION_REQUEST: |
| break; |
| case ICMP_INFORMATION_REPLY: |
| break; |
| } |
| return 0; |
| } |