1#include <unistd.h> 2#include <string.h> 3#include <netinet/in.h> 4#include <sys/types.h> 5#include <sys/socket.h> 6#include <features.h> 7#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 8#include <netpacket/packet.h> 9#include <net/ethernet.h> 10#else 11#include <asm/types.h> 12#include <linux/if_packet.h> 13#include <linux/if_ether.h> 14#endif 15#include <errno.h> 16 17#include "packet.h" 18#include "debug.h" 19#include "dhcpd.h" 20#include "options.h" 21 22 23void init_header(struct dhcpMessage *packet, char type) 24{ 25 memset(packet, 0, sizeof(struct dhcpMessage)); 26 switch (type) { 27 case DHCPDISCOVER: 28 case DHCPREQUEST: 29 case DHCPRELEASE: 30 case DHCPINFORM: 31 case DHCPDECLINE: /* foxconn wklin added, 08/10/2007 */ 32 packet->op = BOOTREQUEST; 33 break; 34 case DHCPOFFER: 35 case DHCPACK: 36 case DHCPNAK: 37 packet->op = BOOTREPLY; 38 } 39 packet->htype = ETH_10MB; 40 packet->hlen = ETH_10MB_LEN; 41 packet->cookie = htonl(DHCP_MAGIC); 42 packet->options[0] = DHCP_END; 43 add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); 44} 45 46 47/* read a packet from socket fd, return -1 on read error, -2 on packet error */ 48int get_packet(struct dhcpMessage *packet, int fd) 49{ 50 int bytes; 51 int i; 52 const char broken_vendors[][8] = { 53 "MSFT 98", 54 "" 55 }; 56 char unsigned *vendor; 57 58 memset(packet, 0, sizeof(struct dhcpMessage)); 59 bytes = read(fd, packet, sizeof(struct dhcpMessage)); 60 if (bytes < 0) { 61 DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring"); 62 return -1; 63 } 64 65 if (ntohl(packet->cookie) != DHCP_MAGIC) { 66 LOG(LOG_ERR, "received bogus message, ignoring"); 67 return -2; 68 } 69 DEBUG(LOG_INFO, "Received a packet"); 70 71 if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) { 72 for (i = 0; broken_vendors[i][0]; i++) { 73 if (vendor[OPT_LEN - 2] == (unsigned char) strlen(broken_vendors[i]) && 74 !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) { 75 DEBUG(LOG_INFO, "broken client (%s), forcing broadcast", 76 broken_vendors[i]); 77 packet->flags |= htons(BROADCAST_FLAG); 78 } 79 } 80 } 81 82 83 return bytes; 84} 85 86 87u_int16_t checksum(void *addr, int count) 88{ 89 /* Compute Internet Checksum for "count" bytes 90 * beginning at location "addr". 91 */ 92 register int32_t sum = 0; 93 u_int16_t *source = (u_int16_t *) addr; 94 95 while (count > 1) { 96 /* This is the inner loop */ 97 sum += *source++; 98 count -= 2; 99 } 100 101 /* Add left-over byte, if any */ 102 if (count > 0) { 103 /* Make sure that the left-over byte is added correctly both 104 * with little and big endian hosts */ 105 u_int16_t tmp = 0; 106 *(unsigned char *) (&tmp) = * (unsigned char *) source; 107 sum += tmp; 108 } 109 /* Fold 32-bit sum to 16 bits */ 110 while (sum >> 16) 111 sum = (sum & 0xffff) + (sum >> 16); 112 113 return ~sum; 114} 115 116 117/* Constuct a ip/udp header for a packet, and specify the source and dest hardware address */ 118int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, 119 u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex) 120{ 121 int fd; 122 int result; 123 struct sockaddr_ll dest; 124 struct udp_dhcp_packet packet; 125 126 if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { 127 DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); 128 return -1; 129 } 130 131 memset(&dest, 0, sizeof(dest)); 132 memset(&packet, 0, sizeof(packet)); 133 134 dest.sll_family = AF_PACKET; 135 dest.sll_protocol = htons(ETH_P_IP); 136 dest.sll_ifindex = ifindex; 137 dest.sll_halen = 6; 138 memcpy(dest.sll_addr, dest_arp, 6); 139 if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) { 140 DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); 141 close(fd); 142 return -1; 143 } 144 145 packet.ip.protocol = IPPROTO_UDP; 146 packet.ip.saddr = source_ip; 147 packet.ip.daddr = dest_ip; 148 packet.udp.source = htons(source_port); 149 packet.udp.dest = htons(dest_port); 150 packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */ 151 packet.ip.tot_len = packet.udp.len; 152 memcpy(&(packet.data), payload, sizeof(struct dhcpMessage)); 153 packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet)); 154 155 packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet)); 156 packet.ip.ihl = sizeof(packet.ip) >> 2; 157 packet.ip.version = IPVERSION; 158 packet.ip.ttl = IPDEFTTL; 159 packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip)); 160 161 result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest)); 162 if (result <= 0) { 163 DEBUG(LOG_ERR, "write on socket failed: %s", strerror(errno)); 164 } 165 close(fd); 166 return result; 167} 168 169 170/* Let the kernel do all the work for packet generation */ 171int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, 172 u_int32_t dest_ip, int dest_port) 173{ 174 int n = 1; 175 int fd, result; 176 struct sockaddr_in client; 177 178 if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 179 return -1; 180 181 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) 182 return -1; 183 184 memset(&client, 0, sizeof(client)); 185 client.sin_family = AF_INET; 186 client.sin_port = htons(source_port); 187 client.sin_addr.s_addr = source_ip; 188 189 if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) 190 return -1; 191 192 memset(&client, 0, sizeof(client)); 193 client.sin_family = AF_INET; 194 client.sin_port = htons(dest_port); 195 client.sin_addr.s_addr = dest_ip; 196 197 if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) 198 return -1; 199 200 result = write(fd, payload, sizeof(struct dhcpMessage)); 201 close(fd); 202 return result; 203} 204 205