aoeping.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /* aoeping.c - userland aoe pinger
  2. * Copyright 2007, Coraid, Inc., and licensed under GPL v.2.
  3. *
  4. * run without arguments for usage
  5. */
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <stdlib.h>
  9. #include <unistd.h>
  10. #include <stdint.h>
  11. #include <ctype.h>
  12. #include <netinet/in.h>
  13. #include <linux/hdreg.h>
  14. #include <errno.h>
  15. #include "dat.h"
  16. #include "fns.h"
  17. struct progopts {
  18. int shelf;
  19. int slot;
  20. char *netif;
  21. int verbose;
  22. int timeout;
  23. u32 tag;
  24. char *smart;
  25. char ata_ident;
  26. char pp_ataid; /* pretty print ATA device identify response */
  27. };
  28. static struct progopts defaults = {
  29. .shelf = 0,
  30. .slot = 0,
  31. .netif = NULL,
  32. .verbose = 0,
  33. .timeout = 0,
  34. .tag = 0,
  35. .smart = NULL,
  36. .ata_ident = 0,
  37. .pp_ataid = 0,
  38. };
  39. static struct progopts opts;
  40. struct smartcmd {
  41. char *name; /* subcommand name from ATA spec */
  42. int cmd; /* for features register */
  43. char data; /* does this subcommand xfer data? */
  44. };
  45. static struct smartcmd smarts[] = {
  46. { "read_data", 0xd0, SmartDataRet },
  47. // { "attr_autosave", 0xd2, 0 }, (unsupported b/c it overloads sector count)
  48. { "offline_immediate", 0xd4, 0 },
  49. { "read_log", 0xd5, SmartDataRet },
  50. { "write_log", 0xd6, SmartDataPut },
  51. { "enable", 0xd8, 0 },
  52. { "disable", 0xd9, 0 },
  53. { "return_status", 0xda, 0 },
  54. };
  55. static char *progname;
  56. static int sfd; /* raw socket file descriptor */
  57. static uchar mac[6];
  58. #if 0
  59. void
  60. show_opts(struct progopts *opts)
  61. {
  62. printf("shelf: %d\n", opts->shelf);
  63. printf("slot: %d\n", opts->slot);
  64. printf("netif: %s\n", opts->netif);
  65. printf("verbose: %d\n", opts->verbose);
  66. printf("timeout: %d\n", opts->timeout);
  67. printf("tag: %d\n", opts->tag);
  68. printf("ata_ident: %d\n", (int) opts->ata_ident);
  69. }
  70. #endif
  71. void
  72. usage(void)
  73. {
  74. fprintf(stderr,
  75. "usage:\t%s [options] {shelf} {slot} {netif}\n",
  76. progname);
  77. fprintf(stderr, "%s\n\t%s\n\t%s\n\t%s\n\t%s\n",
  78. "options:",
  79. "-i\tdo ATA device identify dump as raw hex",
  80. "-I\tdo ATA device identify print fields",
  81. "-v\tbe verbose",
  82. "-h\tshow this usage summary");
  83. fprintf(stderr, "%s\n\t%s\n\t%s\n\t%s\n",
  84. "options taking arguments:",
  85. "-s\ttimeout in seconds",
  86. "-S\tperform SMART command",
  87. "-t\tspecify number for starting AoE tag");
  88. }
  89. void
  90. hex_print(FILE *out, uchar *buf, int n, char *sep)
  91. {
  92. int i;
  93. int per_line = 16;
  94. for (i = 0; i < n;) {
  95. fprintf(out, "%02x%s", *buf++ & 0xff, sep);
  96. if (!(++i % per_line))
  97. putc('\n', out);
  98. }
  99. }
  100. void
  101. find_blade(Conf *c, struct progopts *opts)
  102. {
  103. int n;
  104. uchar buf[1400];
  105. u32 tag;
  106. Aoehdr *h = &c->h;
  107. memset(h, 0, sizeof *h);
  108. memset(h->dst, 0xff, sizeof h->dst);
  109. memmove(h->src, mac, sizeof h->src);
  110. h->type = htons(AOE_ETH_PROTO);
  111. h->flags = AoEver << 4;
  112. h->maj = htons(opts->shelf);
  113. h->min = opts->slot;
  114. h->cmd = Config;
  115. tag = htonl(opts->tag);
  116. memmove(h->tag, &tag, sizeof h->tag);
  117. c->bufcnt = 3;
  118. c->vercmd = 0x10 | Qread;
  119. memset(c->data, 0xED, sizeof c->data);
  120. c->len = htons(1024);
  121. if (write(sfd, c, sizeof *c) == -1) {
  122. perror("send config query");
  123. exit(EXIT_FAILURE);
  124. }
  125. for (;;) {
  126. n = read(sfd, buf, sizeof buf);
  127. if (n < 0) {
  128. perror("read network");
  129. exit(EXIT_FAILURE);
  130. }
  131. if (n < 60)
  132. continue;
  133. h = (Aoehdr *) buf;
  134. if (ntohs(h->type) != AOE_ETH_PROTO
  135. || ntohs(h->maj) != opts->shelf
  136. || h->min != opts->slot
  137. || memcmp(h->tag, &tag, sizeof h->tag))
  138. continue;
  139. break;
  140. }
  141. if (opts->verbose) {
  142. puts("config query response:");
  143. hex_print(stdout, buf, n, " ");
  144. putchar('\n');
  145. }
  146. memcpy(c, buf, sizeof *c);
  147. }
  148. /* read a packet that was sent by the device that returned *c earlier
  149. */
  150. int
  151. aoe_pkt_read(uchar *buf, size_t siz, Conf *c, u32 tag)
  152. {
  153. Aoehdr *h;
  154. int n;
  155. tag = htonl(tag);
  156. for (;;) {
  157. n = read(sfd, buf, siz);
  158. if (n < 0) {
  159. perror("read network");
  160. exit(EXIT_FAILURE);
  161. }
  162. if (n < 60)
  163. continue;
  164. h = (Aoehdr *) buf;
  165. if (ntohs(h->type) != AOE_ETH_PROTO
  166. || h->maj != c->h.maj
  167. || h->min != c->h.min
  168. || memcmp(&tag, h->tag, sizeof h->tag))
  169. continue;
  170. break;
  171. }
  172. return n;
  173. }
  174. /* prepare a packet for doing ATA to a device that gave us Conf *c
  175. */
  176. void
  177. ata_prep(Ata *a, Conf *c, u32 tag)
  178. {
  179. memset(a, 0, sizeof *a);
  180. memcpy(a->h.dst, c->h.src, sizeof a->h.dst);
  181. memcpy(a->h.src, mac, sizeof a->h.src);
  182. a->h.type = htons(AOE_ETH_PROTO);
  183. a->h.flags = AoEver << 4;
  184. a->h.maj = c->h.maj;
  185. a->h.min = c->h.min;
  186. a->h.cmd = ATAcmd;
  187. tag = htonl(tag);
  188. memmove(a->h.tag, &tag, sizeof a->h.tag);
  189. }
  190. /* pretty print ATA device identify text field
  191. * bytes have already been swapped
  192. */
  193. void
  194. pp_idtext(char *prefix, unsigned char *p, size_t len)
  195. {
  196. int i;
  197. fputs(prefix, stdout);
  198. for (i = 0; i < len; ++i, ++p) {
  199. if (*p == '\0')
  200. break;
  201. if (!isgraph((int) *p) && *p != ' ')
  202. break;
  203. putchar(*p);
  204. }
  205. putchar('\n');
  206. }
  207. void
  208. disk_identify(Conf *c, u32 tag)
  209. {
  210. int n;
  211. uchar buf[1400];
  212. Ata a;
  213. Ata *p;
  214. struct hd_driveid *id;
  215. ata_prep(&a, c, tag);
  216. a.sectors = 1;
  217. a.cmd = ATAid_dev;
  218. a.lba[3] = 0xa0;
  219. if (write(sfd, &a, sizeof a) == -1) {
  220. perror("send ATA identify device");
  221. exit(EXIT_FAILURE);
  222. }
  223. n = aoe_pkt_read(buf, sizeof buf, c, tag);
  224. p = (Ata *) buf;
  225. if (!opts.pp_ataid) {
  226. puts("device identify response:");
  227. hex_print(stdout, p->data, 512, " ");
  228. return;
  229. }
  230. for (n = 0; n < 1024; n += 2) {
  231. unsigned char ch;
  232. ch = p->data[n];
  233. p->data[n] = p->data[n+1];
  234. p->data[n+1] = ch;
  235. }
  236. id = (struct hd_driveid *) p->data;
  237. puts("device identify fields:");
  238. printf("vendor_specific_0: 0x%X\n", id->vendor0);
  239. printf("vendor_specific_1: 0x%X\n", id->vendor1);
  240. printf("vendor_specific_2: 0x%X\n", id->vendor2);
  241. pp_idtext("serial_number: ", id->serial_no, sizeof id->serial_no);
  242. pp_idtext("firmware_rev: ", id->fw_rev, sizeof id->fw_rev);
  243. pp_idtext("model: ", id->model, sizeof id->model);
  244. }
  245. struct smartcmd *
  246. smartcmd_lookup(char *nam)
  247. {
  248. int n = sizeof smarts / sizeof smarts[0];
  249. int i;
  250. for (i = 0; i < n; ++i) {
  251. char *p = strchr(nam, ':');
  252. if (p && !strncmp(smarts[i].name, nam, p - nam))
  253. return &smarts[i];
  254. else if (!strcmp(smarts[i].name, nam))
  255. return &smarts[i];
  256. }
  257. return nil;
  258. }
  259. void
  260. smart_registers(Ata *a, char *opts, struct smartcmd *s)
  261. {
  262. a->err = s->cmd;
  263. a->lba[1] = 0x4f;
  264. a->lba[2] = 0xc2;
  265. if (opts++)
  266. a->lba[0] = strtol(opts, NULL, 0);
  267. }
  268. void
  269. show_smart_regs(Ata *a)
  270. {
  271. puts("ATA registers:");
  272. char *names[] = {
  273. "Features", "Sector Count",
  274. "LBA Low", "LBA Mid", "LBA High",
  275. "Status",
  276. };
  277. int regs[] = {
  278. a->err, a->sectors,
  279. a->lba[0], a->lba[1], a->lba[2],
  280. a->cmd,
  281. };
  282. int i;
  283. for (i = 0; i < sizeof regs / sizeof regs[0]; ++i)
  284. printf("%20s: 0x%02x\n", names[i], regs[i]);
  285. }
  286. void
  287. smart(Conf *c, u32 tag, char *smart_cmd)
  288. {
  289. int n;
  290. uchar buf[1400];
  291. Ata a;
  292. Ata *p;
  293. struct smartcmd *s = smartcmd_lookup(smart_cmd);
  294. if (!s) {
  295. fprintf(stderr,
  296. "%s Error: no such SMART command: %s\n",
  297. progname, smart_cmd);
  298. exit(EXIT_FAILURE);
  299. }
  300. ata_prep(&a, c, tag);
  301. a.sectors = !!s->data; /* we only support one-sector data xfer */
  302. a.cmd = ATAsmart;
  303. smart_registers(&a, strchr(smart_cmd, ':'), s);
  304. if (s->data & SmartDataPut) {
  305. if (read(STDIN_FILENO, a.data, 512) == -1) {
  306. perror("reading smart data from stdin");
  307. exit(EXIT_FAILURE);
  308. }
  309. a.h.flags |= Write;
  310. }
  311. if (write(sfd, &a, sizeof a) == -1) {
  312. perror("send ATA identify device");
  313. exit(EXIT_FAILURE);
  314. }
  315. n = aoe_pkt_read(buf, sizeof buf, c, tag);
  316. p = (Ata *) buf;
  317. show_smart_regs(p);
  318. if (s->data & SmartDataRet) {
  319. puts("SMART data:");
  320. hex_print(stdout, p->data, 512, " ");
  321. }
  322. }
  323. void
  324. bad_option(char c)
  325. {
  326. fprintf(stderr, "%s Error: unrecognized option: ", progname);
  327. if (isprint(c))
  328. fprintf(stderr, "%c\n", c);
  329. else
  330. fprintf(stderr, "0x%02x\n", c & 0xff);
  331. }
  332. void
  333. check_timeout(int secs)
  334. {
  335. if (secs < 1) {
  336. fprintf(stderr,
  337. "%s Error: timeout seconds must be one or more\n",
  338. progname);
  339. exit(EXIT_FAILURE);
  340. }
  341. }
  342. void
  343. init_opts(struct progopts *opts, int argc, char *argv[])
  344. {
  345. int c;
  346. while ( (c = getopt(argc, argv, "hviIt:s:S:")) != -1) {
  347. switch (c) {
  348. case 'h':
  349. usage();
  350. exit(EXIT_SUCCESS);
  351. break;
  352. case 'v':
  353. ++opts->verbose;
  354. break;
  355. case 'i':
  356. opts->ata_ident = 1;
  357. break;
  358. case 'I':
  359. opts->ata_ident = 1;
  360. opts->pp_ataid = 1;
  361. break;
  362. case 't':
  363. opts->tag = atoi(optarg);
  364. break;
  365. case 's':
  366. opts->timeout = atoi(optarg);
  367. check_timeout(opts->timeout);
  368. break;
  369. case 'S':
  370. opts->smart = optarg;
  371. break;
  372. case '?':
  373. bad_option(optopt);
  374. usage();
  375. exit(EXIT_FAILURE);
  376. break;
  377. default:
  378. abort(); /* shouldn't happen */
  379. }
  380. }
  381. if (argc - optind != 3) {
  382. usage();
  383. exit(EXIT_FAILURE);
  384. }
  385. opts->shelf = atoi(argv[optind]);
  386. opts->slot = atoi(argv[optind+1]);
  387. opts->netif = argv[optind+2];
  388. }
  389. int
  390. main(int argc, char *argv[])
  391. {
  392. Conf c;
  393. opts = defaults;
  394. progname = strrchr(argv[0], '/');
  395. if (progname)
  396. progname += 1;
  397. else
  398. progname = argv[0];
  399. init_opts(&opts, argc, argv);
  400. opts.tag |= 1UL << 31; /* set high bit for userland AoE */
  401. if (opts.verbose) {
  402. printf("tag: %x\neth: %s\nshelf: %u\nslot: %u\n",
  403. opts.tag, opts.netif, opts.shelf, opts.slot);
  404. fflush(stdout);
  405. }
  406. sfd = dial(opts.netif);
  407. if (!getea(sfd, opts.netif, mac))
  408. exit(EXIT_FAILURE);
  409. alarm(opts.timeout);
  410. find_blade(&c, &opts);
  411. opts.tag += 1;
  412. alarm(0);
  413. if (opts.verbose) {
  414. printf("found e%d.%d with mac ",
  415. ntohs(c.h.maj), c.h.min);
  416. hex_print(stdout, c.h.src, sizeof c.h.src, "");
  417. putchar('\n');
  418. fflush(stdout);
  419. }
  420. if (opts.ata_ident) {
  421. alarm(opts.timeout);
  422. disk_identify(&c, opts.tag);
  423. alarm(0);
  424. opts.tag += 1;
  425. }
  426. if (opts.smart) {
  427. alarm(opts.timeout);
  428. smart(&c, opts.tag, opts.smart);
  429. alarm(0);
  430. opts.tag += 1;
  431. }
  432. return 0;
  433. }