~cyphermox/ubuntu/natty/connman/release-0.64

« back to all changes in this revision

Viewing changes to gdhcp/server.c

  • Committer: Mathieu Trudel-Lapierre
  • Date: 2010-11-30 15:51:10 UTC
  • mfrom: (1.1.13 upstream)
  • Revision ID: mathieu.trudel-lapierre@canonical.com-20101130155110-32g0usyc4jbl131x
New upstream release 0.64.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *
 
3
 *  DHCP Server library with GLib integration
 
4
 *
 
5
 *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
 
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 version 2 as
 
9
 *  published by the Free Software Foundation.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 *
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include <config.h>
 
24
#endif
 
25
 
 
26
#include <stdio.h>
 
27
#include <errno.h>
 
28
#include <unistd.h>
 
29
#include <stdlib.h>
 
30
#include <string.h>
 
31
#include <sys/ioctl.h>
 
32
#include <arpa/inet.h>
 
33
 
 
34
#include <netpacket/packet.h>
 
35
#include <net/ethernet.h>
 
36
#include <net/if_arp.h>
 
37
 
 
38
#include <linux/if.h>
 
39
#include <linux/filter.h>
 
40
 
 
41
#include <glib.h>
 
42
 
 
43
#include "common.h"
 
44
 
 
45
/* 8 hours */
 
46
#define DEFAULT_DHCP_LEASE_SEC (8*60*60)
 
47
 
 
48
/* 5 minutes  */
 
49
#define OFFER_TIME (5*60)
 
50
 
 
51
struct _GDHCPServer {
 
52
        gint ref_count;
 
53
        GDHCPType type;
 
54
        gboolean started;
 
55
        int ifindex;
 
56
        char *interface;
 
57
        uint32_t start_ip;
 
58
        uint32_t end_ip;
 
59
        uint32_t server_nip;
 
60
        uint32_t lease_seconds;
 
61
        int listener_sockfd;
 
62
        guint listener_watch;
 
63
        GIOChannel *listener_channel;
 
64
        GList *lease_list;
 
65
        GHashTable *nip_lease_hash;
 
66
        GHashTable *option_hash; /* Options send to client */
 
67
        GDHCPSaveLeaseFunc save_lease_func;
 
68
        GDHCPDebugFunc debug_func;
 
69
        gpointer debug_data;
 
70
};
 
71
 
 
72
struct dhcp_lease {
 
73
        time_t expire;
 
74
        uint32_t lease_nip;
 
75
        uint8_t lease_mac[ETH_ALEN];
 
76
};
 
77
 
 
78
static inline void debug(GDHCPServer *server, const char *format, ...)
 
79
{
 
80
        char str[256];
 
81
        va_list ap;
 
82
 
 
83
        if (server->debug_func == NULL)
 
84
                return;
 
85
 
 
86
        va_start(ap, format);
 
87
 
 
88
        if (vsnprintf(str, sizeof(str), format, ap) > 0)
 
89
                server->debug_func(str, server->debug_data);
 
90
 
 
91
        va_end(ap);
 
92
}
 
93
 
 
94
static struct dhcp_lease *find_lease_by_mac(GDHCPServer *dhcp_server,
 
95
                                                const uint8_t *mac)
 
96
{
 
97
        GList *list;
 
98
 
 
99
        for (list = dhcp_server->lease_list; list; list = list->next) {
 
100
                struct dhcp_lease *lease = list->data;
 
101
 
 
102
                if (memcmp(lease->lease_mac, mac, ETH_ALEN) == 0)
 
103
                        return lease;
 
104
        }
 
105
 
 
106
        return NULL;
 
107
}
 
108
 
 
109
static void remove_lease(GDHCPServer *dhcp_server, struct dhcp_lease *lease)
 
110
{
 
111
        dhcp_server->lease_list =
 
112
                        g_list_remove(dhcp_server->lease_list, lease);
 
113
 
 
114
        g_hash_table_remove(dhcp_server->nip_lease_hash,
 
115
                                GINT_TO_POINTER((int) lease->lease_nip));
 
116
        g_free(lease);
 
117
}
 
118
 
 
119
/* Clear the old lease and create the new one */
 
120
static int get_lease(GDHCPServer *dhcp_server, uint32_t yiaddr,
 
121
                                const uint8_t *mac, struct dhcp_lease **lease)
 
