Return to dhcp_release6.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / contrib / lease-tools |
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: }