www.usr.com/support/gpl/USR9113_release1.0.tar.gz
[bcm963xx.git] / userapps / opensource / udhcp / clientpacket.c
1 /* clientpacket.c
2  *
3  * Packet generation and dispatching functions for the DHCP client.
4  *
5  * Russ Dill <Russ.Dill@asu.edu> July 2001
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21  
22 #include <string.h>
23 #include <sys/socket.h>
24 #include <features.h>
25 #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
26 #include <netpacket/packet.h>
27 #include <net/ethernet.h>
28 #else
29 #include <asm/types.h>
30 #include <linux/if_packet.h>
31 #include <linux/if_ether.h>
32 #endif
33 #include <stdlib.h>
34 #include <time.h>
35 #include <unistd.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38
39
40 #include "dhcpd.h"
41 #include "packet.h"
42 #include "options.h"
43 #include "dhcpc.h"
44 #include "debug.h"
45
46
47 /* Create a random xid */
48 unsigned long random_xid(void)
49 {
50         static int initialized;
51         if (!initialized) {
52                 srand(time(0));
53                 initialized++;
54         }
55         return rand();
56 }
57
58
59 /* initialize a packet with the proper defaults */
60 static void init_packet(struct dhcpMessage *packet, char type)
61 {
62         //brcm
63         char VIinfo[VENDOR_IDENTIFYING_INFO_LEN];
64
65         struct vendor  {
66                 char vendor, length;
67                 char str[sizeof("uDHCP "VERSION)];
68         } vendor_id = { DHCP_VENDOR,  sizeof("uDHCP "VERSION) - 1, "uDHCP "VERSION};
69         
70         memset(packet, 0, sizeof(struct dhcpMessage));
71         
72         packet->op = BOOTREQUEST;
73         packet->htype = ETH_10MB;
74         packet->hlen = ETH_10MB_LEN;
75         packet->cookie = htonl(DHCP_MAGIC);
76         packet->options[0] = DHCP_END;
77         memcpy(packet->chaddr, client_config.arp, 6);
78         add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type);
79         if (client_config.clientid) add_option_string(packet->options, client_config.clientid);
80         if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
81         // brcm
82         if (strlen(vendor_class_id)) {
83             vendor_id.length = strlen(vendor_class_id);
84             sprintf(vendor_id.str, "%s", vendor_class_id);
85         }
86         add_option_string(packet->options, (char *) &vendor_id);
87         if (createVIoption(VENDOR_IDENTIFYING_FOR_DEVICE,VIinfo) != -1)
88             add_option_string(packet->options,VIinfo);
89 }
90
91
92 /* Add a paramater request list for stubborn DHCP servers */
93 static void add_requests(struct dhcpMessage *packet)
94 {
95         char request_list[] = {DHCP_PARAM_REQ, 0, PARM_REQUESTS};
96         
97         request_list[OPT_LEN] = sizeof(request_list) - 2;
98         add_option_string(packet->options, request_list);
99 }
100
101
102 /* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
103 int send_discover(unsigned long xid, unsigned long requested)
104 {
105         struct dhcpMessage packet;
106
107         init_packet(&packet, DHCPDISCOVER);
108         packet.xid = xid;
109         if (requested)
110                 add_simple_option(packet.options, DHCP_REQUESTED_IP, ntohl(requested));
111
112         add_requests(&packet);
113         // brcm
114         // LOG(LOG_DEBUG, "Sending discover...");
115         return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, 
116                                 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
117 }
118
119
120 /* Broadcasts a DHCP request message */
121 int send_selecting(unsigned long xid, unsigned long server, unsigned long requested)
122 {
123         struct dhcpMessage packet;
124         struct in_addr addr;
125
126         init_packet(&packet, DHCPREQUEST);
127         packet.xid = xid;
128
129         /* expects host order */
130         add_simple_option(packet.options, DHCP_REQUESTED_IP, ntohl(requested));
131
132         /* expects host order */
133         add_simple_option(packet.options, DHCP_SERVER_ID, ntohl(server));
134         
135         add_requests(&packet);
136         addr.s_addr = requested;
137         // brcm
138         //LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr));
139         return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, 
140                                 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
141 }
142
143
144 /* Unicasts or broadcasts a DHCP renew message */
145 int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr)
146 {
147         struct dhcpMessage packet;
148         int ret = 0;
149
150         init_packet(&packet, DHCPREQUEST);
151         packet.xid = xid;
152         packet.ciaddr = ciaddr;
153
154         LOG(LOG_DEBUG, "Sending renew...");
155         if (server) 
156                 ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
157         else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
158                                 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
159         return ret;
160 }       
161
162
163 /* Unicasts a DHCP release message */
164 int send_release(unsigned long server, unsigned long ciaddr)
165 {
166         struct dhcpMessage packet;
167
168         init_packet(&packet, DHCPRELEASE);
169         packet.xid = random_xid();
170         packet.ciaddr = ciaddr;
171         
172         /* expects host order */
173         add_simple_option(packet.options, DHCP_REQUESTED_IP, ntohl(ciaddr));
174         add_simple_option(packet.options, DHCP_SERVER_ID, ntohl(server));
175
176         LOG(LOG_DEBUG, "Sending release...");
177         return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
178 }
179
180
181 int get_raw_packet(struct dhcpMessage *payload, int fd)
182 {
183         int bytes;
184         struct udp_dhcp_packet packet;
185         u_int32_t source, dest;
186         u_int16_t check;
187
188         memset(&packet, 0, sizeof(struct udp_dhcp_packet));
189         bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
190         if (bytes < 0) {
191                 DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring");
192                 usleep(500000); /* possible down interface, looping condition */
193                 return -1;
194         }
195         
196         if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) {
197                 DEBUG(LOG_INFO, "message too short, ignoring");
198                 return -1;
199         }
200         
201         /* Make sure its the right packet for us, and that it passes sanity checks */
202         if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION ||
203             packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) ||
204             ntohs(packet.ip.tot_len) != bytes || bytes > (int) sizeof(struct udp_dhcp_packet) ||
205             ntohs(packet.udp.len) != (short) (bytes - sizeof(packet.ip))) {
206                 DEBUG(LOG_INFO, "unrelated/bogus packet");
207                 return -1;
208         }
209
210         /* check IP checksum */
211         check = packet.ip.check;
212         packet.ip.check = 0;
213         if (check != checksum(&(packet.ip), sizeof(packet.ip))) {
214                 DEBUG(LOG_INFO, "bad IP header checksum, ignoring");
215                 return -1;
216         }
217         
218         /* verify the UDP checksum by replacing the header with a psuedo header */
219         source = packet.ip.saddr;
220         dest = packet.ip.daddr;
221         check = packet.udp.check;
222         packet.udp.check = 0;
223         memset(&packet.ip, 0, sizeof(packet.ip));
224
225         packet.ip.protocol = IPPROTO_UDP;
226         packet.ip.saddr = source;
227         packet.ip.daddr = dest;
228         packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
229         // brcm
230         /*
231         if (check != checksum(&packet, bytes)) {
232                 DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring");
233                 // cwu
234                 //return -1;
235         }
236         */
237         
238         memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
239         
240         if (ntohl(payload->cookie) != DHCP_MAGIC) {
241                 LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring");
242                 return -1;
243         }
244         DEBUG(LOG_INFO, "oooooh!!! got some!");
245         return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
246 }