perfdhcp.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. /*
  2. * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  3. *
  4. * Permission to use, copy, modify, and/or distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  9. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  10. * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  12. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  13. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  14. * PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include <stdio.h>
  17. #include <unistd.h>
  18. #include <unistd.h>
  19. #include <arpa/inet.h>
  20. #include <net/if_arp.h>
  21. #include <ifaddrs.h>
  22. #include <strings.h>
  23. #include <string.h>
  24. #include <stdarg.h>
  25. #include <netdb.h>
  26. #include <errno.h>
  27. #include <time.h>
  28. #include "perfdhcp.h"
  29. #include "cloptions.h"
  30. #include "dkdebug.h"
  31. struct duid {
  32. uint16_t duid_type;
  33. uint16_t htype;
  34. unsigned char hwaddr[6];
  35. };
  36. struct addrinfo *getaddr(int addr_fam, const char *hostname, const char *port);
  37. char *addrName(const struct sockaddr_storage *addr, char *buf, size_t bufsize);
  38. void add_option(int v6, unsigned optnum, unsigned count,
  39. size_t size, int direct, unsigned char options[], size_t *buffer_used,
  40. ...);
  41. int get_linklocal_addr(const char if_name[], struct sockaddr_storage *addr);
  42. const char *optionName(int v6, unsigned optnum);
  43. const unsigned char *find_option(const struct dhcp_packet *pkt, int search_opt);
  44. int socket_setup(int addr_fam, const char *localAddr, const char *port,
  45. const char *type, struct sockaddr_storage *l_addr);
  46. void gen_discover(struct dhcp_packet *discover_pkt, const struct in_addr *giaddr);
  47. void gen_request(struct dhcp_packet *dhcp_pkt, const struct in_addr *giaddr,
  48. const struct in_addr *yiaddr, const unsigned char *server_id);
  49. void dhcp_recv(int v6, void *msg, int recv_fd, const struct sockaddr_storage *recv_laddr);
  50. void dora(const char *server, const char *localAddr);
  51. void sarr(const char *server, const char *if_name);
  52. void print_addrinfo(FILE *f, const struct addrinfo *addr);
  53. void print_sa6_info(FILE *f, const struct sockaddr_in6 *sa);
  54. void gen_solicit(struct dhcpv6_packet *dhcp_pkt, const struct duid *client_id);
  55. void dhcp_send(int v6, const unsigned char *msg, int send_fd, const struct
  56. sockaddr *r_addr, const struct sockaddr_storage *send_laddr);
  57. static const struct dkdesc diagLetters[] = {
  58. { 's', DK_SOCK },
  59. { 'm', DK_MSG },
  60. { 'p', DK_PACKET },
  61. { 'a', DK_ALL },
  62. { '\0', 0 }
  63. };
  64. int
  65. main(int argc, const char *argv[])
  66. {
  67. int ret;
  68. int v6;
  69. // int initialOnly;
  70. const char *localName = NULL;
  71. // unsigned rate;
  72. // unsigned numRequest;
  73. const char *server;
  74. const char *diagSelector = "";
  75. confdata_t confdata;
  76. if ((ret = procArgs(argc, argv, &confdata, &server)) != 1)
  77. exit(ret);
  78. v6 = confdata.map['6']->num > 0;
  79. if (confdata.map['l']->num > 0)
  80. localName = confdata.map['l']->values[0]->value.string;
  81. if (confdata.map['x']->num > 0)
  82. diagSelector = confdata.map['x']->values[0]->value.string;
  83. srand(time(NULL));
  84. if (v6)
  85. sarr(server, localName);
  86. else
  87. dora(server, localName);
  88. dk_setup(diagSelector, diagLetters);
  89. exit(0);
  90. }
  91. /*
  92. * Create a socket for communication with dhcp server:
  93. * - Create socket
  94. * - Bind it to given local address and port, UDP.
  95. *
  96. * Input variables:
  97. * addr_fam is the address family to use, e.g. AF_INET
  98. * localAddr is the local address to bind to.
  99. * port is the port to bind to.
  100. * type is a string giving the purpose of the socket, for verbose output.
  101. * If localAddr is null, the local address etc. is taken from l_addr.
  102. *
  103. * Output variables:
  104. * Socket details are stored in l_addr.
  105. *
  106. * Return value: The network fd.
  107. */
  108. int
  109. socket_setup(int addr_fam, const char *localAddr, const char *port,
  110. const char *type, struct sockaddr_storage *l_addr)
  111. {
  112. char addrbuf[ADDR_NAME_BUFSIZE];
  113. int net_fd;
  114. struct addrinfo *addrs;
  115. if ((addrs = getaddr(addr_fam, localAddr, port)) == NULL) {
  116. fprintf(stderr, "No addresses for %s\n", localAddr);
  117. exit(1);
  118. }
  119. if (localAddr == NULL) {
  120. if (dk_set(DK_SOCK)) {
  121. fprintf(stderr, "local address:\n");
  122. print_sa6_info(stderr, (struct sockaddr_in6 *)l_addr);
  123. }
  124. memcpy(&((struct sockaddr_in6 *)addrs->ai_addr)->sin6_addr,
  125. &((struct sockaddr_in6 *)l_addr)->sin6_addr,
  126. sizeof(struct in6_addr));
  127. ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_flowinfo =
  128. ((struct sockaddr_in6 *)l_addr)->sin6_flowinfo;
  129. ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_scope_id =
  130. ((struct sockaddr_in6 *)l_addr)->sin6_scope_id;
  131. }
  132. if (dk_set(DK_SOCK)) {
  133. print_addrinfo(stderr, addrs);
  134. fprintf(stderr, "Creating socket from addrinfo:\n");
  135. print_addrinfo(stderr, addrs);
  136. }
  137. net_fd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
  138. if (net_fd < 0) {
  139. perror("socket");
  140. exit(1);
  141. }
  142. if (bind(net_fd, addrs->ai_addr, addrs->ai_addrlen) == -1) {
  143. int s_errno = errno;
  144. fprintf(stderr, "Could not bind to %s: %s\n",
  145. addrName((struct sockaddr_storage *)addrs->ai_addr, addrbuf,
  146. sizeof(addrbuf)), strerror(s_errno));
  147. exit(1);
  148. }
  149. dkprintf(DK_SOCK, "%s fd %d bound to %s\n", type, net_fd, addrName((struct sockaddr_storage *)addrs->ai_addr, addrbuf, sizeof(addrbuf)));
  150. memcpy(l_addr, addrs->ai_addr, sizeof(struct sockaddr_storage));
  151. freeaddrinfo(addrs);
  152. return net_fd;
  153. }
  154. /*
  155. * gen_discover: Generate a DHCP discover packet.
  156. *
  157. * Input variables:
  158. * giaddr is the address to be copied into the giaddr (gateway addr) element.
  159. *
  160. * Output variables:
  161. * discover_packet is a pointer to storage for the packet to be generated.
  162. */
  163. void
  164. gen_discover(struct dhcp_packet *discover_pkt, const struct in_addr *giaddr)
  165. {
  166. size_t options_len;
  167. bzero((char *) discover_pkt, sizeof(struct dhcp_packet));
  168. discover_pkt->op = BOOTREQUEST;
  169. discover_pkt->htype = HTYPE_ETHER;
  170. discover_pkt->hlen = 6;
  171. discover_pkt->hops = 1;
  172. discover_pkt->xid = 0x12345678; /* transaction id - fix */
  173. discover_pkt->secs = 0;
  174. discover_pkt->flags = 0;
  175. memcpy(&discover_pkt->giaddr, giaddr, sizeof((*discover_pkt).giaddr));
  176. strncpy((char *)discover_pkt->chaddr, "\x12\x34\x56\x78\x9a\xbc", 6); /* client hardware addr - fix */
  177. memset(discover_pkt->options, DHO_PAD, DHCP_MAX_OPTION_LEN);
  178. strncpy((char *)discover_pkt->options, "\x63\x82\x53\x63", 4); /* magic cookie */
  179. options_len = 4;
  180. add_option(0, DHO_DHCP_MESSAGE_TYPE, 1, 1, 1, discover_pkt->options, &options_len,
  181. DHCPDISCOVER);
  182. add_option(0, DHO_DHCP_PARAMETER_REQUEST_LIST, 4, 1, 1, discover_pkt->options,
  183. &options_len, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME,
  184. DHO_DOMAIN_NAME_SERVERS);
  185. add_option(0, DHO_DHCP_LEASE_TIME, 1, 4, 1, discover_pkt->options, &options_len,
  186. htonl(60));
  187. if (options_len < DHCP_MAX_OPTION_LEN)
  188. discover_pkt->options[options_len++] = DHO_END;
  189. }
  190. /*
  191. * gen_request(): Generate a DHCPv4 request packet.
  192. *
  193. * Input variables
  194. * giaddr is the address to copy into the giaddr element.
  195. * yiaddr is the address to store in the DHO_DHCP_REQUESTED_ADDRESS option.
  196. * server_id is the ID to store in the DHO_DHCP_SERVER_IDENTIFIER option.
  197. *
  198. * Output variables:
  199. * dhcp_pkt points to storage for the packet to be generated.
  200. */
  201. void
  202. gen_request(struct dhcp_packet *dhcp_pkt, const struct in_addr *giaddr,
  203. const struct in_addr *yiaddr, const unsigned char *server_id)
  204. {
  205. size_t options_len;
  206. bzero((char *) dhcp_pkt, sizeof(struct dhcp_packet));
  207. dhcp_pkt->op = BOOTREQUEST;
  208. dhcp_pkt->htype = HTYPE_ETHER;
  209. dhcp_pkt->hlen = 6;
  210. dhcp_pkt->hops = 1;
  211. dhcp_pkt->xid = 0x12345678; /* transaction id - fix */
  212. dhcp_pkt->secs = 0;
  213. dhcp_pkt->flags = 0;
  214. memcpy(&dhcp_pkt->giaddr, giaddr, sizeof((*dhcp_pkt).giaddr));
  215. /*memcpy(&dhcp_pkt->yiaddr, yiaddr, sizeof((*dhcp_pkt).yiaddr));*/
  216. strncpy((char *)dhcp_pkt->chaddr, "\x12\x34\x56\x78\x9a\xbc", 6); /* client hardware addr - fix */
  217. memset(dhcp_pkt->options, DHO_PAD, DHCP_MAX_OPTION_LEN);
  218. strncpy((char *)dhcp_pkt->options, "\x63\x82\x53\x63", 4); /* magic cookie */
  219. options_len = 4;
  220. add_option(0, DHO_DHCP_MESSAGE_TYPE, 1, 1, 1, dhcp_pkt->options, &options_len,
  221. DHCPREQUEST);
  222. add_option(0, DHO_DHCP_SERVER_IDENTIFIER, 1, 4, 0, dhcp_pkt->options,
  223. &options_len, server_id);
  224. add_option(0, DHO_DHCP_REQUESTED_ADDRESS, 1, 4, 1, dhcp_pkt->options,
  225. &options_len, *yiaddr);
  226. add_option(0, DHO_DHCP_LEASE_TIME, 1, 4, 1, dhcp_pkt->options, &options_len,
  227. htonl(60));
  228. if (options_len < DHCP_MAX_OPTION_LEN)
  229. dhcp_pkt->options[options_len++] = DHO_END;
  230. }
  231. /*
  232. * dhcp_send: Send a DHCP packet.
  233. * If the send fails, the program exits with an error message.
  234. *
  235. * Input variables:
  236. * dhcp_packet: DHCP message to send.
  237. * send_fd: Socket to send message on.
  238. * r_addr: Remote address to send message to.
  239. * send_laddr: Local address of socket, for informational messages only.
  240. */
  241. void
  242. dhcp_send(int v6, const unsigned char *msg, int send_fd, const struct sockaddr *r_addr,
  243. const struct sockaddr_storage *send_laddr) {
  244. size_t num_octets;
  245. ssize_t num_written;
  246. char addrbuf[ADDR_NAME_BUFSIZE];
  247. char addrbuf2[ADDR_NAME_BUFSIZE];
  248. num_octets = v6 ? sizeof(struct dhcpv6_packet) : sizeof(struct dhcp_packet);
  249. if (dk_set(DK_MSG)) {
  250. fprintf(stderr, "Sending %zu octets to socket fd %u, local %s remote %s",
  251. num_octets, send_fd,
  252. addrName((struct sockaddr_storage *)send_laddr, addrbuf, sizeof(addrbuf)),
  253. addrName((struct sockaddr_storage *)r_addr, addrbuf2, sizeof(addrbuf2)));
  254. fprintf(stderr, "Packet contents:\n");
  255. print_dhcp_packet(v6, msg, num_octets);
  256. }
  257. num_written = sendto(send_fd, msg, num_octets, 0,
  258. r_addr, sizeof(struct sockaddr_storage));
  259. if (num_written < 0) {
  260. int s_errno = errno;
  261. fprintf(stderr, "Send failed: %s\n", strerror(s_errno));
  262. exit(1);
  263. }
  264. if ((size_t) num_written != num_octets) {
  265. fprintf(stderr, "Only %d of %u octets written\n", (int) num_written, (unsigned) num_octets);
  266. }
  267. }
  268. /*
  269. * dhcp_recv: Receive a DHCP packet.
  270. *
  271. * Input variables:
  272. * recv_fd is the socket to receive on.
  273. * recv_laddr is the socket's address, solely for informational messages.
  274. *
  275. * Output variables:
  276. * msg points to storage for the received message.
  277. */
  278. void
  279. dhcp_recv(int v6, void *msg, int recv_fd,
  280. const struct sockaddr_storage *recv_laddr) {
  281. ssize_t num_octets;
  282. struct sockaddr_storage sourceAddr;
  283. socklen_t addrSize;
  284. char addrbuf[ADDR_NAME_BUFSIZE];
  285. dkprintf(DK_SOCK, "Waiting for response on socket fd %u, %s",
  286. recv_fd,
  287. addrName(recv_laddr, addrbuf, sizeof(addrbuf)));
  288. addrSize = sizeof(sourceAddr);
  289. num_octets = recvfrom(recv_fd, msg, v6 ? sizeof(struct dhcpv6_packet) : sizeof(struct dhcp_packet), 0, (struct sockaddr *)&sourceAddr, &addrSize);
  290. /* TODO: check for recvfrom failure status here */
  291. if (dk_set(DK_MSG)) {
  292. fprintf(stderr, "Got %zd octets from fd %u, %s", num_octets, recv_fd,
  293. addrName(&sourceAddr, addrbuf, sizeof(addrbuf)));
  294. fprintf(stderr, "Received packet contents:\n");
  295. print_dhcp_packet(v6, msg, num_octets);
  296. }
  297. }
  298. void
  299. dora(const char *server, const char *localAddr)
  300. {
  301. struct sockaddr_storage send_laddr, recv_laddr;
  302. struct dhcp_packet discover_pkt, offer_pkt, request_pkt, ack_pkt;
  303. int send_fd, recv_fd;
  304. const unsigned char *type, *server_id;
  305. aaddr_buf a_yiaddr;
  306. struct addrinfo *remote;
  307. struct in_addr *local_address;
  308. send_fd = socket_setup(AF_INET, localAddr, "bootpc", "Send", &send_laddr);
  309. recv_fd = socket_setup(AF_INET, localAddr, "bootps", "Recv", &recv_laddr);
  310. if ((remote = getaddr(AF_INET, server, "bootps")) == NULL) {
  311. fprintf(stderr, "No addresses for %s\n", server);
  312. exit(1);
  313. }
  314. local_address = &((struct sockaddr_in *)&send_laddr)->sin_addr;
  315. gen_discover(&discover_pkt, local_address);
  316. dhcp_send(0, (unsigned char *)&discover_pkt, send_fd, remote->ai_addr, &send_laddr);
  317. dhcp_recv(0, &offer_pkt, recv_fd, &recv_laddr);
  318. type = find_option(&offer_pkt, DHO_DHCP_MESSAGE_TYPE);
  319. if (type == NULL) {
  320. fprintf(stderr, "DHCP reponse did not include message type option\n");
  321. exit(1);
  322. }
  323. if (type[2] != DHCPOFFER) {
  324. fprintf(stderr, "DHCP reponse had message type %d; expecting DHCPOFFER\n", type[2]);
  325. exit(1);
  326. }
  327. server_id = find_option(&offer_pkt, DHO_DHCP_SERVER_IDENTIFIER);
  328. if (type == NULL) {
  329. fprintf(stderr, "DHCP reponse did not include server identifier option\n");
  330. exit(1);
  331. }
  332. server_id += 2;
  333. printf("Server identifier: %08x\n", ntohl(*(int *)server_id));
  334. printf("Offered address: %s\n", addrtoa(AF_INET, &offer_pkt.yiaddr, a_yiaddr));
  335. gen_request(&request_pkt, local_address, &offer_pkt.yiaddr, server_id);
  336. dhcp_send(0, (unsigned char *)&request_pkt, send_fd, remote->ai_addr, &send_laddr);
  337. dhcp_recv(0, &ack_pkt, recv_fd, &recv_laddr);
  338. }
  339. /*
  340. client 546, server 547
  341. All_DHCP_Relay_Agents_and_Servers FF02::1:2
  342. DHCPV6_SOLICIT
  343. DHCPV6_ADVERTISE
  344. DHCPV6_REQUEST
  345. DHCPV6_REPLY
  346. DUID option must be present
  347. A client SHOULD generate a random number that cannot easily be guessed or
  348. predicted to use as the transaction ID for each new message it sends
  349. Discard ADVERTISE messages in which the Client Identifier option does not match the
  350. client's DUID, or in which the transaction ID does not match the SOLICIT transaction ID.
  351. Discard REPLY messsages in whhich the "transaction-id" field in the message does not match the value
  352. used in the original message.
  353. */
  354. void
  355. sarr(const char *server, const char *if_name)
  356. {
  357. struct sockaddr_storage send_laddr, recv_laddr;
  358. struct dhcpv6_packet solicit_pkt, advertise_pkt;
  359. int send_fd, recv_fd;
  360. struct in6_addr *local_address;
  361. struct addrinfo *remote;
  362. struct duid client_id;
  363. get_linklocal_addr(if_name, &send_laddr);
  364. memcpy(&recv_laddr, &send_laddr, sizeof(recv_laddr));
  365. send_fd = socket_setup(AF_INET6, NULL, "546", "Send", &send_laddr);
  366. /*
  367. recv_fd = socket_setup(AF_INET6, NULL, "547", "Recv", &recv_laddr);
  368. */
  369. recv_fd = send_fd;
  370. if (server != NULL) {
  371. if (strcmp(server, "all"))
  372. server = All_DHCP_Relay_Agents_and_Servers;
  373. else if (strcmp(server, "server"))
  374. server = All_DHCP_Servers;
  375. }
  376. if ((remote = getaddr(AF_INET6, server, "547")) == NULL) {
  377. fprintf(stderr, "Conversion failed for %s\n", server);
  378. exit(1);
  379. }
  380. local_address = &((struct sockaddr_in6 *)&send_laddr)->sin6_addr;
  381. client_id.duid_type = htons(DUID_LL);
  382. client_id.htype = htons(HTYPE_ETHER);
  383. memset(client_id.hwaddr, 0xA, 6); /* TEMPORARY - FIX */
  384. gen_solicit(&solicit_pkt, &client_id);
  385. dhcp_send(1, (unsigned char *)&solicit_pkt, send_fd, remote->ai_addr, &send_laddr);
  386. dhcp_recv(1, &advertise_pkt, recv_fd, &recv_laddr);
  387. /*
  388. *
  389. * type = find_option(&offer_pkt, DHO_DHCP_MESSAGE_TYPE);
  390. * if (type == NULL) {
  391. * fprintf(stderr, "DHCP reponse did not include message type option\n");
  392. * exit(1);
  393. * }
  394. * if (type[2] != DHCPOFFER) {
  395. * fprintf(stderr, "DHCP reponse had message type %d; expecting DHCPOFFER\n", type[2]);
  396. * exit(1);
  397. * }
  398. * server_id = find_option(&offer_pkt, DHO_DHCP_SERVER_IDENTIFIER);
  399. * if (type == NULL) {
  400. * fprintf(stderr, "DHCP reponse did not include server identifier option\n");
  401. * exit(1);
  402. * }
  403. * server_id += 2;
  404. * printf("Server identifier: %08x\n", ntohl(*(int *)server_id));
  405. * printf("Offered address: %s\n", addrtoa(AF_INET, &offer_pkt.yiaddr, a_yiaddr));
  406. * gen_request(&request_pkt, local_address, &offer_pkt.yiaddr, server_id);
  407. * dhcp_send(&request_pkt, send_fd, remote->ai_addr, &send_laddr);
  408. * dhcp_recv(&ack_pkt, recv_fd, &recv_laddr);
  409. */
  410. }
  411. /*
  412. * Must include client identifier, server identifier, IA, DUID?
  413. * Solitication: Create an IA. Assign it an IAID. Transmit a Solicit
  414. * message containing an IA option describing the IA.
  415. * Use IA_TA to request temporary addresses
  416. */
  417. void
  418. gen_solicit(struct dhcpv6_packet *dhcp_pkt, const struct duid *client_id)
  419. {
  420. int tid;
  421. int i;
  422. size_t options_len = 0;
  423. bzero((char *) dhcp_pkt, sizeof(struct dhcpv6_packet));
  424. dhcp_pkt->msg_type = DHCPV6_SOLICIT;
  425. tid = rand();
  426. for (i = 0; i < 2; i++) {
  427. dhcp_pkt->transaction_id[i] = (unsigned char)tid;
  428. tid >>= 8;
  429. }
  430. add_option(1, D6O_CLIENTID, 1, sizeof(struct duid), 0, dhcp_pkt->options,
  431. &options_len, client_id);
  432. add_option(1, D6O_IA_TA, 1, 4, 1, dhcp_pkt->options,
  433. &options_len, "0xabcd"); /* Temporary - FIX */
  434. /* D60_ORO: Option Request Option */
  435. add_option(1, D6O_ORO, 1, 2, 1, dhcp_pkt->options,
  436. &options_len, D6O_NAME_SERVERS);
  437. }
  438. /*
  439. * Add an option to a DHCP packet.
  440. *
  441. * Input variables:
  442. * If buffer_size is nonzero, options are added to a DHCP6 packet, and
  443. * buffer_size specifies the size of the buffer. If it is zero, options
  444. * are added to a DHCP4 packet.
  445. * optnum is the option number.
  446. * count is the number of option parameters passed.
  447. * size is the size of each parameter, in bytes.
  448. * direct is true if the options are passed by value, false if they are passed
  449. * by address.
  450. *
  451. * Output variables:
  452. * options[] is the buffer in which to store the option.
  453. *
  454. * Input/output variables:
  455. * buffer_used is the amount of buffer space currently used.
  456. */
  457. void
  458. add_option(int v6, unsigned optnum, unsigned count, size_t size,
  459. int direct, unsigned char options[], size_t *buffer_used, ...)
  460. {
  461. va_list ap;
  462. unsigned i;
  463. size_t buffer_size = v6 ? sizeof(struct dhcpv6_packet) : DHCP_MAX_OPTION_LEN;
  464. if ((*buffer_used + (v6 ? 4 : 2) + count * size) > buffer_size) {
  465. fprintf(stderr, "%s: Insufficient option space\n", progName);
  466. exit(1);
  467. }
  468. if (v6) {
  469. struct v6_option *opt = (struct v6_option *)&options[(*buffer_used)];
  470. opt->code = htons(optnum);
  471. opt->len = htons(count * size);
  472. *buffer_used += 4;
  473. }
  474. else {
  475. options[(*buffer_used)++] = optnum;
  476. options[(*buffer_used)++] = count * size;
  477. }
  478. va_start(ap,buffer_used);
  479. for (i = 1; i <= count; i++) {
  480. if (direct) {
  481. int value = va_arg(ap, int);
  482. memcpy(&options[*buffer_used], (char *)&value, size);
  483. }
  484. else {
  485. char *p = va_arg(ap, char *);
  486. memcpy(&options[*buffer_used], p, size);
  487. }
  488. (*buffer_used) += size;
  489. }
  490. /* ap */
  491. va_end(ap);
  492. }
  493. /*
  494. * Return value:
  495. * buf is returned.
  496. */
  497. char *
  498. addrtoa(int addr_fam, const struct in_addr *addr, aaddr_buf buf)
  499. {
  500. if (inet_ntop(addr_fam, addr, buf, ADDR_NAME_BUFSIZE) == NULL)
  501. strcpy(buf, "untranslatable");
  502. return buf;
  503. }
  504. /*
  505. * getaddr: generate an addrinfo list for a given hostname and port, UDP.
  506. * If getaddrinfo() fails with the provided information, an error message
  507. * is printed and the program exits with status 2.
  508. *
  509. * Input variables:
  510. * hostname: The host name to look up. This can be either a name or an IPv4
  511. * dotted-quad address, or null to not fill in the address.
  512. * port: The port to include in addrinfo. This can be either a service name or
  513. * an ASCII decimal number, or null to not fill in the port number.
  514. *
  515. * Globals:
  516. * progName, for error messages.
  517. *
  518. * Return value:
  519. * A pointer to the addrinfo list. This must be freed by the caller with
  520. * freeaddrinfo().
  521. */
  522. struct addrinfo *
  523. getaddr(int addr_fam, const char *hostname, const char *port)
  524. {
  525. struct addrinfo *ai;
  526. struct addrinfo hints;
  527. int ret;
  528. memset (&hints, '\0', sizeof(hints));
  529. hints.ai_family = addr_fam;
  530. hints.ai_socktype = SOCK_DGRAM;
  531. hints.ai_protocol = IPPROTO_UDP;
  532. if ((ret = getaddrinfo(hostname, port, &hints, &ai)) != 0) {
  533. fprintf(stderr, "%s: %s: getaddrinfo: %s/%s\n", progName,
  534. hostname == NULL ? "" : hostname, port == NULL ? "" : port, gai_strerror(ret));
  535. exit(2);
  536. }
  537. return ai;
  538. }
  539. /*
  540. * addrName(): Convert the address and port associated with a socket into an a
  541. * hostname and numeric string and store them in a buffer.
  542. *
  543. * Input variables:
  544. * addr is the socket to operate on.
  545. * bufsize is the size of the buffer.
  546. *
  547. * Output variables:
  548. * name is the buffer to store in.
  549. *
  550. * Return value:
  551. * buf is returned.
  552. */
  553. char *
  554. addrName(const struct sockaddr_storage *addr, char *name, size_t bufsize)
  555. {
  556. char *buf = name;
  557. char servbuf[30];
  558. if (getnameinfo((struct sockaddr *)addr, sizeof(struct sockaddr_storage),
  559. name, bufsize, servbuf, 30, 0) != 0)
  560. strncpy(buf, "untranslatable", bufsize-1);
  561. else {
  562. size_t len = strlen(buf);
  563. if (len < bufsize)
  564. snprintf(name + len, bufsize - len, " port %s", servbuf);
  565. }
  566. return buf;
  567. }
  568. /*
  569. *
  570. * Input variables:
  571. * if_name is the name of the interface to search for.
  572. *
  573. * Output variables:
  574. * The link-local address for the interface is stored in addr.
  575. *
  576. * Return value:
  577. * 1 on success, 0 if no link-local address is found.
  578. *
  579. * If retrieval of the interface address list fails, an error message is
  580. * printed and the program is exited with status 2.
  581. */
  582. int
  583. get_linklocal_addr(const char if_name[], struct sockaddr_storage *addr)
  584. {
  585. struct ifaddrs *ifaddr, *ifa;
  586. if (getifaddrs(&ifaddr) == -1) {
  587. fprintf(stderr, "%s: Could not get interface addresses: %s\n",
  588. progName, strerror(errno));
  589. exit(2);
  590. }
  591. for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
  592. if (ifa->ifa_addr->sa_family == AF_INET6 && strcmp(ifa->ifa_name, if_name) == 0 &&
  593. (ntohs(((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr.__in6_u.__u6_addr16[0]) & 0xffc0) == 0xfe80)
  594. break;
  595. }
  596. if (ifa != NULL)
  597. memcpy(addr, ifa->ifa_addr, sizeof(struct sockaddr_storage));
  598. freeifaddrs(ifaddr);
  599. return ifa != NULL;
  600. }
  601. void
  602. print_addrinfo(FILE *f, const struct addrinfo *addr)
  603. {
  604. fprintf(f, "Addrinfo:\n");
  605. fprintf(f, "flags: 0x%x; family: %d; socktype: %d; proto: %d;\n",
  606. addr->ai_flags, addr->ai_family, addr->ai_socktype, addr->ai_protocol);
  607. fprintf(f, "addrlen: %u; addr: %p; canonname: %s; next: %p\n",
  608. addr->ai_addrlen, addr->ai_addr, addr->ai_canonname, addr->ai_next);
  609. if (addr->ai_family == AF_INET6)
  610. print_sa6_info(f, (struct sockaddr_in6 *)addr->ai_addr);
  611. }
  612. void
  613. print_sa6_info(FILE *f, const struct sockaddr_in6 *sa)
  614. {
  615. char addrbuf[ADDR_NAME_BUFSIZE];
  616. fprintf(f, "IPv6 sockaddr info:\n");
  617. fprintf(f, "family: %u; flowinfo: 0x%x; scope-id: %u addr: %s\n",
  618. sa->sin6_family, sa->sin6_flowinfo, sa->sin6_scope_id,
  619. addrName((struct sockaddr_storage *)sa, addrbuf, sizeof(addrbuf)));
  620. }
  621. /*
  622. * Search for a specific option in the options stored in a DHCP packet.
  623. *
  624. * Input variables:
  625. * pkt is the packet to search.
  626. * search_opt is the option number to search for.
  627. *
  628. * Return value:
  629. * If the packet contains the option, a pointer to its start (the option
  630. * number) is returned. If not, NULL is returned.
  631. */
  632. const unsigned char *
  633. find_option(const struct dhcp_packet *pkt, int search_opt)
  634. {
  635. const unsigned char *p;
  636. p = &pkt->options[4];
  637. while ((p - pkt->options) < DHCP_MAX_OPTION_LEN && *p != DHO_END) {
  638. if (*p == search_opt)
  639. return p;
  640. else if (*p == DHO_PAD)
  641. p++;
  642. else {
  643. size_t len = p[1];
  644. p += 2 + len;
  645. }
  646. }
  647. return NULL;
  648. }