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

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

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