Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 1 | /* |
| 2 | dhcp_release6 --iface <interface> --client-id <client-id> --server-id |
| 3 | server-id --iaid <iaid> --ip <IP> [--dry-run] [--help] |
Josh Soref | 730c674 | 2017-02-06 16:14:04 +0000 | [diff] [blame] | 4 | MUST be run as root - will fail otherwise |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 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 | |
klemens | 43517fc | 2017-02-19 15:53:37 +0000 | [diff] [blame] | 23 | IP is an IPv6 address to release |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 24 | |
Josh Soref | 730c674 | 2017-02-06 16:14:04 +0000 | [diff] [blame] | 25 | If --dry-run is specified, dhcp_release6 just prints hexadecimal representation of |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 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 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 49 | enum DHCP6_TYPES |
| 50 | { |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 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 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 65 | }; |
| 66 | |
| 67 | enum DHCP6_OPTIONS |
| 68 | { |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 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, |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 88 | }; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 89 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 90 | enum DHCP6_STATUSES |
| 91 | { |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 92 | SUCCESS = 0, |
| 93 | UNSPEC_FAIL = 1, |
| 94 | NOADDR_AVAIL=2, |
| 95 | NO_BINDING = 3, |
| 96 | NOT_ON_LINK = 4, |
| 97 | USE_MULTICAST =5 |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 98 | }; |
| 99 | |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 100 | static struct option longopts[] = { |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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 } |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 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 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 116 | struct dhcp6_option { |
| 117 | uint16_t type; |
| 118 | uint16_t len; |
| 119 | char value[1024]; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 120 | }; |
| 121 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 128 | }; |
| 129 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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]; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 137 | }; |
| 138 | |
| 139 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 140 | struct dhcp6_packet { |
| 141 | size_t len; |
| 142 | char buf[2048]; |
| 143 | }; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 144 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 156 | } |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 157 | |
| 158 | free(tmp_to_free); |
| 159 | return write_pos; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 160 | } |
| 161 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 169 | } |
| 170 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 178 | } |
| 179 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 198 | } |
| 199 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 210 | } |
| 211 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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); |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 258 | } |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 259 | return status; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 260 | } |
| 261 | } |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 262 | |
| 263 | return -2; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 264 | } |
| 265 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 266 | int16_t parse_packet(char* buf, size_t len) |
| 267 | { |
Simon Kelley | a6cee69 | 2017-12-14 22:40:48 +0000 | [diff] [blame] | 268 | int16_t ret = -1; |
| 269 | uint8_t type = buf[0]; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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 | } |
Simon Kelley | a6cee69 | 2017-12-14 22:40:48 +0000 | [diff] [blame] | 297 | |
| 298 | /* Got success status, return that if there's no specific error in an IA_NA. */ |
| 299 | ret = SUCCESS; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 300 | } |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 301 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 310 | } |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 311 | |
Simon Kelley | a6cee69 | 2017-12-14 22:40:48 +0000 | [diff] [blame] | 312 | return ret; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 313 | } |
| 314 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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); |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 319 | } |
| 320 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 331 | } |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 332 | |
| 333 | if (setsockopt(sock, SOL_SOCKET, 25, iface, strlen(iface)) == -1) |
| 334 | { |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 335 | perror("SO_BINDTODEVICE"); |
| 336 | close(sock); |
| 337 | return -1; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 338 | } |
| 339 | |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 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; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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"); |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 356 | exit(4); |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 357 | } |
| 358 | |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 359 | recv_size = recvfrom(sock, response, sizeof(response), MSG_DONTWAIT, NULL, 0); |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 360 | if (recv_size == -1) |
| 361 | { |
| 362 | if (errno == EAGAIN) |
| 363 | { |
| 364 | sleep(1); |
| 365 | continue; |
| 366 | } |
| 367 | else |
| 368 | { |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 369 | perror("recvfrom"); |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 370 | } |
| 371 | } |
| 372 | |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 373 | int16_t result = parse_packet(response, recv_size); |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 374 | if (result == NOT_REPLY_CODE) |
| 375 | { |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 376 | sleep(1); |
| 377 | continue; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 378 | } |
Petr Menšík | 2b38e38 | 2018-08-17 10:20:05 +0200 | [diff] [blame] | 379 | |
| 380 | close(sock); |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 381 | return result; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 382 | } |
Petr Menšík | 2b38e38 | 2018-08-17 10:20:05 +0200 | [diff] [blame] | 383 | |
| 384 | close(sock); |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 385 | fprintf(stderr, "Response timed out\n"); |
| 386 | return -1; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 387 | } |
| 388 | |
| 389 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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; |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 405 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 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 | } |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 446 | } |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 447 | |
| 448 | if (iaid == UNINITIALIZED) |
| 449 | { |
| 450 | fprintf(stderr, "Missing required iaid parameter\n"); |
| 451 | usage(argv[0], stderr); |
| 452 | return -1; |
Sergey Nechaev | 45cb8dd | 2016-05-14 21:36:15 +0100 | [diff] [blame] | 453 | } |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 454 | |
| 455 | if (server_id == UNINITIALIZED) |
| 456 | { |
Sergey Nechaev | 45cb8dd | 2016-05-14 21:36:15 +0100 | [diff] [blame] | 457 | fprintf(stderr, "Missing required server-id parameter\n"); |
| 458 | usage(argv[0], stderr); |
| 459 | return -1; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 460 | } |
| 461 | |
| 462 | if (client_id == UNINITIALIZED) |
| 463 | { |
Sergey Nechaev | 45cb8dd | 2016-05-14 21:36:15 +0100 | [diff] [blame] | 464 | fprintf(stderr, "Missing required client-id parameter\n"); |
| 465 | usage(argv[0], stderr); |
| 466 | return -1; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 467 | } |
| 468 | |
| 469 | if (ip == UNINITIALIZED) |
| 470 | { |
Sergey Nechaev | 45cb8dd | 2016-05-14 21:36:15 +0100 | [diff] [blame] | 471 | fprintf(stderr, "Missing required ip parameter\n"); |
| 472 | usage(argv[0], stderr); |
| 473 | return -1; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 474 | } |
| 475 | |
| 476 | if (iface == UNINITIALIZED) |
| 477 | { |
| 478 | fprintf(stderr, "Missing required iface parameter\n"); |
Sergey Nechaev | 45cb8dd | 2016-05-14 21:36:15 +0100 | [diff] [blame] | 479 | usage(argv[0], stderr); |
| 480 | return -1; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 481 | } |
Sergey Nechaev | 45cb8dd | 2016-05-14 21:36:15 +0100 | [diff] [blame] | 482 | |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 483 | |
| 484 | |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 485 | struct dhcp6_packet packet = create_release_packet(iaid, ip, client_id, server_id); |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 486 | |
| 487 | if (dry_run) |
| 488 | { |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 489 | uint16_t i; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 490 | |
| 491 | for(i=0; i<packet.len; i++) |
| 492 | printf("%hhx", packet.buf[i]); |
| 493 | |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 494 | printf("\n"); |
| 495 | return 0; |
Simon Kelley | 0039920 | 2017-12-14 22:29:31 +0000 | [diff] [blame] | 496 | } |
| 497 | |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 498 | return send_release_packet(iface, &packet); |
Simon Kelley | 69cbf78 | 2016-05-03 21:33:38 +0100 | [diff] [blame] | 499 | } |