1 /* 2 * Copyright (c) 2016 Gerard Garcia <nouboh@gmail.com> 3 * Copyright (c) 2017 Red Hat, Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 3. The names of the authors may not be used to endorse or promote 16 * products derived from this software without specific prior 17 * written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 22 */ 23 24 /* \summary: Linux vsock printer */ 25 26 #ifdef HAVE_CONFIG_H 27 #include <config.h> 28 #endif 29 30 #include "netdissect-stdinc.h" 31 #include <stddef.h> 32 33 #include "netdissect.h" 34 #include "extract.h" 35 36 enum af_vsockmon_transport { 37 AF_VSOCK_TRANSPORT_UNKNOWN = 0, 38 AF_VSOCK_TRANSPORT_NO_INFO = 1, /* No transport information */ 39 AF_VSOCK_TRANSPORT_VIRTIO = 2, /* Virtio transport header */ 40 }; 41 42 static const struct tok vsock_transport[] = { 43 {AF_VSOCK_TRANSPORT_UNKNOWN, "UNKNOWN"}, 44 {AF_VSOCK_TRANSPORT_NO_INFO, "NO_INFO"}, 45 {AF_VSOCK_TRANSPORT_VIRTIO, "VIRTIO"}, 46 { 0, NULL } 47 }; 48 49 enum af_vsockmon_op { 50 AF_VSOCK_OP_UNKNOWN = 0, 51 AF_VSOCK_OP_CONNECT = 1, 52 AF_VSOCK_OP_DISCONNECT = 2, 53 AF_VSOCK_OP_CONTROL = 3, 54 AF_VSOCK_OP_PAYLOAD = 4, 55 }; 56 57 static const struct tok vsock_op[] = { 58 {AF_VSOCK_OP_UNKNOWN, "UNKNOWN"}, 59 {AF_VSOCK_OP_CONNECT, "CONNECT"}, 60 {AF_VSOCK_OP_DISCONNECT, "DISCONNECT"}, 61 {AF_VSOCK_OP_CONTROL, "CONTROL"}, 62 {AF_VSOCK_OP_PAYLOAD, "PAYLOAD"}, 63 { 0, NULL } 64 }; 65 66 enum virtio_vsock_type { 67 VIRTIO_VSOCK_TYPE_STREAM = 1, 68 }; 69 70 static const struct tok virtio_type[] = { 71 {VIRTIO_VSOCK_TYPE_STREAM, "STREAM"}, 72 { 0, NULL } 73 }; 74 75 enum virtio_vsock_op { 76 VIRTIO_VSOCK_OP_INVALID = 0, 77 VIRTIO_VSOCK_OP_REQUEST = 1, 78 VIRTIO_VSOCK_OP_RESPONSE = 2, 79 VIRTIO_VSOCK_OP_RST = 3, 80 VIRTIO_VSOCK_OP_SHUTDOWN = 4, 81 VIRTIO_VSOCK_OP_RW = 5, 82 VIRTIO_VSOCK_OP_CREDIT_UPDATE = 6, 83 VIRTIO_VSOCK_OP_CREDIT_REQUEST = 7, 84 }; 85 86 static const struct tok virtio_op[] = { 87 {VIRTIO_VSOCK_OP_INVALID, "INVALID"}, 88 {VIRTIO_VSOCK_OP_REQUEST, "REQUEST"}, 89 {VIRTIO_VSOCK_OP_RESPONSE, "RESPONSE"}, 90 {VIRTIO_VSOCK_OP_RST, "RST"}, 91 {VIRTIO_VSOCK_OP_SHUTDOWN, "SHUTDOWN"}, 92 {VIRTIO_VSOCK_OP_RW, "RW"}, 93 {VIRTIO_VSOCK_OP_CREDIT_UPDATE, "CREDIT UPDATE"}, 94 {VIRTIO_VSOCK_OP_CREDIT_REQUEST, "CREDIT REQUEST"}, 95 { 0, NULL } 96 }; 97 98 /* All fields are little-endian */ 99 100 struct virtio_vsock_hdr { 101 nd_uint64_t src_cid; 102 nd_uint64_t dst_cid; 103 nd_uint32_t src_port; 104 nd_uint32_t dst_port; 105 nd_uint32_t len; 106 nd_uint16_t type; /* enum virtio_vsock_type */ 107 nd_uint16_t op; /* enum virtio_vsock_op */ 108 nd_uint32_t flags; 109 nd_uint32_t buf_alloc; 110 nd_uint32_t fwd_cnt; 111 }; 112 113 struct af_vsockmon_hdr { 114 nd_uint64_t src_cid; 115 nd_uint64_t dst_cid; 116 nd_uint32_t src_port; 117 nd_uint32_t dst_port; 118 nd_uint16_t op; /* enum af_vsockmon_op */ 119 nd_uint16_t transport; /* enum af_vosckmon_transport */ 120 nd_uint16_t len; /* size of transport header */ 121 nd_uint8_t reserved[2]; 122 }; 123 124 static void 125 vsock_virtio_hdr_print(netdissect_options *ndo, const struct virtio_vsock_hdr *hdr) 126 { 127 uint16_t u16_v; 128 uint32_t u32_v; 129 130 u32_v = GET_LE_U_4(hdr->len); 131 ND_PRINT("len %u", u32_v); 132 133 u16_v = GET_LE_U_2(hdr->type); 134 ND_PRINT(", type %s", 135 tok2str(virtio_type, "Invalid type (%hu)", u16_v)); 136 137 u16_v = GET_LE_U_2(hdr->op); 138 ND_PRINT(", op %s", 139 tok2str(virtio_op, "Invalid op (%hu)", u16_v)); 140 141 u32_v = GET_LE_U_4(hdr->flags); 142 ND_PRINT(", flags %x", u32_v); 143 144 u32_v = GET_LE_U_4(hdr->buf_alloc); 145 ND_PRINT(", buf_alloc %u", u32_v); 146 147 u32_v = GET_LE_U_4(hdr->fwd_cnt); 148 ND_PRINT(", fwd_cnt %u", u32_v); 149 } 150 151 /* 152 * This size had better fit in a u_int. 153 */ 154 static u_int 155 vsock_transport_hdr_size(uint16_t transport) 156 { 157 switch (transport) { 158 case AF_VSOCK_TRANSPORT_VIRTIO: 159 return (u_int)sizeof(struct virtio_vsock_hdr); 160 default: 161 return 0; 162 } 163 } 164 165 /* Returns 0 on success, -1 on truncation */ 166 static int 167 vsock_transport_hdr_print(netdissect_options *ndo, uint16_t transport, 168 const u_char *p, const u_int caplen) 169 { 170 u_int transport_size = vsock_transport_hdr_size(transport); 171 const void *hdr; 172 173 if (caplen < sizeof(struct af_vsockmon_hdr) + transport_size) { 174 return -1; 175 } 176 177 hdr = p + sizeof(struct af_vsockmon_hdr); 178 switch (transport) { 179 case AF_VSOCK_TRANSPORT_VIRTIO: 180 ND_PRINT(" ("); 181 vsock_virtio_hdr_print(ndo, hdr); 182 ND_PRINT(")"); 183 break; 184 default: 185 break; 186 } 187 return 0; 188 } 189 190 static void 191 vsock_hdr_print(netdissect_options *ndo, const u_char *p, const u_int caplen) 192 { 193 const struct af_vsockmon_hdr *hdr = (const struct af_vsockmon_hdr *)p; 194 uint16_t hdr_transport, hdr_op; 195 uint32_t hdr_src_port, hdr_dst_port; 196 uint64_t hdr_src_cid, hdr_dst_cid; 197 u_int total_hdr_size; 198 int ret = 0; 199 200 hdr_transport = GET_LE_U_2(hdr->transport); 201 ND_PRINT("%s", 202 tok2str(vsock_transport, "Invalid transport (%u)", 203 hdr_transport)); 204 205 /* If verbose level is more than 0 print transport details */ 206 if (ndo->ndo_vflag) { 207 ret = vsock_transport_hdr_print(ndo, hdr_transport, p, caplen); 208 if (ret == 0) 209 ND_PRINT("\n\t"); 210 } else 211 ND_PRINT(" "); 212 213 hdr_src_cid = GET_LE_U_8(hdr->src_cid); 214 hdr_dst_cid = GET_LE_U_8(hdr->dst_cid); 215 hdr_src_port = GET_LE_U_4(hdr->src_port); 216 hdr_dst_port = GET_LE_U_4(hdr->dst_port); 217 hdr_op = GET_LE_U_2(hdr->op); 218 ND_PRINT("%" PRIu64 ".%u > %" PRIu64 ".%u %s, length %u", 219 hdr_src_cid, hdr_src_port, 220 hdr_dst_cid, hdr_dst_port, 221 tok2str(vsock_op, " invalid op (%u)", hdr_op), 222 caplen); 223 224 if (ret < 0) 225 goto trunc; 226 227 /* If debug level is more than 1 print payload contents */ 228 /* This size had better fit in a u_int */ 229 total_hdr_size = (u_int)sizeof(struct af_vsockmon_hdr) + 230 vsock_transport_hdr_size(hdr_transport); 231 if (ndo->ndo_vflag > 1 && hdr_op == AF_VSOCK_OP_PAYLOAD) { 232 if (caplen > total_hdr_size) { 233 const u_char *payload = p + total_hdr_size; 234 235 ND_PRINT("\n"); 236 print_unknown_data(ndo, payload, "\t", 237 caplen - total_hdr_size); 238 } else 239 goto trunc; 240 } 241 return; 242 243 trunc: 244 nd_print_trunc(ndo); 245 } 246 247 void 248 vsock_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, 249 const u_char *cp) 250 { 251 u_int caplen = h->caplen; 252 253 ndo->ndo_protocol = "vsock"; 254 255 if (caplen < sizeof(struct af_vsockmon_hdr)) { 256 nd_print_trunc(ndo); 257 ndo->ndo_ll_hdr_len += caplen; 258 return; 259 } 260 ndo->ndo_ll_hdr_len += sizeof(struct af_vsockmon_hdr); 261 vsock_hdr_print(ndo, cp, caplen); 262 } 263