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
8 contrib/lease-tools/Makefile | 4 +-
9 contrib/lease-tools/dhcp_release6.1 | 38 ++++
10 contrib/lease-tools/dhcp_release6.c | 416 ++++++++++++++++++++++++++++++++++++
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
16 diff --git a/CHANGELOG b/CHANGELOG
17 index aa53fba..7d4a061 100644
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.
24 + Move the dhcp_release and dhcp_lease_time tools from
25 + contrib/wrt to contrib/lease-tools.
27 + Add dhcp_release6 to contrib/lease-tools. Many thanks
28 + to Sergey Nechaev for this code.
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
40 -all: dhcp_release dhcp_lease_time
41 +all: dhcp_release dhcp_release6 dhcp_lease_time
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
48 index 0000000..763e01c
50 +++ b/contrib/lease-tools/dhcp_release6.1
54 +dhcp_release6 \- Release a DHCPv6 lease on a the local dnsmasq DHCP server.
56 +.B dhcp_release6 --iface <interface> --client-id <client-id> --server-id
57 +server-id --iaid <iaid> --ip <IP> [--dry-run] [--help]
59 +A utility which forces the DHCP server running on this machine to release a
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.
68 +Print hexadecimal representation of generated DHCPv6 release packet to standard
71 +print usage information to standard output and exit.
73 +Decimal representation of DHCPv6 IAID. Normally it can be found in leases file
74 +both on client and server.
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.
81 +MUST be run as root - will fail otherwise.
83 +Only usable on IPv6 DHCP leases.
87 +This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.
90 diff --git a/contrib/lease-tools/dhcp_release6.c b/contrib/lease-tools/dhcp_release6.c
92 index 0000000..74fb26a
94 +++ b/contrib/lease-tools/dhcp_release6.c
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
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.
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.
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
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.
115 + The iaid argument is numeric string and mandatory. Normally
116 + it can be found in leases file both on client and server.
118 + IP is an IPv6 adress to release
120 + If --dry-run is specified, dhcp_release6 just prints hexadecimal represantation of
121 + packet to send to stdout and exits.
123 + If --help is specified, dhcp_release6 print usage information to stdout and exits
131 +#include <strings.h>
132 +#include <sys/types.h>
133 +#include <sys/socket.h>
134 +#include <arpa/inet.h>
139 +#define NOT_REPLY_CODE 115
140 +typedef unsigned char u8;
141 +typedef unsigned short u16;
142 +typedef unsigned int u32;
155 + INFORMATION_REQUEST = 11,
179 + RECONF_ACCEPT = 20,
182 +enum DHCP6_STATUSES{
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'},
201 +const short DHCP6_CLIENT_PORT = 546;
202 +const short DHCP6_SERVER_PORT = 547;
204 +const char* DHCP6_MULTICAST_ADDRESS = "ff02::1:2";
206 +struct dhcp6_option{
212 +struct dhcp6_iaaddr_option{
215 + struct in6_addr ip;
216 + uint32_t preferred_lifetime;
217 + uint32_t valid_lifetime;
222 +struct dhcp6_iana_option{
228 + char options[1024];
232 +struct dhcp6_packet{
238 +size_t pack_duid(const char* str, char* dst){
240 + char* tmp = strdup(str);
241 + char* tmp_to_free = tmp;
243 + uint8_t write_pos = 0;
244 + while ((ptr = strtok (tmp, ":"))) {
245 + dst[write_pos] = (uint8_t) strtol(ptr, NULL, 16);
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));
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));
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));
280 + fprintf(stderr, "Not in presentation format");
282 + perror("inet_pton");
283 + exit(EXIT_FAILURE);
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));
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));
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));
302 + result.buf[0] = RELEASE;
304 + bzero(result.buf+1, 3);
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);
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;
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){
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);
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]);
351 + size_t current_pos = 4;
352 + if (type != REPLY ){
353 + return NOT_REPLY_CODE;
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){
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);
374 + if (option_type == IA_NA ){
375 + uint16_t result = parse_iana_suboption(buf + current_pos +24, option_len -24);
380 + current_pos += option_len;
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);
392 +int send_release_packet(const char* iface, struct dhcp6_packet* packet){
394 + struct sockaddr_in6 server_addr, client_addr;
395 + char response[1400];
396 + int sock = socket(PF_INET6, SOCK_DGRAM, 0);
399 + perror("creating socket");
402 + if (setsockopt(sock, SOL_SOCKET, 25, iface, strlen(iface)) == -1) {
403 + perror("SO_BINDTODEVICE");
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");
425 + recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
426 + if (recv_size == -1){
427 + if (errno == EAGAIN){
431 + perror("recvfrom");
434 + int16_t result = parse_packet(response, recv_size);
435 + if (result == NOT_REPLY_CODE){
441 + fprintf(stderr, "Response timed out\n");
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 = "";
455 + int option_index = 0;
456 + int c = getopt_long(argc, argv, "a:s:c:n:i:hd", longopts, &option_index);
462 + if (longopts[option_index].flag !=0){
465 + printf ("option %s", longopts[option_index].name);
467 + printf (" with arg %s", optarg);
480 + client_id = optarg;
486 + server_id = optarg;
489 + usage(argv[0], stdout);
492 + usage(argv[0], stderr);
500 + struct dhcp6_packet packet = create_release_packet(iaid, ip, client_id, server_id);
503 + for(i=0;i<packet.len;i++){
504 + printf("%hhx", packet.buf[i]);
509 + return send_release_packet(iface, &packet);
512 diff --git a/debian/rules b/debian/rules
513 index 193b30c..d748829 100755
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