dump.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /*
  2. * QEMU System Emulator
  3. *
  4. * Copyright (c) 2003-2008 Fabrice Bellard
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. #include "qemu/osdep.h"
  25. #include "clients.h"
  26. #include "qapi/error.h"
  27. #include "qemu-common.h"
  28. #include "qemu/error-report.h"
  29. #include "qemu/iov.h"
  30. #include "qemu/log.h"
  31. #include "qemu/timer.h"
  32. #include "qapi/visitor.h"
  33. #include "net/filter.h"
  34. typedef struct DumpState {
  35. int64_t start_ts;
  36. int fd;
  37. int pcap_caplen;
  38. } DumpState;
  39. #define PCAP_MAGIC 0xa1b2c3d4
  40. struct pcap_file_hdr {
  41. uint32_t magic;
  42. uint16_t version_major;
  43. uint16_t version_minor;
  44. int32_t thiszone;
  45. uint32_t sigfigs;
  46. uint32_t snaplen;
  47. uint32_t linktype;
  48. };
  49. struct pcap_sf_pkthdr {
  50. struct {
  51. int32_t tv_sec;
  52. int32_t tv_usec;
  53. } ts;
  54. uint32_t caplen;
  55. uint32_t len;
  56. };
  57. static ssize_t dump_receive_iov(DumpState *s, const struct iovec *iov, int cnt)
  58. {
  59. struct pcap_sf_pkthdr hdr;
  60. int64_t ts;
  61. int caplen;
  62. size_t size = iov_size(iov, cnt);
  63. struct iovec dumpiov[cnt + 1];
  64. /* Early return in case of previous error. */
  65. if (s->fd < 0) {
  66. return size;
  67. }
  68. ts = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
  69. caplen = size > s->pcap_caplen ? s->pcap_caplen : size;
  70. hdr.ts.tv_sec = ts / 1000000 + s->start_ts;
  71. hdr.ts.tv_usec = ts % 1000000;
  72. hdr.caplen = caplen;
  73. hdr.len = size;
  74. dumpiov[0].iov_base = &hdr;
  75. dumpiov[0].iov_len = sizeof(hdr);
  76. cnt = iov_copy(&dumpiov[1], cnt, iov, cnt, 0, caplen);
  77. if (writev(s->fd, dumpiov, cnt + 1) != sizeof(hdr) + caplen) {
  78. error_report("network dump write error - stopping dump");
  79. close(s->fd);
  80. s->fd = -1;
  81. }
  82. return size;
  83. }
  84. static void dump_cleanup(DumpState *s)
  85. {
  86. close(s->fd);
  87. s->fd = -1;
  88. }
  89. static int net_dump_state_init(DumpState *s, const char *filename,
  90. int len, Error **errp)
  91. {
  92. struct pcap_file_hdr hdr;
  93. struct tm tm;
  94. int fd;
  95. fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
  96. if (fd < 0) {
  97. error_setg_errno(errp, errno, "-net dump: can't open %s", filename);
  98. return -1;
  99. }
  100. hdr.magic = PCAP_MAGIC;
  101. hdr.version_major = 2;
  102. hdr.version_minor = 4;
  103. hdr.thiszone = 0;
  104. hdr.sigfigs = 0;
  105. hdr.snaplen = len;
  106. hdr.linktype = 1;
  107. if (write(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) {
  108. error_setg_errno(errp, errno, "-net dump write error");
  109. close(fd);
  110. return -1;
  111. }
  112. s->fd = fd;
  113. s->pcap_caplen = len;
  114. qemu_get_timedate(&tm, 0);
  115. s->start_ts = mktime(&tm);
  116. return 0;
  117. }
  118. /* Dumping via VLAN netclient */
  119. struct DumpNetClient {
  120. NetClientState nc;
  121. DumpState ds;
  122. };
  123. typedef struct DumpNetClient DumpNetClient;
  124. static ssize_t dumpclient_receive(NetClientState *nc, const uint8_t *buf,
  125. size_t size)
  126. {
  127. DumpNetClient *dc = DO_UPCAST(DumpNetClient, nc, nc);
  128. struct iovec iov = {
  129. .iov_base = (void *)buf,
  130. .iov_len = size
  131. };
  132. return dump_receive_iov(&dc->ds, &iov, 1);
  133. }
  134. static ssize_t dumpclient_receive_iov(NetClientState *nc,
  135. const struct iovec *iov, int cnt)
  136. {
  137. DumpNetClient *dc = DO_UPCAST(DumpNetClient, nc, nc);
  138. return dump_receive_iov(&dc->ds, iov, cnt);
  139. }
  140. static void dumpclient_cleanup(NetClientState *nc)
  141. {
  142. DumpNetClient *dc = DO_UPCAST(DumpNetClient, nc, nc);
  143. dump_cleanup(&dc->ds);
  144. }
  145. static NetClientInfo net_dump_info = {
  146. .type = NET_CLIENT_DRIVER_DUMP,
  147. .size = sizeof(DumpNetClient),
  148. .receive = dumpclient_receive,
  149. .receive_iov = dumpclient_receive_iov,
  150. .cleanup = dumpclient_cleanup,
  151. };
  152. int net_init_dump(const Netdev *netdev, const char *name,
  153. NetClientState *peer, Error **errp)
  154. {
  155. int len, rc;
  156. const char *file;
  157. char def_file[128];
  158. const NetdevDumpOptions *dump;
  159. NetClientState *nc;
  160. DumpNetClient *dnc;
  161. assert(netdev->type == NET_CLIENT_DRIVER_DUMP);
  162. dump = &netdev->u.dump;
  163. assert(peer);
  164. if (dump->has_file) {
  165. file = dump->file;
  166. } else {
  167. int id;
  168. int ret;
  169. ret = net_hub_id_for_client(peer, &id);
  170. assert(ret == 0); /* peer must be on a hub */
  171. snprintf(def_file, sizeof(def_file), "qemu-vlan%d.pcap", id);
  172. file = def_file;
  173. }
  174. if (dump->has_len) {
  175. if (dump->len > INT_MAX) {
  176. error_setg(errp, "invalid length: %"PRIu64, dump->len);
  177. return -1;
  178. }
  179. len = dump->len;
  180. } else {
  181. len = 65536;
  182. }
  183. nc = qemu_new_net_client(&net_dump_info, peer, "dump", name);
  184. snprintf(nc->info_str, sizeof(nc->info_str),
  185. "dump to %s (len=%d)", file, len);
  186. dnc = DO_UPCAST(DumpNetClient, nc, nc);
  187. rc = net_dump_state_init(&dnc->ds, file, len, errp);
  188. if (rc) {
  189. qemu_del_net_client(nc);
  190. }
  191. return rc;
  192. }
  193. /* Dumping via filter */
  194. #define TYPE_FILTER_DUMP "filter-dump"
  195. #define FILTER_DUMP(obj) \
  196. OBJECT_CHECK(NetFilterDumpState, (obj), TYPE_FILTER_DUMP)
  197. struct NetFilterDumpState {
  198. NetFilterState nfs;
  199. DumpState ds;
  200. char *filename;
  201. uint32_t maxlen;
  202. };
  203. typedef struct NetFilterDumpState NetFilterDumpState;
  204. static ssize_t filter_dump_receive_iov(NetFilterState *nf, NetClientState *sndr,
  205. unsigned flags, const struct iovec *iov,
  206. int iovcnt, NetPacketSent *sent_cb)
  207. {
  208. NetFilterDumpState *nfds = FILTER_DUMP(nf);
  209. dump_receive_iov(&nfds->ds, iov, iovcnt);
  210. return 0;
  211. }
  212. static void filter_dump_cleanup(NetFilterState *nf)
  213. {
  214. NetFilterDumpState *nfds = FILTER_DUMP(nf);
  215. dump_cleanup(&nfds->ds);
  216. }
  217. static void filter_dump_setup(NetFilterState *nf, Error **errp)
  218. {
  219. NetFilterDumpState *nfds = FILTER_DUMP(nf);
  220. if (!nfds->filename) {
  221. error_setg(errp, "dump filter needs 'file' property set!");
  222. return;
  223. }
  224. net_dump_state_init(&nfds->ds, nfds->filename, nfds->maxlen, errp);
  225. }
  226. static void filter_dump_get_maxlen(Object *obj, Visitor *v, const char *name,
  227. void *opaque, Error **errp)
  228. {
  229. NetFilterDumpState *nfds = FILTER_DUMP(obj);
  230. uint32_t value = nfds->maxlen;
  231. visit_type_uint32(v, name, &value, errp);
  232. }
  233. static void filter_dump_set_maxlen(Object *obj, Visitor *v, const char *name,
  234. void *opaque, Error **errp)
  235. {
  236. NetFilterDumpState *nfds = FILTER_DUMP(obj);
  237. Error *local_err = NULL;
  238. uint32_t value;
  239. visit_type_uint32(v, name, &value, &local_err);
  240. if (local_err) {
  241. goto out;
  242. }
  243. if (value == 0) {
  244. error_setg(&local_err, "Property '%s.%s' doesn't take value '%u'",
  245. object_get_typename(obj), name, value);
  246. goto out;
  247. }
  248. nfds->maxlen = value;
  249. out:
  250. error_propagate(errp, local_err);
  251. }
  252. static char *file_dump_get_filename(Object *obj, Error **errp)
  253. {
  254. NetFilterDumpState *nfds = FILTER_DUMP(obj);
  255. return g_strdup(nfds->filename);
  256. }
  257. static void file_dump_set_filename(Object *obj, const char *value, Error **errp)
  258. {
  259. NetFilterDumpState *nfds = FILTER_DUMP(obj);
  260. g_free(nfds->filename);
  261. nfds->filename = g_strdup(value);
  262. }
  263. static void filter_dump_instance_init(Object *obj)
  264. {
  265. NetFilterDumpState *nfds = FILTER_DUMP(obj);
  266. nfds->maxlen = 65536;
  267. object_property_add(obj, "maxlen", "int", filter_dump_get_maxlen,
  268. filter_dump_set_maxlen, NULL, NULL, NULL);
  269. object_property_add_str(obj, "file", file_dump_get_filename,
  270. file_dump_set_filename, NULL);
  271. }
  272. static void filter_dump_instance_finalize(Object *obj)
  273. {
  274. NetFilterDumpState *nfds = FILTER_DUMP(obj);
  275. g_free(nfds->filename);
  276. }
  277. static void filter_dump_class_init(ObjectClass *oc, void *data)
  278. {
  279. NetFilterClass *nfc = NETFILTER_CLASS(oc);
  280. nfc->setup = filter_dump_setup;
  281. nfc->cleanup = filter_dump_cleanup;
  282. nfc->receive_iov = filter_dump_receive_iov;
  283. }
  284. static const TypeInfo filter_dump_info = {
  285. .name = TYPE_FILTER_DUMP,
  286. .parent = TYPE_NETFILTER,
  287. .class_init = filter_dump_class_init,
  288. .instance_init = filter_dump_instance_init,
  289. .instance_finalize = filter_dump_instance_finalize,
  290. .instance_size = sizeof(NetFilterDumpState),
  291. };
  292. static void filter_dump_register_types(void)
  293. {
  294. type_register_static(&filter_dump_info);
  295. }
  296. type_init(filter_dump_register_types);