122
{
 
123
        struct dhcp_lease *lease_nip, *lease_mac;
 
124
 
 
125
        if (yiaddr == 0)
 
126
                return -ENXIO;
 
127
 
 
128
        if (ntohl(yiaddr) < dhcp_server->start_ip)
 
129
                return -ENXIO;
 
130
 
 
131
        if (ntohl(yiaddr) > dhcp_server->end_ip)
 
132
                return -ENXIO;
 
133
 
 
134
        if (memcmp(mac, MAC_BCAST_ADDR, ETH_ALEN) == 0)
 
135
                return -ENXIO;
 
136
 
 
137
        if (memcmp(mac, MAC_ANY_ADDR, ETH_ALEN) == 0)
 
138
                return -ENXIO;
 
139
 
 
140
        lease_mac = find_lease_by_mac(dhcp_server, mac);
 
141
 
 
142
        lease_nip = g_hash_table_lookup(dhcp_server->nip_lease_hash,
 
143
                                                GINT_TO_POINTER((int) yiaddr));
 
144
        debug(dhcp_server, "lease_mac %p lease_nip %p", lease_mac, lease_nip);
 
145
 
 
146
        if (lease_nip != NULL) {
 
147
                dhcp_server->lease_list =
 
148
                                g_list_remove(dhcp_server->lease_list,
 
149
                                                                lease_nip);
 
150
                g_hash_table_remove(dhcp_server->nip_lease_hash,
 
151
                                        GINT_TO_POINTER((int) yiaddr));
 
152
 
 
153
                if (lease_mac == NULL)
 
154
                        *lease = lease_nip;
 
155
                else if (lease_nip != lease_mac) {
 
156
                        remove_lease(dhcp_server, lease_mac);
 
157
                        *lease = lease_nip;
 
158
                } else
 
159
                        *lease = lease_nip;
 
160
 
 
161
                return 0;
 
162
        }
 
163
 
 
164
        if (lease_mac != NULL) {
 
165
                dhcp_server->lease_list =
 
166
                                g_list_remove(dhcp_server->lease_list,
 
167
                                                                lease_mac);
 
168
                g_hash_table_remove(dhcp_server->nip_lease_hash,
 
169
                                GINT_TO_POINTER((int) lease_mac->lease_nip));
 
170
                *lease = lease_mac;
 
171
 
 
172
                return 0;
 
173
        }
 
174
 
 
175
        *lease = g_try_new0(struct dhcp_lease, 1);
 
176
        if (*lease == NULL)
 
177
                return -ENOMEM;
 
178
 
 
179
        return 0;
 
180
}
 
181
 
 
182
static gint compare_expire(gconstpointer a, gconstpointer b)
 
183
{
 
184
        const struct dhcp_lease *lease1 = a;
 
185
        const struct dhcp_lease *lease2 = b;
 
186
 
 
187
        return lease2->expire - lease1->expire;
 
188
}
 
189
 
 
190
static struct dhcp_lease *add_lease(GDHCPServer *dhcp_server, uint32_t expire,
 
191
                                        const uint8_t *chaddr, uint32_t yiaddr)
 
192
{
 
193
        struct dhcp_lease *lease = NULL;
 
194
        int ret;
 
195
 
 
196
        ret = get_lease(dhcp_server, yiaddr, chaddr, &lease);
 
197
        if (ret != 0)
 
198
                return NULL;
 
199
 
 
200
        memset(lease, 0, sizeof(*lease));
 
201
 
 
202
        memcpy(lease->lease_mac, chaddr, ETH_ALEN);
 
203
        lease->lease_nip = yiaddr;
 
204
 
 
205
        if (expire == 0)
 
206
                lease->expire = time(NULL) + dhcp_server->lease_seconds;
 
207
        else
 
208
                lease->expire = expire;
 
209
 
 
210
        dhcp_server->lease_list = g_list_insert_sorted(dhcp_server->lease_list,
 
211
                                                        lease, compare_expire);
 
212
 
 
213
        g_hash_table_insert(dhcp_server->nip_lease_hash,
 
214
                                GINT_TO_POINTER((int) lease->lease_nip), lease);
 
215
 
 
216
        return lease;
 
217
}
 
218
 
 
219
static struct dhcp_lease *find_lease_by_nip(GDHCPServer *dhcp_server,
 
220
                                                                uint32_t nip)
 
221
{
 
222
        return g_hash_table_lookup(dhcp_server->nip_lease_hash,
 
223
                                                GINT_TO_POINTER((int) nip));
 
224
}
 
225
 
 
226
/* Check if the IP is taken; if it is, add it to the lease table */
 
