| /****************************************************************************** |
| * 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 |
| *****************************************************************************/ |
| |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| #include <time.h> |
| |
| #include <ethernet.h> |
| #include <ipv4.h> |
| #include <udp.h> |
| #include <dhcp.h> |
| |
| #define DEBUG 0 |
| |
| static char * response_buffer; |
| |
| #if DEBUG |
| static void |
| print_ip(char *ip) |
| { |
| printf("%d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); |
| } |
| #endif |
| |
| /* IP header checksum calculation */ |
| 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 int |
| send_bootp(filename_ip_t * fn_ip) |
| { |
| #if DEBUG |
| int i; |
| #endif |
| unsigned int packetsize = |
| sizeof(struct iphdr) + sizeof(struct ethhdr) + |
| sizeof(struct udphdr) + sizeof(struct btphdr); |
| unsigned char packet[packetsize]; |
| struct iphdr *iph; |
| struct udphdr *udph; |
| struct btphdr *btph; |
| |
| iph = (struct iphdr *) packet; |
| udph = (struct udphdr *) (iph + 1); |
| btph = (struct btphdr *) (udph + 1); |
| |
| memset(packet, 0, packetsize); |
| |
| fill_iphdr((uint8_t *) iph, htons(packetsize - sizeof(struct ethhdr)), |
| IPTYPE_UDP, 0, fn_ip->server_ip); |
| fill_udphdr((uint8_t *) udph, |
| htons(sizeof(struct udphdr) + sizeof(struct btphdr)), |
| htons(UDPPORT_BOOTPC), htons(UDPPORT_BOOTPS)); |
| btph->op = 1; |
| btph->htype = 1; |
| btph->hlen = 6; |
| strcpy((char *) btph->file, "bla"); |
| memcpy(btph->chaddr, get_mac_address(), 6); |
| |
| #if DEBUG |
| printf("Sending packet\n"); |
| printf("Packet is "); |
| for (i = 0; i < packetsize; i++) |
| printf(" %02x", packet[i]); |
| printf(".\n"); |
| #endif |
| |
| send_ipv4(fn_ip->fd, packet, iph->ip_len); |
| #if DEBUG |
| printf("%d bytes transmitted over socket.\n", i); |
| #endif |
| |
| return 0; |
| } |
| |
| |
| static int |
| receive_bootp(filename_ip_t * fn_ip) |
| { |
| int len, old_sum; |
| unsigned int packetsize = 2000; |
| unsigned char packet[packetsize]; |
| struct iphdr *iph; |
| struct udphdr *udph; |
| struct btphdr *btph; |
| |
| #if DEBUG |
| struct ethhdr *ethh; |
| ethh = (struct ethhdr *) packet; |
| #endif |
| |
| iph = (struct iphdr *) (packet + sizeof(struct ethhdr)); |
| udph = (struct udphdr *) (iph + 1); |
| btph = (struct btphdr *) (udph + 1); |
| |
| memset(packet, 0, packetsize); |
| |
| /* setting up a timer with a timeout of one second */ |
| set_timer(TICKS_SEC); |
| |
| do { |
| |
| /* let's receive a packet */ |
| len = recv(fn_ip->fd, packet, packetsize, 0); |
| |
| #if DEBUG |
| int j; |
| printf("%d bytes received, %d expected \n", len, packetsize); |
| if (len == 346) { |
| printf("Rec packet\n"); |
| printf("Packet is "); |
| for (j = 0; j < len; j++) { |
| if (j % 16 == 0) |
| printf("\n"); |
| printf(" %02x", packet[j]); |
| } |
| printf(".\n"); |
| } |
| #endif |
| if (len == 0) |
| continue; |
| |
| /* check if the ip checksum is correct */ |
| old_sum = iph->ip_sum; |
| iph->ip_sum = 0x00; |
| if (old_sum != |
| checksum((unsigned short *) iph, sizeof(struct iphdr) >> 1)) |
| /* checksum failed */ |
| continue; |
| /* is it a udp packet */ |
| if (iph->ip_p != IPTYPE_UDP) |
| continue; |
| /* check if the source port and destination port and the packet |
| * say that it is a bootp answer */ |
| if (udph->uh_dport != htons(UDPPORT_BOOTPC) || udph->uh_sport != htons(UDPPORT_BOOTPS)) |
| continue; |
| /* check if it is a Boot Reply */ |
| if (btph->op != 2) |
| continue; |
| /* Comparing our mac address with the one in the bootp reply */ |
| if (memcmp(get_mac_address(), btph->chaddr, ETH_ALEN)) |
| continue; |
| |
| if(response_buffer) |
| memcpy(response_buffer, btph, 1720); |
| |
| fn_ip->own_ip = btph->yiaddr; |
| fn_ip->server_ip = btph->siaddr; |
| strcpy(fn_ip->filename, (char *)btph->file); |
| |
| #if DEBUG |
| printf("\nThese are the details of the bootp reply:\n"); |
| printf("Our IP address: "); |
| print_ip((char*) &fn_ip->own_ip); |
| printf("Next server IP address: "); |
| print_ip((char*) &fn_ip->server_ip); |
| printf("Boot file name: %s\n", btph->file); |
| printf("Packet is: %s\n", btph->file); |
| for (j = 0; j < len; j++) { |
| if (j % 16 == 0) |
| printf("\n"); |
| printf(" %02x", packet[j]); |
| } |
| printf(".\n"); |
| printf("fn_ip->own_mac: %02x:%02x:%02x:%02x:%02x:%02x\n", |
| get_mac_address()[0], get_mac_address()[1], |
| get_mac_address()[2], get_mac_address()[3], |
| get_mac_address()[4], get_mac_address()[5]); |
| printf("Header ethh->dest_mac: %02x:%02x:%02x:%02x:%02x:%02x\n", |
| ethh->dest_mac[0], ethh->dest_mac[1], ethh->dest_mac[2], |
| ethh->dest_mac[3], ethh->dest_mac[4], ethh->dest_mac[5]); |
| printf("Header ethh->src_mac: %02x:%02x:%02x:%02x:%02x:%02x\n", |
| ethh->src_mac[0], ethh->src_mac[1], ethh->src_mac[2], |
| ethh->src_mac[3], ethh->src_mac[4], ethh->src_mac[5]); |
| printf("Header ethh->typ: %x\n",ethh->type); |
| printf("Header iph->ip_hlv: %x\n",iph->ip_hlv); |
| printf("Header iph->ip_len: %x\n",iph->ip_len); |
| printf("Header iph->ip_id: %x\n",iph->ip_id); |
| printf("Header iph->ip_off: %x\n",iph->ip_off); |
| printf("Header iph->ip_ttl: %x\n",iph->ip_ttl); |
| printf("Header iph->ip_p: %x\n",iph->ip_p); |
| printf("Header iph->ip_sum: %x\n",iph->ip_sum); |
| printf("Header iph->ip_src: %x\n",iph->ip_src); |
| printf("Header iph->ip_dst: %x\n",iph->ip_dst); |
| |
| printf("Header btph->op: %x\n",btph->op); |
| printf("Header btph->htype: %x\n",btph->htype); |
| printf("Header btph->hlen: %x\n",btph->hlen); |
| printf("Header btph->hops: %x\n",btph->hops); |
| printf("Header btph->xid: %x\n",btph->xid); |
| printf("Header btph->secs: %x\n",btph->secs); |
| printf("Header btph->ciaddr: %x\n",btph->ciaddr); |
| printf("Header btph->yiaddr: %x\n",btph->yiaddr); |
| printf("Header btph->siaddr: %x\n",btph->siaddr); |
| printf("Header btph->giaddr: %x\n",btph->giaddr); |
| |
| printf("Header btph->chaddr: %02x:%02x:%02x:%02x:%02x:%02x:\n", |
| btph->chaddr[0], btph->chaddr[1], btph->chaddr[2], |
| btph->chaddr[3], btph->chaddr[4], btph->chaddr[5]); |
| #endif |
| return 0; |
| |
| /* only do this for the time specified during set_timer() */ |
| } while (get_timer() > 0); |
| return -1; |
| } |
| |
| |
| int |
| bootp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries) |
| { |
| int i = (int) retries+1; |
| fn_ip->own_ip = 0; |
| |
| printf(" Requesting IP address via BOOTP: "); |
| |
| response_buffer = ret_buffer; |
| |
| do { |
| printf("\b\b%02d", i); |
| if (!i--) { |
| printf("\nGiving up after %d bootp requests\n", |
| retries+1); |
| return -1; |
| } |
| send_bootp(fn_ip); |
| /* if the timer in receive_bootp expired it will return |
| * -1 and we will just send another bootp request just |
| * in case the previous one was lost. And because we don't |
| * trust the network cable we keep on doing this 30 times */ |
| } while (receive_bootp(fn_ip) != 0); |
| |
| printf("\b\b\bdone\n"); |
| return 0; |
| } |