Annotation of embedaddon/dnsmasq/contrib/lease-tools/dhcp_release6.c, revision 1.1.1.1
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]
4: MUST be run as root - will fail othewise
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:
23: IP is an IPv6 adress to release
24:
25: If --dry-run is specified, dhcp_release6 just prints hexadecimal represantation of
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:
49: enum DHCP6_TYPES{
50: SOLICIT = 1,
51: ADVERTISE = 2,
52: REQUEST = 3,
53: CONFIRM = 4,
54: RENEW = 5,
55: REBIND = 6,
56: REPLY = 7,
57: RELEASE = 8,
58: DECLINE = 9,
59: RECONFIGURE = 10,
60: INFORMATION_REQUEST = 11,
61: RELAY_FORW = 12,
62: RELAY_REPL = 13
63:
64: };
65: enum DHCP6_OPTIONS{
66: CLIENTID = 1,
67: SERVERID = 2,
68: IA_NA = 3,
69: IA_TA = 4,
70: IAADDR = 5,
71: ORO = 6,
72: PREFERENCE = 7,
73: ELAPSED_TIME = 8,
74: RELAY_MSG = 9,
75: AUTH = 11,
76: UNICAST = 12,
77: STATUS_CODE = 13,
78: RAPID_COMMIT = 14,
79: USER_CLASS = 15,
80: VENDOR_CLASS = 16,
81: VENDOR_OPTS = 17,
82: INTERFACE_ID = 18,
83: RECONF_MSG = 19,
84: RECONF_ACCEPT = 20,
85: };
86:
87: enum DHCP6_STATUSES{
88: SUCCESS = 0,
89: UNSPEC_FAIL = 1,
90: NOADDR_AVAIL=2,
91: NO_BINDING = 3,
92: NOT_ON_LINK = 4,
93: USE_MULTICAST =5
94: };
95: static struct option longopts[] = {
96: {"ip", required_argument, 0, 'a'},
97: {"server-id", required_argument, 0, 's'},
98: {"client-id", required_argument, 0, 'c'},
99: {"iface", required_argument, 0, 'n'},
100: {"iaid", required_argument, 0, 'i'},
101: {"dry-run", no_argument, 0, 'd'},
102: {"help", no_argument, 0, 'h'},
103: {0, 0, 0, 0}
104: };
105:
106: const short DHCP6_CLIENT_PORT = 546;
107: const short DHCP6_SERVER_PORT = 547;
108:
109: const char* DHCP6_MULTICAST_ADDRESS = "ff02::1:2";
110:
111: struct dhcp6_option{
112: uint16_t type;
113: uint16_t len;
114: char value[1024];
115: };
116:
117: struct dhcp6_iaaddr_option{
118: uint16_t type;
119: uint16_t len;
120: struct in6_addr ip;
121: uint32_t preferred_lifetime;
122: uint32_t valid_lifetime;
123:
124:
125: };
126:
127: struct dhcp6_iana_option{
128: uint16_t type;
129: uint16_t len;
130: uint32_t iaid;
131: uint32_t t1;
132: uint32_t t2;
133: char options[1024];
134: };
135:
136:
137: struct dhcp6_packet{
138: size_t len;
139: char buf[2048];
140:
141: } ;
142:
143: size_t pack_duid(const char* str, char* dst){
144:
145: char* tmp = strdup(str);
146: char* tmp_to_free = tmp;
147: char *ptr;
148: uint8_t write_pos = 0;
149: while ((ptr = strtok (tmp, ":"))) {
150: dst[write_pos] = (uint8_t) strtol(ptr, NULL, 16);
151: write_pos += 1;
152: tmp = NULL;
153:
154: }
155: free(tmp_to_free);
156: return write_pos;
157: }
158:
159: struct dhcp6_option create_client_id_option(const char* duid){
160: struct dhcp6_option option;
161: option.type = htons(CLIENTID);
162: bzero(option.value, sizeof(option.value));
163: option.len = htons(pack_duid(duid, option.value));
164: return option;
165: }
166:
167: struct dhcp6_option create_server_id_option(const char* duid){
168: struct dhcp6_option option;
169: option.type = htons(SERVERID);
170: bzero(option.value, sizeof(option.value));
171: option.len = htons(pack_duid(duid, option.value));
172: return option;
173: }
174:
175: struct dhcp6_iaaddr_option create_iaadr_option(const char* ip){
176: struct dhcp6_iaaddr_option result;
177: result.type =htons(IAADDR);
178: /* no suboptions needed here, so length is 24 */
179: result.len = htons(24);
180: result.preferred_lifetime = 0;
181: result.valid_lifetime = 0;
182: int s = inet_pton(AF_INET6, ip, &(result.ip));
183: if (s <= 0) {
184: if (s == 0)
185: fprintf(stderr, "Not in presentation format");
186: else
187: perror("inet_pton");
188: exit(EXIT_FAILURE);
189: }
190: return result;
191: }
192: struct dhcp6_iana_option create_iana_option(const char * iaid, struct dhcp6_iaaddr_option ia_addr){
193: struct dhcp6_iana_option result;
194: result.type = htons(IA_NA);
195: result.iaid = htonl(atoi(iaid));
196: result.t1 = 0;
197: result.t2 = 0;
198: result.len = htons(12 + ntohs(ia_addr.len) + 2 * sizeof(uint16_t));
199: memcpy(result.options, &ia_addr, ntohs(ia_addr.len) + 2 * sizeof(uint16_t));
200: return result;
201: }
202:
203: struct dhcp6_packet create_release_packet(const char* iaid, const char* ip, const char* client_id, const char* server_id){
204: struct dhcp6_packet result;
205: bzero(result.buf, sizeof(result.buf));
206: /* message_type */
207: result.buf[0] = RELEASE;
208: /* tx_id */
209: bzero(result.buf+1, 3);
210:
211: struct dhcp6_option client_option = create_client_id_option(client_id);
212: struct dhcp6_option server_option = create_server_id_option(server_id);
213: struct dhcp6_iaaddr_option iaaddr_option = create_iaadr_option(ip);
214: struct dhcp6_iana_option iana_option = create_iana_option(iaid, iaaddr_option);
215: int offset = 4;
216: memcpy(result.buf + offset, &client_option, ntohs(client_option.len) + 2*sizeof(uint16_t));
217: offset += (ntohs(client_option.len)+ 2 *sizeof(uint16_t) );
218: memcpy(result.buf + offset, &server_option, ntohs(server_option.len) + 2*sizeof(uint16_t) );
219: offset += (ntohs(server_option.len)+ 2* sizeof(uint16_t));
220: memcpy(result.buf + offset, &iana_option, ntohs(iana_option.len) + 2*sizeof(uint16_t) );
221: offset += (ntohs(iana_option.len)+ 2* sizeof(uint16_t));
222: result.len = offset;
223: return result;
224: }
225:
226: uint16_t parse_iana_suboption(char* buf, size_t len){
227: size_t current_pos = 0;
228: char option_value[1024];
229: while (current_pos < len) {
230: uint16_t option_type, option_len;
231: memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
232: memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
233: option_type = ntohs(option_type);
234: option_len = ntohs(option_len);
235: current_pos += 2 * sizeof(uint16_t);
236: if (option_type == STATUS_CODE){
237: uint16_t status;
238: memcpy(&status, buf + current_pos, sizeof(uint16_t));
239: status = ntohs(status);
240: if (status != SUCCESS){
241: memcpy(option_value, buf + current_pos + sizeof(uint16_t) , option_len - sizeof(uint16_t));
242: option_value[option_len-sizeof(uint16_t)] ='\0';
243: fprintf(stderr, "Error: %s\n", option_value);
244: }
245: return status;
246: }
247: }
248: return -2;
249: }
250:
251: int16_t parse_packet(char* buf, size_t len){
252: uint8_t type = buf[0];
253: /*skipping tx id. you need it, uncomment following line
254: uint16_t tx_id = ntohs((buf[1] <<16) + (buf[2] <<8) + buf[3]);
255: */
256: size_t current_pos = 4;
257: if (type != REPLY ){
258: return NOT_REPLY_CODE;
259: }
260: char option_value[1024];
261: while (current_pos < len) {
262: uint16_t option_type, option_len;
263: memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
264: memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
265: option_type = ntohs(option_type);
266: option_len = ntohs(option_len);
267: current_pos += 2 * sizeof(uint16_t);
268: if (option_type == STATUS_CODE){
269: uint16_t status;
270: memcpy(&status, buf + current_pos, sizeof(uint16_t));
271: status = ntohs(status);
272: if (status != SUCCESS){
273: memcpy(option_value, buf + current_pos +sizeof(uint16_t) , option_len -sizeof(uint16_t));
274: fprintf(stderr, "Error: %d %s\n", status, option_value);
275: return status;
276: }
277:
278: }
279: if (option_type == IA_NA ){
280: uint16_t result = parse_iana_suboption(buf + current_pos +24, option_len -24);
281: if (result){
282: return result;
283: }
284: }
285: current_pos += option_len;
286:
287: }
288: return -1;
289: }
290:
291: void usage(const char* arg, FILE* stream){
292: const char* usage_string ="--ip IPv6 --iface IFACE --server-id SERVER_ID --client-id CLIENT_ID --iaid IAID [--dry-run] | --help";
293: fprintf (stream, "Usage: %s %s\n", arg, usage_string);
294:
295: }
296:
297: int send_release_packet(const char* iface, struct dhcp6_packet* packet){
298:
299: struct sockaddr_in6 server_addr, client_addr;
300: char response[1400];
301: int sock = socket(PF_INET6, SOCK_DGRAM, 0);
302: int i = 0;
303: if (sock < 0) {
304: perror("creating socket");
305: return -1;
306: }
307: if (setsockopt(sock, SOL_SOCKET, 25, iface, strlen(iface)) == -1) {
308: perror("SO_BINDTODEVICE");
309: close(sock);
310: return -1;
311: }
312: memset(&server_addr, 0, sizeof(server_addr));
313: server_addr.sin6_family = AF_INET6;
314: client_addr.sin6_family = AF_INET6;
315: client_addr.sin6_port = htons(DHCP6_CLIENT_PORT);
316: client_addr.sin6_flowinfo = 0;
317: client_addr.sin6_scope_id =0;
318: inet_pton(AF_INET6, "::", &client_addr.sin6_addr);
319: bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6));
320: inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr);
321: server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
322: int16_t recv_size = 0;
323: for (i = 0; i < 5; i++) {
324: if (sendto(sock, packet->buf, packet->len, 0,
325: (struct sockaddr *)&server_addr,
326: sizeof(server_addr)) < 0) {
327: perror("sendto failed");
328: exit(4);
329: }
330: recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
331: if (recv_size == -1){
332: if (errno == EAGAIN){
333: sleep(1);
334: continue;
335: }else {
336: perror("recvfrom");
337: }
338: }
339: int16_t result = parse_packet(response, recv_size);
340: if (result == NOT_REPLY_CODE){
341: sleep(1);
342: continue;
343: }
344: return result;
345: }
346: fprintf(stderr, "Response timed out\n");
347: return -1;
348:
349: }
350:
351:
352: int main(int argc, char * const argv[]) {
353: const char* UNINITIALIZED = "";
354: const char* iface = UNINITIALIZED;
355: const char* ip = UNINITIALIZED;
356: const char* client_id = UNINITIALIZED;
357: const char* server_id = UNINITIALIZED;
358: const char* iaid = UNINITIALIZED;
359: int dry_run = 0;
360: while (1) {
361: int option_index = 0;
362: int c = getopt_long(argc, argv, "a:s:c:n:i:hd", longopts, &option_index);
363: if (c == -1){
364: break;
365: }
366: switch(c){
367: case 0:
368: if (longopts[option_index].flag !=0){
369: break;
370: }
371: printf ("option %s", longopts[option_index].name);
372: if (optarg)
373: printf (" with arg %s", optarg);
374: printf ("\n");
375: break;
376: case 'i':
377: iaid = optarg;
378: break;
379: case 'n':
380: iface = optarg;
381: break;
382: case 'a':
383: ip = optarg;
384: break;
385: case 'c':
386: client_id = optarg;
387: break;
388: case 'd':
389: dry_run = 1;
390: break;
391: case 's':
392: server_id = optarg;
393: break;
394: case 'h':
395: usage(argv[0], stdout);
396: return 0;
397: case '?':
398: usage(argv[0], stderr);
399: return -1;
400: default:
401: abort();
402:
403: }
404:
405: }
406: if (iaid == UNINITIALIZED){
407: fprintf(stderr, "Missing required iaid parameter\n");
408: usage(argv[0], stderr);
409: return -1;
410: }
411: if (server_id == UNINITIALIZED){
412: fprintf(stderr, "Missing required server-id parameter\n");
413: usage(argv[0], stderr);
414: return -1;
415: }
416: if (client_id == UNINITIALIZED){
417: fprintf(stderr, "Missing required client-id parameter\n");
418: usage(argv[0], stderr);
419: return -1;
420: }
421: if (ip == UNINITIALIZED){
422: fprintf(stderr, "Missing required ip parameter\n");
423: usage(argv[0], stderr);
424: return -1;
425: }
426: if (iface == UNINITIALIZED){
427: fprintf(stderr, "Missing required iface parameter\n");
428: usage(argv[0], stderr);
429: return -1;
430: }
431:
432:
433:
434: struct dhcp6_packet packet = create_release_packet(iaid, ip, client_id, server_id);
435: if (dry_run){
436: uint16_t i;
437: for(i=0;i<packet.len;i++){
438: printf("%hhx", packet.buf[i]);
439: }
440: printf("\n");
441: return 0;
442: }
443: return send_release_packet(iface, &packet);
444:
445: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>