Mercurial > illumos > git > illumos-omnios
changeset 21327:a99734b8cddf
10962 Want a way to extract SMB packets from a crash dump
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Approved by: Joshua M. Clulow <josh@sysmgr.org>
author | Gordon Ross <gwr@nexenta.com> |
---|---|
date | Wed, 27 May 2015 17:25:56 -0400 |
parents | db8ec8447779 |
children | 603acf6e5b38 |
files | usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c usr/src/cmd/mdb/common/modules/smbsrv/smbsrv_pcap.c usr/src/cmd/mdb/common/modules/smbsrv/smbsrv_pcap.h usr/src/cmd/mdb/intel/amd64/libfksmbsrv/Makefile usr/src/cmd/mdb/intel/amd64/smbsrv/Makefile usr/src/cmd/mdb/intel/ia32/libfksmbsrv/Makefile usr/src/cmd/mdb/sparc/v9/smbsrv/Makefile |
diffstat | 7 files changed, 823 insertions(+), 30 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c Mon Apr 20 23:02:00 2015 -0400 +++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c Wed May 27 17:25:56 2015 -0400 @@ -32,6 +32,10 @@ #include <smbsrv/smb.h> #include <smbsrv/smb_ktypes.h> +#ifndef _KMDB +#include "smbsrv_pcap.h" +#endif + #ifdef _KERNEL #define SMBSRV_OBJNAME "smbsrv" #else @@ -392,6 +396,15 @@ static int smb_obj_expand(uintptr_t, uint_t, const smb_exp_t *, ulong_t); static int smb_obj_list(const char *, uint_t, uint_t); static int smb_worker_findstack(uintptr_t); +static void smb_inaddr_ntop(smb_inaddr_t *, char *, size_t); + +typedef int (*dump_func_t)(struct mbuf_chain *, int32_t, + smb_inaddr_t *, uint16_t, smb_inaddr_t *, uint16_t, + hrtime_t, boolean_t); +static int smb_req_dump(struct mbuf_chain *, int32_t, + smb_inaddr_t *, uint16_t, smb_inaddr_t *, uint16_t, + hrtime_t, boolean_t); +static int smb_req_dump_m(uintptr_t, const void *, void *); /* * ***************************************************************************** @@ -622,7 +635,7 @@ !(opts & SMB_OPT_WALK)) { char cipaddr[INET6_ADDRSTRLEN]; char lipaddr[INET6_ADDRSTRLEN]; - int ipaddrstrlen; + int ipaddrstrlen = INET6_ADDRSTRLEN; smb_session_t *se; const char *state; @@ -638,28 +651,10 @@ else state = smb_session_state[se->s_state]; - switch (se->ipaddr.a_family) { - case AF_INET: + if (se->ipaddr.a_family == AF_INET) ipaddrstrlen = INET_ADDRSTRLEN; - (void) mdb_snprintf(cipaddr, sizeof (cipaddr), - "%I", se->ipaddr.a_ipv4); - (void) mdb_snprintf(lipaddr, sizeof (lipaddr), - "%I", se->local_ipaddr.a_ipv4); - break; - case AF_INET6: - ipaddrstrlen = INET6_ADDRSTRLEN; - (void) mdb_snprintf(cipaddr, sizeof (cipaddr), - "%N", &(se->ipaddr.a_ipv6)); - (void) mdb_snprintf(lipaddr, sizeof (lipaddr), - "%N", &(se->local_ipaddr.a_ipv6)); - break; - default: - ipaddrstrlen = INET_ADDRSTRLEN; - (void) mdb_snprintf(cipaddr, sizeof (cipaddr), - "unknown"); - (void) mdb_snprintf(lipaddr, sizeof (lipaddr), - "unknown"); - } + smb_inaddr_ntop(&se->ipaddr, cipaddr, ipaddrstrlen); + smb_inaddr_ntop(&se->local_ipaddr, lipaddr, ipaddrstrlen); if (opts & SMB_OPT_VERBOSE) { mdb_printf("%<b>%<u>SMB session information " @@ -876,6 +871,209 @@ return (DCMD_OK); } +static void +smbreq_dump_help(void) +{ + mdb_printf( + "Dump the network data for an smb_request_t, either" + " command, reply, or (by default) both. Optionally" + " append data to a pcap file (mdb only, not kmdb).\n\n"); + (void) mdb_dec_indent(2); + mdb_printf("%<b>OPTIONS%</b>\n"); + (void) mdb_inc_indent(2); + mdb_printf( + "-c\tDump only the SMB command message\n" + "-r\tDump only the SMB reply message (if present)\n" + "-o FILE\tOutput to FILE (append) in pcap format\n"); +} + +#define SMB_RDOPT_COMMAND 1 +#define SMB_RDOPT_REPLY 2 +#define SMB_RDOPT_OUTFILE 4 + +/* + * Like "smbreq" but just dump the command/reply messages. + * With the output file option, append to a pcap file. + */ +static int +smbreq_dump_dcmd(uintptr_t rqaddr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + smb_session_t *ssn; + smb_request_t *sr; + char *outfile = NULL; + dump_func_t dump_func; + uint64_t msgid; + uintptr_t ssnaddr; + uint_t opts = 0; + int rc = DCMD_OK; + + if (!(flags & DCMD_ADDRSPEC)) + return (DCMD_USAGE); + + if (mdb_getopts(argc, argv, + 'c', MDB_OPT_SETBITS, SMB_RDOPT_COMMAND, &opts, + 'r', MDB_OPT_SETBITS, SMB_RDOPT_REPLY, &opts, + 'o', MDB_OPT_STR, &outfile, + NULL) != argc) + return (DCMD_USAGE); +#ifdef _KMDB + if (outfile != NULL) { + mdb_warn("smbreq_dump -o option not supported in kmdb\n"); + return (DCMD_ERR); + } +#endif /* _KMDB */ + + /* + * Default without -c or -r is to dump both. + */ + if ((opts & (SMB_RDOPT_COMMAND | SMB_RDOPT_REPLY)) == 0) + opts |= SMB_RDOPT_COMMAND | SMB_RDOPT_REPLY; + + /* + * Get the smb_request_t, for the cmd/reply messages. + */ + sr = mdb_alloc(sizeof (*sr), UM_SLEEP | UM_GC); + if (mdb_vread(sr, sizeof (*sr), rqaddr) == -1) { + mdb_warn("failed to read smb_request at %p", rqaddr); + return (DCMD_ERR); + } + if (sr->sr_magic != SMB_REQ_MAGIC) { + mdb_warn("not an smb_request_t (%p)>", rqaddr); + return (DCMD_ERR); + } + + /* + * Get the session too, for the IP addresses & ports. + */ + ssnaddr = (uintptr_t)sr->session; + ssn = mdb_alloc(sizeof (*ssn), UM_SLEEP | UM_GC); + if (mdb_vread(ssn, sizeof (*ssn), ssnaddr) == -1) { + mdb_warn("failed to read smb_request at %p", ssnaddr); + return (DCMD_ERR); + } + if (ssn->s_magic != SMB_SESSION_MAGIC) { + mdb_warn("not an smb_session_t (%p)>", ssnaddr); + return (DCMD_ERR); + } + +#ifndef _KMDB + if (outfile != NULL) { + rc = smbsrv_pcap_open(outfile); + if (rc != DCMD_OK) + return (rc); + dump_func = smbsrv_pcap_dump; + } else +#endif /* _KMDB */ + { + dump_func = smb_req_dump; + } + + if (sr->smb2_messageid != 0) + msgid = sr->smb2_messageid; + else + msgid = sr->smb_mid; + mdb_printf("Dumping request %-?p, Msg_ID 0x%llx\n", + rqaddr, msgid); + + if (opts & SMB_RDOPT_COMMAND) { + /* + * Dump the command, length=max_bytes + * src=remote, dst=local + */ + rc = dump_func(&sr->command, sr->command.max_bytes, + &ssn->ipaddr, ssn->s_remote_port, + &ssn->local_ipaddr, ssn->s_local_port, + sr->sr_time_submitted, B_FALSE); + } + + if ((opts & SMB_RDOPT_REPLY) != 0 && + rc == DCMD_OK) { + /* + * Dump the reply, length=chain_offset + * src=local, dst=remote + */ + rc = dump_func(&sr->reply, sr->reply.chain_offset, + &ssn->local_ipaddr, ssn->s_local_port, + &ssn->ipaddr, ssn->s_remote_port, + sr->sr_time_start, B_TRUE); + } + +#ifndef _KMDB + if (outfile != NULL) { + smbsrv_pcap_close(); + } +#endif + + return (DCMD_OK); +} + +struct req_dump_state { + int32_t rem_len; +}; + +static int +smb_req_dump(struct mbuf_chain *mbc, int32_t smb_len, + smb_inaddr_t *src_ip, uint16_t src_port, + smb_inaddr_t *dst_ip, uint16_t dst_port, + hrtime_t rqtime, boolean_t is_reply) +{ + char src_buf[INET6_ADDRSTRLEN]; + char dst_buf[INET6_ADDRSTRLEN]; + struct req_dump_state dump_state; + _NOTE(ARGUNUSED(rqtime)); + + if (smb_len < 4) + return (DCMD_OK); + if (mbc->chain == NULL) + return (DCMD_ERR); + + smb_inaddr_ntop(src_ip, src_buf, sizeof (src_buf)); + smb_inaddr_ntop(dst_ip, dst_buf, sizeof (dst_buf)); + + mdb_printf("%-8s SRC: %s/%u DST: %s/%u LEN: %u\n", + (is_reply) ? "Reply:" : "Call:", + src_buf, src_port, dst_buf, dst_port, smb_len); + + /* + * Calling "smb_mbuf_dump" with a wrapper function + * so we can set its length arg, and decrement + * req_dump_state.rem_len as it goes. + */ + dump_state.rem_len = smb_len; + if (mdb_pwalk("smb_mbuf_walker", smb_req_dump_m, + &dump_state, (uintptr_t)mbc->chain) == -1) { + mdb_warn("cannot walk smb_req mbuf_chain"); + return (DCMD_ERR); + } + return (DCMD_OK); +} + +static int +smb_req_dump_m(uintptr_t m_addr, const void *data, void *arg) +{ + struct req_dump_state *st = arg; + const struct mbuf *m = data; + mdb_arg_t argv; + int cnt; + + cnt = st->rem_len; + if (cnt > m->m_len) + cnt = m->m_len; + if (cnt <= 0) + return (WALK_DONE); + + argv.a_type = MDB_TYPE_IMMEDIATE; + argv.a_un.a_val = cnt; + if (mdb_call_dcmd("smb_mbuf_dump", m_addr, 0, 1, &argv) < 0) { + mdb_warn("%p::smb_mbuf_dump failed\n", m_addr); + return (WALK_ERR); + } + + st->rem_len -= cnt; + return (WALK_NEXT); +} + /* * ***************************************************************************** * ****************************** smb_user_t *********************************** @@ -1922,6 +2120,92 @@ } /* + * ******************************************************************* + * (smb) mbuf_t + * + * ::smb_mbuf_dump [max_len] + * dcmd to dump the data portion of an mbuf_t + * stop at max_len + */ +static int +smb_mbuf_dump_dcmd(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + struct m_hdr mh; + uintptr_t mdata; + int len, max_len; + int dumpptr_flags; + + if (mdb_vread(&mh, sizeof (mh), addr) < 0) { + mdb_warn("failed to read mbuf at %p", addr); + return (DCMD_ERR); + } + len = mh.mh_len; + mdata = (uintptr_t)mh.mh_data; + + if (argc > 0) { + if (argv[0].a_type == MDB_TYPE_IMMEDIATE) + max_len = argv[0].a_un.a_val; + else + max_len = mdb_strtoull(argv[0].a_un.a_str); + if (len > max_len) + len = max_len; + } + if (len <= 0) + return (DCMD_OK); + + if (DCMD_HDRSPEC(flags)) { + mdb_printf("%<u>%-16s %-16s %-12s%</u>\n", + "mbuf_t", "m_data", "m_len"); + } + mdb_printf("%-16p %-16p %-12u\n", + addr, mdata, mh.mh_len); + + dumpptr_flags = MDB_DUMP_RELATIVE | MDB_DUMP_ASCII | MDB_DUMP_HEADER; + if (mdb_dumpptr(mdata, len, dumpptr_flags, + (mdb_dumpptr_cb_t)mdb_vread, NULL) < 0) + return (DCMD_ERR); + + return (DCMD_OK); +} + +static int +smb_mbuf_walk_init(mdb_walk_state_t *wsp) +{ + mbuf_t *m; + + if (wsp->walk_addr == 0) { + mdb_printf("require address of an mbuf_t\n"); + return (WALK_ERR); + } + m = mdb_alloc(sizeof (*m), UM_SLEEP | UM_GC); + wsp->walk_data = m; + return (WALK_NEXT); +} + +static int +smb_mbuf_walk_step(mdb_walk_state_t *wsp) +{ + uintptr_t addr = wsp->walk_addr; + mbuf_t *m = wsp->walk_data; + int rc; + + if (wsp->walk_addr == 0) + return (WALK_DONE); + + if (mdb_vread(m, sizeof (*m), addr) == -1) { + mdb_warn("failed to read mbuf_t at %p", addr); + return (WALK_ERR); + } + + rc = wsp->walk_callback(addr, m, wsp->walk_cbdata); + wsp->walk_addr = (uintptr_t)m->m_next; + + return (rc); +} + +/* + * ******************************************************************* * ::smbstat * * Prints SMB requests statistics. @@ -2466,6 +2750,23 @@ return (DCMD_OK); } +static void +smb_inaddr_ntop(smb_inaddr_t *ina, char *buf, size_t sz) +{ + + switch (ina->a_family) { + case AF_INET: + (void) mdb_snprintf(buf, sz, "%I", ina->a_ipv4); + break; + case AF_INET6: + (void) mdb_snprintf(buf, sz, "%N", ina->a_ipv6); + break; + default: + (void) mdb_snprintf(buf, sz, "(?)"); + break; + } +} + /* * MDB module linkage information: * @@ -2505,6 +2806,12 @@ ":[-v]", "print smb_request_t information", smbreq_dcmd }, + { "smbreq_dump", + ":[-cr] [-o outfile]", + "dump smb_request_t packets (cmd/reply)", + smbreq_dump_dcmd, + smbreq_dump_help, + }, { "smblock", ":[-v]", "print smb_lock_t information", smblock_dcmd }, @@ -2550,6 +2857,9 @@ { "smbfssd", "[-v]", "print smb_fssd_t information", smbfssd_dcmd }, + { "smb_mbuf_dump", ":[max_len]", + "print mbuf_t data", + smb_mbuf_dump_dcmd }, { NULL } }; @@ -2578,6 +2888,12 @@ smb_ace_walk_step, NULL, NULL }, + { "smb_mbuf_walker", + "walk list of mbuf_t structures", + smb_mbuf_walk_init, + smb_mbuf_walk_step, + NULL, + NULL }, { NULL } };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv_pcap.c Wed May 27 17:25:56 2015 -0400 @@ -0,0 +1,439 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Support functions for dumping SMB request and response data from a + * crash dump as a pcap file. This allows using tools like wireshark + * to examine the request we were working on when we crashed. + * + * This feature is only available in mdb (not in kmdb). + */ + +#ifdef _KMDB +#error "Makefile should have excluded this file." +#endif + +#include <mdb/mdb_modapi.h> +#include <mdb/mdb_ks.h> +#include <sys/thread.h> +#include <sys/taskq.h> +#include <smbsrv/smb_vops.h> +#include <smbsrv/smb.h> +#include <smbsrv/smb_ktypes.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <inet/tcp.h> + +#include <fcntl.h> +#include <unistd.h> + +#include "smbsrv_pcap.h" + +/* Not sure why this isn't declared... */ +extern int fstat(int, struct stat *); + +/* + * In the capture file, packets are truncated at 64k. + * The SMB len is shorter so that after we add the + * (faked up) headers we're still below PCAP_SNAPLEN. + */ +#define PCAP_SNAPLEN (1<<16) +#define MAX_SMB_LEN (PCAP_SNAPLEN - 0x40) + +/* + * pcap file format stuff, mostly from: + * wiki.wireshark.org/Development/LibpcapFileFormat + */ + +#define PCAP_MAGIC 0xa1b2c3d4 +#define PCAP_VMAJOR 2 +#define PCAP_VMINOR 4 +#define PCAP_DLT_RAW 0xc + +struct pcap_file_hdr { + uint32_t magic_number; + uint16_t version_major; + uint16_t version_minor; + uint32_t thiszone; /* TZ correction */ + uint32_t sigflags; /* accuracy of timestamps */ + uint32_t snaplen; /* max legnth of captured packets */ + uint32_t network; /* data link type */ +}; + +struct pcap_frame_hdr { + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ +}; + +struct my_ip6_hdr { + uint8_t ip6_vers; /* 6 */ + uint8_t ip6_class; + uint16_t ip6_xflow; + uint16_t ip6_paylen; + uint8_t ip6_nexthdr; + uint8_t ip6_hoplim; + in6_addr_t ip6_src; + in6_addr_t ip6_dst; +}; + +static int pcap_fd = -1; + +/* For faking TCP sequence numbers. */ +static uint32_t call_seqno; +static uint32_t reply_seqno; + +static int pcap_file_header(char *, int); +static int smb_req_pcap_m(uintptr_t, const void *, void *); + +void +smbsrv_pcap_close(void) +{ + if (pcap_fd != -1) { + close(pcap_fd); + pcap_fd = -1; + } +} + +int +smbsrv_pcap_open(char *outfile) +{ + int fd; + + fd = open(outfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0644); + if (fd < 0) { + mdb_warn("Can't open pcap output file: %s\n", outfile); + return (DCMD_ERR); + } + if (pcap_file_header(outfile, fd) < 0) { + close(fd); + return (DCMD_ERR); + } + pcap_fd = fd; + call_seqno = 1; + reply_seqno = 1; + + return (DCMD_OK); +} + +/* + * Check or create a pcap file header + */ +static int +pcap_file_header(char *outfile, int fd) +{ + struct stat st; + struct pcap_file_hdr hdr; + int n; + + if (fstat(fd, &st) < 0) { + mdb_warn("Can't stat pcap output file: %s\n", outfile); + return (-1); + } + if (st.st_size < sizeof (hdr)) + goto create; + + n = read(fd, &hdr, sizeof (hdr)); + if (n != sizeof (hdr)) + goto create; + + /* + * This only supports appending to files we created, + * so the file headers should all be native endian + * and have the values we write when creating. + */ + if (hdr.magic_number != PCAP_MAGIC || + hdr.version_major != PCAP_VMAJOR || + hdr.version_minor != PCAP_VMINOR || + hdr.snaplen != PCAP_SNAPLEN || + hdr.network != PCAP_DLT_RAW) { + mdb_warn("Existing file not pcap: %s\n", outfile); + return (-1); + } + + /* We will append to this file. */ + (void) lseek(fd, st.st_size, SEEK_SET); + return (0); + +create: + hdr.magic_number = PCAP_MAGIC; + hdr.version_major = PCAP_VMAJOR; + hdr.version_minor = PCAP_VMINOR; + hdr.thiszone = 0; + hdr.sigflags = 0; + hdr.snaplen = PCAP_SNAPLEN; + hdr.network = PCAP_DLT_RAW; + + (void) lseek(fd, (off_t)0, SEEK_SET); + n = write(fd, &hdr, sizeof (hdr)); + if (n != sizeof (hdr)) { + mdb_warn("Can't write output file: %s\n", outfile); + return (-1); + } + (void) ftruncate(fd, (off_t)sizeof (hdr)); + return (0); +} + +struct req_dump_state { + int32_t rem_len; + int tbuf_size; + char *tbuf; +}; + +/* + * Simlar to smb_req_dump, but write a pcap frame. + * The headers are faked up, intended only to be + * good enough so wireshark will display this. + * These NEVER go over any network. + */ +int +smbsrv_pcap_dump(struct mbuf_chain *mbc, int32_t smb_len, + smb_inaddr_t *src_ip, uint16_t src_port, + smb_inaddr_t *dst_ip, uint16_t dst_port, + hrtime_t rqtime, boolean_t is_reply) +{ + struct req_dump_state dump_state; + struct pcap_frame_hdr phdr; + struct my_ip6_hdr ip6_hdr; + struct ipha_s ip_hdr; + tcpha_t tcp_hdr; + uint32_t nb_hdr; + uint32_t *seqno; + uint32_t *ackno; + void *ip_hdr_p; + int ip_hdr_len; + int len_w_hdrs; + int truncated; + int n, rc; + off_t pkt_off; + + if (smb_len < sizeof (nb_hdr)) + return (DCMD_OK); + if (mbc->chain == NULL) + return (DCMD_ERR); + + /* + * This code is not making fragments (for now), so just + * limit SMB frames to 64k - header(s) size. + */ + if (smb_len > MAX_SMB_LEN) { + truncated = smb_len - MAX_SMB_LEN; + smb_len = MAX_SMB_LEN; + } else { + truncated = 0; + } + + switch (src_ip->a_family) { + case AF_INET: + ip_hdr_len = sizeof (ip_hdr); + break; + case AF_INET6: + ip_hdr_len = sizeof (ip6_hdr); + break; + default: + mdb_warn("unknown network addr family\n"); + return (DCMD_ERR); + } + + /* Which is seq/ack? */ + if (is_reply) { + /* it's a reply */ + seqno = &reply_seqno; + ackno = &call_seqno; + } else { + /* it's a call */ + seqno = &call_seqno; + ackno = &reply_seqno; + } + + /* + * Build & dump the (faked up) frame headers: + * pcap packet header + * IP header (v4 or v6) + * TCP header + * NetBIOS header + * + * Build back to front, computing lengths, + * then write them all out. + */ + + /* NetBIOS (just a 32-bit payload len) */ + nb_hdr = htonl(smb_len); + len_w_hdrs = smb_len + sizeof (nb_hdr); + + /* TCP (w/ faked seq. numbers) */ + tcp_hdr.tha_lport = htons(src_port); + tcp_hdr.tha_fport = htons(dst_port); + tcp_hdr.tha_seq = htonl(*seqno); + tcp_hdr.tha_ack = htonl(*ackno); + tcp_hdr.tha_offset_and_reserved = 0x50; + tcp_hdr.tha_flags = 0x10; /* ACK */ + tcp_hdr.tha_win = htons(0xFF00); + tcp_hdr.tha_sum = 0; + tcp_hdr.tha_urp = 0; + (*seqno) += len_w_hdrs; + len_w_hdrs += sizeof (tcp_hdr); + + /* IP header */ + switch (src_ip->a_family) { + case AF_INET: + ip_hdr_p = &ip_hdr; + ip_hdr_len = sizeof (ip_hdr); + /* IPv4 len includes the IP4 header */ + len_w_hdrs += ip_hdr_len; + ip_hdr.ipha_version_and_hdr_length = 0x45; + ip_hdr.ipha_type_of_service = 0; + if (len_w_hdrs > 0xFFFF) + ip_hdr.ipha_length = 0xFFFF; + else + ip_hdr.ipha_length = htons(len_w_hdrs); + ip_hdr.ipha_ident = 0; + ip_hdr.ipha_fragment_offset_and_flags = 0; + ip_hdr.ipha_ttl = 60; + ip_hdr.ipha_protocol = 6; /* TCP */ + ip_hdr.ipha_hdr_checksum = 0; + ip_hdr.ipha_src = src_ip->a_ipv4; + ip_hdr.ipha_dst = dst_ip->a_ipv4; + break; + + case AF_INET6: + ip_hdr_p = &ip_hdr; + ip_hdr_len = sizeof (ip6_hdr); + ip6_hdr.ip6_vers = 6; + ip6_hdr.ip6_class = 0; + ip6_hdr.ip6_xflow = 0; + if (len_w_hdrs > 0xFFFF) + ip6_hdr.ip6_paylen = 0xFFFF; + else + ip6_hdr.ip6_paylen = htons(len_w_hdrs); + ip6_hdr.ip6_nexthdr = 6; /* TCP */ + ip6_hdr.ip6_hoplim = 64; + bcopy(&src_ip->a_ipv6, &ip6_hdr.ip6_src, + sizeof (ip6_hdr.ip6_src)); + bcopy(&dst_ip->a_ipv6, &ip6_hdr.ip6_dst, + sizeof (ip6_hdr.ip6_dst)); + len_w_hdrs += ip_hdr_len; + break; + default: + ip_hdr_p = NULL; + ip_hdr_len = 0; + break; + } + + /* pcap header */ + phdr.ts_sec = rqtime / NANOSEC; + phdr.ts_usec = (rqtime / 1000) % MICROSEC; + phdr.incl_len = len_w_hdrs; /* not incl. pcap header */ + phdr.orig_len = len_w_hdrs + truncated; + len_w_hdrs += sizeof (phdr); + + /* + * Write out all the headers: + * pcap, IP, TCP, NetBIOS + * + * To avoid any possibility of scrambling the + * pcap file, save the offset here and seek to + * where we should be when done writing. + */ + pkt_off = lseek(pcap_fd, (off_t)0, SEEK_CUR); + n = write(pcap_fd, &phdr, sizeof (phdr)); + if (n != sizeof (phdr)) { + mdb_warn("failed to write pcap hdr\n"); + goto errout; + } + n = write(pcap_fd, ip_hdr_p, ip_hdr_len); + if (n != ip_hdr_len) { + mdb_warn("failed to write IP hdr\n"); + goto errout; + } + n = write(pcap_fd, &tcp_hdr, sizeof (tcp_hdr)); + if (n != sizeof (tcp_hdr)) { + mdb_warn("failed to write TCP hdr\n"); + goto errout; + } + n = write(pcap_fd, &nb_hdr, sizeof (nb_hdr)); + if (n != sizeof (nb_hdr)) { + mdb_warn("failed to write NBT hdr\n"); + goto errout; + } + + /* + * Finally, walk the mbuf chain writing SMB data + * to the pcap file, for exactly smb_len bytes. + */ + dump_state.rem_len = smb_len; + dump_state.tbuf_size = MCLBYTES; + dump_state.tbuf = mdb_alloc(dump_state.tbuf_size, UM_SLEEP); + rc = mdb_pwalk("smb_mbuf_walker", smb_req_pcap_m, + &dump_state, (uintptr_t)mbc->chain); + mdb_free(dump_state.tbuf, dump_state.tbuf_size); + if (rc < 0) { + mdb_warn("cannot walk smb_req mbuf_chain"); + goto errout; + } + + pkt_off += len_w_hdrs; + (void) lseek(pcap_fd, pkt_off, SEEK_SET); + return (DCMD_OK); + +errout: + (void) lseek(pcap_fd, pkt_off, SEEK_SET); + (void) ftruncate(pcap_fd, pkt_off); + return (DCMD_ERR); +} + +/* + * Call-back function, called for each mbuf_t in a chain. + * Copy data from this mbuf to the pcap file. + */ +static int +smb_req_pcap_m(uintptr_t mbuf_addr, const void *data, void *arg) +{ + struct req_dump_state *st = arg; + const struct mbuf *m = data; + uintptr_t addr; + int cnt, mlen, n, x; + + addr = (uintptr_t)m->m_data; + mlen = m->m_len; + if (mlen > st->rem_len) + mlen = st->rem_len; + if (mlen <= 0) + return (WALK_DONE); + + cnt = mlen; + while (cnt > 0) { + x = MIN(cnt, st->tbuf_size); + n = mdb_vread(st->tbuf, x, addr); + if (n != x) { + mdb_warn("failed copying mbuf %p\n", mbuf_addr); + return (WALK_ERR); + } + n = write(pcap_fd, st->tbuf, x); + if (n != x) { + mdb_warn("failed writing pcap data\n"); + return (WALK_ERR); + } + addr += x; + cnt -= x; + } + + st->rem_len -= mlen; + return (WALK_NEXT); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv_pcap.h Wed May 27 17:25:56 2015 -0400 @@ -0,0 +1,26 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _SMBSRV_PCAP_H +#define _SMBSRV_PCAP_H + +extern void smbsrv_pcap_close(); +extern int smbsrv_pcap_open(char *); + +extern int smbsrv_pcap_dump(struct mbuf_chain *, int32_t, + smb_inaddr_t *, uint16_t, smb_inaddr_t *, uint16_t, + hrtime_t, boolean_t); + +#endif /* _SMBSRV_PCAP_H */
--- a/usr/src/cmd/mdb/intel/amd64/libfksmbsrv/Makefile Mon Apr 20 23:02:00 2015 -0400 +++ b/usr/src/cmd/mdb/intel/amd64/libfksmbsrv/Makefile Wed May 27 17:25:56 2015 -0400 @@ -23,13 +23,13 @@ # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2015 Nexenta Systems, Inc. All rights reserved. # MODULE = libfksmbsrv.so MDBTGT = proc -MODSRCS = smbsrv.c list.c +MODSRCS = smbsrv.c smbsrv_pcap.c list.c include ../../../../Makefile.cmd include ../../../../Makefile.cmd.64
--- a/usr/src/cmd/mdb/intel/amd64/smbsrv/Makefile Mon Apr 20 23:02:00 2015 -0400 +++ b/usr/src/cmd/mdb/intel/amd64/smbsrv/Makefile Wed May 27 17:25:56 2015 -0400 @@ -22,13 +22,19 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2015 Nexenta Systems, Inc. All rights reserved. # MODULE = smbsrv.so MDBTGT = kvm -MODSRCS = smbsrv.c +# +# This signals that $(KMODSRCS) != $(MODSRCS). Dump to a pcap file +# is not available in kmdb, so don't bother compiling that code. +KMOD_SOURCES_DIFFERENT=$(POUND_SIGN) + +KMODSRCS = smbsrv.c +MODSRCS = smbsrv.c smbsrv_pcap.c include ../../../../Makefile.cmd include ../../../../Makefile.cmd.64
--- a/usr/src/cmd/mdb/intel/ia32/libfksmbsrv/Makefile Mon Apr 20 23:02:00 2015 -0400 +++ b/usr/src/cmd/mdb/intel/ia32/libfksmbsrv/Makefile Wed May 27 17:25:56 2015 -0400 @@ -23,13 +23,13 @@ # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2015 Nexenta Systems, Inc. All rights reserved. # MODULE = libfksmbsrv.so MDBTGT = proc -MODSRCS = smbsrv.c list.c +MODSRCS = smbsrv.c smbsrv_pcap.c list.c include ../../../../Makefile.cmd include ../../Makefile.ia32
--- a/usr/src/cmd/mdb/sparc/v9/smbsrv/Makefile Mon Apr 20 23:02:00 2015 -0400 +++ b/usr/src/cmd/mdb/sparc/v9/smbsrv/Makefile Wed May 27 17:25:56 2015 -0400 @@ -22,13 +22,19 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2015 Nexenta Systems, Inc. All rights reserved. # MODULE = smbsrv.so MDBTGT = kvm -MODSRCS = smbsrv.c +# +# This signals that $(KMODSRCS) != $(MODSRCS). Dump to a pcap file +# is not available in kmdb, so don't bother compiling that code. +KMOD_SOURCES_DIFFERENT=$(POUND_SIGN) + +KMODSRCS = smbsrv.c +MODSRCS = smbsrv.c smbsrv_pcap.c include ../../../../Makefile.cmd include ../../../../Makefile.cmd.64