227
static gboolean arp_check(uint32_t nip, const uint8_t *safe_mac)
 
228
{
 
229
        /* TODO: Add ARP checking */
 
230
        return TRUE;
 
231
}
 
232
 
 
233
static gboolean is_expired_lease(struct dhcp_lease *lease)
 
234
{
 
235
        if (lease->expire < time(NULL))
 
236
                return TRUE;
 
237
 
 
238
        return FALSE;
 
239
}
 
240
 
 
241
static uint32_t find_free_or_expired_nip(GDHCPServer *dhcp_server,
 
242
                                        const uint8_t *safe_mac)
 
243
{
 
244
        uint32_t ip_addr;
 
245
        struct dhcp_lease *lease;
 
246
        GList *list;
 
247
        ip_addr = dhcp_server->start_ip;
 
248
        for (; ip_addr <= dhcp_server->end_ip; ip_addr++) {
 
249
                /* e.g. 192.168.55.0 */
 
250
                if ((ip_addr & 0xff) == 0)
 
251
                        continue;
 
252
 
 
253
                /* e.g. 192.168.55.255 */
 
254
                if ((ip_addr & 0xff) == 0xff)
 
255
                        continue;
 
256
 
 
257
                lease = find_lease_by_nip(dhcp_server,
 
258
                                (uint32_t) htonl(ip_addr));
 
259
                if (lease != NULL)
 
260
                        continue;
 
261
 
 
262
                if (arp_check(htonl(ip_addr), safe_mac) == TRUE)
 
263
                        return htonl(ip_addr);
 
264
        }
 
265
 
 
266
        /* The last lease is the oldest one */
 
267
        list = g_list_last(dhcp_server->lease_list);
 
268
        if (list == NULL)
 
269
                return 0;
 
270
 
 
271
        lease = list->data;
 
272
        if (lease == NULL)
 
273
                return 0;
 
274
 
 
275
         if (is_expired_lease(lease) == FALSE)
 
276
                return 0;
 
277
 
 
278
         if (arp_check(lease->lease_nip, safe_mac) == FALSE)
 
279
                return 0;
 
280
 
 
281
        return lease->lease_nip;
 
282
}
 
283
 
 
284
static void lease_set_expire(GDHCPServer *dhcp_server,
 
285
                        struct dhcp_lease *lease, uint32_t expire)
 
286
{
 
287
        dhcp_server->lease_list = g_list_remove(dhcp_server->lease_list, lease);
 
288
 
 
289
        lease->expire = expire;
 
290
 
 
291
        dhcp_server->lease_list = g_list_insert_sorted(dhcp_server->lease_list,
 
292
                                                        lease, compare_expire);
 
293
}
 
294
 
 
295
static void destroy_lease_table(GDHCPServer *dhcp_server)
 
296
{
 
297
        GList *list;
 
298
 
 
299
        g_hash_table_destroy(dhcp_server->nip_lease_hash);
 
300
 
 
301
        dhcp_server->nip_lease_hash = NULL;
 
302
 
 
303
        for (list = dhcp_server->lease_list; list; list = list->next) {
 
304
                struct dhcp_lease *lease = list->data;
 
305
 
 
306
                g_free(lease);
 
307
        }
 
308
 
 
309
        g_list_free(dhcp_server->lease_list);
 
310
 
 
311
        dhcp_server->lease_list = NULL;
 
312
}
 
313
static uint32_t get_interface_address(int index)
 
314
{
 
315
        struct ifreq ifr;
 
316
        int sk, err;
 
317
        struct sockaddr_in *server_ip;
 
318
        uint32_t ret = 0;
 
319
 
 
320
        sk = socket(PF_INET, SOCK_DGRAM, 0);
 
321
        if (sk < 0) {
 
322
                perror("Open socket error");
 
323
                return 0;
 
324
        }
 
325
 
 
326
        memset(&ifr, 0, sizeof(ifr));
 
327
        ifr.ifr_ifindex = index;
 
328
 
 
329
        err = ioctl(sk, SIOCGIFNAME, &ifr);
 
330
        if (err < 0) {
 
331
                perror("Get interface name error");
 
332
                goto done;
 
333
        }
 
334
 
 
335
        err = ioctl(sk, SIOCGIFADDR, &ifr);
 
336
        if (err < 0) {
 
337
                perror("Get ip address error");
 
338
                goto done;
 
339
        }
 
340
 
 
341
        server_ip = (struct sockaddr_in *) &ifr.ifr_addr;
 
342
        ret = server_ip->sin_addr.s_addr;
 
343
 
 
344
done:
 
345
        close(sk);
 
346
 
 
347
        return ret;
 
348
}
 
