Annotation of embedaddon/dnsmasq/contrib/lease-tools/dhcp_release6.c, revision 1.1.1.3
1.1 misho 1: /*
2: dhcp_release6 --iface <interface> --client-id <client-id> --server-id
3: server-id --iaid <iaid> --ip <IP> [--dry-run] [--help]
1.1.1.2 misho 4: MUST be run as root - will fail otherwise
1.1 misho 5: */
6:
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.
9:
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.
13:
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
16:
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.
19:
20: The iaid argument is numeric string and mandatory. Normally
21: it can be found in leases file both on client and server.
22:
1.1.1.2 misho 23: IP is an IPv6 address to release
1.1 misho 24:
1.1.1.2 misho 25: If --dry-run is specified, dhcp_release6 just prints hexadecimal representation of
1.1 misho 26: packet to send to stdout and exits.
27:
28: If --help is specified, dhcp_release6 print usage information to stdout and exits
29:
30:
31:
32: */
33: #include <stdio.h>
34: #include <stdlib.h>
35: #include <string.h>
36: #include <strings.h>
37: #include <sys/types.h>
38: #include <sys/socket.h>
39: #include <arpa/inet.h>
40: #include <getopt.h>
41: #include <errno.h>
42: #include <unistd.h>
43:
44: #define NOT_REPLY_CODE 115
45: typedef unsigned char u8;
46: typedef unsigned short u16;
47: typedef unsigned int u32;
48:
1.1.1.2 misho 49: enum DHCP6_TYPES
50: {
1.1 misho 51: SOLICIT = 1,
52: ADVERTISE = 2,
53: REQUEST = 3,
54: CONFIRM = 4,
55: RENEW = 5,
56: REBIND = 6,
57: REPLY = 7,
58: RELEASE = 8,
59: DECLINE = 9,
60: RECONFIGURE = 10,
61: INFORMATION_REQUEST = 11,
62: RELAY_FORW = 12,
63: RELAY_REPL = 13
64:
1.1.1.2 misho 65: };
66:
67: enum DHCP6_OPTIONS
68: {
1.1 misho 69: CLIENTID = 1,
70: SERVERID = 2,
71: IA_NA = 3,
72: IA_TA = 4,
73: IAADDR = 5,
74: ORO = 6,
75: PREFERENCE = 7,
76: ELAPSED_TIME = 8,
77: RELAY_MSG = 9,
78: AUTH = 11,
79: UNICAST = 12,
80: STATUS_CODE = 13,
81: RAPID_COMMIT = 14,
82: USER_CLASS = 15,
83: VENDOR_CLASS = 16,
84: VENDOR_OPTS = 17,
85: INTERFACE_ID = 18,
86: RECONF_MSG = 19,
87: RECONF_ACCEPT = 20,
1.1.1.2 misho 88: };
1.1 misho 89:
1.1.1.2 misho 90: enum DHCP6_STATUSES
91: {
1.1 misho 92: SUCCESS = 0,
93: UNSPEC_FAIL = 1,
94: NOADDR_AVAIL=2,
95: NO_BINDING = 3,
96: NOT_ON_LINK = 4,
97: USE_MULTICAST =5
1.1.1.2 misho 98: };
99:
1.1 misho 100: static struct option longopts[] = {
1.1.1.2 misho 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' },
108: {0, 0, 0, 0 }
1.1 misho 109: };
110:
111: const short DHCP6_CLIENT_PORT = 546;
112: const short DHCP6_SERVER_PORT = 547;
113:
114: const char* DHCP6_MULTICAST_ADDRESS = "ff02::1:2";
115:
1.1.1.2 misho 116: struct dhcp6_option {
117: uint16_t type;
118: uint16_t len;
119: char value[1024];
1.1 misho 120: };
121:
1.1.1.2 misho 122: struct dhcp6_iaaddr_option {
123: uint16_t type;
124: uint16_t len;
125: struct in6_addr ip;
126: uint32_t preferred_lifetime;
127: uint32_t valid_lifetime;
1.1 misho 128: };
129:
1.1.1.2 misho 130: struct dhcp6_iana_option {
131: uint16_t type;
132: uint16_t len;
133: uint32_t iaid;
134: uint32_t t1;
135: uint32_t t2;
136: char options[1024];
1.1 misho 137: };
138:
139:
1.1.1.2 misho 140: struct dhcp6_packet {
141: size_t len;
142: char buf[2048];
143: };
1.1 misho 144:
1.1.1.2 misho 145: size_t pack_duid(const char* str, char* dst)
146: {
147: char* tmp = strdup(str);
148: char* tmp_to_free = tmp;
149: char *ptr;
150: uint8_t write_pos = 0;
151: while ((ptr = strtok (tmp, ":")))
152: {
153: dst[write_pos] = (uint8_t) strtol(ptr, NULL, 16);
154: write_pos += 1;
155: tmp = NULL;
156: }
157:
158: free(tmp_to_free);
159: return write_pos;
160: }
161:
162: struct dhcp6_option create_client_id_option(const char* duid)
163: {
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));
168: return option;
169: }
170:
171: struct dhcp6_option create_server_id_option(const char* duid)
172: {
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));
177: return option;
178: }
179:
180: struct dhcp6_iaaddr_option create_iaadr_option(const char* ip)
181: {
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));
189: if (s <= 0) {
190: if (s == 0)
191: fprintf(stderr, "Not in presentation format");
192: else
193: perror("inet_pton");
194: exit(EXIT_FAILURE);
195: }
196:
197: return result;
198: }
199:
200: struct dhcp6_iana_option create_iana_option(const char * iaid, struct dhcp6_iaaddr_option ia_addr)
201: {
202: struct dhcp6_iana_option result;
203: result.type = htons(IA_NA);
204: result.iaid = htonl(atoi(iaid));
205: result.t1 = 0;
206: result.t2 = 0;
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));
209: return result;
210: }
211:
212: struct dhcp6_packet create_release_packet(const char* iaid, const char* ip, const char* client_id, const char* server_id)
213: {
214: struct dhcp6_packet result;
215: bzero(result.buf, sizeof(result.buf));
216: /* message_type */
217: result.buf[0] = RELEASE;
218: /* tx_id */
219: bzero(result.buf+1, 3);
220:
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);
225: int offset = 4;
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));
232: result.len = offset;
233: return result;
234: }
235:
236: uint16_t parse_iana_suboption(char* buf, size_t len)
237: {
238: size_t current_pos = 0;
239: char option_value[1024];
240: while (current_pos < len)
241: {
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)
249: {
250: uint16_t status;
251: memcpy(&status, buf + current_pos, sizeof(uint16_t));
252: status = ntohs(status);
253: if (status != SUCCESS)
254: {
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);
1.1 misho 258: }
1.1.1.2 misho 259: return status;
1.1 misho 260: }
261: }
262:
1.1.1.2 misho 263: return -2;
1.1 misho 264: }
265:
1.1.1.2 misho 266: int16_t parse_packet(char* buf, size_t len)
267: {
268: int16_t ret = -1;
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]);
272: */
273: size_t current_pos = 4;
274: if (type != REPLY )
275: return NOT_REPLY_CODE;
276:
277: char option_value[1024];
278: while (current_pos < len)
279: {
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)
287: {
288: uint16_t status;
289: memcpy(&status, buf + current_pos, sizeof(uint16_t));
290: status = ntohs(status);
291: if (status != SUCCESS)
292: {
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);
295: return status;
296: }
1.1 misho 297:
1.1.1.2 misho 298: /* Got success status, return that if there's no specific error in an IA_NA. */
299: ret = SUCCESS;
300: }
301:
302: if (option_type == IA_NA )
303: {
304: uint16_t result = parse_iana_suboption(buf + current_pos +24, option_len -24);
305: if (result)
306: return result;
307: }
308:
309: current_pos += option_len;
310: }
311:
312: return ret;
313: }
314:
315: void usage(const char* arg, FILE* stream)
316: {
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);
319: }
320:
1.1.1.3 ! misho 321: static void fail_fatal(const char *errstr, int exitcode)
! 322: {
! 323: perror(errstr);
! 324: exit(exitcode);
! 325: }
! 326:
1.1.1.2 misho 327: int send_release_packet(const char* iface, struct dhcp6_packet* packet)
328: {
329: struct sockaddr_in6 server_addr, client_addr;
330: char response[1400];
331: int sock = socket(PF_INET6, SOCK_DGRAM, 0);
332: int i = 0;
333: if (sock < 0)
334: {
335: perror("creating socket");
336: return -1;
337: }
338:
339: if (setsockopt(sock, SOL_SOCKET, 25, iface, strlen(iface)) == -1)
340: {
1.1 misho 341: perror("SO_BINDTODEVICE");
342: close(sock);
343: return -1;
1.1.1.2 misho 344: }
345:
1.1 misho 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;
1.1.1.3 ! misho 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);
1.1 misho 358: server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
1.1.1.3 ! misho 359: ssize_t recv_size = 0;
! 360: int result;
1.1.1.2 misho 361: for (i = 0; i < 5; i++)
362: {
363: if (sendto(sock, packet->buf, packet->len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
1.1.1.3 ! misho 364: fail_fatal("sendto failed", 4);
1.1.1.2 misho 365:
1.1 misho 366: recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
1.1.1.2 misho 367: if (recv_size == -1)
368: {
369: if (errno == EAGAIN)
370: {
371: sleep(1);
372: continue;
373: }
374: else
375: {
1.1 misho 376: perror("recvfrom");
1.1.1.3 ! misho 377: result = UNSPEC_FAIL;
1.1.1.2 misho 378: }
379: }
1.1.1.3 ! misho 380: else
1.1.1.2 misho 381: {
1.1.1.3 ! misho 382: result = parse_packet(response, recv_size);
! 383: if (result == NOT_REPLY_CODE)
! 384: {
! 385: sleep(1);
! 386: continue;
! 387: }
1.1.1.2 misho 388: }
389: close(sock);
1.1 misho 390: return result;
1.1.1.2 misho 391: }
392:
393: close(sock);
1.1 misho 394: fprintf(stderr, "Response timed out\n");
1.1.1.2 misho 395: return -1;
1.1 misho 396: }
397:
398:
1.1.1.2 misho 399: int main(int argc, char * const argv[])
400: {
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;
407: int dry_run = 0;
408: while (1)
409: {
410: int option_index = 0;
411: int c = getopt_long(argc, argv, "a:s:c:n:i:hd", longopts, &option_index);
412: if (c == -1)
413: break;
1.1 misho 414:
1.1.1.2 misho 415: switch(c)
416: {
417: case 0:
418: if (longopts[option_index].flag !=0)
419: break;
420:
421: printf ("option %s", longopts[option_index].name);
422: if (optarg)
423: printf (" with arg %s", optarg);
424: printf ("\n");
425: break;
426:
427: case 'i':
428: iaid = optarg;
429: break;
430: case 'n':
431: iface = optarg;
432: break;
433: case 'a':
434: ip = optarg;
435: break;
436: case 'c':
437: client_id = optarg;
438: break;
439: case 'd':
440: dry_run = 1;
441: break;
442: case 's':
443: server_id = optarg;
444: break;
445: case 'h':
446: usage(argv[0], stdout);
447: return 0;
448: case '?':
449: usage(argv[0], stderr);
450: return -1;
451: default:
452: abort();
453:
454: }
1.1 misho 455: }
1.1.1.2 misho 456:
457: if (iaid == UNINITIALIZED)
458: {
459: fprintf(stderr, "Missing required iaid parameter\n");
460: usage(argv[0], stderr);
461: return -1;
462: }
463:
464: if (server_id == UNINITIALIZED)
465: {
1.1 misho 466: fprintf(stderr, "Missing required server-id parameter\n");
467: usage(argv[0], stderr);
468: return -1;
1.1.1.2 misho 469: }
470:
471: if (client_id == UNINITIALIZED)
472: {
1.1 misho 473: fprintf(stderr, "Missing required client-id parameter\n");
474: usage(argv[0], stderr);
475: return -1;
1.1.1.2 misho 476: }
477:
478: if (ip == UNINITIALIZED)
479: {
1.1 misho 480: fprintf(stderr, "Missing required ip parameter\n");
481: usage(argv[0], stderr);
482: return -1;
1.1.1.2 misho 483: }
484:
485: if (iface == UNINITIALIZED)
486: {
487: fprintf(stderr, "Missing required iface parameter\n");
1.1 misho 488: usage(argv[0], stderr);
489: return -1;
1.1.1.2 misho 490: }
1.1 misho 491:
1.1.1.2 misho 492:
493:
1.1 misho 494: struct dhcp6_packet packet = create_release_packet(iaid, ip, client_id, server_id);
1.1.1.2 misho 495:
496: if (dry_run)
497: {
1.1 misho 498: uint16_t i;
1.1.1.2 misho 499:
500: for(i=0; i<packet.len; i++)
501: printf("%hhx", packet.buf[i]);
502:
1.1 misho 503: printf("\n");
504: return 0;
1.1.1.2 misho 505: }
506:
1.1 misho 507: return send_release_packet(iface, &packet);
508: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>