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>