349
 
 
350
GDHCPServer *g_dhcp_server_new(GDHCPType type,
 
351
                int ifindex, GDHCPServerError *error)
 
352
{
 
353
        GDHCPServer *dhcp_server = NULL;
 
354
 
 
355
        if (ifindex < 0) {
 
356
                *error = G_DHCP_SERVER_ERROR_INVALID_INDEX;
 
357
                return NULL;
 
358
        }
 
359
 
 
360
        dhcp_server = g_try_new0(GDHCPServer, 1);
 
361
        if (dhcp_server == NULL) {
 
362
                *error = G_DHCP_SERVER_ERROR_NOMEM;
 
363
                return NULL;
 
364
        }
 
365
 
 
366
        dhcp_server->interface = get_interface_name(ifindex);
 
367
        if (dhcp_server->interface == NULL) {
 
368
                *error = G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE;
 
369
                goto error;
 
370
        }
 
371
 
 
372
        if (interface_is_up(ifindex) == FALSE) {
 
373
                *error = G_DHCP_SERVER_ERROR_INTERFACE_DOWN;
 
374
                goto error;
 
375
        }
 
376
 
 
377
        dhcp_server->server_nip = get_interface_address(ifindex);
 
378
        if (dhcp_server->server_nip == 0) {
 
379
                *error = G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID;
 
380
                goto error;
 
381
        }
 
382
 
 
383
        dhcp_server->nip_lease_hash = g_hash_table_new_full(g_direct_hash,
 
384
                                                g_direct_equal, NULL, NULL);
 
385
        dhcp_server->option_hash = g_hash_table_new_full(g_direct_hash,
 
386
                                                g_direct_equal, NULL, NULL);
 
387
 
 
388
        dhcp_server->started = FALSE;
 
389
 
 
390
        /* All the leases have the same fixed lease time,
 
391
         * do not support DHCP_LEASE_TIME option from client.
 
392
         */
 
393
        dhcp_server->lease_seconds = DEFAULT_DHCP_LEASE_SEC;
 
394
 
 
395
        dhcp_server->type = type;
 
396
        dhcp_server->ref_count = 1;
 
397
        dhcp_server->ifindex = ifindex;
 
398
        dhcp_server->listener_sockfd = -1;
 
399
        dhcp_server->listener_watch = -1;
 
400
        dhcp_server->listener_channel = NULL;
 
401
        dhcp_server->save_lease_func = NULL;
 
402
        dhcp_server->debug_func = NULL;
 
403
        dhcp_server->debug_data = NULL;
 
404
 
 
405
        *error = G_DHCP_SERVER_ERROR_NONE;
 
406
 
 
407
        return dhcp_server;
 
408
 
 
409
error:
 
410
        g_free(dhcp_server->interface);
 
411
        g_free(dhcp_server);
 
412
        return NULL;
 
413
}
 
414
 
 
415
 
 
416
static uint8_t check_packet_type(struct dhcp_packet *packet)
 
417
{
 
418
        uint8_t *type;
 
419
 
 
420
        if (packet->hlen != ETH_ALEN)
 
421
                return 0;
 
422
 
 
423
        if (packet->op != BOOTREQUEST)
 
424
                return 0;
 
425
 
 
426
        type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE);
 
427
 
 
428
        if (type == NULL)
 
429
                return 0;
 
430
 
 
431
        if (*type < DHCP_MINTYPE)
 
432
                return 0;
 
433
 
 
434
        if (*type > DHCP_MAXTYPE)
 
435
                return 0;
 
436
 
 
437
        return *type;
 
438
}
 
439
 
 
440
static void init_packet(GDHCPServer *dhcp_server, struct dhcp_packet *packet,
 
441
                                struct dhcp_packet *client_packet, char type)
 
442
{
 
443
        /* Sets op, htype, hlen, cookie fields
 
444
         * and adds DHCP_MESSAGE_TYPE option */
 
445
        dhcp_init_header(packet, type);
 
446
 
 
447
        packet->xid = client_packet->xid;
 
448
        memcpy(packet->chaddr, client_packet->chaddr,
 
449
                                sizeof(client_packet->chaddr));
 
450
        packet->flags = client_packet->flags;
 
451
        packet->gateway_nip = client_packet->gateway_nip;
 
452
        packet->ciaddr = client_packet->ciaddr;
 
453
        dhcp_add_simple_option(packet, DHCP_SERVER_ID, dhcp_server->server_nip);
 
454
}
 
