/* packet-9p.c * Routines for 9P dissection * Copyright 2005, Nils O. Selaasdal * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * File permission bits decoding taken from packet-nfs.c * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include #include #include #include "packet-tcp.h" /* * Protocol specifications: * * 9P2000: http://ericvh.github.io/9p-rfc/rfc9p2000.html * 9P2000.L: https://github.com/chaos/diod/blob/master/protocol.md * 9P2000.u: https://ericvh.github.io/9p-rfc/rfc9p2000.u.html */ /** * enum _9p_msg_t - 9P message types * @_9P_TLERROR: not used * @_9P_RLERROR: response for any failed request for 9P2000.L * @_9P_TSTATFS: file system status request * @_9P_RSTATFS: file system status response * @_9P_TSYMLINK: make symlink request * @_9P_RSYMLINK: make symlink response * @_9P_TMKNOD: create a special file object request * @_9P_RMKNOD: create a special file object response * @_9P_TLCREATE: prepare a handle for I/O on an new file for 9P2000.L * @_9P_RLCREATE: response with file access information for 9P2000.L * @_9P_TRENAME: rename request * @_9P_RRENAME: rename response * @_9P_TMKDIR: create a directory request * @_9P_RMKDIR: create a directory response * @_9P_TVERSION: version handshake request * @_9P_RVERSION: version handshake response * @_9P_TAUTH: request to establish authentication channel * @_9P_RAUTH: response with authentication information * @_9P_TATTACH: establish user access to file service * @_9P_RATTACH: response with top level handle to file hierarchy * @_9P_TERROR: not used * @_9P_RERROR: response for any failed request * @_9P_TFLUSH: request to abort a previous request * @_9P_RFLUSH: response when previous request has been cancelled * @_9P_TWALK: descend a directory hierarchy * @_9P_RWALK: response with new handle for position within hierarchy * @_9P_TOPEN: prepare a handle for I/O on an existing file * @_9P_ROPEN: response with file access information * @_9P_TCREATE: prepare a handle for I/O on a new file * @_9P_RCREATE: response with file access information * @_9P_TREAD: request to transfer data from a file or directory * @_9P_RREAD: response with data requested * @_9P_TWRITE: request to transfer data to a file * @_9P_RWRITE: response with out much data was transferred to file * @_9P_TCLUNK: forget about a handle to an entity within the file system * @_9P_RCLUNK: response when server has forgotten about the handle * @_9P_TREMOVE: request to remove an entity from the hierarchy * @_9P_RREMOVE: response when server has removed the entity * @_9P_TSTAT: request file entity attributes * @_9P_RSTAT: response with file entity attributes * @_9P_TWSTAT: request to update file entity attributes * @_9P_RWSTAT: response when file entity attributes are updated * * There are 14 basic operations in 9P2000, paired as * requests and responses. The one special case is ERROR * as there is no @_9P_TERROR request for clients to transmit to * the server, but the server may respond to any other request * with an @_9P_RERROR. * * See Also: http://plan9.bell-labs.com/sys/man/5/INDEX.html */ static dissector_handle_t ninep_handle; enum _9p_msg_t { _9P_TLERROR = 6, _9P_RLERROR, _9P_TSTATFS = 8, _9P_RSTATFS, _9P_TLOPEN = 12, _9P_RLOPEN, _9P_TLCREATE = 14, _9P_RLCREATE, _9P_TSYMLINK = 16, _9P_RSYMLINK, _9P_TMKNOD = 18, _9P_RMKNOD, _9P_TRENAME = 20, _9P_RRENAME, _9P_TREADLINK = 22, _9P_RREADLINK, _9P_TGETATTR = 24, _9P_RGETATTR, _9P_TSETATTR = 26, _9P_RSETATTR, _9P_TXATTRWALK = 30, _9P_RXATTRWALK, _9P_TXATTRCREATE = 32, _9P_RXATTRCREATE, _9P_TREADDIR = 40, _9P_RREADDIR, _9P_TFSYNC = 50, _9P_RFSYNC, _9P_TLOCK = 52, _9P_RLOCK, _9P_TGETLOCK = 54, _9P_RGETLOCK, _9P_TLINK = 70, _9P_RLINK, _9P_TMKDIR = 72, _9P_RMKDIR, _9P_TRENAMEAT = 74, _9P_RRENAMEAT, _9P_TUNLINKAT = 76, _9P_RUNLINKAT, _9P_TVERSION = 100, _9P_RVERSION, _9P_TAUTH = 102, _9P_RAUTH, _9P_TATTACH = 104, _9P_RATTACH, _9P_TERROR = 106, _9P_RERROR, _9P_TFLUSH = 108, _9P_RFLUSH, _9P_TWALK = 110, _9P_RWALK, _9P_TOPEN = 112, _9P_ROPEN, _9P_TCREATE = 114, _9P_RCREATE, _9P_TREAD = 116, _9P_RREAD, _9P_TWRITE = 118, _9P_RWRITE, _9P_TCLUNK = 120, _9P_RCLUNK, _9P_TREMOVE = 122, _9P_RREMOVE, _9P_TSTAT = 124, _9P_RSTAT, _9P_TWSTAT = 126, _9P_RWSTAT }; /* 9P Msg types to name mapping */ static const value_string ninep_msg_type[] = { {_9P_TLERROR, "Tlerror"}, {_9P_RLERROR, "Rlerror"}, {_9P_TSTATFS, "Tstatfs"}, {_9P_RSTATFS, "Rstatfs"}, {_9P_TLOPEN, "Tlopen"}, {_9P_RLOPEN, "Rlopen"}, {_9P_TLCREATE, "Tlcreate"}, {_9P_RLCREATE, "Rlcreate"}, {_9P_TSYMLINK, "Tsymlink"}, {_9P_RSYMLINK, "Rsymlink"}, {_9P_TMKNOD, "Tmknod"}, {_9P_RMKNOD, "Rmknod"}, {_9P_TRENAME, "Trename"}, {_9P_RRENAME, "Rrename"}, {_9P_TREADLINK, "Treadlink"}, {_9P_RREADLINK, "Rreadlink"}, {_9P_TGETATTR, "Tgetattr"}, {_9P_RGETATTR, "Rgetattr"}, {_9P_TSETATTR, "Tsetattr"}, {_9P_RSETATTR, "Rsetattr"}, {_9P_TXATTRWALK, "Txattrwalk"}, {_9P_RXATTRWALK, "Rxattrwalk"}, {_9P_TXATTRCREATE, "Txattrcreate"}, {_9P_RXATTRCREATE, "Rxattrcreate"}, {_9P_TREADDIR, "Treaddir"}, {_9P_RREADDIR, "Rreaddir"}, {_9P_TFSYNC, "Tfsync"}, {_9P_RFSYNC, "Rfsync"}, {_9P_TLOCK, "Tlock"}, {_9P_RLOCK, "Rlock"}, {_9P_TGETLOCK, "Tgetlock"}, {_9P_RGETLOCK, "Rgetlock"}, {_9P_TLINK, "Tlink"}, {_9P_RLINK, "Rlink"}, {_9P_TMKDIR, "Tmkdir"}, {_9P_RMKDIR, "Rmkdir"}, {_9P_TRENAMEAT, "Trenameat"}, {_9P_RRENAMEAT, "Rrenameat"}, {_9P_TUNLINKAT, "Tunlinkat"}, {_9P_RUNLINKAT, "Runlinkat"}, {_9P_TVERSION, "Tversion"}, {_9P_RVERSION, "Rversion"}, {_9P_TAUTH, "Tauth"}, {_9P_RAUTH, "Rauth"}, {_9P_TATTACH, "Tattach"}, {_9P_RATTACH, "Rattach"}, {_9P_TERROR, "Terror"}, {_9P_RERROR, "Rerror"}, {_9P_TFLUSH, "Tflush"}, {_9P_RFLUSH, "Rflush"}, {_9P_TWALK, "Twalk"}, {_9P_RWALK, "Rwalk"}, {_9P_TOPEN, "Topen"}, {_9P_ROPEN, "Ropen"}, {_9P_TCREATE, "Tcreate"}, {_9P_RCREATE, "Rcreate"}, {_9P_TREAD, "Tread"}, {_9P_RREAD, "Rread"}, {_9P_TWRITE, "Twrite"}, {_9P_RWRITE, "Rwrite"}, {_9P_TCLUNK, "Tclunk"}, {_9P_RCLUNK, "Rclunk"}, {_9P_TREMOVE, "Tremove"}, {_9P_RREMOVE, "Rremove"}, {_9P_TSTAT, "Tstat"}, {_9P_RSTAT, "Rstat"}, {_9P_TWSTAT, "Twstat"}, {_9P_RWSTAT, "Rwstat"}, {0, NULL}, }; static value_string_ext ninep_msg_type_ext = VALUE_STRING_EXT_INIT(ninep_msg_type); enum _9p_version { _9P = 1, _9P2000, _9P2000_L, _9P2000_u }; static const value_string ninep_version[] = { {_9P, "9P"}, {_9P2000, "9P2000"}, {_9P2000_L, "9P2000.L"}, {_9P2000_u, "9P2000.u"}, {0, NULL}, }; static value_string_ext ninep_version_ext = VALUE_STRING_EXT_INIT(ninep_version); /* File open modes */ #define _9P_OREAD 0x0 #define _9P_OWRITE 0x1 #define _9P_ORDWR 0x2 #define _9P_OEXEC 0x3 #define _9P_MODEMASK 0x3 #define _9P_OTRUNC 0x10 #define _9P_ORCLOSE 0x40 /* Open/Create modes */ static const value_string ninep_mode_vals[] = { {_9P_OREAD, "Read Access"}, {_9P_OWRITE, "Write Access"}, {_9P_ORDWR, "Read/Write Access "}, {_9P_OEXEC, "Execute Access"}, {0, NULL}, }; static value_string_ext ninep_mode_vals_ext = VALUE_STRING_EXT_INIT(ninep_mode_vals); /* stat mode flags */ #define DMDIR 0x80000000 /* Directory */ #define DMAPPEND 0x40000000 /* Append only */ #define DMEXCL 0x20000000 /* Exclusive use */ #define DMMOUNT 0x10000000 /* Mounted channel */ #define DMAUTH 0x08000000 /* Authentication */ #define DMTMP 0x04000000 /* Temporary */ /** * enum _9p_qid_t - QID types * @_9P_QTDIR: directory * @_9P_QTAPPEND: append-only * @_9P_QTEXCL: excluse use (only one open handle allowed) * @_9P_QTMOUNT: mount points * @_9P_QTAUTH: authentication file * @_9P_QTTMP: non-backed-up files * @_9P_QTSYMLINK: symbolic links (9P2000.u) * @_9P_QTLINK: hard-link (9P2000.u) * @_9P_QTFILE: normal files * * QID types are a subset of permissions - they are primarily * used to differentiate semantics for a file system entity via * a jump-table. Their value is also the most significant 16 bits * of the permission_t * * See Also: http://plan9.bell-labs.com/magic/man2html/2/stat */ enum _9p_qid_t { _9P_QTDIR = 0x80, _9P_QTAPPEND = 0x40, _9P_QTEXCL = 0x20, _9P_QTMOUNT = 0x10, _9P_QTAUTH = 0x08, _9P_QTTMP = 0x04, _9P_QTSYMLINK = 0x02, _9P_QTLINK = 0x01, _9P_QTFILE = 0x00 }; /* 9P Magic Numbers */ #define _9P_NOTAG (uint16_t)(~0) #define _9P_NOFID (uint32_t)(~0) #define _9P_NONUNAME (uint32_t)(~0) #define _9P_MAXWELEM 16 /** * @brief Length prefixed string type * * The protocol uses length prefixed strings for all * string data, so we replicate that for our internal * string members. */ struct _9p_str { uint16_t len; /* Length of the string */ char *str; /* The string */ }; /** * @brief file system entity information * * qids are /identifiers used by 9P servers to track file system * entities. The type is used to differentiate semantics for operations * on the entity (ie. read means something different on a directory than * on a file). The path provides a server unique index for an entity * (roughly analogous to an inode number), while the version is updated * every time a file is modified and can be used to maintain cache * coherency between clients and serves. * Servers will often differentiate purely synthetic entities by setting * their version to 0, signaling that they should never be cached and * should be accessed synchronously. * * See Also://plan9.bell-labs.com/magic/man2html/2/stat */ struct _9p_qid { uint8_t type; /* Type */ uint32_t version; /* Monotonically incrementing version number */ uint64_t path; /* Per-server-unique ID for a file system element */ }; /* Bit values for getattr valid field. */ #define _9P_GETATTR_MODE 0x00000001U #define _9P_GETATTR_NLINK 0x00000002U #define _9P_GETATTR_UID 0x00000004U #define _9P_GETATTR_GID 0x00000008U #define _9P_GETATTR_RDEV 0x00000010U #define _9P_GETATTR_ATIME 0x00000020U #define _9P_GETATTR_MTIME 0x00000040U #define _9P_GETATTR_CTIME 0x00000080U #define _9P_GETATTR_INO 0x00000100U #define _9P_GETATTR_SIZE 0x00000200U #define _9P_GETATTR_BLOCKS 0x00000400U #define _9P_GETATTR_BTIME 0x00000800U #define _9P_GETATTR_GEN 0x00001000U #define _9P_GETATTR_DATA_VERSION 0x00002000U #if 0 #define _9P_GETATTR_BASIC 0x000007ffU /* Mask for fields up to BLOCKS */ #endif #define _9P_GETATTR_ALL 0x00003fffU /* Mask for All fields above */ /* Bit values for setattr valid field from . */ #define _9P_SETATTR_MODE 0x00000001U #define _9P_SETATTR_UID 0x00000002U #define _9P_SETATTR_GID 0x00000004U #define _9P_SETATTR_SIZE 0x00000008U #define _9P_SETATTR_ATIME 0x00000010U #define _9P_SETATTR_MTIME 0x00000020U #define _9P_SETATTR_CTIME 0x00000040U #define _9P_SETATTR_ATIME_SET 0x00000080U #define _9P_SETATTR_MTIME_SET 0x00000100U #define _9P_SETATTR_ALL 0x000001FFU /* 9p2000.L open flags */ #define _9P_DOTL_RDONLY 00000000 #define _9P_DOTL_WRONLY 00000001 #define _9P_DOTL_RDWR 00000002 #define _9P_DOTL_NOACCESS 00000003 #define _9P_DOTL_CREATE 00000100 #define _9P_DOTL_EXCL 00000200 #define _9P_DOTL_NOCTTY 00000400 #define _9P_DOTL_TRUNC 00001000 #define _9P_DOTL_APPEND 00002000 #define _9P_DOTL_NONBLOCK 00004000 #define _9P_DOTL_DSYNC 00010000 #define _9P_DOTL_FASYNC 00020000 #define _9P_DOTL_DIRECT 00040000 #define _9P_DOTL_LARGEFILE 00100000 #define _9P_DOTL_DIRECTORY 00200000 #define _9P_DOTL_NOFOLLOW 00400000 #define _9P_DOTL_NOATIME 01000000 #define _9P_DOTL_CLOEXEC 02000000 #define _9P_DOTL_SYNC 04000000 /* Bit values for lock type. */ #define _9P_LOCK_TYPE_RDLCK 0 #define _9P_LOCK_TYPE_WRLCK 1 #define _9P_LOCK_TYPE_UNLCK 2 /* 9P lock type to string table */ static const value_string ninep_lock_type[] = { {_9P_LOCK_TYPE_RDLCK, "Read lock"}, {_9P_LOCK_TYPE_WRLCK, "Write lock"}, {_9P_LOCK_TYPE_UNLCK, "Unlock"}, { 0, NULL}, }; static value_string_ext ninep_lock_type_ext = VALUE_STRING_EXT_INIT(ninep_lock_type); /* Bit values for lock status. */ #define _9P_LOCK_SUCCESS 0 #define _9P_LOCK_BLOCKED 1 #define _9P_LOCK_ERROR 2 #define _9P_LOCK_GRACE 3 /* 9P lock status to string table */ static const value_string ninep_lock_status[] = { {_9P_LOCK_SUCCESS, "Success"}, {_9P_LOCK_BLOCKED, "Blocked"}, {_9P_LOCK_ERROR, "Error"}, {_9P_LOCK_GRACE, "Grace"}, { 0, NULL}, }; static value_string_ext ninep_lock_status_ext = VALUE_STRING_EXT_INIT(ninep_lock_status); /* Bit values for lock flags. */ #define _9P_LOCK_FLAGS_NONE 0 #define _9P_LOCK_FLAGS_BLOCK 1 #define _9P_LOCK_FLAGS_RECLAIM 2 /* 9P lock flag to string table */ static const value_string ninep_lock_flag[] = { {_9P_LOCK_FLAGS_NONE, "No flag"}, {_9P_LOCK_FLAGS_BLOCK, "Block"}, {_9P_LOCK_FLAGS_RECLAIM,"Reclaim"}, { 0, NULL}, }; static value_string_ext ninep_lock_flag_ext = VALUE_STRING_EXT_INIT(ninep_lock_flag); /* * Linux error code values to descriptions table. * Note that the error code values on Linux are platform-dependent; * Linux, on some platforms, tried to match the values of existing UN*Xes * on the platform in question. * * The platforms in question appear to be: * * 32-bit PowerPC (AIX?) * 32-bit and 64-bit SPARC (SunOS - pre-5, or 5?) * PA-RISC (HP-UX) * Alpha (Tru64 UNIX) * 32-bit MIPS (IRIX?) * 64-bit MIPS (IRIX?) * * For now, we don't worry about this, and use the errno values used * on most Linux platforms. */ static const value_string linux_errno[] = { {1, "Operation not permitted"}, /* EPERM */ {2, "No such file or directory"}, /* ENOENT */ {3, "No such process"}, /* ESRCH */ {4, "Interrupted system call"}, /* EINTR */ {5, "I/O error"}, /* EIO */ {6, "No such device or address"}, /* ENXIO */ {7, "Argument list too long"}, /* E2BIG */ {8, "Exec format error"}, /* ENOEXEC */ {9, "Bad file number"}, /* EBADF */ {10, "No child processes"}, /* ECHILD */ {11, "Try again"}, /* EAGAIN */ {12, "Out of memory"}, /* ENOMEM */ {13, "Permission denied"}, /* EACCES */ {14, "Bad address"}, /* EFAULT */ {15, "Block device required"}, /* ENOTBLK */ {16, "Device or resource busy"}, /* EBUSY */ {17, "File exists"}, /* EEXIST */ {18, "Cross-device link"}, /* EXDEV */ {19, "No such device"}, /* ENODEV */ {20, "Not a directory"}, /* ENOTDIR */ {21, "Is a directory"}, /* EISDIR */ {22, "Invalid argument"}, /* EINVAL */ {23, "File table overflow"}, /* ENFILE */ {24, "Too many open files"}, /* EMFILE */ {25, "Not a typewriter"}, /* ENOTTY */ {26, "Text file busy"}, /* ETXTBSY */ {27, "File too large"}, /* EFBIG */ {28, "No space left on device"}, /* ENOSPC */ {29, "Illegal seek"}, /* ESPIPE */ {30, "Read-only file system"}, /* EROFS */ {31, "Too many links"}, /* EMLINK */ {32, "Broken pipe"}, /* EPIPE */ {33, "Math argument out of domain of func"}, /* EDOM */ {34, "Math result not representable"}, /* ERANGE */ {35, "Resource deadlock would occur"}, /* EDEADLK */ {36, "File name too long"}, /* ENAMETOOLONG */ {37, "No record locks available"}, /* ENOLCK */ {38, "Function not implemented"}, /* ENOSYS */ {39, "Directory not empty"}, /* ENOTEMPTY */ {40, "Too many symbolic links encountered"}, /* ELOOP */ {41, "Operation would block"}, /* EWOULDBLOCK */ {42, "No message of desired type"}, /* ENOMSG */ {43, "Identifier removed"}, /* EIDRM */ {44, "Channel number out of range"}, /* ECHRNG */ {45, "Level 2 not synchronized"}, /* EL2NSYNC */ {46, "Level 3 halted"}, /* EL3HLT */ {47, "Level 3 reset"}, /* EL3RST */ {48, "Link number out of range"}, /* ELNRNG */ {49, "Protocol driver not attached"}, /* EUNATCH */ {50, "No CSI structure available"}, /* ENOCSI */ {51, "Level 2 halted"}, /* EL2HLT */ {52, "Invalid exchange"}, /* EBADE */ {53, "Invalid request descriptor"}, /* EBADR */ {54, "Exchange full"}, /* EXFULL */ {55, "No anode"}, /* ENOANO */ {56, "Invalid request code"}, /* EBADRQC */ {57, "Invalid slot"}, /* EBADSLT */ {58, "File locking deadlock error"}, /* EDEADLOCK */ {59, "Bad font file format"}, /* EBFONT */ {60, "Device not a stream"}, /* ENOSTR */ {61, "No data available"}, /* ENODATA */ {62, "Timer expired"}, /* ETIME */ {63, "Out of streams resources"}, /* ENOSR */ {64, "Machine is not on the network"}, /* ENONET */ {65, "Package not installed"}, /* ENOPKG */ {66, "Object is remote"}, /* EREMOTE */ {67, "Link has been severed"}, /* ENOLINK */ {68, "Advertise error"}, /* EADV */ {69, "Srmount error"}, /* ESRMNT */ {70, "Communication error on send"}, /* ECOMM */ {71, "Protocol error"}, /* EPROTO */ {72, "Multihop attempted"}, /* EMULTIHOP */ {73, "RFS specific error"}, /* EDOTDOT */ {74, "Not a data message"}, /* EBADMSG */ {75, "Value too large for defined data type"}, /* EOVERFLOW */ {76, "Name not unique on network"}, /* ENOTUNIQ */ {77, "File descriptor in bad state"}, /* EBADFD */ {78, "Remote address changed"}, /* EREMCHG */ {79, "Can not access a needed shared library"}, /* ELIBACC */ {80, "Accessing a corrupted shared library"}, /* ELIBBAD */ {81, ".lib section in a.out corrupted"}, /* ELIBSCN */ {82, "Attempting to link in too many shared libraries"}, /* ELIBMAX */ {83, "Cannot exec a shared library directly"}, /* ELIBEXEC */ {84, "Illegal byte sequence"}, /* EILSEQ */ {85, "Interrupted system call should be restarted"}, /* ERESTART */ {86, "Streams pipe error"}, /* ESTRPIPE */ {87, "Too many users"}, /* EUSERS */ {88, "Socket operation on non-socket"}, /* ENOTSOCK */ {89, "Destination address required"}, /* EDESTADDRREQ */ {90, "Message too long"}, /* EMSGSIZE */ {91, "Protocol wrong type for socket"}, /* EPROTOTYPE */ {92, "Protocol not available"}, /* ENOPROTOOPT */ {93, "Protocol not supported"}, /* EPROTONOSUPPORT */ {94, "Socket type not supported"}, /* ESOCKTNOSUPPORT */ {95, "Operation not supported on transport endpoint"}, /* EOPNOTSUPP */ {96, "Protocol family not supported"}, /* EPFNOSUPPORT */ {97, "Address family not supported by protocol"}, /* EAFNOSUPPORT */ {98, "Address already in use"}, /* EADDRINUSE */ {99, "Cannot assign requested address"}, /* EADDRNOTAVAIL */ {100, "Network is down"}, /* ENETDOWN */ {101, "Network is unreachable"}, /* ENETUNREACH */ {102, "Network dropped connection because of reset"}, /* ENETRESET */ {103, "Software caused connection abort"}, /* ECONNABORTED */ {104, "Connection reset by peer"}, /* ECONNRESET */ {105, "No buffer space available"}, /* ENOBUFS */ {106, "Transport endpoint is already connected"}, /* EISCONN */ {107, "Transport endpoint is not connected"}, /* ENOTCONN */ {108, "Cannot send after transport endpoint shutdown"}, /* ESHUTDOWN */ {109, "Too many references: cannot splice"}, /* ETOOMANYREFS */ {110, "Connection timed out"}, /* ETIMEDOUT */ {111, "Connection refused"}, /* ECONNREFUSED */ {112, "Host is down"}, /* EHOSTDOWN */ {113, "No route to host"}, /* EHOSTUNREACH */ {114, "Operation already in progress"}, /* EALREADY */ {115, "Operation now in progress"}, /* EINPROGRESS */ {116, "Stale NFS file handle"}, /* ESTALE */ {117, "Structure needs cleaning"}, /* EUCLEAN */ {118, "Not a XENIX named type file"}, /* ENOTNAM */ {119, "No XENIX semaphores available"}, /* ENAVAIL */ {120, "Is a named type file"}, /* EISNAM */ {121, "Remote I/O error"}, /* EREMOTEIO */ {122, "Quota exceeded"}, /* EDQUOT */ {123, "No medium found"}, /* ENOMEDIUM */ {124, "Wrong medium type"}, /* EMEDIUMTYPE */ {125, "Operation Canceled"}, /* ECANCELED */ {126, "Required key not available"}, /* ENOKEY */ {127, "Key has expired"}, /* EKEYEXPIRED */ {128, "Key has been revoked"}, /* EKEYREVOKED */ {129, "Key was rejected by service"}, /* EKEYREJECTED */ {130, "Owner died"}, /* EOWNERDEAD */ {131, "State not recoverable"}, /* ENOTRECOVERABLE */ {132, "Operation not possible due to RF-kill"}, /* ERFKILL */ {133, "Memory page has hardware error"}, /* EHWPOISON */ {0, NULL} }; static value_string_ext linux_errno_ext = VALUE_STRING_EXT_INIT(linux_errno); static const char *const invalid_fid_str = ""; static const char *const afid_str = ""; /* Structures for Protocol Operations */ struct _9p_rlerror { uint32_t ecode; }; struct _9p_tstatfs { uint32_t fid; }; struct _9p_rstatfs { uint32_t type; uint32_t bsize; uint64_t blocks; uint64_t bfree; uint64_t bavail; uint64_t files; uint64_t ffree; uint64_t fsid; uint32_t namelen; }; struct _9p_tlopen { uint32_t fid; uint32_t flags; }; struct _9p_rlopen { struct _9p_qid qid; uint32_t iounit; }; struct _9p_tlcreate { uint32_t fid; struct _9p_str name; uint32_t flags; uint32_t mode; uint32_t gid; }; struct _9p_rlcreate { struct _9p_qid qid; uint32_t iounit; }; struct _9p_tsymlink { uint32_t fid; struct _9p_str name; struct _9p_str symtgt; uint32_t gid; }; struct _9p_rsymlink { struct _9p_qid qid; }; struct _9p_tmknod { uint32_t fid; struct _9p_str name; uint32_t mode; uint32_t major; uint32_t minor; uint32_t gid; }; struct _9p_rmknod { struct _9p_qid qid; }; struct _9p_trename { uint32_t fid; uint32_t dfid; struct _9p_str name; }; #if 0 struct _9p_rrename { }; #endif struct _9p_treadlink { uint32_t fid; }; struct _9p_rreadlink { struct _9p_str target; }; struct _9p_tgetattr { uint32_t fid; uint64_t request_mask; }; struct _9p_rgetattr { uint64_t valid; struct _9p_qid qid; uint32_t mode; uint32_t uid; uint32_t gid; uint64_t nlink; uint64_t rdev; uint64_t size; uint64_t blksize; uint64_t blocks; uint64_t atime_sec; uint64_t atime_nsec; uint64_t mtime_sec; uint64_t mtime_nsec; uint64_t ctime_sec; uint64_t ctime_nsec; uint64_t btime_sec; uint64_t btime_nsec; uint64_t gen; uint64_t data_version; }; struct _9p_tsetattr { uint32_t fid; uint32_t valid; uint32_t mode; uint32_t uid; uint32_t gid; uint64_t size; uint64_t atime_sec; uint64_t atime_nsec; uint64_t mtime_sec; uint64_t mtime_nsec; }; #if 0 struct _9p_rsetattr { }; #endif struct _9p_txattrwalk { uint32_t fid; uint32_t attrfid; struct _9p_str name; }; struct _9p_rxattrwalk { uint64_t size; }; struct _9p_txattrcreate { uint32_t fid; struct _9p_str name; uint64_t size; uint32_t flag; }; #if 0 struct _9p_rxattrcreate { }; #endif struct _9p_treaddir { uint32_t fid; uint64_t offset; uint32_t count; }; struct _9p_rreaddir { uint32_t count; uint8_t *data; }; struct _9p_tfsync { uint32_t fid; }; #if 0 struct _9p_rfsync { }; #endif struct _9p_tlock { uint32_t fid; uint8_t type; uint32_t flags; uint64_t start; uint64_t length; uint32_t proc_id; struct _9p_str client_id; }; struct _9p_rlock { uint8_t status; }; struct _9p_tgetlock { uint32_t fid; uint8_t type; uint64_t start; uint64_t length; uint32_t proc_id; struct _9p_str client_id; }; struct _9p_rgetlock { uint8_t type; uint64_t start; uint64_t length; uint32_t proc_id; struct _9p_str client_id; }; struct _9p_tlink { uint32_t dfid; uint32_t fid; struct _9p_str name; }; #if 0 struct _9p_rlink { }; #endif struct _9p_tmkdir { uint32_t fid; struct _9p_str name; uint32_t mode; uint32_t gid; }; struct _9p_rmkdir { struct _9p_qid qid; }; struct _9p_trenameat { uint32_t olddirfid; struct _9p_str oldname; uint32_t newdirfid; struct _9p_str newname; }; #if 0 struct _9p_rrenameat { }; #endif struct _9p_tunlinkat { uint32_t dirfid; struct _9p_str name; uint32_t flags; }; #if 0 struct _9p_runlinkat { }; #endif struct _9p_tawrite { uint32_t fid; uint8_t datacheck; uint64_t offset; uint32_t count; uint32_t rsize; uint8_t *data; uint32_t check; }; struct _9p_rawrite { uint32_t count; }; struct _9p_tversion { uint32_t msize ; struct _9p_str version ; }; struct _9p_rversion { uint32_t msize; struct _9p_str version; }; struct _9p_tauth { uint32_t afid; struct _9p_str uname; struct _9p_str aname; uint32_t n_uname; /* 9P2000.u extensions */ }; struct _9p_rauth { struct _9p_qid qid; }; struct _9p_rerror { struct _9p_str error; uint32_t errnum; /* 9p2000.u extension */ }; struct _9p_tflush { uint16_t oldtag; }; #if 0 struct _9p_rflush { }; #endif struct _9p_tattach { uint32_t fid; uint32_t afid; struct _9p_str uname; struct _9p_str aname; uint32_t n_uname; /* 9P2000.u extensions */ }; struct _9p_rattach { struct _9p_qid qid; }; struct _9p_twalk { uint32_t fid; uint32_t newfid; uint16_t nwname; struct _9p_str wnames[_9P_MAXWELEM]; }; struct _9p_rwalk { uint16_t nwqid; struct _9p_qid wqids[_9P_MAXWELEM]; }; struct _9p_topen { uint32_t fid; uint8_t mode; }; struct _9p_ropen { struct _9p_qid qid; uint32_t iounit; }; struct _9p_tcreate { uint32_t fid; struct _9p_str name; uint32_t perm; uint8_t mode; struct _9p_str extension; }; struct _9p_rcreate { struct _9p_qid qid; uint32_t iounit; }; struct _9p_tread { uint32_t fid; uint64_t offset; uint32_t count; }; struct _9p_rread { uint32_t count; uint8_t *data; }; struct _9p_twrite { uint32_t fid; uint64_t offset; uint32_t count; uint8_t *data; }; struct _9p_rwrite { uint32_t count; }; struct _9p_tclunk { uint32_t fid; }; #if 0 struct _9p_rclunk { }; #endif struct _9p_tremove { uint32_t fid; }; #if 0 struct _9p_rremove { }; union _9p_tmsg { } ; #endif #define NINEPORT 564 /* Forward declarations */ void proto_register_9P(void); void proto_reg_handoff_9P(void); /* Initialize the protocol and registered fields */ static int proto_9P; static int hf_9P_msgsz; static int hf_9P_msgtype; static int hf_9P_tag; static int hf_9P_oldtag; static int hf_9P_parmsz; static int hf_9P_maxsize; static int hf_9P_fid; static int hf_9P_nqid; static int hf_9P_mode; static int hf_9P_mode_rwx; static int hf_9P_mode_t; static int hf_9P_mode_c; static int hf_9P_extension; static int hf_9P_iounit; static int hf_9P_count; static int hf_9P_offset; static int hf_9P_perm; static int hf_9P_qidtype; static int hf_9P_qidtype_dir; static int hf_9P_qidtype_append; static int hf_9P_qidtype_exclusive; static int hf_9P_qidtype_mount; static int hf_9P_qidtype_auth_file; static int hf_9P_qidtype_temp_file; static int hf_9P_qidvers; static int hf_9P_qidpath; static int hf_9P_dm_dir; static int hf_9P_dm_append; static int hf_9P_dm_exclusive; static int hf_9P_dm_mount; static int hf_9P_dm_auth_file; static int hf_9P_dm_temp_file; static int hf_9P_dm_read_owner; static int hf_9P_dm_write_owner; static int hf_9P_dm_exec_owner; static int hf_9P_dm_read_group; static int hf_9P_dm_write_group; static int hf_9P_dm_exec_group; static int hf_9P_dm_read_others; static int hf_9P_dm_write_others; static int hf_9P_dm_exec_others; static int hf_9P_stattype; static int hf_9P_statmode; static int hf_9P_atime; static int hf_9P_mtime; static int hf_9P_ctime; static int hf_9P_btime; static int hf_9P_length; static int hf_9P_dev; static int hf_9P_wname; static int hf_9P_version; static int hf_9P_afid; static int hf_9P_uname; static int hf_9P_aname; static int hf_9P_ename; static int hf_9P_enum; /* static int hf_9P_name; */ static int hf_9P_filename; static int hf_9P_sdlen; static int hf_9P_user; static int hf_9P_group; static int hf_9P_uid; static int hf_9P_gid; static int hf_9P_muid; static int hf_9P_nwalk; static int hf_9P_newfid; static int hf_9P_dfid; static int hf_9P_getattr_flags; static int hf_9P_getattr_mode; static int hf_9P_getattr_nlink; static int hf_9P_getattr_uid; static int hf_9P_getattr_gid; static int hf_9P_getattr_rdev; static int hf_9P_getattr_atime; static int hf_9P_getattr_mtime; static int hf_9P_getattr_ctime; static int hf_9P_getattr_ino; static int hf_9P_getattr_size; static int hf_9P_getattr_blocks; static int hf_9P_getattr_btime; static int hf_9P_getattr_gen; static int hf_9P_getattr_dataversion; static int hf_9P_setattr_flags; static int hf_9P_setattr_mode; static int hf_9P_setattr_uid; static int hf_9P_setattr_gid; static int hf_9P_setattr_size; static int hf_9P_setattr_atime; static int hf_9P_setattr_mtime; static int hf_9P_setattr_ctime; static int hf_9P_setattr_atime_set; static int hf_9P_setattr_mtime_set; static int hf_9P_unlinkat_flags; static int hf_9P_nlink; static int hf_9P_rdev; static int hf_9P_size; static int hf_9P_blksize; static int hf_9P_blocks; static int hf_9P_gen; static int hf_9P_dataversion; static int hf_9P_fstype; static int hf_9P_bfree; static int hf_9P_bavail; static int hf_9P_files; static int hf_9P_ffree; static int hf_9P_fsid; static int hf_9P_namelen; static int hf_9P_mknod_major; static int hf_9P_mknod_minor; static int hf_9P_lflags; static int hf_9P_lflags_rdonly; static int hf_9P_lflags_wronly; static int hf_9P_lflags_rdwr; static int hf_9P_lflags_create; static int hf_9P_lflags_excl; static int hf_9P_lflags_noctty; static int hf_9P_lflags_trunc; static int hf_9P_lflags_append; static int hf_9P_lflags_nonblock; static int hf_9P_lflags_dsync; static int hf_9P_lflags_fasync; static int hf_9P_lflags_direct; static int hf_9P_lflags_largefile; static int hf_9P_lflags_directory; static int hf_9P_lflags_nofollow; static int hf_9P_lflags_noatime; static int hf_9P_lflags_cloexec; static int hf_9P_lflags_sync; static int hf_9P_xattr_flag; static int hf_9P_lock_type; static int hf_9P_lock_flag; static int hf_9P_lock_start; static int hf_9P_lock_length; static int hf_9P_lock_procid; static int hf_9P_lock_status; static int hf_9P_unknown_message; /* subtree pointers */ static int ett_9P; static int ett_9P_omode; static int ett_9P_dm; static int ett_9P_wname; static int ett_9P_aname; static int ett_9P_ename; static int ett_9P_uname; static int ett_9P_user; static int ett_9P_group; static int ett_9P_muid; static int ett_9P_filename; static int ett_9P_version; static int ett_9P_qid; static int ett_9P_qidtype; static int ett_9P_getattr_flags; static int ett_9P_setattr_flags; static int ett_9P_lflags; static expert_field ei_9P_first_250; static expert_field ei_9P_msgtype; static wmem_map_t *_9p_hashtable; static void dissect_9P_dm(tvbuff_t *tvb, proto_item *tree, int offset, int iscreate); static void dissect_9P_qid(tvbuff_t *tvb, proto_tree *tree, int offset); static void dissect_9P_lflags(tvbuff_t *tvb, proto_tree *tree, int offset); static void dissect_9P_getattrflags(tvbuff_t *tvb, proto_tree *tree, int offset); static void dissect_9P_setattrflags(tvbuff_t *tvb, proto_tree *tree, int offset); static int * const _9P_modes[] = { &hf_9P_mode_c, &hf_9P_mode_t, &hf_9P_mode_rwx, NULL }; struct _9p_hashkey { uint32_t conv_index; uint16_t tag; uint32_t fid; }; struct _9p_hashval { size_t len; void *data; }; struct _9p_taginfo { enum _9p_msg_t msgtype; uint32_t fid; /* fid path used for create and lcreate */ char *fid_path; }; static int _9p_hash_equal(const void *k1, const void *k2) { const struct _9p_hashkey *key1 = (const struct _9p_hashkey *)k1, *key2 = (const struct _9p_hashkey *)k2; return ((key1->conv_index == key2->conv_index) && (key1->tag == key2->tag) && (key1->fid == key2->fid)); } static unsigned _9p_hash_hash(const void *k) { const struct _9p_hashkey *key = (const struct _9p_hashkey *)k; return (key->conv_index ^ key->tag ^ key->fid); } static struct _9p_hashval *_9p_hash_new_val(size_t len) { struct _9p_hashval *val; val = wmem_new(wmem_file_scope(), struct _9p_hashval); val->data = wmem_alloc(wmem_file_scope(), len); val->len = len; return val; } static void _9p_hash_set(packet_info *pinfo, uint16_t tag, uint32_t fid, struct _9p_hashval *val) { struct _9p_hashkey *key; struct _9p_hashval *oldval; conversation_t *conv; conv = find_or_create_conversation(pinfo); key = wmem_new(wmem_file_scope(), struct _9p_hashkey); key->conv_index = conv->conv_index; key->tag = tag; key->fid = fid; /* remove eventual old entry */ oldval = (struct _9p_hashval *)wmem_map_lookup(_9p_hashtable, key); if (oldval) { wmem_map_remove(_9p_hashtable, key); } wmem_map_insert(_9p_hashtable, key, val); } static struct _9p_hashval *_9p_hash_get(packet_info *pinfo, uint16_t tag, uint32_t fid) { struct _9p_hashkey key; conversation_t *conv; conv = find_or_create_conversation(pinfo); key.conv_index = conv->conv_index; key.tag = tag; key.fid = fid; return (struct _9p_hashval *)wmem_map_lookup(_9p_hashtable, &key); } static void _9p_hash_free(packet_info *pinfo, uint16_t tag, uint32_t fid) { struct _9p_hashkey key; conversation_t *conv; conv = find_or_create_conversation(pinfo); key.conv_index = conv->conv_index; key.tag = tag; key.fid = fid; wmem_map_remove(_9p_hashtable, &key); } static void conv_set_version(packet_info *pinfo, enum _9p_version version) { struct _9p_hashval *val; val = _9p_hash_new_val(sizeof(enum _9p_version)); *(enum _9p_version*)val->data = version; _9p_hash_set(pinfo, _9P_NOTAG, _9P_NOFID, val); } static enum _9p_version conv_get_version(packet_info *pinfo) { struct _9p_hashval *val; val = _9p_hash_get(pinfo, _9P_NOTAG, _9P_NOFID); return val ? *(enum _9p_version*)val->data : _9P; } static void conv_set_fid_nocopy(packet_info *pinfo, uint32_t fid, const char *path) { struct _9p_hashval *val; if (pinfo->fd->visited || fid == _9P_NOFID) return; /* get or create&insert fid tree */ val = _9p_hash_get(pinfo, _9P_NOTAG, fid); if (!val) { val = _9p_hash_new_val(0); val->data = wmem_tree_new(wmem_file_scope()); /* val->len is intentionally left to 0 so the tree won't be freed */ _9p_hash_set(pinfo, _9P_NOTAG, fid, val); } /* fill it */ wmem_tree_insert32((wmem_tree_t *)val->data, pinfo->num, (void *)path); } static void conv_set_fid(packet_info *pinfo, uint32_t fid, const char *path, size_t len) { char *str; if (pinfo->fd->visited || fid == _9P_NOFID || len == 0) return; str = (char*)wmem_alloc(wmem_file_scope(), len); (void) g_strlcpy(str, path, len); conv_set_fid_nocopy(pinfo, fid, str); } static const char *conv_get_fid(packet_info *pinfo, uint32_t fid) { struct _9p_hashval *val; if (fid == _9P_NOFID) return invalid_fid_str; val = _9p_hash_get(pinfo, _9P_NOTAG, fid); if (!val) return invalid_fid_str; /* -1 because the fid needs to have been set on a previous message. Let's ignore the possibility of num == 0... */ return (char*)wmem_tree_lookup32_le((wmem_tree_t*)val->data, pinfo->num-1); } static inline void conv_free_fid(packet_info *pinfo, uint32_t fid) { conv_set_fid_nocopy(pinfo, fid, invalid_fid_str); } static void conv_set_tag(packet_info *pinfo, uint16_t tag, enum _9p_msg_t msgtype, uint32_t fid, wmem_strbuf_t *fid_path) { struct _9p_hashval *val; struct _9p_taginfo *taginfo; if (pinfo->fd->visited || tag == _9P_NOTAG) return; val = _9p_hash_new_val(sizeof(struct _9p_taginfo)); taginfo = (struct _9p_taginfo*)val->data; taginfo->msgtype = msgtype; taginfo->fid = fid; if (fid_path) { taginfo->fid_path = (char*)wmem_alloc(wmem_file_scope(), wmem_strbuf_get_len(fid_path)+1); (void) g_strlcpy(taginfo->fid_path, wmem_strbuf_get_str(fid_path), wmem_strbuf_get_len(fid_path)+1); } else { taginfo->fid_path = NULL; } _9p_hash_set(pinfo, tag, _9P_NOFID, val); } static inline struct _9p_taginfo *conv_get_tag(packet_info *pinfo, uint16_t tag) { struct _9p_hashval *val; /* get tag only makes sense on first pass, as tree isn't built like fid */ if (pinfo->fd->visited || tag == _9P_NOTAG) return NULL; /* check that length matches? */ val = _9p_hash_get(pinfo, tag, _9P_NOFID); return val ? (struct _9p_taginfo*)val->data : NULL; } static inline void conv_free_tag(packet_info *pinfo, uint16_t tag) { if (pinfo->fd->visited || tag == _9P_NOTAG) return; _9p_hash_free(pinfo, tag, _9P_NOFID); } /* dissects a string[s] (2 bytes followed by UTF-8 string) */ static unsigned _9p_dissect_string(tvbuff_t *tvb, proto_tree *ninep_tree, unsigned offset, int hf_string, int ett_string) { uint16_t _9p_len; proto_item *ti; proto_tree *sub_tree; _9p_len = tvb_get_letohs(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_string, tvb, offset + 2, _9p_len, ENC_UTF_8|ENC_NA); sub_tree = proto_item_add_subtree(ti, ett_string); proto_tree_add_item(sub_tree, hf_9P_parmsz, tvb, offset, 2, ENC_LITTLE_ENDIAN); return 2 + _9p_len; } /* Dissect 9P messages*/ static int dissect_9P_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { uint32_t u32, i, fid, dfid, newfid; uint16_t u16, tag, _9p_len; enum _9p_msg_t ninemsg; unsigned offset = 0; const char *mname, *fid_path; char *tvb_s; wmem_strbuf_t *tmppath = NULL; int len, reportedlen; tvbuff_t *next_tvb; proto_item *ti, *msg_item; proto_tree *ninep_tree; struct _9p_taginfo *taginfo; int _9p_version; _9p_version = conv_get_version(pinfo); col_set_str(pinfo->cinfo, COL_PROTOCOL, val_to_str_ext_const(_9p_version, &ninep_version_ext, "9P")); col_clear(pinfo->cinfo, COL_INFO); /*ninesz = tvb_get_letohl(tvb, offset);*/ ninemsg = (enum _9p_msg_t)tvb_get_uint8(tvb, offset + 4); mname = val_to_str_ext_const(ninemsg, &ninep_msg_type_ext, "Unknown"); if(strcmp(mname, "Unknown") == 0) { col_add_fstr(pinfo->cinfo, COL_INFO, "9P Data (Message type %u)", (unsigned)ninemsg); return 0; } tag = tvb_get_letohs(tvb, offset+5); col_append_fstr(pinfo->cinfo, COL_INFO, "%s Tag=%u", mname, (unsigned)tag); ti = proto_tree_add_item(tree, proto_9P, tvb, 0, -1, ENC_NA); ninep_tree = proto_item_add_subtree(ti, ett_9P); proto_tree_add_item(ninep_tree, hf_9P_msgsz, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset+= 4; msg_item = proto_tree_add_item(ninep_tree, hf_9P_msgtype, tvb, offset, 1, ENC_LITTLE_ENDIAN); ++offset; proto_tree_add_item(ninep_tree, hf_9P_tag, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; switch(ninemsg) { case _9P_RVERSION: case _9P_TVERSION: proto_tree_add_item(ninep_tree, hf_9P_maxsize, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; if (!pinfo->fd->visited) { _9p_len = tvb_get_letohs(tvb, offset); tvb_s = (char *)tvb_get_string_enc(pinfo->pool, tvb, offset+2, _9p_len, ENC_UTF_8|ENC_NA); if (!strcmp(tvb_s, "9P2000.L")) { u32 = _9P2000_L; } else if (!strcmp(tvb_s, "9P2000")) { u32 = _9P2000; } else if (!strcmp(tvb_s, "9P2000.u")) { u32 = _9P2000_u; } else { u32 = _9P; } conv_set_version(pinfo, (enum _9p_version)u32); } offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_version, ett_9P_version); /* don't set tag for tversion/free it for rversion, we need that for the actual version number */ break; case _9P_TAUTH: proto_tree_add_item(ninep_tree, hf_9P_afid, tvb, offset, 4, ENC_LITTLE_ENDIAN); fid = tvb_get_letohl(tvb, offset); conv_set_fid_nocopy(pinfo, fid, afid_str); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_uname, ett_9P_uname); offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_aname, ett_9P_aname); conv_set_tag(pinfo, tag, ninemsg, fid, NULL); break; case _9P_RERROR: if (_9p_version == _9P2000_L) { u32 = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_enum, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", val_to_str_ext_const(u32, &linux_errno_ext, "Unknown")); offset += 4; } else { offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_ename, ett_9P_ename); } /* conv_get_tag checks we're in first pass */ taginfo = conv_get_tag(pinfo, tag); if (taginfo && (taginfo->msgtype == _9P_TWALK || taginfo->msgtype == _9P_TATTACH)) conv_free_fid(pinfo, taginfo->fid); conv_free_tag(pinfo, tag); break; case _9P_TFLUSH: u16 = tvb_get_letohs(tvb, offset); proto_tree_add_item(ninep_tree, hf_9P_oldtag, tvb, offset, 2, ENC_LITTLE_ENDIAN); conv_free_tag(pinfo, u16); conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TATTACH: fid = tvb_get_letohl(tvb, offset); proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_afid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_uname, ett_9P_uname); if(!pinfo->fd->visited) { _9p_len = tvb_get_letohs(tvb, offset); tvb_s = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset+2, _9p_len, ENC_UTF_8|ENC_NA); conv_set_fid(pinfo, fid, tvb_s, strlen(tvb_s)+1); } offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_aname, ett_9P_aname); if (_9p_version == _9P2000_u || _9p_version == _9P2000_L) { proto_tree_add_item(ninep_tree, hf_9P_uid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; } conv_set_tag(pinfo, tag, ninemsg, fid, NULL); break; case _9P_TWALK: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); fid_path = conv_get_fid(pinfo, fid); proto_item_append_text(ti, " (%s)", fid_path); if (!pinfo->fd->visited) { tmppath = wmem_strbuf_create(pinfo->pool); wmem_strbuf_append(tmppath, fid_path); } offset += 4; fid = tvb_get_letohl(tvb, offset); proto_tree_add_item(ninep_tree, hf_9P_newfid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; u16 = tvb_get_letohs(tvb, offset); proto_tree_add_item(ninep_tree, hf_9P_nwalk, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; for(i = 0 ; i < u16; i++) { if (!pinfo->fd->visited) { _9p_len = tvb_get_letohs(tvb, offset); tvb_s = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset+2, _9p_len, ENC_UTF_8|ENC_NA); wmem_strbuf_append_c(tmppath, '/'); wmem_strbuf_append(tmppath, tvb_s); } if (i < 250) { offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); } } /* I can't imagine anyone having a directory depth more than 25, Limit to 10 times that to be sure, 2^16 is too much */ if(u16 > 250) { expert_add_info(pinfo, ti, &ei_9P_first_250); } if (!pinfo->fd->visited) { conv_set_fid(pinfo, fid, wmem_strbuf_get_str(tmppath), wmem_strbuf_get_len(tmppath)+1); } conv_set_tag(pinfo, tag, ninemsg, fid, NULL); break; case _9P_RWALK: u16 = tvb_get_letohs(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_nqid, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; /* I can't imagine anyone having a directory depth more than 25, Limit to 10 times that to be sure, 2^16 is too much */ if(u16 > 250) { u16 = 250; } for(i = 0; i < u16; i++) { dissect_9P_qid(tvb, ninep_tree, offset); offset += 13; } if (i >= 250) { expert_add_info(pinfo, ti, &ei_9P_first_250); } conv_free_tag(pinfo, tag); break; case _9P_TLOPEN: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; ti = proto_tree_add_item(ninep_tree, hf_9P_statmode, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_lflags(tvb, ti, offset); offset += 4; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TOPEN: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; proto_tree_add_bitmask(ninep_tree, tvb, offset, hf_9P_mode, ett_9P_omode, _9P_modes, ENC_LITTLE_ENDIAN); offset += 1; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TCREATE: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); fid_path = conv_get_fid(pinfo, fid); proto_item_append_text(ti, " (%s)", fid_path); offset += 4; if (!pinfo->fd->visited) { _9p_len = tvb_get_letohs(tvb, offset); tmppath = wmem_strbuf_create(pinfo->pool); wmem_strbuf_append(tmppath, fid_path); wmem_strbuf_append_c(tmppath, '/'); tvb_s = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset+2, _9p_len, ENC_UTF_8|ENC_NA); wmem_strbuf_append(tmppath, tvb_s); } offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_filename, ett_9P_filename); ti = proto_tree_add_item(ninep_tree, hf_9P_perm, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_dm(tvb, ti, offset, 1); offset += 4; proto_tree_add_bitmask(ninep_tree, tvb, offset, hf_9P_mode, ett_9P_omode, _9P_modes, ENC_LITTLE_ENDIAN); offset += 1; if (_9p_version == _9P2000_u) { _9p_len = tvb_get_letohs(tvb, offset); proto_tree_add_item(ninep_tree, hf_9P_extension, tvb, offset+2, 4, ENC_ASCII); offset += 2 + _9p_len; } conv_set_tag(pinfo, tag, ninemsg, fid, tmppath); break; case _9P_TLCREATE: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); fid_path = conv_get_fid(pinfo, fid); proto_item_append_text(ti, " (%s)", fid_path); offset += 4; if (!pinfo->fd->visited) { _9p_len = tvb_get_letohs(tvb, offset); tmppath = wmem_strbuf_create(pinfo->pool); wmem_strbuf_append(tmppath, fid_path); wmem_strbuf_append_c(tmppath, '/'); tvb_s = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset+2, _9p_len, ENC_UTF_8|ENC_NA); wmem_strbuf_append(tmppath, tvb_s); } offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_filename, ett_9P_filename); ti = proto_tree_add_item(ninep_tree, hf_9P_lflags, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_lflags(tvb, ti, offset); offset += 4; ti = proto_tree_add_item(ninep_tree, hf_9P_statmode, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_dm(tvb, ti, offset, 0); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_gid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; conv_set_tag(pinfo, tag, ninemsg, fid, tmppath); break; case _9P_TREAD: case _9P_TREADDIR: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_offset, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_count, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_RREAD: case _9P_RREADDIR: u32 = tvb_get_letohl(tvb, offset); proto_tree_add_item(ninep_tree, hf_9P_count, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; len = tvb_reported_length_remaining(tvb, offset); reportedlen = ((int)u32&0xffff) > len ? len : (int)u32&0xffff; next_tvb = tvb_new_subset_length_caplen(tvb, offset, len, reportedlen); call_data_dissector(next_tvb, pinfo, tree); offset += len; conv_free_tag(pinfo, tag); break; case _9P_TWRITE: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_offset, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; u32 = tvb_get_letohl(tvb, offset); proto_tree_add_item(ninep_tree, hf_9P_count, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; len = tvb_reported_length_remaining(tvb, offset); reportedlen = ((int)u32&0xffff) > len ? len : (int)u32&0xffff; next_tvb = tvb_new_subset_length_caplen(tvb, offset, len, reportedlen); call_data_dissector(next_tvb, pinfo, tree); offset += len; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_RWRITE: proto_tree_add_item(ninep_tree, hf_9P_count, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; conv_free_tag(pinfo, tag); break; case _9P_RSTAT: proto_tree_add_item(ninep_tree, hf_9P_parmsz, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(ninep_tree, hf_9P_sdlen, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(ninep_tree, hf_9P_stattype, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(ninep_tree, hf_9P_dev, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; dissect_9P_qid(tvb, ninep_tree, offset); offset += 13; ti = proto_tree_add_item(ninep_tree, hf_9P_statmode, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_dm(tvb, ti, offset, 0); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_atime, tvb, offset, 4, ENC_TIME_SECS|ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_mtime, tvb, offset, 4, ENC_TIME_SECS|ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_length, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_filename, ett_9P_filename); offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_user, ett_9P_user); offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_group, ett_9P_group); offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_muid, ett_9P_muid); conv_free_tag(pinfo, tag); break; case _9P_TWSTAT: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_parmsz, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(ninep_tree, hf_9P_sdlen, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(ninep_tree, hf_9P_stattype, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(ninep_tree, hf_9P_dev, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; dissect_9P_qid(tvb, ninep_tree, offset); offset += 13; ti = proto_tree_add_item(ninep_tree, hf_9P_statmode, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_dm(tvb, ti, offset, 0); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_atime, tvb, offset, 4, ENC_TIME_SECS|ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_mtime, tvb, offset, 4, ENC_TIME_SECS|ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_length, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_filename, ett_9P_filename); offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_user, ett_9P_user); offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_group, ett_9P_group); offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_muid, ett_9P_muid); conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TGETATTR: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; ti = proto_tree_add_item(ninep_tree, hf_9P_getattr_flags, tvb, offset, 8, ENC_LITTLE_ENDIAN); dissect_9P_getattrflags(tvb, ti, offset); offset += 8; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_RGETATTR: ti = proto_tree_add_item(ninep_tree, hf_9P_getattr_flags, tvb, offset, 8, ENC_LITTLE_ENDIAN); dissect_9P_getattrflags(tvb, ti, offset); offset += 8; dissect_9P_qid(tvb, ninep_tree, offset); offset += 13; ti = proto_tree_add_item(ninep_tree, hf_9P_statmode, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_dm(tvb, ti, offset, 0); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_uid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_gid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_nlink, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_rdev, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_size, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_blksize, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_blocks, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_atime, tvb, offset, 16, ENC_TIME_SECS_NSECS|ENC_LITTLE_ENDIAN); offset += 16; proto_tree_add_item(ninep_tree, hf_9P_mtime, tvb, offset, 16, ENC_TIME_SECS_NSECS|ENC_LITTLE_ENDIAN); offset += 16; proto_tree_add_item(ninep_tree, hf_9P_ctime, tvb, offset, 16, ENC_TIME_SECS_NSECS|ENC_LITTLE_ENDIAN); offset += 16; proto_tree_add_item(ninep_tree, hf_9P_btime, tvb, offset, 16, ENC_TIME_SECS_NSECS|ENC_LITTLE_ENDIAN); offset += 16; proto_tree_add_item(ninep_tree, hf_9P_gen, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_dataversion, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; conv_free_tag(pinfo, tag); break; case _9P_TSETATTR: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; ti = proto_tree_add_item(ninep_tree, hf_9P_setattr_flags, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_setattrflags(tvb, ti, offset); offset += 4; ti = proto_tree_add_item(ninep_tree, hf_9P_statmode, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_dm(tvb, ti, offset, 0); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_uid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_gid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_size, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_atime, tvb, offset, 16, ENC_TIME_SECS_NSECS|ENC_LITTLE_ENDIAN); offset += 16; proto_tree_add_item(ninep_tree, hf_9P_mtime, tvb, offset, 16, ENC_TIME_SECS_NSECS|ENC_LITTLE_ENDIAN); offset += 16; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_RSTATFS: proto_tree_add_item(ninep_tree, hf_9P_fstype, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_blksize, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_blocks, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_bfree, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_bavail, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_files, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_ffree, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_fsid, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_namelen, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; conv_free_tag(pinfo, tag); break; case _9P_TSYMLINK: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; /* name */ offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); /* XXX: maybe use a new field for symtgt? */ offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); proto_tree_add_item(ninep_tree, hf_9P_gid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TMKNOD: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); ti = proto_tree_add_item(ninep_tree, hf_9P_statmode, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_dm(tvb, ti, offset, 0); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_mknod_major, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_mknod_minor, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_gid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TRENAME: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; dfid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_dfid, tvb, offset, 4, ENC_LITTLE_ENDIAN); fid_path = conv_get_fid(pinfo, dfid); proto_item_append_text(ti, " (%s)", fid_path); offset += 4; if (!pinfo->fd->visited) { _9p_len = tvb_get_letohs(tvb, offset); tmppath = wmem_strbuf_create(pinfo->pool); wmem_strbuf_append(tmppath, conv_get_fid(pinfo, dfid)); wmem_strbuf_append_c(tmppath, '/'); tvb_s = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset+2, _9p_len, ENC_UTF_8|ENC_NA); wmem_strbuf_append(tmppath, tvb_s); conv_set_fid(pinfo, fid, wmem_strbuf_get_str(tmppath), wmem_strbuf_get_len(tmppath)+1); } offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_RREADLINK: offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); conv_free_tag(pinfo, tag); break; case _9P_TXATTRWALK: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); fid_path = conv_get_fid(pinfo, fid); proto_item_append_text(ti, " (%s)", fid_path); offset += 4; newfid = tvb_get_letohl(tvb, offset); proto_tree_add_item(ninep_tree, hf_9P_newfid, tvb, offset, 4, ENC_LITTLE_ENDIAN); conv_set_fid_nocopy(pinfo, newfid, fid_path); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_RXATTRWALK: proto_tree_add_item(ninep_tree, hf_9P_size, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; conv_free_tag(pinfo, tag); break; case _9P_TXATTRCREATE: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); proto_tree_add_item(ninep_tree, hf_9P_size, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_xattr_flag, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TLOCK: case _9P_TGETLOCK: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_lock_type, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; proto_tree_add_item(ninep_tree, hf_9P_lock_flag, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_lock_start, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_lock_length, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_lock_procid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_RLOCK: proto_tree_add_item(ninep_tree, hf_9P_lock_status, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; conv_free_tag(pinfo, tag); break; case _9P_RGETLOCK: proto_tree_add_item(ninep_tree, hf_9P_lock_type, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; proto_tree_add_item(ninep_tree, hf_9P_lock_flag, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_lock_start, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_lock_length, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; proto_tree_add_item(ninep_tree, hf_9P_lock_procid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); conv_free_tag(pinfo, tag); break; case _9P_TLINK: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_dfid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TMKDIR: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); ti = proto_tree_add_item(ninep_tree, hf_9P_statmode, tvb, offset, 4, ENC_LITTLE_ENDIAN); dissect_9P_dm(tvb, ti, offset, 0); offset += 4; proto_tree_add_item(ninep_tree, hf_9P_gid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TRENAMEAT: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_dfid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_newfid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TUNLINKAT: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_dfid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); offset += 4; offset += _9p_dissect_string(tvb, ninep_tree, offset, hf_9P_wname, ett_9P_wname); proto_tree_add_item(ninep_tree, hf_9P_unlinkat_flags, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; case _9P_TREMOVE: case _9P_TCLUNK: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); conv_free_fid(pinfo, fid); conv_set_tag(pinfo, tag, ninemsg, fid, NULL); break; /* Request with only fid */ case _9P_TSTATFS: case _9P_TREADLINK: case _9P_TFSYNC: case _9P_TSTAT: fid = tvb_get_letohl(tvb, offset); ti = proto_tree_add_item(ninep_tree, hf_9P_fid, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; proto_item_append_text(ti, " (%s)", conv_get_fid(pinfo, fid)); conv_set_tag(pinfo, tag, ninemsg, _9P_NOFID, NULL); break; /* Reply with qid and ionuit */ case _9P_RCREATE: case _9P_RLCREATE: dissect_9P_qid(tvb, ninep_tree, offset); offset += 13; proto_tree_add_item(ninep_tree, hf_9P_iounit, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; taginfo = conv_get_tag(pinfo, tag); if (taginfo && taginfo->fid_path) { conv_set_fid_nocopy(pinfo, taginfo->fid, taginfo->fid_path); } conv_free_tag(pinfo, tag); break; case _9P_ROPEN: case _9P_RLOPEN: dissect_9P_qid(tvb, ninep_tree, offset); offset += 13; proto_tree_add_item(ninep_tree, hf_9P_iounit, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; conv_free_tag(pinfo, tag); break; /* Reply with only qid */ case _9P_RSYMLINK: case _9P_RMKNOD: case _9P_RMKDIR: case _9P_RAUTH: case _9P_RATTACH: dissect_9P_qid(tvb, ninep_tree, offset); offset += 13; conv_free_tag(pinfo, tag); break; /* Empty reply */ case _9P_RRENAME: case _9P_RSETATTR: case _9P_RXATTRCREATE: case _9P_RFSYNC: case _9P_RLINK: case _9P_RRENAMEAT: case _9P_RUNLINKAT: case _9P_RFLUSH: case _9P_RCLUNK: case _9P_RREMOVE: /* Unhandled reply */ case _9P_RWSTAT: case _9P_RLERROR: conv_free_tag(pinfo, tag); break; /* Should-not-happen query */ case _9P_TLERROR: case _9P_TERROR: default: expert_add_info(pinfo, msg_item, &ei_9P_msgtype); break; } /* Show any extra data at the end of the message (but only if it was captured) */ if (offset != tvb_captured_length(tvb)) proto_tree_add_item(ninep_tree, hf_9P_unknown_message, tvb, offset, -1, ENC_NA); return tvb_captured_length(tvb); } static unsigned get_9P_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) { return (unsigned) tvb_get_letohl(tvb, offset); } static int dissect_9P(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { tcp_dissect_pdus(tvb, pinfo, tree, true, 4, get_9P_message_len, dissect_9P_message, data); return tvb_captured_length(tvb); } /* dissect 9P Qid */ static void dissect_9P_qid(tvbuff_t *tvb, proto_tree *tree, int offset) { proto_item *qidtype_item; proto_tree *qid_tree,*qidtype_tree; uint64_t path; uint32_t vers; uint8_t type; if(!tree) return; type = tvb_get_uint8(tvb, offset); vers = tvb_get_letohs(tvb, offset+1); path = tvb_get_letoh64(tvb, offset+1+4); qid_tree = proto_tree_add_subtree_format(tree, tvb, offset, 13, ett_9P_qid, NULL, "Qid type=0x%02x vers=%d path=%" PRIu64, type, vers, path); qidtype_item = proto_tree_add_item(qid_tree, hf_9P_qidtype, tvb, offset, 1, ENC_LITTLE_ENDIAN); qidtype_tree = proto_item_add_subtree(qidtype_item, ett_9P_qidtype); proto_tree_add_item(qidtype_tree, hf_9P_qidtype_dir, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(qidtype_tree, hf_9P_qidtype_append, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(qidtype_tree, hf_9P_qidtype_exclusive, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(qidtype_tree, hf_9P_qidtype_mount, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(qidtype_tree, hf_9P_qidtype_auth_file, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(qidtype_tree, hf_9P_qidtype_temp_file, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(qid_tree, hf_9P_qidvers, tvb, offset+1, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(qid_tree, hf_9P_qidpath, tvb, offset+1+4, 8, ENC_LITTLE_ENDIAN); } /*dissect 9P stat mode and create perm flags */ static void dissect_9P_dm(tvbuff_t *tvb, proto_item *item, int offset, int iscreate) { proto_item *mode_tree; mode_tree = proto_item_add_subtree(item, ett_9P_dm); if(!mode_tree) return; proto_tree_add_item(mode_tree, hf_9P_dm_dir, tvb, offset, 4, ENC_LITTLE_ENDIAN); if(!iscreate) { /* Not applicable to Tcreate (?) */ proto_tree_add_item(mode_tree, hf_9P_dm_append, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_exclusive, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_mount, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_auth_file, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_temp_file, tvb, offset, 4, ENC_LITTLE_ENDIAN); } proto_tree_add_item(mode_tree, hf_9P_dm_read_owner, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_write_owner, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_exec_owner, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_read_group, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_write_group, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_exec_group, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_read_others, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_write_others, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(mode_tree, hf_9P_dm_exec_others, tvb, offset, 4, ENC_LITTLE_ENDIAN); } /* Dissect 9P getattr_flags */ static void dissect_9P_getattrflags(tvbuff_t *tvb, proto_item *item, int offset) { proto_item *attrmask_tree; attrmask_tree = proto_item_add_subtree(item, ett_9P_getattr_flags); if(!attrmask_tree) return; /* fixme: This is actually 8 bytes (64bit) long, but masks have to fit on 32bit. */ proto_tree_add_item(attrmask_tree, hf_9P_getattr_mode, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_nlink, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_uid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_gid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_rdev, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_atime, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_mtime, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_ctime, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_ino, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_size, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_blocks, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_btime, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_gen, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_getattr_dataversion, tvb, offset, 4, ENC_LITTLE_ENDIAN); } /* Dissect 9P setattr_flags */ static void dissect_9P_setattrflags(tvbuff_t *tvb, proto_item *item, int offset) { proto_item *attrmask_tree; attrmask_tree = proto_item_add_subtree(item, ett_9P_setattr_flags); if(!attrmask_tree) return; proto_tree_add_item(attrmask_tree, hf_9P_setattr_mode, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_setattr_uid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_setattr_gid, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_setattr_size, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_setattr_atime, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_setattr_mtime, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_setattr_ctime, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_setattr_atime_set, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_setattr_mtime_set, tvb, offset, 4, ENC_LITTLE_ENDIAN); } /* Dissect 9P lflags */ static void dissect_9P_lflags(tvbuff_t *tvb, proto_item *item, int offset) { proto_item *attrmask_tree; attrmask_tree = proto_item_add_subtree(item, ett_9P_lflags); if(!attrmask_tree) return; proto_tree_add_item(attrmask_tree, hf_9P_lflags_rdonly, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_wronly, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_rdwr, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_create, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_excl, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_noctty, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_trunc, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_append, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_nonblock, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_dsync, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_fasync, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_direct, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_largefile, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_directory, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_nofollow, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_noatime, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_cloexec, tvb, offset, 4, ENC_LITTLE_ENDIAN); proto_tree_add_item(attrmask_tree, hf_9P_lflags_sync, tvb, offset, 4, ENC_LITTLE_ENDIAN); } /* Register 9P with Wireshark */ void proto_register_9P(void) { static hf_register_info hf[] = { {&hf_9P_msgsz, {"Msg length", "9p.msglen", FT_UINT32, BASE_DEC, NULL, 0x0, "9P Message Length", HFILL}}, {&hf_9P_msgtype, {"Msg Type", "9p.msgtype", FT_UINT8, BASE_DEC | BASE_EXT_STRING, &ninep_msg_type_ext, 0x0, "Message Type", HFILL}}, {&hf_9P_tag, {"Tag", "9p.tag", FT_UINT16, BASE_DEC, NULL, 0x0, "9P Tag", HFILL}}, {&hf_9P_oldtag, {"Old tag", "9p.oldtag", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_parmsz, {"Param length", "9p.paramsz", FT_UINT16, BASE_DEC, NULL, 0x0, "Parameter length", HFILL}}, {&hf_9P_maxsize, {"Max msg size", "9p.maxsize", FT_UINT32, BASE_DEC, NULL, 0x0, "Max message size", HFILL}}, {&hf_9P_fid, {"Fid", "9p.fid", FT_UINT32, BASE_DEC, NULL, 0x0, "File ID", HFILL}}, {&hf_9P_nqid, {"Nr Qids", "9p.nqid", FT_UINT16, BASE_DEC, NULL, 0x0, "Number of Qid results", HFILL}}, {&hf_9P_mode, {"Mode", "9p.mode", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}}, {&hf_9P_mode_rwx, {"Open/Create Mode", "9p.mode.rwx", FT_UINT8, BASE_OCT | BASE_EXT_STRING, &ninep_mode_vals_ext, _9P_MODEMASK, NULL, HFILL}}, {&hf_9P_mode_t, {"Trunc", "9p.mode.trunc", FT_BOOLEAN, 8, TFS(&tfs_set_notset), _9P_OTRUNC, "Truncate", HFILL}}, {&hf_9P_mode_c, {"Remove on close", "9p.mode.orclose", FT_BOOLEAN, 8, TFS(&tfs_set_notset), _9P_ORCLOSE, NULL, HFILL}}, {&hf_9P_extension, {"Extension string", "9p.extension", FT_STRING, BASE_NONE, NULL, 0x0, "Link target for DSYMLINK mode, major+minor for DMDEVICE, empty for normal files", HFILL}}, {&hf_9P_iounit, {"I/O Unit", "9p.iounit", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_count, {"Count", "9p.count", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_offset, {"Offset", "9p.offset", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_perm, {"Permissions", "9p.perm", FT_UINT32, BASE_OCT, NULL, 0x0, "Permission bits", HFILL}}, {&hf_9P_qidpath, {"Qid path", "9p.qidpath", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_dm_dir, {"Directory", "9p.dm.dir", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x80000000, NULL, HFILL}}, {&hf_9P_dm_append, {"Append only", "9p.dm.append", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x40000000, NULL, HFILL}}, {&hf_9P_dm_exclusive, {"Exclusive use", "9p.dm.exclusive", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x20000000, NULL, HFILL}}, {&hf_9P_dm_mount, {"Mounted channel", "9p.dm.mount", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x10000000, NULL, HFILL}}, {&hf_9P_dm_auth_file, {"Authentication file", "9p.dm.auth_file", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x08000000, NULL, HFILL}}, {&hf_9P_dm_temp_file, {"Temporary file (not backed up)", "9p.dm.temp_file", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x04000000, NULL, HFILL}}, {&hf_9P_dm_read_owner, {"Read permission for owner", "9p.dm.read_owner", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x00000100, NULL, HFILL}}, {&hf_9P_dm_write_owner, {"Write permission for owner", "9p.dm.write_owner", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x00000080, NULL, HFILL}}, {&hf_9P_dm_exec_owner, {"Execute permission for owner", "9p.dm.exec_owner", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x00000040, NULL, HFILL}}, {&hf_9P_dm_read_group, {"Read permission for group", "9p.dm.read_group", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x00000020, NULL, HFILL}}, {&hf_9P_dm_write_group, {"Write permission for group", "9p.dm.write_group", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x00000010, NULL, HFILL}}, {&hf_9P_dm_exec_group, {"Execute permission for group", "9p.dm.exec_group", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x00000008, NULL, HFILL}}, {&hf_9P_dm_read_others, {"Read permission for others", "9p.dm.read_others", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x00000004, NULL, HFILL}}, {&hf_9P_dm_write_others, {"Write permission for others", "9p.dm.write_others", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x00000002, NULL, HFILL}}, {&hf_9P_dm_exec_others, {"Execute permission for others", "9p.dm.exec_others", FT_BOOLEAN, 32, TFS(&tfs_yes_no), 0x00000001, NULL, HFILL}}, {&hf_9P_qidvers, {"Qid version", "9p.qidvers", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_qidtype, {"Qid type", "9p.qidtype", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}}, {&hf_9P_qidtype_dir, {"Directory", "9p.qidtype.dir", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x80, NULL, HFILL}}, {&hf_9P_qidtype_append, {"Append only", "9p.qidtype.append", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x40, NULL, HFILL}}, {&hf_9P_qidtype_exclusive, {"Exclusive use", "9p.qidtype.exclusive", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x20, NULL, HFILL}}, {&hf_9P_qidtype_mount, {"Mounted channel", "9p.qidtype.mount", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x10, NULL, HFILL}}, {&hf_9P_qidtype_auth_file, {"Authentication file", "9p.qidtype.auth_file", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x08, NULL, HFILL}}, {&hf_9P_qidtype_temp_file, {"Temporary file (not backed up)", "9p.qidtype.temp_file", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x04, NULL, HFILL}}, {&hf_9P_statmode, {"Mode", "9p.statmode", FT_UINT32, BASE_OCT, NULL, 0x0, "File mode flags", HFILL}}, {&hf_9P_stattype, {"Type", "9p.stattype", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_atime, {"Atime", "9p.atime", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, "Access Time", HFILL}}, {&hf_9P_mtime, {"Mtime", "9p.mtime", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, "Modified Time", HFILL}}, {&hf_9P_ctime, {"Ctime", "9p.ctime", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, "Creation Time", HFILL}}, {&hf_9P_btime, {"Btime", "9p.btime", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, "Btime (Synchronization information)", HFILL}}, {&hf_9P_length, {"Length", "9p.length", FT_UINT64, BASE_DEC, NULL, 0x0, "File Length", HFILL}}, {&hf_9P_dev, {"Dev", "9p.dev", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_wname, {"Wname", "9p.wname", FT_STRING, BASE_NONE, NULL, 0x0, "Path Name Element", HFILL}}, {&hf_9P_version, {"Version", "9p.version", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, {&hf_9P_afid, {"Afid", "9p.afid", FT_UINT32, BASE_DEC, NULL, 0x0, "Authenticating FID", HFILL}}, {&hf_9P_uname, {"Uname", "9p.uname", FT_STRING, BASE_NONE, NULL, 0x0, "User Name", HFILL}}, {&hf_9P_aname, {"Aname", "9p.aname", FT_STRING, BASE_NONE, NULL, 0x0, "Access Name", HFILL}}, {&hf_9P_ename, {"Ename", "9p.ename", FT_STRING, BASE_NONE, NULL, 0x0, "Error", HFILL}}, {&hf_9P_enum, {"Enum", "9p.enum", FT_UINT32, BASE_DEC, NULL, 0x0, "Error", HFILL}}, #if 0 {&hf_9P_name, {"Name", "9p.name", FT_STRING, BASE_NONE, NULL, 0x0, "Name of file", HFILL}}, #endif {&hf_9P_sdlen, {"Stat data length", "9p.sdlen", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_filename, {"File name", "9p.filename", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}}, {&hf_9P_user, {"User", "9p.user", FT_STRING, BASE_NONE, NULL, 0x0, "User name", HFILL}}, {&hf_9P_group, {"Group", "9p.group", FT_STRING, BASE_NONE, NULL, 0x0, "Group name", HFILL}}, {&hf_9P_uid, {"Uid", "9p.uid", FT_UINT32, BASE_DEC, NULL, 0x0, "User id", HFILL}}, {&hf_9P_gid, {"Gid", "9p.gid", FT_UINT32, BASE_DEC, NULL, 0x0, "Group id", HFILL}}, {&hf_9P_muid, {"Muid", "9p.muid", FT_STRING, BASE_NONE, NULL, 0x0, "Last modifiers uid", HFILL}}, {&hf_9P_newfid, {"New fid", "9p.newfid", FT_UINT32, BASE_DEC, NULL, 0x0, "New file ID", HFILL}}, {&hf_9P_dfid, {"Directory fid", "9p.dfid", FT_UINT32, BASE_DEC, NULL, 0x0, "Directory ID", HFILL}}, {&hf_9P_nwalk, {"Nr Walks", "9p.nwalk", FT_UINT32, BASE_DEC, NULL, 0x0, "Nr of walk items", HFILL}}, {&hf_9P_nlink, {"nlink", "9p.nlink", FT_UINT64, BASE_DEC, NULL, 0x0, "Number of links", HFILL}}, {&hf_9P_getattr_flags, {"getattr_flags", "9p.getattr.flags", FT_UINT64, BASE_HEX, NULL, _9P_GETATTR_ALL, "Getattr flags", HFILL}}, {&hf_9P_getattr_mode, {"Mode", "9p.getattr.mode", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_MODE, NULL, HFILL}}, {&hf_9P_getattr_nlink, {"Nlink", "9p.getattr.nlink", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_NLINK, NULL, HFILL}}, {&hf_9P_getattr_uid, {"UID", "9p.getattr.uid", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_UID, NULL, HFILL}}, {&hf_9P_getattr_gid, {"GID", "9p.getattr.gid", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_GID, NULL, HFILL}}, {&hf_9P_getattr_rdev, {"Rdev", "9p.getattr.rdev", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_RDEV, NULL, HFILL}}, {&hf_9P_getattr_atime, {"Atime", "9p.getattr.atime", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_ATIME, NULL, HFILL}}, {&hf_9P_getattr_mtime, {"Mtime", "9p.getattr.mtime", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_MTIME, NULL, HFILL}}, {&hf_9P_getattr_ctime, {"Ctime", "9p.getattr.ctime", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_CTIME, NULL, HFILL}}, {&hf_9P_getattr_ino, {"Inode", "9p.getattr.inode", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_INO, NULL, HFILL}}, {&hf_9P_getattr_size, {"Size", "9p.getattr.size", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_SIZE, NULL, HFILL}}, {&hf_9P_getattr_blocks, {"Blocks", "9p.getattr.blocks", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_BLOCKS, NULL, HFILL}}, {&hf_9P_getattr_btime, {"Btime", "9p.getattr.btime", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_BTIME, NULL, HFILL}}, {&hf_9P_getattr_gen, {"Gen", "9p.getattr.gen", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_GEN, NULL, HFILL}}, {&hf_9P_getattr_dataversion, {"Data version", "9p.getattr.dataversion", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_GETATTR_DATA_VERSION, NULL, HFILL}}, {&hf_9P_setattr_flags, {"setattr_flags", "9p.setattr.flags", FT_UINT32, BASE_HEX, NULL, _9P_SETATTR_ALL, "Setattr flags", HFILL}}, {&hf_9P_setattr_mode, {"Mode", "9p.setattr.mode", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_SETATTR_MODE, NULL, HFILL}}, {&hf_9P_setattr_uid, {"UID", "9p.setattr.uid", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_SETATTR_UID, NULL, HFILL}}, {&hf_9P_setattr_gid, {"GID", "9p.setattr.gid", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_SETATTR_GID, NULL, HFILL}}, {&hf_9P_setattr_size, {"Size", "9p.setattr.size", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_SETATTR_SIZE, NULL, HFILL}}, {&hf_9P_setattr_atime, {"Atime", "9p.setattr.atime", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_SETATTR_ATIME, NULL, HFILL}}, {&hf_9P_setattr_mtime, {"Mtime", "9p.setattr.mtime", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_SETATTR_MTIME, NULL, HFILL}}, {&hf_9P_setattr_ctime, {"Ctime", "9p.setattr.ctime", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_SETATTR_CTIME, NULL, HFILL}}, {&hf_9P_setattr_atime_set, {"Atime set", "9p.setattr.atimeset", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_SETATTR_ATIME_SET, NULL, HFILL}}, {&hf_9P_setattr_mtime_set, {"Mtime set", "9p.setattr.mtimeset", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_SETATTR_MTIME_SET, NULL, HFILL}}, {&hf_9P_unlinkat_flags, {"unlinkat flags", "9p.unlinkat.flags", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}}, {&hf_9P_rdev, {"rdev", "9p.rdev", FT_UINT64, BASE_DEC, NULL, 0x0, "Device associated with file", HFILL}}, {&hf_9P_size, {"Size", "9p.size", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_blksize, {"Blksize", "9p.blksize", FT_UINT64, BASE_DEC, NULL, 0x0, "Block size", HFILL}}, {&hf_9P_blocks, {"Blocks", "9p.blocks", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}}, {&hf_9P_gen, {"Gen", "9p.gen", FT_UINT64, BASE_DEC, NULL, 0x0, "inode generation number", HFILL}}, {&hf_9P_dataversion, {"Dataversion", "9p.dataversion", FT_UINT64, BASE_DEC, NULL, 0x0, "Data version", HFILL}}, {&hf_9P_fstype, {"fstype", "9p.fstype", FT_UINT32, BASE_HEX, NULL, 0x0, "Filesystem type", HFILL}}, {&hf_9P_bfree, {"bfree", "9p.bfree", FT_UINT64, BASE_DEC, NULL, 0x0, "Free blocks", HFILL}}, {&hf_9P_bavail, {"bavail", "9p.bavail", FT_UINT64, BASE_DEC, NULL, 0x0, "Available blocks", HFILL}}, {&hf_9P_files, {"files", "9p.files", FT_UINT64, BASE_DEC, NULL, 0x0, "Total files", HFILL}}, {&hf_9P_ffree, {"ffree", "9p.ffree", FT_UINT64, BASE_DEC, NULL, 0x0, "Free files", HFILL}}, {&hf_9P_fsid, {"fsid", "9p.fsid", FT_UINT64, BASE_DEC, NULL, 0x0, "Filesystem id", HFILL}}, {&hf_9P_namelen, {"namelen", "9p.namelen", FT_UINT32, BASE_DEC, NULL, 0x0, "Max name length", HFILL}}, {&hf_9P_mknod_major, {"mknod_major", "9p.mknod.major", FT_UINT32, BASE_DEC, NULL, 0x0, "Major node number", HFILL}}, {&hf_9P_mknod_minor, {"mknod_minor", "9p.mknod.minor", FT_UINT32, BASE_DEC, NULL, 0x0, "Minor node number", HFILL}}, {&hf_9P_lflags, {"lflags", "9p.lcreate.flags", FT_UINT32, BASE_HEX, NULL, 0x0, "Lcreate flags", HFILL}}, /* rdonly is 0x00, check instead that we are neither wronly nor rdwrite */ {&hf_9P_lflags_rdonly, {"Read only", "9p.lflags.rdonly", FT_BOOLEAN, 32, TFS(&tfs_no_yes), _9P_DOTL_WRONLY|_9P_DOTL_RDWR, NULL, HFILL}}, {&hf_9P_lflags_wronly, {"Write only", "9p.lflags.wronly", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_WRONLY, NULL, HFILL}}, {&hf_9P_lflags_rdwr, {"Read Write", "9p.lflags.rdwr", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_RDWR, NULL, HFILL}}, {&hf_9P_lflags_create, {"Create", "9p.lflags.create", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_CREATE, NULL, HFILL}}, {&hf_9P_lflags_excl, {"Exclusive", "9p.lflags.excl", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_EXCL, NULL, HFILL}}, {&hf_9P_lflags_noctty, {"noctty", "9p.lflags.noctty", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_NOCTTY, NULL, HFILL}}, {&hf_9P_lflags_trunc, {"Truncate", "9p.lflags.trunc", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_TRUNC, NULL, HFILL}}, {&hf_9P_lflags_append, {"Append", "9p.lflags.append", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_APPEND, NULL, HFILL}}, {&hf_9P_lflags_nonblock, {"Nonblock", "9p.lflags.nonblock", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_NONBLOCK, NULL, HFILL}}, {&hf_9P_lflags_dsync, {"dsync", "9p.lflags.dsync", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_DSYNC, NULL, HFILL}}, {&hf_9P_lflags_fasync, {"fasync", "9p.lflags.fasync", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_FASYNC, NULL, HFILL}}, {&hf_9P_lflags_direct, {"Direct", "9p.lflags.direct", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_DIRECT, NULL, HFILL}}, {&hf_9P_lflags_largefile, {"Large File", "9p.lflags.largefile", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_LARGEFILE, NULL, HFILL}}, {&hf_9P_lflags_directory, {"Directory", "9p.lflags.directory", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_DIRECTORY, NULL, HFILL}}, {&hf_9P_lflags_nofollow, {"No follow", "9p.lflags.nofollow", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_NOFOLLOW, NULL, HFILL}}, {&hf_9P_lflags_noatime, {"No atime", "9p.lflags.noatime", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_NOATIME, NULL, HFILL}}, {&hf_9P_lflags_cloexec, {"cloexec", "9p.lflags.cloexec", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_CLOEXEC, NULL, HFILL}}, {&hf_9P_lflags_sync, {"Sync", "9p.lflags.sync", FT_BOOLEAN, 32, TFS(&tfs_yes_no), _9P_DOTL_SYNC, NULL, HFILL}}, {&hf_9P_xattr_flag, {"xattr_flag", "9p.xattr.flag", FT_UINT32, BASE_HEX, NULL, 0x0, "Xattr flag", HFILL}}, {&hf_9P_lock_type, {"lock_type", "9p.lock.type", FT_UINT32, BASE_HEX | BASE_EXT_STRING, &ninep_lock_type_ext, 0x0, "Lock type", HFILL}}, {&hf_9P_lock_flag, {"lock_flag", "9p.lock.flag", FT_UINT32, BASE_HEX | BASE_EXT_STRING, &ninep_lock_flag_ext, 0x0, "Lock flag", HFILL}}, {&hf_9P_lock_start, {"lock_start", "9p.lock.start", FT_UINT64, BASE_DEC, NULL, 0x0, "Lock start", HFILL}}, {&hf_9P_lock_length, {"lock_length", "9p.lock.length", FT_UINT64, BASE_DEC, NULL, 0x0, "Lock length", HFILL}}, {&hf_9P_lock_procid, {"lock_procid", "9p.lock.procid", FT_UINT32, BASE_HEX, NULL, 0x0, "Lock procid", HFILL}}, {&hf_9P_lock_status, {"lock_status", "9p.lock.status", FT_UINT8, BASE_HEX | BASE_EXT_STRING, &ninep_lock_status_ext, 0x0, "Lock status", HFILL}}, {&hf_9P_unknown_message, {"Message data", "9p.message_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}} }; static int *ett[] = { &ett_9P, &ett_9P_omode, &ett_9P_dm, &ett_9P_wname, &ett_9P_aname, &ett_9P_ename, &ett_9P_uname, &ett_9P_user, &ett_9P_group, &ett_9P_muid, &ett_9P_filename, &ett_9P_version, &ett_9P_qid, &ett_9P_qidtype, &ett_9P_getattr_flags, &ett_9P_setattr_flags, &ett_9P_lflags, }; expert_module_t* expert_9P; static ei_register_info ei[] = { { &ei_9P_first_250, { "9p.first_250", PI_PROTOCOL, PI_NOTE, "Only first 250 items shown", EXPFILL }}, { &ei_9P_msgtype, { "9p.msgtype.unknown", PI_PROTOCOL, PI_WARN, "This message type should not happen", EXPFILL }}, }; proto_9P = proto_register_protocol("Plan 9", "9P", "9p"); proto_register_field_array(proto_9P, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); expert_9P = expert_register_protocol(proto_9P); expert_register_field_array(expert_9P, ei, array_length(ei)); _9p_hashtable = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), _9p_hash_hash, _9p_hash_equal); ninep_handle = register_dissector("9p", dissect_9P, proto_9P); } void proto_reg_handoff_9P(void) { dissector_add_uint_with_preference("tcp.port", NINEPORT, ninep_handle); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 8 * tab-width: 8 * indent-tabs-mode: t * End: * * vi: set shiftwidth=8 tabstop=8 noexpandtab: * :indentSize=8:tabSize=8:noTabs=false: */