]> git.ipfire.org Git - ipfire-2.x.git/blame - src/patches/dnsmasq/004-Add_contrib_lease-tools_dhcp_release6.patch
dnsmasq: Update to 2.76test13 with latest patches (001-006)
[ipfire-2.x.git] / src / patches / dnsmasq / 004-Add_contrib_lease-tools_dhcp_release6.patch
CommitLineData
4d36732f
MF
1From 69cbf78bb676e493f0a4cd6dc7ffec0fcafafed5 Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Tue, 3 May 2016 21:33:38 +0100
4Subject: [PATCH] Add contrib/lease-tools/dhcp_release6
5
6---
7 CHANGELOG | 6 +
8 contrib/lease-tools/Makefile | 4 +-
9 contrib/lease-tools/dhcp_release6.1 | 38 ++++
10 contrib/lease-tools/dhcp_release6.c | 416 ++++++++++++++++++++++++++++++++++++
11 debian/rules | 3 +
12 5 files changed, 465 insertions(+), 2 deletions(-)
13 create mode 100644 contrib/lease-tools/dhcp_release6.1
14 create mode 100644 contrib/lease-tools/dhcp_release6.c
15
16diff --git a/CHANGELOG b/CHANGELOG
17index aa53fba..7d4a061 100644
18--- a/CHANGELOG
19+++ b/CHANGELOG
20@@ -65,6 +65,12 @@ version 2.76
21 --servers-file to define upstream DNS servers. Thanks to
22 Scott Bonar for the bug report.
23
24+ Move the dhcp_release and dhcp_lease_time tools from
25+ contrib/wrt to contrib/lease-tools.
26+
27+ Add dhcp_release6 to contrib/lease-tools. Many thanks
28+ to Sergey Nechaev for this code.
29+
30
31 version 2.75
32 Fix reversion on 2.74 which caused 100% CPU use when a
33diff --git a/contrib/lease-tools/Makefile b/contrib/lease-tools/Makefile
34index 68e8d32..f38f2ed 100644
35--- a/contrib/lease-tools/Makefile
36+++ b/contrib/lease-tools/Makefile
37@@ -1,6 +1,6 @@
38 CFLAGS?= -O2 -Wall -W
39
40-all: dhcp_release dhcp_lease_time
41+all: dhcp_release dhcp_release6 dhcp_lease_time
42
43 clean:
44- rm -f *~ *.o core dhcp_release dhcp_lease_time
45+ rm -f *~ *.o core dhcp_release dhcp_release6 dhcp_lease_time
46diff --git a/contrib/lease-tools/dhcp_release6.1 b/contrib/lease-tools/dhcp_release6.1
47new file mode 100644
48index 0000000..763e01c
49--- /dev/null
50+++ b/contrib/lease-tools/dhcp_release6.1
51@@ -0,0 +1,38 @@
52+.TH DHCP_RELEASE 1
53+.SH NAME
54+dhcp_release6 \- Release a DHCPv6 lease on a the local dnsmasq DHCP server.
55+.SH SYNOPSIS
56+.B dhcp_release6 --iface <interface> --client-id <client-id> --server-id
57+server-id --iaid <iaid> --ip <IP> [--dry-run] [--help]
58+.SH "DESCRIPTION"
59+A utility which forces the DHCP server running on this machine to release a
60+DHCPv6 lease.
61+.SS OPTIONS
62+.IP "-a, --ip"
63+IPv6 address to release.
64+.IP "-c, --client-id"
65+Colon-separated hex string representing DHCPv6 client id. Normally
66+it can be found in leases file both on client and server.
67+.IP "-d, --dry-run"
68+Print hexadecimal representation of generated DHCPv6 release packet to standard
69+output and exit.
70+.IP "-h, --help"
71+print usage information to standard output and exit.
72+.IP "-i, --iaid"
73+Decimal representation of DHCPv6 IAID. Normally it can be found in leases file
74+both on client and server.
75+.IP "-n, --iface"
76+Network interface to send a DHCPv6 release packet from.
77+.IP "-s, --server-id"
78+Colon-separated hex string representing DHCPv6 server id. Normally
79+it can be found in leases file both on client and server.
80+.SH NOTES
81+MUST be run as root - will fail otherwise.
82+.SH LIMITATIONS
83+Only usable on IPv6 DHCP leases.
84+.SH SEE ALSO
85+.BR dnsmasq (8)
86+.SH AUTHOR
87+This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.
88+
89+
90diff --git a/contrib/lease-tools/dhcp_release6.c b/contrib/lease-tools/dhcp_release6.c
91new file mode 100644
92index 0000000..74fb26a
93--- /dev/null
94+++ b/contrib/lease-tools/dhcp_release6.c
95@@ -0,0 +1,416 @@
96+/*
97+ dhcp_release6 --iface <interface> --client-id <client-id> --server-id
98+ server-id --iaid <iaid> --ip <IP> [--dry-run] [--help]
99+ MUST be run as root - will fail othewise
100+ */
101+
102+/* Send a DHCPRELEASE message to IPv6 multicast address via the specified interface
103+ to tell the local DHCP server to delete a particular lease.
104+
105+ The interface argument is the interface in which a DHCP
106+ request _would_ be received if it was coming from the client,
107+ rather than being faked up here.
108+
109+ The client-id argument is colon-separated hex string and mandatory. Normally
110+ it can be found in leases file both on client and server
111+
112+ The server-id argument is colon-separated hex string and mandatory. Normally
113+ it can be found in leases file both on client and server.
114+
115+ The iaid argument is numeric string and mandatory. Normally
116+ it can be found in leases file both on client and server.
117+
118+ IP is an IPv6 adress to release
119+
120+ If --dry-run is specified, dhcp_release6 just prints hexadecimal represantation of
121+ packet to send to stdout and exits.
122+
123+ If --help is specified, dhcp_release6 print usage information to stdout and exits
124+
125+
126+
127+ */
128+#include <stdio.h>
129+#include <stdlib.h>
130+#include <string.h>
131+#include <strings.h>
132+#include <sys/types.h>
133+#include <sys/socket.h>
134+#include <arpa/inet.h>
135+#include <getopt.h>
136+#include <errno.h>
137+#include <unistd.h>
138+
139+#define NOT_REPLY_CODE 115
140+typedef unsigned char u8;
141+typedef unsigned short u16;
142+typedef unsigned int u32;
143+
144+enum DHCP6_TYPES{
145+ SOLICIT = 1,
146+ ADVERTISE = 2,
147+ REQUEST = 3,
148+ CONFIRM = 4,
149+ RENEW = 5,
150+ REBIND = 6,
151+ REPLY = 7,
152+ RELEASE = 8,
153+ DECLINE = 9,
154+ RECONFIGURE = 10,
155+ INFORMATION_REQUEST = 11,
156+ RELAY_FORW = 12,
157+ RELAY_REPL = 13
158+
159+};
160+enum DHCP6_OPTIONS{
161+ CLIENTID = 1,
162+ SERVERID = 2,
163+ IA_NA = 3,
164+ IA_TA = 4,
165+ IAADDR = 5,
166+ ORO = 6,
167+ PREFERENCE = 7,
168+ ELAPSED_TIME = 8,
169+ RELAY_MSG = 9,
170+ AUTH = 11,
171+ UNICAST = 12,
172+ STATUS_CODE = 13,
173+ RAPID_COMMIT = 14,
174+ USER_CLASS = 15,
175+ VENDOR_CLASS = 16,
176+ VENDOR_OPTS = 17,
177+ INTERFACE_ID = 18,
178+ RECONF_MSG = 19,
179+ RECONF_ACCEPT = 20,
180+};
181+
182+enum DHCP6_STATUSES{
183+ SUCCESS = 0,
184+ UNSPEC_FAIL = 1,
185+ NOADDR_AVAIL=2,
186+ NO_BINDING = 3,
187+ NOT_ON_LINK = 4,
188+ USE_MULTICAST =5
189+};
190+static struct option longopts[] = {
191+ {"ip", required_argument, 0, 'a'},
192+ {"server-id", required_argument, 0, 's'},
193+ {"client-id", required_argument, 0, 'c'},
194+ {"iface", required_argument, 0, 'n'},
195+ {"iaid", required_argument, 0, 'i'},
196+ {"dry-run", no_argument, 0, 'd'},
197+ {"help", no_argument, 0, 'h'},
198+ {0, 0, 0, 0}
199+};
200+
201+const short DHCP6_CLIENT_PORT = 546;
202+const short DHCP6_SERVER_PORT = 547;
203+
204+const char* DHCP6_MULTICAST_ADDRESS = "ff02::1:2";
205+
206+struct dhcp6_option{
207+ uint16_t type;
208+ uint16_t len;
209+ char value[1024];
210+};
211+
212+struct dhcp6_iaaddr_option{
213+ uint16_t type;
214+ uint16_t len;
215+ struct in6_addr ip;
216+ uint32_t preferred_lifetime;
217+ uint32_t valid_lifetime;
218+
219+
220+};
221+
222+struct dhcp6_iana_option{
223+ uint16_t type;
224+ uint16_t len;
225+ uint32_t iaid;
226+ uint32_t t1;
227+ uint32_t t2;
228+ char options[1024];
229+};
230+
231+
232+struct dhcp6_packet{
233+ size_t len;
234+ char buf[2048];
235+
236+} ;
237+
238+size_t pack_duid(const char* str, char* dst){
239+
240+ char* tmp = strdup(str);
241+ char* tmp_to_free = tmp;
242+ char *ptr;
243+ uint8_t write_pos = 0;
244+ while ((ptr = strtok (tmp, ":"))) {
245+ dst[write_pos] = (uint8_t) strtol(ptr, NULL, 16);
246+ write_pos += 1;
247+ tmp = NULL;
248+
249+ }
250+ free(tmp_to_free);
251+ return write_pos;
252+}
253+
254+struct dhcp6_option create_client_id_option(const char* duid){
255+ struct dhcp6_option option;
256+ option.type = htons(CLIENTID);
257+ bzero(option.value, sizeof(option.value));
258+ option.len = htons(pack_duid(duid, option.value));
259+ return option;
260+}
261+
262+struct dhcp6_option create_server_id_option(const char* duid){
263+ struct dhcp6_option option;
264+ option.type = htons(SERVERID);
265+ bzero(option.value, sizeof(option.value));
266+ option.len = htons(pack_duid(duid, option.value));
267+ return option;
268+}
269+
270+struct dhcp6_iaaddr_option create_iaadr_option(const char* ip){
271+ struct dhcp6_iaaddr_option result;
272+ result.type =htons(IAADDR);
273+ /* no suboptions needed here, so length is 24 */
274+ result.len = htons(24);
275+ result.preferred_lifetime = 0;
276+ result.valid_lifetime = 0;
277+ int s = inet_pton(AF_INET6, ip, &(result.ip));
278+ if (s <= 0) {
279+ if (s == 0)
280+ fprintf(stderr, "Not in presentation format");
281+ else
282+ perror("inet_pton");
283+ exit(EXIT_FAILURE);
284+ }
285+ return result;
286+}
287+struct dhcp6_iana_option create_iana_option(const char * iaid, struct dhcp6_iaaddr_option ia_addr){
288+ struct dhcp6_iana_option result;
289+ result.type = htons(IA_NA);
290+ result.iaid = htonl(atoi(iaid));
291+ result.t1 = 0;
292+ result.t2 = 0;
293+ result.len = htons(12 + ntohs(ia_addr.len) + 2 * sizeof(uint16_t));
294+ memcpy(result.options, &ia_addr, ntohs(ia_addr.len) + 2 * sizeof(uint16_t));
295+ return result;
296+}
297+
298+struct dhcp6_packet create_release_packet(const char* iaid, const char* ip, const char* client_id, const char* server_id){
299+ struct dhcp6_packet result;
300+ bzero(result.buf, sizeof(result.buf));
301+ /* message_type */
302+ result.buf[0] = RELEASE;
303+ /* tx_id */
304+ bzero(result.buf+1, 3);
305+
306+ struct dhcp6_option client_option = create_client_id_option(client_id);
307+ struct dhcp6_option server_option = create_server_id_option(server_id);
308+ struct dhcp6_iaaddr_option iaaddr_option = create_iaadr_option(ip);
309+ struct dhcp6_iana_option iana_option = create_iana_option(iaid, iaaddr_option);
310+ int offset = 4;
311+ memcpy(result.buf + offset, &client_option, ntohs(client_option.len) + 2*sizeof(uint16_t));
312+ offset += (ntohs(client_option.len)+ 2 *sizeof(uint16_t) );
313+ memcpy(result.buf + offset, &server_option, ntohs(server_option.len) + 2*sizeof(uint16_t) );
314+ offset += (ntohs(server_option.len)+ 2* sizeof(uint16_t));
315+ memcpy(result.buf + offset, &iana_option, ntohs(iana_option.len) + 2*sizeof(uint16_t) );
316+ offset += (ntohs(iana_option.len)+ 2* sizeof(uint16_t));
317+ result.len = offset;
318+ return result;
319+}
320+
321+uint16_t parse_iana_suboption(char* buf, size_t len){
322+ size_t current_pos = 0;
323+ char option_value[1024];
324+ while (current_pos < len) {
325+ uint16_t option_type, option_len;
326+ memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
327+ memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
328+ option_type = ntohs(option_type);
329+ option_len = ntohs(option_len);
330+ current_pos += 2 * sizeof(uint16_t);
331+ if (option_type == STATUS_CODE){
332+ uint16_t status;
333+ memcpy(&status, buf + current_pos, sizeof(uint16_t));
334+ status = ntohs(status);
335+ if (status != SUCCESS){
336+ memcpy(option_value, buf + current_pos + sizeof(uint16_t) , option_len - sizeof(uint16_t));
337+ option_value[option_len-sizeof(uint16_t)] ='\0';
338+ fprintf(stderr, "Error: %s\n", option_value);
339+ }
340+ return status;
341+ }
342+ }
343+ return -2;
344+}
345+
346+int16_t parse_packet(char* buf, size_t len){
347+ uint8_t type = buf[0];
348+ /*skipping tx id. you need it, uncomment following line
349+ uint16_t tx_id = ntohs((buf[1] <<16) + (buf[2] <<8) + buf[3]);
350+ */
351+ size_t current_pos = 4;
352+ if (type != REPLY ){
353+ return NOT_REPLY_CODE;
354+ }
355+ char option_value[1024];
356+ while (current_pos < len) {
357+ uint16_t option_type, option_len;
358+ memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
359+ memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
360+ option_type = ntohs(option_type);
361+ option_len = ntohs(option_len);
362+ current_pos += 2 * sizeof(uint16_t);
363+ if (option_type == STATUS_CODE){
364+ uint16_t status;
365+ memcpy(&status, buf + current_pos, sizeof(uint16_t));
366+ status = ntohs(status);
367+ if (status != SUCCESS){
368+ memcpy(option_value, buf + current_pos +sizeof(uint16_t) , option_len -sizeof(uint16_t));
369+ fprintf(stderr, "Error: %d %s\n", status, option_value);
370+ return status;
371+ }
372+
373+ }
374+ if (option_type == IA_NA ){
375+ uint16_t result = parse_iana_suboption(buf + current_pos +24, option_len -24);
376+ if (result){
377+ return result;
378+ }
379+ }
380+ current_pos += option_len;
381+
382+ }
383+ return -1;
384+}
385+
386+void usage(const char* arg, FILE* stream){
387+ const char* usage_string ="--ip IPv6 --iface IFACE --server-id SERVER_ID --client-id CLIENT_ID --iaid IAID [--dry-run] | --help";
388+ fprintf (stream, "Usage: %s %s\n", arg, usage_string);
389+
390+}
391+
392+int send_release_packet(const char* iface, struct dhcp6_packet* packet){
393+
394+ struct sockaddr_in6 server_addr, client_addr;
395+ char response[1400];
396+ int sock = socket(PF_INET6, SOCK_DGRAM, 0);
397+ int i = 0;
398+ if (sock < 0) {
399+ perror("creating socket");
400+ return -1;
401+ }
402+ if (setsockopt(sock, SOL_SOCKET, 25, iface, strlen(iface)) == -1) {
403+ perror("SO_BINDTODEVICE");
404+ close(sock);
405+ return -1;
406+ }
407+ memset(&server_addr, 0, sizeof(server_addr));
408+ server_addr.sin6_family = AF_INET6;
409+ client_addr.sin6_family = AF_INET6;
410+ client_addr.sin6_port = htons(DHCP6_CLIENT_PORT);
411+ client_addr.sin6_flowinfo = 0;
412+ client_addr.sin6_scope_id =0;
413+ inet_pton(AF_INET6, "::", &client_addr.sin6_addr);
414+ bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6));
415+ inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr);
416+ server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
417+ int16_t recv_size = 0;
418+ for (i = 0; i < 5; i++) {
419+ if (sendto(sock, packet->buf, packet->len, 0,
420+ (struct sockaddr *)&server_addr,
421+ sizeof(server_addr)) < 0) {
422+ perror("sendto failed");
423+ exit(4);
424+ }
425+ recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
426+ if (recv_size == -1){
427+ if (errno == EAGAIN){
428+ sleep(1);
429+ continue;
430+ }else {
431+ perror("recvfrom");
432+ }
433+ }
434+ int16_t result = parse_packet(response, recv_size);
435+ if (result == NOT_REPLY_CODE){
436+ sleep(1);
437+ continue;
438+ }
439+ return result;
440+ }
441+ fprintf(stderr, "Response timed out\n");
442+ return -1;
443+
444+}
445+
446+
447+int main(int argc, char * const argv[]) {
448+ const char* iface = "";
449+ const char* ip = "";
450+ const char* client_id = "";
451+ const char* server_id = "";
452+ const char* iaid = "";
453+ int dry_run = 0;
454+ while (1) {
455+ int option_index = 0;
456+ int c = getopt_long(argc, argv, "a:s:c:n:i:hd", longopts, &option_index);
457+ if (c == -1){
458+ break;
459+ }
460+ switch(c){
461+ case 0:
462+ if (longopts[option_index].flag !=0){
463+ break;
464+ }
465+ printf ("option %s", longopts[option_index].name);
466+ if (optarg)
467+ printf (" with arg %s", optarg);
468+ printf ("\n");
469+ break;
470+ case 'i':
471+ iaid = optarg;
472+ break;
473+ case 'n':
474+ iface = optarg;
475+ break;
476+ case 'a':
477+ ip = optarg;
478+ break;
479+ case 'c':
480+ client_id = optarg;
481+ break;
482+ case 'd':
483+ dry_run = 1;
484+ break;
485+ case 's':
486+ server_id = optarg;
487+ break;
488+ case 'h':
489+ usage(argv[0], stdout);
490+ break;
491+ case '?':
492+ usage(argv[0], stderr);
493+ return -1;
494+ default:
495+ abort();
496+
497+ }
498+
499+ }
500+ struct dhcp6_packet packet = create_release_packet(iaid, ip, client_id, server_id);
501+ if (dry_run){
502+ uint16_t i;
503+ for(i=0;i<packet.len;i++){
504+ printf("%hhx", packet.buf[i]);
505+ }
506+ printf("\n");
507+ return 0;
508+ }
509+ return send_release_packet(iface, &packet);
510+
511+}
512diff --git a/debian/rules b/debian/rules
513index 193b30c..d748829 100755
514--- a/debian/rules
515+++ b/debian/rules
516@@ -188,6 +188,9 @@ ifeq ($(DEB_HOST_ARCH_OS),linux)
517 install -m 755 contrib/lease-tools/dhcp_release debian/utils/usr/bin/dhcp_release
518 install -m 644 contrib/lease-tools/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
519 gzip -9n debian/utils/usr/share/man/man1/dhcp_release.1
520+ install -m 755 contrib/lease-tools/dhcp_release6 debian/utils/usr/bin/dhcp_release6
521+ install -m 644 contrib/lease-tools/dhcp_release6.1 debian/utils/usr/share/man/man1/dhcp_release6.1
522+ gzip -9n debian/utils/usr/share/man/man1/dhcp_release6.1
523 install -m 755 contrib/lease-tools/dhcp_lease_time debian/utils/usr/bin/dhcp_lease_time
524 install -m 644 contrib/lease-tools/dhcp_lease_time.1 debian/utils/usr/share/man/man1/dhcp_lease_time.1
525 install -m 644 debian/copyright debian/utils/usr/share/doc/dnsmasq-utils/copyright
526--
5272.5.5
528