455
 
 
456
static void add_option(gpointer key, gpointer value, gpointer user_data)
 
457
{
 
458
        const char *option_value = value;
 
459
        uint8_t option_code = GPOINTER_TO_INT(key);
 
460
        struct in_addr nip;
 
461
        struct dhcp_packet *packet = user_data;
 
462
 
 
463
        if (option_value == NULL)
 
464
                return;
 
465
 
 
466
        switch (option_code) {
 
467
        case G_DHCP_SUBNET:
 
468
        case G_DHCP_ROUTER:
 
469
        case G_DHCP_DNS_SERVER:
 
470
                if (inet_aton(option_value, &nip) == 0)
 
471
                        return;
 
472
 
 
473
                dhcp_add_simple_option(packet, (uint8_t) option_code,
 
474
                                                                nip.s_addr);
 
475
                break;
 
476
        default:
 
477
                return;
 
478
        }
 
479
}
 
480
 
 
481
static void add_server_options(GDHCPServer *dhcp_server,
 
482
                                struct dhcp_packet *packet)
 
483
{
 
484
        g_hash_table_foreach(dhcp_server->option_hash,
 
485
                                add_option, packet);
 
486
}
 
487
 
 
488
static gboolean check_requested_nip(GDHCPServer *dhcp_server,
 
489
                                        uint32_t requested_nip)
 
490
{
 
491
        struct dhcp_lease *lease;
 
492
 
 
493
        if (requested_nip == 0)
 
494
                return FALSE;
 
495
 
 
496
        if (ntohl(requested_nip) < dhcp_server->start_ip)
 
497
                return FALSE;
 
498
 
 
499
        if (ntohl(requested_nip) > dhcp_server->end_ip)
 
500
                return FALSE;
 
501
 
 
502
        lease = find_lease_by_nip(dhcp_server, requested_nip);
 
503
        if (lease == NULL)
 
504
                return TRUE;
 
505
 
 
506
        if (is_expired_lease(lease) == FALSE)
 
507
                return FALSE;
 
508
 
 
509
        return TRUE;
 
510
}
 
511
 
 
512
static void send_packet_to_client(GDHCPServer *dhcp_server,
 
513
                                struct dhcp_packet *dhcp_pkt)
 
514
{
 
515
        const uint8_t *chaddr;
 
516
        uint32_t ciaddr;
 
517
 
 
518
        if ((dhcp_pkt->flags & htons(BROADCAST_FLAG))
 
519
                                || dhcp_pkt->ciaddr == 0) {
 
520
                debug(dhcp_server, "Broadcasting packet to client");
 
521
                ciaddr = INADDR_BROADCAST;
 
522
                chaddr = MAC_BCAST_ADDR;
 
523
        } else {
 
524
                debug(dhcp_server, "Unicasting packet to client ciaddr");
 
525
                ciaddr = dhcp_pkt->ciaddr;
 
526
                chaddr = dhcp_pkt->chaddr;
 
527
        }
 
528
 
 
529
        dhcp_send_raw_packet(dhcp_pkt,
 
530
                dhcp_server->server_nip, SERVER_PORT,
 
531
                ciaddr, CLIENT_PORT, chaddr,
 
532
                dhcp_server->ifindex);
 
533
}
 
534
 
 
535
static void send_offer(GDHCPServer *dhcp_server,
 
536
                        struct dhcp_packet *client_packet,
 
537
                                struct dhcp_lease *lease,
 
538
                                        uint32_t requested_nip)
 
539
{
 
540
        struct dhcp_packet packet;
 
541
        struct in_addr addr;
 
542
 
 
543
        init_packet(dhcp_server, &packet, client_packet, DHCPOFFER);
 
544
 
 
545
        if (lease)
 
546
                packet.yiaddr = lease->lease_nip;
 
547
        else if (check_requested_nip(dhcp_server, requested_nip) == TRUE)
 
548
                packet.yiaddr = requested_nip;
 
549
        else
 
550
                packet.yiaddr = find_free_or_expired_nip(
 
551
                                dhcp_server, client_packet->chaddr);
 
552
 
 
553
        debug(dhcp_server, "find yiaddr %u", packet.yiaddr);
 
554
 
 
555
        if (!packet.yiaddr) {
 
556
                debug(dhcp_server, "Err: Can not found lease and send offer");
 
557
                return;
 
558
        }
 
559
 
 
560
        lease = add_lease(dhcp_server, OFFER_TIME,
 
561
                                packet.chaddr, packet.yiaddr);
 
562
        if (lease == NULL) {
 
563
                debug(dhcp_server,
 
564
                                "Err: No free IP addresses. OFFER abandoned");
 
565
                return;
 
566
        }
 
567
 
 
568
        dhcp_add_simple_option(&packet, DHCP_LEASE_TIME,
 
569
                                htonl(dhcp_server->lease_seconds));
 
570
        add_server_options(dhcp_server, &packet);
 
571
 
 
572
        addr.s_addr = packet.yiaddr;
 
573
 
 
574
        debug(dhcp_server, "Sending OFFER of %s", inet_ntoa(addr));
 
575
        send_packet_to_client(dhcp_server, &packet);
 
576
}
 
