Annotation of embedaddon/dnsmasq/contrib/lease-tools/dhcp_release6.c, revision 1.1.1.3

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>