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#ifdef ARLO_SUPPORT
28#include <linux/ioctl.h>
29#include <stdio.h>
30#include <fcntl.h>
31#include <sys/stat.h>
32#endif
33#include "packet.h"
34#include "debug.h"
35#include "dhcpd.h"
36#include "options.h"
37#include "leases.h"
38
39/* Foxconn added start pling 06/19/2013 */
40/* For wireless client associated to guest network, we want to assign lease time
41 *  of 30 minutes only.
42 */
43#include <sys/sysinfo.h>
44#include <stdlib.h>
45
46#define GUEST_LEASE_TIME	1800	//secs = 30 minutes
47#define MAX_REFRESH_TIME	3		// 3 secs, to refresh the guest assoc list
48#define GUEST_ASSOC_LIST	"/tmp/wlan_guest_clients"
49
50/*
51 * Declare a buffer to store wireless assoclist.
52 * response of "wl assoclist":
53 *
54 *  assoclist 00:11:22:33:44:55
55 *  assoclist 00:22:33:44:55:66
56 *  ...
57 *
58 * To accept max 64 clients, we need (27+2) * 64 = 1856.
59 * So choose 2048 buf size to store all guest clients.
60 */
61static char guest_assoclist[2048];
62
63static int get_guest_assoclist(void)
64{
65	static long last_update =0;
66	struct sysinfo info;
67	char command[128];
68	FILE *fp = NULL;
69
70	sysinfo(&info);
71	if (info.uptime - last_update >= MAX_REFRESH_TIME) {
72		/* Issue wl commands to create the new list */
73		sprintf(command, "wl -i wl0.1 assoclist > %s", GUEST_ASSOC_LIST);
74		system(command);
75		sprintf(command, "wl -i wl1.1 assoclist >> %s", GUEST_ASSOC_LIST);
76		system(command);
77
78		/* Read the command output to internal buffer */
79		memset(guest_assoclist, 0, sizeof(guest_assoclist));
80		fp = fopen(GUEST_ASSOC_LIST, "r");
81		if (fp) {
82			fread(guest_assoclist, 1, sizeof(guest_assoclist), fp);
83			fclose(fp);
84			last_update = info.uptime;
85		}
86	}
87}
88
89static int is_guest_network(char *mac)
90{
91	char client_mac[32];
92
93	sprintf(client_mac, "%02X:%02X:%02X:%02X:%02X:%02X",
94						(unsigned char)mac[0],
95						(unsigned char)mac[1],
96						(unsigned char)mac[2],
97						(unsigned char)mac[3],
98						(unsigned char)mac[4],
99						(unsigned char)mac[5]);
100	get_guest_assoclist();
101	if (strstr(guest_assoclist, client_mac))
102		return 1;
103	else
104		return 0;
105}
106/* Foxconn added end pling 06/19/2013 */
107
108
109/* Foxconn added start James Hsu 03/05/2015 */
110/* For arlo client associated to wl0.2, we want to assign lease time to one year
111 */
112#ifdef ARLO_SUPPORT
113#define ARLO_ASSOC_LIST 	"/tmp/arlo_guest_clients"
114#define Arlo_lease_time 94608000 //secs for three year
115
116/*
117 * Declare a buffer to store wireless assoclist.
118 * response of "wl assoclist":
119 *
120 *  assoclist 00:11:22:33:44:55
121 *  assoclist 00:22:33:44:55:66
122 *  ...
123 *
124 * To accept max 64 clients, we need (27+2) * 64 = 1856.
125 * So choose 2048 buf size to store all guest clients.
126 */
127static char arlo_assoclist[2048];
128
129static int get_arlo_assoclist(void)
130{
131	static long last_update =0;
132	struct sysinfo info;
133	char command[128];
134	FILE *fp = NULL;
135
136	sysinfo(&info);
137	if (info.uptime - last_update >= MAX_REFRESH_TIME) {
138		/* Issue wl commands to create the new list */
139		sprintf(command, "wl -i wl0.2 assoclist > %s", ARLO_ASSOC_LIST);
140		system(command);
141
142		/* Read the command output to internal buffer */
143		memset(arlo_assoclist, 0, sizeof(arlo_assoclist));
144		fp = fopen(ARLO_ASSOC_LIST, "r");
145		if (fp) {
146			fread(arlo_assoclist, 1, sizeof(arlo_assoclist), fp);
147			fclose(fp);
148			last_update = info.uptime;
149		}
150	}
151}
152
153
154static int is_arlo_network(char *mac)
155{
156	char client_mac[32];
157
158	sprintf(client_mac, "%02X:%02X:%02X:%02X:%02X:%02X",
159						(unsigned char)mac[0],
160						(unsigned char)mac[1],
161						(unsigned char)mac[2],
162						(unsigned char)mac[3],
163						(unsigned char)mac[4],
164						(unsigned char)mac[5]);
165	get_arlo_assoclist();
166	if (strstr(arlo_assoclist, client_mac))
167		return 1;
168	else
169		return 0;
170}
171#endif
172/* Foxconn added end, James Hsu */
173
174
175/* send a packet to giaddr using the kernel ip stack */
176static int send_packet_to_relay(struct dhcpMessage *payload)
177{
178	DEBUG(LOG_INFO, "Forwarding packet to relay");
179
180	return kernel_packet(payload, server_config.server, SERVER_PORT,
181			payload->giaddr, SERVER_PORT);
182}
183
184
185/* send a packet to a specific arp address and ip address by creating our own ip packet */
186static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast)
187{
188	unsigned char *chaddr;
189	u_int32_t ciaddr;
190
191	if (force_broadcast) {
192		DEBUG(LOG_INFO, "broadcasting packet to client (NAK)");
193		ciaddr = INADDR_BROADCAST;
194		chaddr = MAC_BCAST_ADDR;
195	} else if (payload->ciaddr) {
196		DEBUG(LOG_INFO, "unicasting packet to client ciaddr");
197		ciaddr = payload->ciaddr;
198		chaddr = payload->chaddr;
199	} else if (ntohs(payload->flags) & BROADCAST_FLAG) {
200		DEBUG(LOG_INFO, "broadcasting packet to client (requested)");
201		ciaddr = INADDR_BROADCAST;
202		chaddr = MAC_BCAST_ADDR;
203	} else {
204		DEBUG(LOG_INFO, "unicasting packet to client yiaddr");
205		ciaddr = payload->yiaddr;
206		chaddr = payload->chaddr;
207	}
208	return raw_packet(payload, server_config.server, SERVER_PORT,
209			ciaddr, CLIENT_PORT, chaddr, server_config.ifindex);
210}
211
212
213/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */
214static int send_packet(struct dhcpMessage *payload, int force_broadcast)
215{
216	int ret;
217
218	if (payload->giaddr)
219		ret = send_packet_to_relay(payload);
220	else ret = send_packet_to_client(payload, force_broadcast);
221	return ret;
222}
223
224
225static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type)
226{
227	init_header(packet, type);
228	packet->xid = oldpacket->xid;
229	memcpy(packet->chaddr, oldpacket->chaddr, 16);
230	packet->flags = oldpacket->flags;
231	packet->giaddr = oldpacket->giaddr;
232	packet->ciaddr = oldpacket->ciaddr;
233	add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server);
234}
235
236
237/* add in the bootp options */
238static void add_bootp_options(struct dhcpMessage *packet)
239{
240	packet->siaddr = server_config.siaddr;
241	if (server_config.sname)
242		strncpy(packet->sname, server_config.sname, sizeof(packet->sname) - 1);
243	if (server_config.boot_file)
244		strncpy(packet->file, server_config.boot_file, sizeof(packet->file) - 1);
245}
246
247
248/* send a DHCP OFFER to a DHCP DISCOVER */
249int sendOffer(struct dhcpMessage *oldpacket)
250{
251	struct dhcpMessage packet;
252	struct dhcpOfferedAddr *lease = NULL;
253	u_int32_t req_align, lease_time_align = server_config.lease;
254	unsigned char *req, *lease_time;
255	struct option_set *curr;
256	struct in_addr addr;
257    unsigned char mac[6];
258	u_int32_t reserved_ip;
259
260    memcpy(mac, oldpacket->chaddr, 6);
261
262	init_packet(&packet, oldpacket, DHCPOFFER);
263
264	/* ADDME: if static, short circuit */
265	/* the client is in our lease/offered table */
266	if ((lease = find_lease_by_chaddr(oldpacket->chaddr)) &&
267
268        /* Make sure the IP is not already used on network */
269        !check_ip(lease->yiaddr)) {
270
271		if (!lease_expired(lease))
272			lease_time_align = lease->expires - time(0);
273		packet.yiaddr = lease->yiaddr;
274
275    /* Find a reserved ip for this MAC */
276	} else if ( (reserved_ip = find_reserved_ip(mac)) != 0) {
277		packet.yiaddr = htonl(reserved_ip);
278
279	/* Or the client has a requested ip */
280	} else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) &&
281
282		/* Don't look here (ugly hackish thing to do) */
283		memcpy(&req_align, req, 4) &&
284
285        /* check if the requested ip has been reserved */
286        check_reserved_ip(req_align, mac) &&
287
288		/* and the ip is in the lease range */
289		ntohl(req_align) >= ntohl(server_config.start) &&
290		ntohl(req_align) <= ntohl(server_config.end) &&
291
292         /* Check that this request ip is not on network */
293         //!check_ip(ntohl(req_align)) &&
294	 /* the input parameter of check_ip() should be network order */
295	 !check_ip(req_align) && /* Foxconn modified by Max Ding, 07/07/2011 @TD #42 of WNR3500Lv2 */
296
297		/* and its not already taken/offered */ /* ADDME: check that its not a static lease */
298		((!(lease = find_lease_by_yiaddr(req_align)) ||
299
300		/* or its taken, but expired */ /* ADDME: or maybe in here */
301		lease_expired(lease)))) {
302		    packet.yiaddr = req_align;
303
304	/* otherwise, find a free IP */ /*ADDME: is it a static lease? */
305	} else {
306		packet.yiaddr = find_address2(0, mac);
307
308		/* try for an expired lease */
309		if (!packet.yiaddr) packet.yiaddr = find_address2(1, mac);
310	}
311
312	if(!packet.yiaddr) {
313		LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned");
314		return -1;
315	}
316
317	if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) {
318		LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned");
319		return -1;
320	}
321
322	if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
323		memcpy(&lease_time_align, lease_time, 4);
324		lease_time_align = ntohl(lease_time_align);
325		if (lease_time_align > server_config.lease)
326			lease_time_align = server_config.lease;
327	}
328
329	/* Make sure we aren't just using the lease time from the previous offer */
330	if (lease_time_align < server_config.min_lease)
331		lease_time_align = server_config.lease;
332
333	/* Foxconn added start pling 06/19/2013 */
334	/* For guest network clients, set lease time to 30 minutes */
335	if (is_guest_network(mac)) {
336		lease_time_align = GUEST_LEASE_TIME;
337		DEBUG(LOG_INFO, "send OFFER to guest network client with lease time %d sec", GUEST_LEASE_TIME);
338	}
339	/* Foxconn added end pling 06/19/2013 */
340
341	/* Foxconn added start, James Hsu, 03/05/2015 */
342	/* For arlo network client, set lease time to 3 year */
343#ifdef ARLO_SUPPORT
344	if (is_arlo_network(mac)) {
345		lease_time_align = Arlo_lease_time;
346		DEBUG(LOG_INFO, "send OFFER to arlo network client with lease time %d sec", Arlo_lease_time);
347	}
348#endif
349	/* Foxconn added end, James Hsu, 03/05/2015 */
350
351	/* ADDME: end of short circuit */
352	add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
353
354	curr = server_config.options;
355	while (curr) {
356		if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
357			add_option_string(packet.options, curr->data);
358		curr = curr->next;
359	}
360
361	add_bootp_options(&packet);
362
363	addr.s_addr = packet.yiaddr;
364	LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr));
365	return send_packet(&packet, 0);
366}
367
368
369int sendNAK(struct dhcpMessage *oldpacket)
370{
371	struct dhcpMessage packet;
372
373	init_packet(&packet, oldpacket, DHCPNAK);
374
375	DEBUG(LOG_INFO, "sending NAK");
376	return send_packet(&packet, 1);
377}
378
379
380int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr)
381{
382	struct dhcpMessage packet;
383	struct option_set *curr;
384	unsigned char *lease_time;
385
386	//Foxconn added start Bob Guo 11/15/2006
387	FILE *fp;
388	unsigned char *client_mac, *client_ip;
389	char logBuffer[96];
390	//Foxconn added end Bob Guo 11/15/2006
391
392	u_int32_t lease_time_align = server_config.lease;
393	struct in_addr addr;
394
395	init_packet(&packet, oldpacket, DHCPACK);
396	packet.yiaddr = yiaddr;
397
398	if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
399		memcpy(&lease_time_align, lease_time, 4);
400		lease_time_align = ntohl(lease_time_align);
401		if (lease_time_align > server_config.lease)
402			lease_time_align = server_config.lease;
403		else if (lease_time_align < server_config.min_lease)
404			lease_time_align = server_config.lease;
405	}
406
407	/* Foxconn added start pling 06/19/2013 */
408	/* For guest network clients, set lease time to 30 minutes */
409	if (is_guest_network(packet.chaddr)) {
410		lease_time_align = GUEST_LEASE_TIME;
411		DEBUG(LOG_INFO, "send ACK to guest network client with lease time %d sec", GUEST_LEASE_TIME);
412	}
413	/* Foxconn added end pling 06/19/2013 */
414
415	/* Foxconn added start, James Hsu, 03/05/2015 */
416	/* For arlo network client, set lease time to 1 year */
417#ifdef ARLO_SUPPORT
418	int arlo_arp_buf[64];
419	char arlo_mac[32];
420	struct in_addr arlo_addr;
421	if (is_arlo_network(packet.chaddr)) {
422		arlo_addr.s_addr=packet.yiaddr;
423		lease_time_align = Arlo_lease_time;
424		DEBUG(LOG_INFO, "send OFFER to arlo network client with lease time %d sec", Arlo_lease_time);
425		sprintf(arlo_mac, "%02X:%02X:%02X:%02X:%02X:%02X",
426						(unsigned char)packet.chaddr[0],
427						(unsigned char)packet.chaddr[1],
428						(unsigned char)packet.chaddr[2],
429						(unsigned char)packet.chaddr[3],
430						(unsigned char)packet.chaddr[4],
431						(unsigned char)packet.chaddr[5]);
432		sprintf(arlo_arp_buf,"arp -s %s %s",inet_ntoa(arlo_addr), arlo_mac);
433		system(arlo_arp_buf);
434
435		FILE *arlo_client_mac_file;
436        /* Fxconn added start, James Hsu, 04/15/2015 Record arlo client mac to nvram */
437        arlo_client_mac_file=fopen("/tmp/arlo_client_mac.txt","w");
438        if (arlo_client_mac_file)
439		{
440        fwrite(arlo_mac,sizeof(arlo_mac),1,arlo_client_mac_file);
441		fclose(arlo_client_mac_file);
442		system ("killall -SIGPIPE arlo_monitor");
443		}
444		/* Fxconn added end, James Hsu, 04/15/2015 */
445
446		/* Fxconn added start, James Hsu, 04/08/2015, for isolation arlo network */
447		#define DEV_FILE_NAME_W_PATH    "/dev/acos_nat_cli"
448		#define MAJOR_NUM 100
449		#define IOCTL_AG_ARLO_CLIENT_MAC              _IOW(MAJOR_NUM, 203, char *)
450		int ret_val=0, file_desc;
451		char *arlo_client_mac;
452		memcpy(arlo_mac, packet.chaddr,6);
453		file_desc = open(DEV_FILE_NAME_W_PATH, O_RDWR);
454		if (file_desc < 0)
455		{
456			printf("Can't open device file: %s\n", DEV_FILE_NAME_W_PATH);
457			DEBUG(LOG_INFO, "Can't open device file: %s\n", DEV_FILE_NAME_W_PATH);
458			return 0;
459		}
460		ret_val = ioctl(file_desc, IOCTL_AG_ARLO_CLIENT_MAC, arlo_mac);
461		close(file_desc);
462		/* Fxconn added end, James Hsu, 04/08/2015, for isolation arlo network */
463	}
464#endif
465	/* Foxconn added end, James Hsu, 03/05/2015 */
466
467	add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
468
469	curr = server_config.options;
470	while (curr) {
471		if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
472			add_option_string(packet.options, curr->data);
473		curr = curr->next;
474	}
475
476	add_bootp_options(&packet);
477
478	addr.s_addr = packet.yiaddr;
479	LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr));
480
481	if (send_packet(&packet, 0) < 0)
482		return -1;
483
484	add_lease(packet.chaddr, packet.yiaddr, lease_time_align);
485	//Foxconn added start Bob Guo 11/15/2006
486	client_mac = (unsigned char *)packet.chaddr;
487	client_ip = (unsigned char *)&packet.yiaddr;
488	sprintf(logBuffer, "[DHCP IP: (%d.%d.%d.%d)] to MAC address %02X:%02X:%02X:%02X:%02X:%02X,",
489	                                          *client_ip, *(client_ip+1), *(client_ip+2), *(client_ip+3),
490	                                          *client_mac, *(client_mac+1), *(client_mac+2), *(client_mac+3), *(client_mac+4), *(client_mac+5));
491	if ((fp = fopen("/dev/aglog", "r+")) != NULL)
492  {
493        fwrite(logBuffer, sizeof(char), strlen(logBuffer)+1, fp);
494        fclose(fp);
495  }
496	//Foxconn added end Bob Guo 11/15/2006
497
498	return 0;
499}
500
501
502int send_inform(struct dhcpMessage *oldpacket)
503{
504	struct dhcpMessage packet;
505	struct option_set *curr;
506
507	init_packet(&packet, oldpacket, DHCPACK);
508
509	curr = server_config.options;
510	while (curr) {
511		if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
512			add_option_string(packet.options, curr->data);
513		curr = curr->next;
514	}
515
516	add_bootp_options(&packet);
517
518	return send_packet(&packet, 0);
519}
520
521
522
523