577
 
 
578
static void save_lease(GDHCPServer *dhcp_server)
 
579
{
 
580
        GList *list;
 
581
 
 
582
        if (dhcp_server->save_lease_func == NULL)
 
583
                return;
 
584
 
 
585
        for (list = dhcp_server->lease_list; list; list = list->next) {
 
586
                struct dhcp_lease *lease = list->data;
 
587
                dhcp_server->save_lease_func(lease->lease_mac,
 
588
                                        lease->lease_nip, lease->expire);
 
589
        }
 
590
}
 
591
 
 
592
static void send_ACK(GDHCPServer *dhcp_server,
 
593
                struct dhcp_packet *client_packet, uint32_t yiaddr)
 
594
{
 
595
        struct dhcp_packet packet;
 
596
        uint32_t lease_time_sec;
 
597
        struct in_addr addr;
 
598
 
 
599
        init_packet(dhcp_server, &packet, client_packet, DHCPACK);
 
600
        packet.yiaddr = yiaddr;
 
601
 
 
602
        lease_time_sec = dhcp_server->lease_seconds;
 
603
 
 
604
        dhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
 
605
 
 
606
        add_server_options(dhcp_server, &packet);
 
607
 
 
608
        addr.s_addr = yiaddr;
 
609
 
 
610
        debug(dhcp_server, "Sending ACK to %s", inet_ntoa(addr));
 
611
 
 
612
        send_packet_to_client(dhcp_server, &packet);
 
613
 
 
614
        add_lease(dhcp_server, 0, packet.chaddr, packet.yiaddr);
 
615
}
 
616
 
 
617
static void send_NAK(GDHCPServer *dhcp_server,
 
618
                        struct dhcp_packet *client_packet)
 
619
{
 
620
        struct dhcp_packet packet;
 
621
 
 
622
        init_packet(dhcp_server, &packet, client_packet, DHCPNAK);
 
623
 
 
624
        debug(dhcp_server, "Sending NAK");
 
625
 
 
626
        dhcp_send_raw_packet(&packet,
 
627
                        dhcp_server->server_nip, SERVER_PORT,
 
628
                        INADDR_BROADCAST, CLIENT_PORT, MAC_BCAST_ADDR,
 
629
                        dhcp_server->ifindex);
 
630
}
 
631
 
 
632
static void send_inform(GDHCPServer *dhcp_server,
 
633
                                struct dhcp_packet *client_packet)
 
634
{
 
635
        struct dhcp_packet packet;
 
636
 
 
637
        init_packet(dhcp_server, &packet, client_packet, DHCPACK);
 
638
        add_server_options(dhcp_server, &packet);
 
639
        send_packet_to_client(dhcp_server, &packet);
 
640
}
 
641
 
 
642
static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
 
643
                                                        gpointer user_data)
 
644
{
 
645
        GDHCPServer *dhcp_server = user_data;
 
646
        struct dhcp_packet packet;
 
647
        struct dhcp_lease *lease;
 
648
        uint32_t requested_nip = 0;
 
649
        uint8_t type, *server_id_option, *request_ip_option;
 
650
        int re;
 
651
 
 
652
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
 
653
                dhcp_server->listener_watch = 0;
 
654
                return FALSE;
 
655
        }
 
656
 
 
657
        re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
 
658
        if (re < 0)
 
659
                return TRUE;
 
660
 
 
661
        type = check_packet_type(&packet);
 
662
        if (type == 0)
 
663
                return TRUE;
 
664
 
 
665
        server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
 
666
        if (server_id_option) {
 
667
                uint32_t server_nid = dhcp_get_unaligned(
 
668
                                        (uint32_t *) server_id_option);
 
669
 
 
670
                if (server_nid != dhcp_server->server_nip)
 
671
                        return TRUE;
 
672
        }
 
