]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - src/patches/dnsmasq/004-Add_contrib_lease-tools_dhcp_release6.patch
6426d390be300f54dacb6744ecccca9d68d75fa1
[people/pmueller/ipfire-2.x.git] / src / patches / dnsmasq / 004-Add_contrib_lease-tools_dhcp_release6.patch
1 From 69cbf78bb676e493f0a4cd6dc7ffec0fcafafed5 Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Tue, 3 May 2016 21:33:38 +0100
4 Subject: [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
16 diff --git a/CHANGELOG b/CHANGELOG
17 index 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
33 diff --git a/contrib/lease-tools/Makefile b/contrib/lease-tools/Makefile
34 index 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
46 diff --git a/contrib/lease-tools/dhcp_release6.1 b/contrib/lease-tools/dhcp_release6.1
47 new file mode 100644
48 index 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 +
90 diff --git a/contrib/lease-tools/dhcp_release6.c b/contrib/lease-tools/dhcp_release6.c
91 new file mode 100644
92 index 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 +}
512 diff --git a/debian/rules b/debian/rules
513 index 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 --
527 2.5.5
528