1/* serverpacket.c
2 *
3 * Constuct and send DHCP server packets
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 <sys/socket.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25#include <string.h>
26#include <time.h>
27
28#include "packet.h"
29#include "debug.h"
30#include "dhcpd.h"
31#include "options.h"
32#include "leases.h"
33
34/* send a packet to giaddr using the kernel ip stack */
35static int send_packet_to_relay(struct dhcpMessage *payload)
36{
37	DEBUG(LOG_INFO, "Forwarding packet to relay");
38
39	return kernel_packet(payload, server_config.server, SERVER_PORT,
40			payload->giaddr, SERVER_PORT);
41}
42
43
44/* send a packet to a specific arp address and ip address by creating our own ip packet */
45static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast)
46{
47	unsigned char *chaddr;
48	u_int32_t ciaddr;
49
50	if (force_broadcast) {
51		DEBUG(LOG_INFO, "broadcasting packet to client (NAK)");
52		ciaddr = INADDR_BROADCAST;
53		chaddr = MAC_BCAST_ADDR;
54	} else if (payload->ciaddr) {
55		DEBUG(LOG_INFO, "unicasting packet to client ciaddr");
56		ciaddr = payload->ciaddr;
57		chaddr = payload->chaddr;
58	} else if (ntohs(payload->flags) & BROADCAST_FLAG) {
59		DEBUG(LOG_INFO, "broadcasting packet to client (requested)");
60		ciaddr = INADDR_BROADCAST;
61		chaddr = MAC_BCAST_ADDR;
62	} else {
63		DEBUG(LOG_INFO, "unicasting packet to client yiaddr");
64		ciaddr = payload->yiaddr;
65		chaddr = payload->chaddr;
66	}
67	return raw_packet(payload, server_config.server, SERVER_PORT,
68			ciaddr, CLIENT_PORT, chaddr, server_config.ifindex);
69}
70
71
72/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */
73static int send_packet(struct dhcpMessage *payload, int force_broadcast)
74{
75	int ret;
76
77	if (payload->giaddr)
78		ret = send_packet_to_relay(payload);
79	else ret = send_packet_to_client(payload, force_broadcast);
80	return ret;
81}
82
83
84static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type)
85{
86	init_header(packet, type);
87	packet->xid = oldpacket->xid;
88	memcpy(packet->chaddr, oldpacket->chaddr, 16);
89	packet->flags = oldpacket->flags;
90	packet->giaddr = oldpacket->giaddr;
91	packet->ciaddr = oldpacket->ciaddr;
92	add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server);
93}
94
95
96/* add in the bootp options */
97static void add_bootp_options(struct dhcpMessage *packet)
98{
99	packet->siaddr = server_config.siaddr;
100	if (server_config.sname)
101		strncpy(packet->sname, server_config.sname, sizeof(packet->sname) - 1);
102	if (server_config.boot_file)
103		strncpy(packet->file, server_config.boot_file, sizeof(packet->file) - 1);
104}
105
106
107/* send a DHCP OFFER to a DHCP DISCOVER */
108int sendOffer(struct dhcpMessage *oldpacket)
109{
110	struct dhcpMessage packet;
111	struct dhcpOfferedAddr *lease = NULL;
112	u_int32_t req_align, lease_time_align = server_config.lease;
113	unsigned char *req, *lease_time;
114	struct option_set *curr;
115	struct in_addr addr;
116    unsigned char mac[6];
117	u_int32_t reserved_ip;
118
119    memcpy(mac, oldpacket->chaddr, 6);
120
121	init_packet(&packet, oldpacket, DHCPOFFER);
122
123	/* ADDME: if static, short circuit */
124	/* the client is in our lease/offered table */
125	if ((lease = find_lease_by_chaddr(oldpacket->chaddr)) &&
126
127        /* Make sure the IP is not already used on network */
128        !check_ip(lease->yiaddr)) {
129
130		if (!lease_expired(lease))
131			lease_time_align = lease->expires - time(0);
132		packet.yiaddr = lease->yiaddr;
133
134    /* Find a reserved ip for this MAC */
135	} else if ( (reserved_ip = find_reserved_ip(mac)) != 0) {
136		packet.yiaddr = htonl(reserved_ip);
137
138	/* Or the client has a requested ip */
139	} else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) &&
140
141		/* Don't look here (ugly hackish thing to do) */
142		memcpy(&req_align, req, 4) &&
143
144        /* check if the requested ip has been reserved */
145        check_reserved_ip(req_align, mac) &&
146
147		/* and the ip is in the lease range */
148		ntohl(req_align) >= ntohl(server_config.start) &&
149		ntohl(req_align) <= ntohl(server_config.end) &&
150
151         /* Check that this request ip is not on network */
152         //!check_ip(ntohl(req_align)) &&
153	 /* the input parameter of check_ip() should be network order */
154	 !check_ip(req_align) && /* Foxconn modified by Max Ding, 07/07/2011 @TD #42 of WNR3500Lv2 */
155
156		/* and its not already taken/offered */ /* ADDME: check that its not a static lease */
157		((!(lease = find_lease_by_yiaddr(req_align)) ||
158
159		/* or its taken, but expired */ /* ADDME: or maybe in here */
160		lease_expired(lease)))) {
161		    packet.yiaddr = req_align;
162
163	/* otherwise, find a free IP */ /*ADDME: is it a static lease? */
164	} else {
165		packet.yiaddr = find_address2(0, mac);
166
167		/* try for an expired lease */
168		if (!packet.yiaddr) packet.yiaddr = find_address2(1, mac);
169	}
170
171	if(!packet.yiaddr) {
172		LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned");
173		return -1;
174	}
175
176	if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) {
177		LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned");
178		return -1;
179	}
180
181	if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
182		memcpy(&lease_time_align, lease_time, 4);
183		lease_time_align = ntohl(lease_time_align);
184		if (lease_time_align > server_config.lease)
185			lease_time_align = server_config.lease;
186	}
187
188	/* Make sure we aren't just using the lease time from the previous offer */
189	if (lease_time_align < server_config.min_lease)
190		lease_time_align = server_config.lease;
191	/* ADDME: end of short circuit */
192	add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
193
194	curr = server_config.options;
195	while (curr) {
196		if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
197			add_option_string(packet.options, curr->data);
198		curr = curr->next;
199	}
200
201	add_bootp_options(&packet);
202
203	addr.s_addr = packet.yiaddr;
204	LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr));
205	return send_packet(&packet, 0);
206}
207
208
209int sendNAK(struct dhcpMessage *oldpacket)
210{
211	struct dhcpMessage packet;
212
213	init_packet(&packet, oldpacket, DHCPNAK);
214
215	DEBUG(LOG_INFO, "sending NAK");
216	return send_packet(&packet, 1);
217}
218
219
220int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr)
221{
222	struct dhcpMessage packet;
223	struct option_set *curr;
224	unsigned char *lease_time;
225
226	//Foxconn added start Bob Guo 11/15/2006
227	FILE *fp;
228	unsigned char *client_mac, *client_ip;
229	char logBuffer[96];
230	//Foxconn added end Bob Guo 11/15/2006
231
232	u_int32_t lease_time_align = server_config.lease;
233	struct in_addr addr;
234
235	init_packet(&packet, oldpacket, DHCPACK);
236	packet.yiaddr = yiaddr;
237
238	if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
239		memcpy(&lease_time_align, lease_time, 4);
240		lease_time_align = ntohl(lease_time_align);
241		if (lease_time_align > server_config.lease)
242			lease_time_align = server_config.lease;
243		else if (lease_time_align < server_config.min_lease)
244			lease_time_align = server_config.lease;
245	}
246
247	add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
248
249	curr = server_config.options;
250	while (curr) {
251		if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
252			add_option_string(packet.options, curr->data);
253		curr = curr->next;
254	}
255
256	add_bootp_options(&packet);
257
258	addr.s_addr = packet.yiaddr;
259	LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr));
260
261	if (send_packet(&packet, 0) < 0)
262		return -1;
263
264	add_lease(packet.chaddr, packet.yiaddr, lease_time_align);
265	//Foxconn added start Bob Guo 11/15/2006
266	client_mac = (unsigned char *)packet.chaddr;
267	client_ip = (unsigned char *)&packet.yiaddr;
268	sprintf(logBuffer, "[DHCP IP: (%d.%d.%d.%d)] to MAC address %02X:%02X:%02X:%02X:%02X:%02X,",
269	                                          *client_ip, *(client_ip+1), *(client_ip+2), *(client_ip+3),
270	                                          *client_mac, *(client_mac+1), *(client_mac+2), *(client_mac+3), *(client_mac+4), *(client_mac+5));
271	if ((fp = fopen("/dev/aglog", "r+")) != NULL)
272  {
273        fwrite(logBuffer, sizeof(char), strlen(logBuffer)+1, fp);
274        fclose(fp);
275  }
276	//Foxconn added end Bob Guo 11/15/2006
277
278	return 0;
279}
280
281
282int send_inform(struct dhcpMessage *oldpacket)
283{
284	struct dhcpMessage packet;
285	struct option_set *curr;
286
287	init_packet(&packet, oldpacket, DHCPACK);
288
289	curr = server_config.options;
290	while (curr) {
291		if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
292			add_option_string(packet.options, curr->data);
293		curr = curr->next;
294	}
295
296	add_bootp_options(&packet);
297
298	return send_packet(&packet, 0);
299}
300
301
302
303