673
 
 
674
        request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
 
675
        if (request_ip_option)
 
676
                requested_nip = dhcp_get_unaligned(
 
677
                                        (uint32_t *) request_ip_option);
 
678
 
 
679
        lease = find_lease_by_mac(dhcp_server, packet.chaddr);
 
680
 
 
681
        switch (type) {
 
682
                case DHCPDISCOVER:
 
683
                        debug(dhcp_server, "Received DISCOVER");
 
684
 
 
685
                        send_offer(dhcp_server, &packet, lease, requested_nip);
 
686
                break;
 
687
                case DHCPREQUEST:
 
688
                        debug(dhcp_server, "Received REQUEST NIP %d",
 
689
                                                        requested_nip);
 
690
                        if (requested_nip == 0) {
 
691
                                requested_nip = packet.ciaddr;
 
692
                                if (requested_nip == 0)
 
693
                                        break;
 
694
                        }
 
695
 
 
696
                        if (lease && requested_nip == lease->lease_nip) {
 
697
                                debug(dhcp_server, "Sending ACK");
 
698
                                send_ACK(dhcp_server, &packet,
 
699
                                                lease->lease_nip);
 
700
                                break;
 
701
                        }
 
702
 
 
703
                        if (server_id_option || lease == NULL) {
 
704
                                debug(dhcp_server, "Sending NAK");
 
705
                                send_NAK(dhcp_server, &packet);
 
706
                        }
 
707
 
 
708
                break;
 
709
                case DHCPDECLINE:
 
710
                        debug(dhcp_server, "Received DECLINE");
 
711
 
 
712
                        if (server_id_option == NULL)
 
713
                                break;
 
714
 
 
715
                        if (request_ip_option == NULL)
 
716
                                break;
 
717
 
 
718
                        if (lease == NULL)
 
719
                                break;
 
720
 
 
721
                        if (requested_nip == lease->lease_nip)
 
722
                                remove_lease(dhcp_server, lease);
 
723
 
 
724
                break;
 
725
                case DHCPRELEASE:
 
726
                        debug(dhcp_server, "Received RELEASE");
 
727
 
 
728
                        if (server_id_option == NULL)
 
729
                                break;
 
730
 
 
731
                        if (lease == NULL)
 
732
                                break;
 
733
 
 
734
                        if (packet.ciaddr == lease->lease_nip)
 
735
                                lease_set_expire(dhcp_server, lease,
 
736
                                                                time(NULL));
 
737
                break;
 
738
                case DHCPINFORM:
 
739
                        debug(dhcp_server, "Received INFORM");
 
740
                        send_inform(dhcp_server, &packet);
 
741
                break;
 
742
        }
 
743
 
 
744
        return TRUE;
 
745
}
 
746
 
 
747
/* Caller need to load leases before call it */
 
748
int g_dhcp_server_start(GDHCPServer *dhcp_server)
 
749
{
 
750
        GIOChannel *listener_channel;
 
751
        int listener_sockfd;
 
752
 
 
753
        if (dhcp_server->started == TRUE)
 
754
                return 0;
 
755
 
 
756
        listener_sockfd = dhcp_l3_socket(SERVER_PORT,
 
757
                                dhcp_server->interface);
 
758
        if (listener_sockfd < 0)
 
759
                return -EIO;
 
760
 
 
761
        listener_channel = g_io_channel_unix_new(listener_sockfd);
 
762
        if (listener_channel == NULL) {
 
763
                close(listener_sockfd);
 
764
                return -EIO;
 
765
        }
 
766
 
 
767
        dhcp_server->listener_sockfd = listener_sockfd;
 
768
        dhcp_server->listener_channel = listener_channel;
 
769
 
 
770
        g_io_channel_set_close_on_unref(listener_channel, TRUE);
 
771
        dhcp_server->listener_watch =
 
772
                        g_io_add_watch_full(listener_channel,
 
773
                                                G_PRIORITY_HIGH, G_IO_IN,
 
774
                                                listener_event, dhcp_server,
 
775
                                                                NULL);
 
776
        g_io_channel_unref(dhcp_server->listener_channel);
 
777
 
 
778
        dhcp_server->started = TRUE;
 
779
 
 
780
        return 0;
 
781
}
 
782
 
 
783
int g_dhcp_server_set_option(GDHCPServer *dhcp_server,
 
784
                unsigned char option_code, const char *option_value)
 
