aoeping.c 9.1 KB

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