Annotation of embedaddon/dnsmasq/contrib/lease-tools/dhcp_release6.c, revision 1.1.1.2
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:
! 321: int send_release_packet(const char* iface, struct dhcp6_packet* packet)
! 322: {
! 323: struct sockaddr_in6 server_addr, client_addr;
! 324: char response[1400];
! 325: int sock = socket(PF_INET6, SOCK_DGRAM, 0);
! 326: int i = 0;
! 327: if (sock < 0)
! 328: {
! 329: perror("creating socket");
! 330: return -1;
! 331: }
! 332:
! 333: if (setsockopt(sock, SOL_SOCKET, 25, iface, strlen(iface)) == -1)
! 334: {
1.1 misho 335: perror("SO_BINDTODEVICE");
336: close(sock);
337: return -1;
1.1.1.2 ! misho 338: }
! 339:
1.1 misho 340: memset(&server_addr, 0, sizeof(server_addr));
341: server_addr.sin6_family = AF_INET6;
342: client_addr.sin6_family = AF_INET6;
343: client_addr.sin6_port = htons(DHCP6_CLIENT_PORT);
344: client_addr.sin6_flowinfo = 0;
345: client_addr.sin6_scope_id =0;
346: inet_pton(AF_INET6, "::", &client_addr.sin6_addr);
347: bind(sock, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in6));
348: inet_pton(AF_INET6, DHCP6_MULTICAST_ADDRESS, &server_addr.sin6_addr);
349: server_addr.sin6_port = htons(DHCP6_SERVER_PORT);
350: int16_t recv_size = 0;
1.1.1.2 ! misho 351: for (i = 0; i < 5; i++)
! 352: {
! 353: if (sendto(sock, packet->buf, packet->len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
! 354: {
! 355: perror("sendto failed");
1.1 misho 356: exit(4);
1.1.1.2 ! misho 357: }
! 358:
1.1 misho 359: recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0);
1.1.1.2 ! misho 360: if (recv_size == -1)
! 361: {
! 362: if (errno == EAGAIN)
! 363: {
! 364: sleep(1);
! 365: continue;
! 366: }
! 367: else
! 368: {
1.1 misho 369: perror("recvfrom");
1.1.1.2 ! misho 370: }
! 371: }
! 372:
1.1 misho 373: int16_t result = parse_packet(response, recv_size);
1.1.1.2 ! misho 374: if (result == NOT_REPLY_CODE)
! 375: {
1.1 misho 376: sleep(1);
377: continue;
1.1.1.2 ! misho 378: }
! 379:
! 380: close(sock);
1.1 misho 381: return result;
1.1.1.2 ! misho 382: }
! 383:
! 384: close(sock);
1.1 misho 385: fprintf(stderr, "Response timed out\n");
1.1.1.2 ! misho 386: return -1;
1.1 misho 387: }
388:
389:
1.1.1.2 ! misho 390: int main(int argc, char * const argv[])
! 391: {
! 392: const char* UNINITIALIZED = "";
! 393: const char* iface = UNINITIALIZED;
! 394: const char* ip = UNINITIALIZED;
! 395: const char* client_id = UNINITIALIZED;
! 396: const char* server_id = UNINITIALIZED;
! 397: const char* iaid = UNINITIALIZED;
! 398: int dry_run = 0;
! 399: while (1)
! 400: {
! 401: int option_index = 0;
! 402: int c = getopt_long(argc, argv, "a:s:c:n:i:hd", longopts, &option_index);
! 403: if (c == -1)
! 404: break;
1.1 misho 405:
1.1.1.2 ! misho 406: switch(c)
! 407: {
! 408: case 0:
! 409: if (longopts[option_index].flag !=0)
! 410: break;
! 411:
! 412: printf ("option %s", longopts[option_index].name);
! 413: if (optarg)
! 414: printf (" with arg %s", optarg);
! 415: printf ("\n");
! 416: break;
! 417:
! 418: case 'i':
! 419: iaid = optarg;
! 420: break;
! 421: case 'n':
! 422: iface = optarg;
! 423: break;
! 424: case 'a':
! 425: ip = optarg;
! 426: break;
! 427: case 'c':
! 428: client_id = optarg;
! 429: break;
! 430: case 'd':
! 431: dry_run = 1;
! 432: break;
! 433: case 's':
! 434: server_id = optarg;
! 435: break;
! 436: case 'h':
! 437: usage(argv[0], stdout);
! 438: return 0;
! 439: case '?':
! 440: usage(argv[0], stderr);
! 441: return -1;
! 442: default:
! 443: abort();
! 444:
! 445: }
1.1 misho 446: }
1.1.1.2 ! misho 447:
! 448: if (iaid == UNINITIALIZED)
! 449: {
! 450: fprintf(stderr, "Missing required iaid parameter\n");
! 451: usage(argv[0], stderr);
! 452: return -1;
! 453: }
! 454:
! 455: if (server_id == UNINITIALIZED)
! 456: {
1.1 misho 457: fprintf(stderr, "Missing required server-id parameter\n");
458: usage(argv[0], stderr);
459: return -1;
1.1.1.2 ! misho 460: }
! 461:
! 462: if (client_id == UNINITIALIZED)
! 463: {
1.1 misho 464: fprintf(stderr, "Missing required client-id parameter\n");
465: usage(argv[0], stderr);
466: return -1;
1.1.1.2 ! misho 467: }
! 468:
! 469: if (ip == UNINITIALIZED)
! 470: {
1.1 misho 471: fprintf(stderr, "Missing required ip parameter\n");
472: usage(argv[0], stderr);
473: return -1;
1.1.1.2 ! misho 474: }
! 475:
! 476: if (iface == UNINITIALIZED)
! 477: {
! 478: fprintf(stderr, "Missing required iface parameter\n");
1.1 misho 479: usage(argv[0], stderr);
480: return -1;
1.1.1.2 ! misho 481: }
1.1 misho 482:
1.1.1.2 ! misho 483:
! 484:
1.1 misho 485: struct dhcp6_packet packet = create_release_packet(iaid, ip, client_id, server_id);
1.1.1.2 ! misho 486:
! 487: if (dry_run)
! 488: {
1.1 misho 489: uint16_t i;
1.1.1.2 ! misho 490:
! 491: for(i=0; i<packet.len; i++)
! 492: printf("%hhx", packet.buf[i]);
! 493:
1.1 misho 494: printf("\n");
495: return 0;
1.1.1.2 ! misho 496: }
! 497:
1.1 misho 498: return send_release_packet(iface, &packet);
499: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>