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>