2 dhcp_release6 --iface <interface> --client-id <client-id> --server-id
3 server-id --iaid <iaid> --ip <IP> [--dry-run] [--help]
4 MUST be run as root - will fail otherwise
7 /* Send a DHCPRELEASE message to IPv6 multicast address via the specified interface
8 to tell the local DHCP server to delete a particular lease.
10 The interface argument is the interface in which a DHCP
11 request _would_ be received if it was coming from the client,
12 rather than being faked up here.
14 The client-id argument is colon-separated hex string and mandatory. Normally
15 it can be found in leases file both on client and server
17 The server-id argument is colon-separated hex string and mandatory. Normally
18 it can be found in leases file both on client and server.
20 The iaid argument is numeric string and mandatory. Normally
21 it can be found in leases file both on client and server.
23 IP is an IPv6 address to release
25 If --dry-run is specified, dhcp_release6 just prints hexadecimal representation of
26 packet to send to stdout and exits.
28 If --help is specified, dhcp_release6 print usage information to stdout and exits
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <arpa/inet.h>
44 #define NOT_REPLY_CODE 115
45 typedef unsigned char u8;
46 typedef unsigned short u16;
47 typedef unsigned int u32;
61 INFORMATION_REQUEST = 11,
100 static struct option longopts[] = {
101 {"ip", required_argument, 0, 'a' },
102 {"server-id", required_argument, 0, 's' },
103 {"client-id", required_argument, 0, 'c' },
104 {"iface", required_argument, 0, 'n' },
105 {"iaid", required_argument, 0, 'i' },
106 {"dry-run", no_argument, 0, 'd' },
107 {"help", no_argument, 0, 'h' },
111 const short DHCP6_CLIENT_PORT = 546;
112 const short DHCP6_SERVER_PORT = 547;
114 const char* DHCP6_MULTICAST_ADDRESS = "ff02::1:2";
116 struct dhcp6_option {
122 struct dhcp6_iaaddr_option {
126 uint32_t preferred_lifetime;
127 uint32_t valid_lifetime;
130 struct dhcp6_iana_option {
140 struct dhcp6_packet {
145 size_t pack_duid(const char* str, char* dst)
147 char* tmp = strdup(str);
148 char* tmp_to_free = tmp;
150 uint8_t write_pos = 0;
151 while ((ptr = strtok (tmp, ":")))
153 dst[write_pos] = (uint8_t) strtol(ptr, NULL, 16);
162 struct dhcp6_option create_client_id_option(const char* duid)
164 struct dhcp6_option option;
165 option.type = htons(CLIENTID);
166 bzero(option.value, sizeof(option.value));
167 option.len = htons(pack_duid(duid, option.value));
171 struct dhcp6_option create_server_id_option(const char* duid)
173 struct dhcp6_option option;
174 option.type = htons(SERVERID);
175 bzero(option.value, sizeof(option.value));
176 option.len = htons(pack_duid(duid, option.value));
180 struct dhcp6_iaaddr_option create_iaadr_option(const char* ip)
182 struct dhcp6_iaaddr_option result;
183 result.type =htons(IAADDR);
184 /* no suboptions needed here, so length is 24 */
185 result.len = htons(24);
186 result.preferred_lifetime = 0;
187 result.valid_lifetime = 0;
188 int s = inet_pton(AF_INET6, ip, &(result.ip));
191 fprintf(stderr, "Not in presentation format");
200 struct dhcp6_iana_option create_iana_option(const char * iaid, struct dhcp6_iaaddr_option ia_addr)
202 struct dhcp6_iana_option result;
203 result.type = htons(IA_NA);
204 result.iaid = htonl(atoi(iaid));
207 result.len = htons(12 + ntohs(ia_addr.len) + 2 * sizeof(uint16_t));
208 memcpy(result.options, &ia_addr, ntohs(ia_addr.len) + 2 * sizeof(uint16_t));
212 struct dhcp6_packet create_release_packet(const char* iaid, const char* ip, const char* client_id, const char* server_id)
214 struct dhcp6_packet result;
215 bzero(result.buf, sizeof(result.buf));
217 result.buf[0] = RELEASE;
219 bzero(result.buf+1, 3);
221 struct dhcp6_option client_option = create_client_id_option(client_id);
222 struct dhcp6_option server_option = create_server_id_option(server_id);
223 struct dhcp6_iaaddr_option iaaddr_option = create_iaadr_option(ip);
224 struct dhcp6_iana_option iana_option = create_iana_option(iaid, iaaddr_option);
226 memcpy(result.buf + offset, &client_option, ntohs(client_option.len) + 2*sizeof(uint16_t));
227 offset += (ntohs(client_option.len)+ 2 *sizeof(uint16_t) );
228 memcpy(result.buf + offset, &server_option, ntohs(server_option.len) + 2*sizeof(uint16_t) );
229 offset += (ntohs(server_option.len)+ 2* sizeof(uint16_t));
230 memcpy(result.buf + offset, &iana_option, ntohs(iana_option.len) + 2*sizeof(uint16_t) );
231 offset += (ntohs(iana_option.len)+ 2* sizeof(uint16_t));
236 uint16_t parse_iana_suboption(char* buf, size_t len)
238 size_t current_pos = 0;
239 char option_value[1024];
240 while (current_pos < len)
242 uint16_t option_type, option_len;
243 memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
244 memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
245 option_type = ntohs(option_type);
246 option_len = ntohs(option_len);
247 current_pos += 2 * sizeof(uint16_t);
248 if (option_type == STATUS_CODE)
251 memcpy(&status, buf + current_pos, sizeof(uint16_t));
252 status = ntohs(status);
253 if (status != SUCCESS)
255 memcpy(option_value, buf + current_pos + sizeof(uint16_t) , option_len - sizeof(uint16_t));
256 option_value[option_len-sizeof(uint16_t)] ='\0';
257 fprintf(stderr, "Error: %s\n", option_value);
266 int16_t parse_packet(char* buf, size_t len)
269 uint8_t type = buf[0];
270 /*skipping tx id. you need it, uncomment following line
271 uint16_t tx_id = ntohs((buf[1] <<16) + (buf[2] <<8) + buf[3]);
273 size_t current_pos = 4;
275 return NOT_REPLY_CODE;
277 char option_value[1024];
278 while (current_pos < len)
280 uint16_t option_type, option_len;
281 memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
282 memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
283 option_type = ntohs(option_type);
284 option_len = ntohs(option_len);
285 current_pos += 2 * sizeof(uint16_t);
286 if (option_type == STATUS_CODE)
289 memcpy(&status, buf + current_pos, sizeof(uint16_t));
290 status = ntohs(status);
291 if (status != SUCCESS)
293 memcpy(option_value, buf + current_pos +sizeof(uint16_t) , option_len -sizeof(uint16_t));
294 fprintf(stderr, "Error: %d %s\n", status, option_value);
298 /* Got success status, return that if there's no specific error in an IA_NA. */
302 if (option_type == IA_NA )
304 uint16_t result = parse_iana_suboption(buf + current_pos +24, option_len -24);
309 current_pos += option_len;
315 void usage(const char* arg, FILE* stream)
317 const char* usage_string ="--ip IPv6 --iface IFACE --server-id SERVER_ID --client-id CLIENT_ID --iaid IAID [--dry-run] | --help";
318 fprintf (stream, "Usage: %s %s\n", arg, usage_string);
321 static void fail_fatal(const char *errstr, int exitcode)
327 int send_release_packet(const char* iface, struct dhcp6_packet* packet)
329 struct sockaddr_in6 server_addr, client_addr;
331 int sock = socket(PF_INET6, SOCK_DGRAM, 0);
335 perror("creating socket");
339 if (setsockopt(sock, SOL_SOCKET, 25, iface, strlen(iface)) == -1)
341 perror("SO_BINDTODEVICE");
346 memset(&server_addr, 0, sizeof(server_addr));
347 server_addr.sin6_family = AF_INET6;
348 client_addr.sin6_family = AF_INET6;
349 client_addr.sin6_port = htons(DHCP6_CLIENT_PORT);
350 client_addr.sin6_flowinfo = 0;
351 client_addr.sin6_scope_id =0;
352 if (inet_pton(AF_INET6, "::", &client_addr.sin6_addr) <= 0)
353 fail_fatal("inet_pton", 5);
354 if (bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6)) != 0)
355 perror("bind"); /* continue on bind error */
356 if (inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr) <= 0)
357 fail_fatal("inet_pton", 5);
358 server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
359 ssize_t recv_size = 0;
361 for (i = 0; i < 5; i++)
363 if (sendto(sock, packet->buf, packet->len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
364 fail_fatal("sendto failed", 4);
366 recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
377 result = UNSPEC_FAIL;
382 result = parse_packet(response, recv_size);
383 if (result == NOT_REPLY_CODE)
394 fprintf(stderr, "Response timed out\n");
399 int main(int argc, char * const argv[])
401 const char* UNINITIALIZED = "";
402 const char* iface = UNINITIALIZED;
403 const char* ip = UNINITIALIZED;
404 const char* client_id = UNINITIALIZED;
405 const char* server_id = UNINITIALIZED;
406 const char* iaid = UNINITIALIZED;
410 int option_index = 0;
411 int c = getopt_long(argc, argv, "a:s:c:n:i:hd", longopts, &option_index);
418 if (longopts[option_index].flag !=0)
421 printf ("option %s", longopts[option_index].name);
423 printf (" with arg %s", optarg);
446 usage(argv[0], stdout);
449 usage(argv[0], stderr);
457 if (iaid == UNINITIALIZED)
459 fprintf(stderr, "Missing required iaid parameter\n");
460 usage(argv[0], stderr);
464 if (server_id == UNINITIALIZED)
466 fprintf(stderr, "Missing required server-id parameter\n");
467 usage(argv[0], stderr);
471 if (client_id == UNINITIALIZED)
473 fprintf(stderr, "Missing required client-id parameter\n");
474 usage(argv[0], stderr);
478 if (ip == UNINITIALIZED)
480 fprintf(stderr, "Missing required ip parameter\n");
481 usage(argv[0], stderr);
485 if (iface == UNINITIALIZED)
487 fprintf(stderr, "Missing required iface parameter\n");
488 usage(argv[0], stderr);
494 struct dhcp6_packet packet = create_release_packet(iaid, ip, client_id, server_id);
500 for(i=0; i<packet.len; i++)
501 printf("%hhx", packet.buf[i]);
507 return send_release_packet(iface, &packet);