Annotation of embedaddon/dnsmasq/contrib/lease-tools/dhcp_release6.c, revision 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>