785
{
 
786
        struct in_addr nip;
 
787
 
 
788
        if (option_value == NULL)
 
789
                return -EINVAL;
 
790
 
 
791
        debug(dhcp_server, "option_code %d option_value %s",
 
792
                                        option_code, option_value);
 
793
        switch (option_code) {
 
794
        case G_DHCP_SUBNET:
 
795
        case G_DHCP_ROUTER:
 
796
        case G_DHCP_DNS_SERVER:
 
797
                if (inet_aton(option_value, &nip) == 0)
 
798
                        return -ENXIO;
 
799
                break;
 
800
        default:
 
801
                return -EINVAL;
 
802
        }
 
803
 
 
804
        g_hash_table_replace(dhcp_server->option_hash,
 
805
                        GINT_TO_POINTER((int) option_code),
 
806
                                        (gpointer) option_value);
 
807
        return 0;
 
808
}
 
809
 
 
810
void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server,
 
811
                                GDHCPSaveLeaseFunc func, gpointer user_data)
 
812
{
 
813
        if (dhcp_server == NULL)
 
814
                return;
 
815
 
 
816
        dhcp_server->save_lease_func = func;
 
817
}
 
818
 
 
819
GDHCPServer *g_dhcp_server_ref(GDHCPServer *dhcp_server)
 
820
{
 
821
        if (dhcp_server == NULL)
 
822
                return NULL;
 
823
 
 
824
        g_atomic_int_inc(&dhcp_server->ref_count);
 
825
 
 
826
        return dhcp_server;
 
827
}
 
828
 
 
829
void g_dhcp_server_stop(GDHCPServer *dhcp_server)
 
830
{
 
831
        /* Save leases, before stop; load them before start */
 
832
        save_lease(dhcp_server);
 
833
 
 
834
        if (dhcp_server->listener_watch > 0) {
 
835
                g_source_remove(dhcp_server->listener_watch);
 
836
                dhcp_server->listener_watch = 0;
 
837
        }
 
838
 
 
839
        dhcp_server->listener_channel = NULL;
 
840
 
 
841
        dhcp_server->started = FALSE;
 
842
}
 
843
 
 
844
void g_dhcp_server_unref(GDHCPServer *dhcp_server)
 
845
{
 
846
        if (dhcp_server == NULL)
 
847
                return;
 
848
 
 
849
        if (g_atomic_int_dec_and_test(&dhcp_server->ref_count) == FALSE)
 
850
                return;
 
851
 
 
852
        g_dhcp_server_stop(dhcp_server);
 
853
 
 
854
        g_hash_table_destroy(dhcp_server->option_hash);
 
855
 
 
856
        destroy_lease_table(dhcp_server);
 
857
 
 
858
        g_free(dhcp_server->interface);
 
859
 
 
860
        g_free(dhcp_server);
 
861
}
 
862
 
 
863
void g_dhcp_server_load_lease(GDHCPServer *dhcp_server, unsigned int expire,
 
864
                                unsigned char *mac, unsigned int lease_ip)
 
865
{
 
866
        add_lease(dhcp_server, expire, mac, lease_ip);
 
867
}
 
868
 
 
869
int g_dhcp_server_set_ip_range(GDHCPServer *dhcp_server,
 
870
                const char *start_ip, const char *end_ip)
 
871
{
 
872
        struct in_addr _host_addr;
 
873
 
 
874
        if (inet_aton(start_ip, &_host_addr) == 0)
 
875
                return -ENXIO;
 
876
 
 
877
        dhcp_server->start_ip = ntohl(_host_addr.s_addr);
 
878
 
 
879
        if (inet_aton(end_ip, &_host_addr) == 0)
 
880
                return -ENXIO;
 
881
 
 
882
        dhcp_server->end_ip = ntohl(_host_addr.s_addr);
 
883
 
 
884
        return 0;
 
885
}
 
886
 
 
887
void g_dhcp_server_set_lease_time(GDHCPServer *dhcp_server, unsigned int lease_time)
 
888
{
 
889
        if (dhcp_server == NULL)
 
890
                return;
 
891
 
 
892
        dhcp_server->lease_seconds = lease_time;
 
893
}
 
894
 
 
895
void g_dhcp_server_set_debug(GDHCPServer *dhcp_server,
 
896
                                GDHCPDebugFunc func, gpointer user_data)
 
897
{
 
898
        if (dhcp_server == NULL)
 
899
                return;
 
900
 
 
901
        dhcp_server->debug_func = func;
 
902
        dhcp_server->debug_data = user_data;
 
903
}