You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

515 lines
13 KiB

2 years ago
  1. /*
  2. * Copyright 2008, The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <dirent.h>
  17. #include <errno.h>
  18. #include <poll.h>
  19. #include <netinet/in.h>
  20. #include <stdarg.h>
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <sys/select.h>
  25. #include <sys/socket.h>
  26. #include <sys/time.h>
  27. #include <sys/types.h>
  28. #include <net/if.h>
  29. #include <time.h>
  30. #include <unistd.h>
  31. #include <net/if.h>
  32. #include "../ifutils.h"
  33. #include "dhcpmsg.h"
  34. #include "packet.h"
  35. #define VERBOSE 2
  36. static int verbose = 1;
  37. static char errmsg[2048];
  38. typedef unsigned long long msecs_t;
  39. #if VERBOSE
  40. void dump_dhcp_msg();
  41. #endif
  42. msecs_t get_msecs(void)
  43. {
  44. struct timespec ts;
  45. if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
  46. return 0;
  47. } else {
  48. return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) +
  49. (((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000));
  50. }
  51. }
  52. void printerr(char *fmt, ...)
  53. {
  54. va_list ap;
  55. va_start(ap, fmt);
  56. vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
  57. va_end(ap);
  58. printf("%s\n", errmsg);
  59. }
  60. const char *dhcp_lasterror()
  61. {
  62. return errmsg;
  63. }
  64. int fatal(const char *reason)
  65. {
  66. printerr("%s: %s\n", reason, strerror(errno));
  67. return -1;
  68. // exit(1);
  69. }
  70. typedef struct dhcp_info dhcp_info;
  71. struct dhcp_info {
  72. uint32_t type;
  73. uint32_t ipaddr;
  74. uint32_t gateway;
  75. uint32_t prefixLength;
  76. uint32_t dns1;
  77. uint32_t dns2;
  78. uint32_t serveraddr;
  79. uint32_t lease;
  80. };
  81. dhcp_info last_good_info;
  82. void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength,
  83. uint32_t *dns1, uint32_t *dns2, uint32_t *server,
  84. uint32_t *lease)
  85. {
  86. *ipaddr = last_good_info.ipaddr;
  87. *gateway = last_good_info.gateway;
  88. *prefixLength = last_good_info.prefixLength;
  89. *dns1 = last_good_info.dns1;
  90. *dns2 = last_good_info.dns2;
  91. *server = last_good_info.serveraddr;
  92. *lease = last_good_info.lease;
  93. }
  94. static int dhcp_configure(const char *ifname, dhcp_info *info)
  95. {
  96. last_good_info = *info;
  97. return if_set_network_v4(ifname, info->ipaddr, info->prefixLength, info->gateway,
  98. info->dns1, info->dns2);
  99. }
  100. static const char *dhcp_type_to_name(uint32_t type)
  101. {
  102. switch(type) {
  103. case DHCPDISCOVER: return "discover";
  104. case DHCPOFFER: return "offer";
  105. case DHCPREQUEST: return "request";
  106. case DHCPDECLINE: return "decline";
  107. case DHCPACK: return "ack";
  108. case DHCPNAK: return "nak";
  109. case DHCPRELEASE: return "release";
  110. case DHCPINFORM: return "inform";
  111. default: return "???";
  112. }
  113. }
  114. void dump_dhcp_info(dhcp_info *info)
  115. {
  116. char addr[20], gway[20];
  117. printf("--- dhcp %s (%d) ---\n",
  118. dhcp_type_to_name(info->type), info->type);
  119. strcpy(addr, ipaddr_to_string_v4(info->ipaddr));
  120. strcpy(gway, ipaddr_to_string_v4(info->gateway));
  121. printf("ip %s gw %s prefixLength %d\n", addr, gway, info->prefixLength);
  122. if (info->dns1) printf("dns1: %s\n", ipaddr_to_string_v4(info->dns1));
  123. if (info->dns2) printf("dns2: %s\n", ipaddr_to_string_v4(info->dns2));
  124. printf("server %s, lease %d seconds\n",
  125. ipaddr_to_string_v4(info->serveraddr), info->lease);
  126. }
  127. int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info)
  128. {
  129. uint8_t *x;
  130. unsigned int opt;
  131. int optlen;
  132. memset(info, 0, sizeof(dhcp_info));
  133. if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
  134. len -= (DHCP_MSG_FIXED_SIZE + 4);
  135. if (msg->options[0] != OPT_COOKIE1) return -1;
  136. if (msg->options[1] != OPT_COOKIE2) return -1;
  137. if (msg->options[2] != OPT_COOKIE3) return -1;
  138. if (msg->options[3] != OPT_COOKIE4) return -1;
  139. x = msg->options + 4;
  140. while (len > 2) {
  141. opt = *x++;
  142. if (opt == OPT_PAD) {
  143. len--;
  144. continue;
  145. }
  146. if (opt == OPT_END) {
  147. break;
  148. }
  149. optlen = *x++;
  150. len -= 2;
  151. if (optlen > len) {
  152. break;
  153. }
  154. switch(opt) {
  155. case OPT_SUBNET_MASK:
  156. if (optlen >= 4) {
  157. in_addr_t mask;
  158. memcpy(&mask, x, 4);
  159. info->prefixLength = mask_to_prefix_v4(mask);
  160. }
  161. break;
  162. case OPT_GATEWAY:
  163. if (optlen >= 4) memcpy(&info->gateway, x, 4);
  164. break;
  165. case OPT_DNS:
  166. if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
  167. if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
  168. break;
  169. case OPT_LEASE_TIME:
  170. if (optlen >= 4) {
  171. memcpy(&info->lease, x, 4);
  172. info->lease = ntohl(info->lease);
  173. }
  174. break;
  175. case OPT_SERVER_ID:
  176. if (optlen >= 4) memcpy(&info->serveraddr, x, 4);
  177. break;
  178. case OPT_MESSAGE_TYPE:
  179. info->type = *x;
  180. break;
  181. default:
  182. break;
  183. }
  184. x += optlen;
  185. len -= optlen;
  186. }
  187. info->ipaddr = msg->yiaddr;
  188. return 0;
  189. }
  190. #if VERBOSE
  191. static void hex2str(char *buf, size_t buf_size, const unsigned char *array, int len)
  192. {
  193. int i;
  194. char *cp = buf;
  195. char *buf_end = buf + buf_size;
  196. for (i = 0; i < len; i++) {
  197. cp += snprintf(cp, buf_end - cp, " %02x ", array[i]);
  198. }
  199. }
  200. void dump_dhcp_msg(dhcp_msg *msg, int len)
  201. {
  202. unsigned char *x;
  203. unsigned int n,c;
  204. int optsz;
  205. const char *name;
  206. char buf[2048];
  207. if (len < DHCP_MSG_FIXED_SIZE) {
  208. printf("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE);
  209. return;
  210. }
  211. len -= DHCP_MSG_FIXED_SIZE;
  212. if (msg->op == OP_BOOTREQUEST)
  213. name = "BOOTREQUEST";
  214. else if (msg->op == OP_BOOTREPLY)
  215. name = "BOOTREPLY";
  216. else
  217. name = "????";
  218. c = msg->hlen > 16 ? 16 : msg->hlen;
  219. hex2str(buf, sizeof(buf), msg->chaddr, c);
  220. for (n = 0; n < 64; n++) {
  221. unsigned char x = msg->sname[n];
  222. if ((x < ' ') || (x > 127)) {
  223. if (x == 0) break;
  224. msg->sname[n] = '.';
  225. }
  226. }
  227. msg->sname[63] = 0;
  228. for (n = 0; n < 128; n++) {
  229. unsigned char x = msg->file[n];
  230. if ((x < ' ') || (x > 127)) {
  231. if (x == 0) break;
  232. msg->file[n] = '.';
  233. }
  234. }
  235. msg->file[127] = 0;
  236. if (len < 4) return;
  237. len -= 4;
  238. x = msg->options + 4;
  239. while (len > 2) {
  240. if (*x == 0) {
  241. x++;
  242. len--;
  243. continue;
  244. }
  245. if (*x == OPT_END) {
  246. break;
  247. }
  248. len -= 2;
  249. optsz = x[1];
  250. if (optsz > len) break;
  251. if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) {
  252. if ((unsigned int)optsz < sizeof(buf) - 1) {
  253. n = optsz;
  254. } else {
  255. n = sizeof(buf) - 1;
  256. }
  257. memcpy(buf, &x[2], n);
  258. buf[n] = '\0';
  259. } else {
  260. hex2str(buf, sizeof(buf), &x[2], optsz);
  261. }
  262. if (x[0] == OPT_MESSAGE_TYPE)
  263. name = dhcp_type_to_name(x[2]);
  264. else
  265. name = NULL;
  266. len -= optsz;
  267. x = x + optsz + 2;
  268. }
  269. }
  270. #endif
  271. static int send_message(int sock, int if_index, dhcp_msg *msg, int size)
  272. {
  273. #if VERBOSE > 1
  274. dump_dhcp_msg(msg, size);
  275. #endif
  276. return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST,
  277. PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER);
  278. }
  279. static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz)
  280. {
  281. if (sz < DHCP_MSG_FIXED_SIZE) {
  282. if (verbose) printf("Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
  283. return 0;
  284. }
  285. if (reply->op != OP_BOOTREPLY) {
  286. if (verbose) printf("Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
  287. return 0;
  288. }
  289. if (reply->xid != msg->xid) {
  290. if (verbose) printf("Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid),
  291. ntohl(msg->xid));
  292. return 0;
  293. }
  294. if (reply->htype != msg->htype) {
  295. if (verbose) printf("Wrong Htype %d != %d\n", reply->htype, msg->htype);
  296. return 0;
  297. }
  298. if (reply->hlen != msg->hlen) {
  299. if (verbose) printf("Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
  300. return 0;
  301. }
  302. if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {
  303. if (verbose) printf("Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
  304. return 0;
  305. }
  306. return 1;
  307. }
  308. #define STATE_SELECTING 1
  309. #define STATE_REQUESTING 2
  310. #define TIMEOUT_INITIAL 4000
  311. #define TIMEOUT_MAX 32000
  312. int dhcp_init_ifc(const char *ifname)
  313. {
  314. dhcp_msg discover_msg;
  315. dhcp_msg request_msg;
  316. dhcp_msg reply;
  317. dhcp_msg *msg;
  318. dhcp_info info;
  319. int s, r, size;
  320. int valid_reply;
  321. uint32_t xid;
  322. unsigned char hwaddr[6];
  323. struct pollfd pfd;
  324. unsigned int state;
  325. unsigned int timeout;
  326. int if_index;
  327. xid = (uint32_t) get_msecs();
  328. if (if_get_hwaddr(ifname, hwaddr)) {
  329. return fatal("cannot obtain interface address");
  330. }
  331. if ((if_index = if_nametoindex(ifname)) == 0) {
  332. return fatal("cannot obtain interface index");
  333. }
  334. s = open_raw_socket(ifname, hwaddr, if_index);
  335. timeout = TIMEOUT_INITIAL;
  336. state = STATE_SELECTING;
  337. info.type = 0;
  338. goto transmit;
  339. for (;;) {
  340. pfd.fd = s;
  341. pfd.events = POLLIN;
  342. pfd.revents = 0;
  343. r = poll(&pfd, 1, timeout);
  344. if (r == 0) {
  345. #if VERBOSE
  346. printerr("TIMEOUT\n");
  347. #endif
  348. if (timeout >= TIMEOUT_MAX) {
  349. printerr("timed out\n");
  350. if ( info.type == DHCPOFFER ) {
  351. printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);
  352. return dhcp_configure(ifname, &info);
  353. }
  354. errno = ETIME;
  355. close(s);
  356. return -1;
  357. }
  358. timeout = timeout * 2;
  359. transmit:
  360. size = 0;
  361. msg = NULL;
  362. switch(state) {
  363. case STATE_SELECTING:
  364. msg = &discover_msg;
  365. size = init_dhcp_discover_msg(msg, hwaddr, xid);
  366. break;
  367. case STATE_REQUESTING:
  368. msg = &request_msg;
  369. size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);
  370. break;
  371. default:
  372. r = 0;
  373. }
  374. if (size != 0) {
  375. r = send_message(s, if_index, msg, size);
  376. if (r < 0) {
  377. printerr("error sending dhcp msg: %s\n", strerror(errno));
  378. }
  379. }
  380. continue;
  381. }
  382. if (r < 0) {
  383. if ((errno == EAGAIN) || (errno == EINTR)) {
  384. continue;
  385. }
  386. return fatal("poll failed");
  387. }
  388. errno = 0;
  389. r = receive_packet(s, &reply);
  390. if (r < 0) {
  391. if (errno != 0) {
  392. printf("receive_packet failed (%d): %s", r, strerror(errno));
  393. if (errno == ENETDOWN || errno == ENXIO) {
  394. return -1;
  395. }
  396. }
  397. continue;
  398. }
  399. #if VERBOSE > 1
  400. dump_dhcp_msg(&reply, r);
  401. #endif
  402. decode_dhcp_msg(&reply, r, &info);
  403. if (state == STATE_SELECTING) {
  404. valid_reply = is_valid_reply(&discover_msg, &reply, r);
  405. } else {
  406. valid_reply = is_valid_reply(&request_msg, &reply, r);
  407. }
  408. if (!valid_reply) {
  409. printerr("invalid reply\n");
  410. continue;
  411. }
  412. if (verbose) dump_dhcp_info(&info);
  413. switch(state) {
  414. case STATE_SELECTING:
  415. if (info.type == DHCPOFFER) {
  416. state = STATE_REQUESTING;
  417. timeout = TIMEOUT_INITIAL;
  418. xid++;
  419. goto transmit;
  420. }
  421. break;
  422. case STATE_REQUESTING:
  423. if (info.type == DHCPACK) {
  424. printerr("configuring %s\n", ifname);
  425. close(s);
  426. return dhcp_configure(ifname, &info);
  427. } else if (info.type == DHCPNAK) {
  428. printerr("configuration request denied\n");
  429. close(s);
  430. return -1;
  431. } else {
  432. printerr("ignoring %s message in state %d\n",
  433. dhcp_type_to_name(info.type), state);
  434. }
  435. break;
  436. }
  437. }
  438. close(s);
  439. return 0;
  440. }
  441. int do_dhcp(char *iname)
  442. {
  443. if (if_set_addr_v4(iname, 0, 32)) {
  444. printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno));
  445. return -1;
  446. }
  447. if (if_link_up(iname)) {
  448. printerr("failed to bring up interface %s: %s\n", iname, strerror(errno));
  449. return -1;
  450. }
  451. return dhcp_init_ifc(iname);
  452. }