[1770] | 1 | /* vi: set sw=4 ts=4: */
|
---|
[821] | 2 | /* clientpacket.c
|
---|
| 3 | *
|
---|
| 4 | * Packet generation and dispatching functions for the DHCP client.
|
---|
| 5 | *
|
---|
| 6 | * Russ Dill <Russ.Dill@asu.edu> July 2001
|
---|
| 7 | *
|
---|
| 8 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
|
---|
| 9 | */
|
---|
| 10 |
|
---|
| 11 | #include <features.h>
|
---|
[1770] | 12 | #if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
|
---|
[821] | 13 | #include <netpacket/packet.h>
|
---|
| 14 | #include <net/ethernet.h>
|
---|
| 15 | #else
|
---|
| 16 | #include <asm/types.h>
|
---|
| 17 | #include <linux/if_packet.h>
|
---|
| 18 | #include <linux/if_ether.h>
|
---|
| 19 | #endif
|
---|
| 20 |
|
---|
[1770] | 21 | #include "common.h"
|
---|
[821] | 22 | #include "dhcpd.h"
|
---|
[1770] | 23 | #include "dhcpc.h"
|
---|
[821] | 24 | #include "options.h"
|
---|
| 25 |
|
---|
| 26 |
|
---|
| 27 | /* Create a random xid */
|
---|
[1770] | 28 | uint32_t random_xid(void)
|
---|
[821] | 29 | {
|
---|
[1770] | 30 | static smallint initialized;
|
---|
| 31 |
|
---|
[821] | 32 | if (!initialized) {
|
---|
[1770] | 33 | srand(monotonic_us());
|
---|
| 34 | initialized = 1;
|
---|
[821] | 35 | }
|
---|
| 36 | return rand();
|
---|
| 37 | }
|
---|
| 38 |
|
---|
| 39 |
|
---|
| 40 | /* initialize a packet with the proper defaults */
|
---|
| 41 | static void init_packet(struct dhcpMessage *packet, char type)
|
---|
| 42 | {
|
---|
| 43 | udhcp_init_header(packet, type);
|
---|
| 44 | memcpy(packet->chaddr, client_config.arp, 6);
|
---|
| 45 | if (client_config.clientid)
|
---|
[1770] | 46 | add_option_string(packet->options, client_config.clientid);
|
---|
| 47 | if (client_config.hostname)
|
---|
| 48 | add_option_string(packet->options, client_config.hostname);
|
---|
| 49 | if (client_config.fqdn)
|
---|
| 50 | add_option_string(packet->options, client_config.fqdn);
|
---|
[821] | 51 | add_option_string(packet->options, client_config.vendorclass);
|
---|
| 52 | }
|
---|
| 53 |
|
---|
| 54 |
|
---|
| 55 | /* Add a parameter request list for stubborn DHCP servers. Pull the data
|
---|
| 56 | * from the struct in options.c. Don't do bounds checking here because it
|
---|
| 57 | * goes towards the head of the packet. */
|
---|
| 58 | static void add_requests(struct dhcpMessage *packet)
|
---|
| 59 | {
|
---|
| 60 | int end = end_option(packet->options);
|
---|
| 61 | int i, len = 0;
|
---|
| 62 |
|
---|
| 63 | packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
|
---|
| 64 | for (i = 0; dhcp_options[i].code; i++)
|
---|
| 65 | if (dhcp_options[i].flags & OPTION_REQ)
|
---|
| 66 | packet->options[end + OPT_DATA + len++] = dhcp_options[i].code;
|
---|
| 67 | packet->options[end + OPT_LEN] = len;
|
---|
| 68 | packet->options[end + OPT_DATA + len] = DHCP_END;
|
---|
| 69 |
|
---|
| 70 | }
|
---|
| 71 |
|
---|
| 72 |
|
---|
| 73 | /* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
|
---|
[1770] | 74 | int send_discover(uint32_t xid, uint32_t requested)
|
---|
[821] | 75 | {
|
---|
| 76 | struct dhcpMessage packet;
|
---|
| 77 |
|
---|
| 78 | init_packet(&packet, DHCPDISCOVER);
|
---|
| 79 | packet.xid = xid;
|
---|
| 80 | if (requested)
|
---|
| 81 | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
|
---|
| 82 |
|
---|
| 83 | add_requests(&packet);
|
---|
[1770] | 84 | bb_info_msg("Sending discover...");
|
---|
[821] | 85 | return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
|
---|
[1770] | 86 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
|
---|
[821] | 87 | }
|
---|
| 88 |
|
---|
| 89 |
|
---|
| 90 | /* Broadcasts a DHCP request message */
|
---|
[1770] | 91 | int send_selecting(uint32_t xid, uint32_t server, uint32_t requested)
|
---|
[821] | 92 | {
|
---|
| 93 | struct dhcpMessage packet;
|
---|
| 94 | struct in_addr addr;
|
---|
| 95 |
|
---|
| 96 | init_packet(&packet, DHCPREQUEST);
|
---|
| 97 | packet.xid = xid;
|
---|
| 98 |
|
---|
| 99 | add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
|
---|
| 100 | add_simple_option(packet.options, DHCP_SERVER_ID, server);
|
---|
| 101 |
|
---|
| 102 | add_requests(&packet);
|
---|
| 103 | addr.s_addr = requested;
|
---|
[1770] | 104 | bb_info_msg("Sending select for %s...", inet_ntoa(addr));
|
---|
[821] | 105 | return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
|
---|
| 106 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
|
---|
| 107 | }
|
---|
| 108 |
|
---|
| 109 |
|
---|
| 110 | /* Unicasts or broadcasts a DHCP renew message */
|
---|
[1770] | 111 | int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
|
---|
[821] | 112 | {
|
---|
| 113 | struct dhcpMessage packet;
|
---|
| 114 |
|
---|
| 115 | init_packet(&packet, DHCPREQUEST);
|
---|
| 116 | packet.xid = xid;
|
---|
| 117 | packet.ciaddr = ciaddr;
|
---|
| 118 |
|
---|
| 119 | add_requests(&packet);
|
---|
[1770] | 120 | bb_info_msg("Sending renew...");
|
---|
[821] | 121 | if (server)
|
---|
[1770] | 122 | return udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
|
---|
| 123 |
|
---|
| 124 | return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
|
---|
[821] | 125 | SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
|
---|
| 126 | }
|
---|
| 127 |
|
---|
| 128 |
|
---|
| 129 | /* Unicasts a DHCP release message */
|
---|
[1770] | 130 | int send_release(uint32_t server, uint32_t ciaddr)
|
---|
[821] | 131 | {
|
---|
| 132 | struct dhcpMessage packet;
|
---|
| 133 |
|
---|
| 134 | init_packet(&packet, DHCPRELEASE);
|
---|
| 135 | packet.xid = random_xid();
|
---|
| 136 | packet.ciaddr = ciaddr;
|
---|
| 137 |
|
---|
| 138 | add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr);
|
---|
| 139 | add_simple_option(packet.options, DHCP_SERVER_ID, server);
|
---|
| 140 |
|
---|
[1770] | 141 | bb_info_msg("Sending release...");
|
---|
[821] | 142 | return udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
|
---|
| 143 | }
|
---|
| 144 |
|
---|
| 145 |
|
---|
| 146 | /* return -1 on errors that are fatal for the socket, -2 for those that aren't */
|
---|
| 147 | int get_raw_packet(struct dhcpMessage *payload, int fd)
|
---|
| 148 | {
|
---|
| 149 | int bytes;
|
---|
| 150 | struct udp_dhcp_packet packet;
|
---|
| 151 | uint32_t source, dest;
|
---|
| 152 | uint16_t check;
|
---|
| 153 |
|
---|
| 154 | memset(&packet, 0, sizeof(struct udp_dhcp_packet));
|
---|
| 155 | bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
|
---|
| 156 | if (bytes < 0) {
|
---|
[1770] | 157 | DEBUG("Cannot read on raw listening socket - ignoring");
|
---|
[821] | 158 | usleep(500000); /* possible down interface, looping condition */
|
---|
| 159 | return -1;
|
---|
| 160 | }
|
---|
| 161 |
|
---|
| 162 | if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) {
|
---|
[1770] | 163 | DEBUG("Message too short, ignoring");
|
---|
[821] | 164 | return -2;
|
---|
| 165 | }
|
---|
| 166 |
|
---|
| 167 | if (bytes < ntohs(packet.ip.tot_len)) {
|
---|
[1770] | 168 | DEBUG("Truncated packet");
|
---|
[821] | 169 | return -2;
|
---|
| 170 | }
|
---|
| 171 |
|
---|
| 172 | /* ignore any extra garbage bytes */
|
---|
| 173 | bytes = ntohs(packet.ip.tot_len);
|
---|
| 174 |
|
---|
| 175 | /* Make sure its the right packet for us, and that it passes sanity checks */
|
---|
[1770] | 176 | if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION
|
---|
| 177 | || packet.ip.ihl != sizeof(packet.ip) >> 2
|
---|
| 178 | || packet.udp.dest != htons(CLIENT_PORT)
|
---|
| 179 | || bytes > (int) sizeof(struct udp_dhcp_packet)
|
---|
| 180 | || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip))
|
---|
| 181 | ) {
|
---|
| 182 | DEBUG("Unrelated/bogus packet");
|
---|
[821] | 183 | return -2;
|
---|
| 184 | }
|
---|
| 185 |
|
---|
| 186 | /* check IP checksum */
|
---|
| 187 | check = packet.ip.check;
|
---|
| 188 | packet.ip.check = 0;
|
---|
| 189 | if (check != udhcp_checksum(&(packet.ip), sizeof(packet.ip))) {
|
---|
[1770] | 190 | DEBUG("bad IP header checksum, ignoring");
|
---|
[821] | 191 | return -1;
|
---|
| 192 | }
|
---|
| 193 |
|
---|
| 194 | /* verify the UDP checksum by replacing the header with a psuedo header */
|
---|
| 195 | source = packet.ip.saddr;
|
---|
| 196 | dest = packet.ip.daddr;
|
---|
| 197 | check = packet.udp.check;
|
---|
| 198 | packet.udp.check = 0;
|
---|
| 199 | memset(&packet.ip, 0, sizeof(packet.ip));
|
---|
| 200 |
|
---|
| 201 | packet.ip.protocol = IPPROTO_UDP;
|
---|
| 202 | packet.ip.saddr = source;
|
---|
| 203 | packet.ip.daddr = dest;
|
---|
| 204 | packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
|
---|
| 205 | if (check && check != udhcp_checksum(&packet, bytes)) {
|
---|
[1770] | 206 | bb_error_msg("packet with bad UDP checksum received, ignoring");
|
---|
[821] | 207 | return -2;
|
---|
| 208 | }
|
---|
| 209 |
|
---|
| 210 | memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
|
---|
| 211 |
|
---|
| 212 | if (ntohl(payload->cookie) != DHCP_MAGIC) {
|
---|
[1770] | 213 | bb_error_msg("received bogus message (bad magic) - ignoring");
|
---|
[821] | 214 | return -2;
|
---|
| 215 | }
|
---|
[1770] | 216 | DEBUG("oooooh!!! got some!");
|
---|
[821] | 217 | return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
|
---|
| 218 | }
|
---|