monitor: Improve message and attribute display handling

This commit is contained in:
Marcel Holtmann 2014-08-09 22:59:42 -07:00
parent 9635f317c0
commit 7a1bb1e953
4 changed files with 231 additions and 104 deletions

View File

@ -24,6 +24,20 @@
bool use_color(void); bool use_color(void);
#define COLOR_OFF "\x1B[0m"
#define COLOR_BLACK "\x1B[0;30m"
#define COLOR_RED "\x1B[0;31m"
#define COLOR_GREEN "\x1B[0;32m"
#define COLOR_YELLOW "\x1B[0;33m"
#define COLOR_BLUE "\x1B[0;34m"
#define COLOR_MAGENTA "\x1B[0;35m"
#define COLOR_CYAN "\x1B[0;36m"
#define COLOR_WHITE "\x1B[0;37m"
#define COLOR_WHITE_BG "\x1B[0;47m"
#define COLOR_HIGHLIGHT "\x1B[1;39m"
#define COLOR_ERROR "\x1B[1;31m"
unsigned int num_columns(void); unsigned int num_columns(void);
void open_pager(void); void open_pager(void);

View File

@ -168,14 +168,10 @@ static int process_pcap(struct pcap *pcap)
switch (L_BE16_TO_CPU(proto_type)) { switch (L_BE16_TO_CPU(proto_type)) {
case NETLINK_ROUTE: case NETLINK_ROUTE:
printf("RTNL packet\n"); nlmon_print_rtnl(nlmon, buf, len);
break; break;
case NETLINK_GENERIC: case NETLINK_GENERIC:
nlmon_print(nlmon, buf + 16, len - 16); nlmon_print_genl(nlmon, buf + 16, len - 16);
break;
default:
printf("Other protocol %u\n",
L_BE16_TO_CPU(proto_type));
break; break;
} }
} }

View File

@ -39,8 +39,23 @@
#include <ell/ell.h> #include <ell/ell.h>
#include "linux/nl80211.h" #include "linux/nl80211.h"
#include "monitor/display.h"
#include "monitor/nlmon.h" #include "monitor/nlmon.h"
#define COLOR_REQUEST COLOR_BLUE
#define COLOR_RESPONSE COLOR_MAGENTA
#define COLOR_COMPLETE COLOR_MAGENTA
#define COLOR_RESULT COLOR_MAGENTA
#define COLOR_EVENT COLOR_CYAN
enum msg_type {
MSG_REQUEST,
MSG_RESPONSE,
MSG_COMPLETE,
MSG_RESULT,
MSG_EVENT,
};
struct nlmon { struct nlmon {
uint16_t id; uint16_t id;
struct l_io *io; struct l_io *io;
@ -51,6 +66,8 @@ struct nlmon_req {
uint32_t seq; uint32_t seq;
uint32_t pid; uint32_t pid;
uint16_t flags; uint16_t flags;
uint8_t cmd;
uint8_t version;
}; };
static void nlmon_req_free(void *data) static void nlmon_req_free(void *data)
@ -60,13 +77,38 @@ static void nlmon_req_free(void *data)
l_free(req); l_free(req);
} }
#define print_indent(indent, fmt, args...) \ #define print_indent(indent, color1, prefix, title, color2, fmt, args...) \
printf("%*c" fmt "\n", (indent), ' ', ## args) do { \
printf("%*c%s%s%s%s" fmt "%s\n", (indent), ' ', \
use_color() ? (color1) : "", prefix, title, \
use_color() ? (color2) : "", ## args, \
use_color() ? COLOR_OFF : ""); \
} while (0)
#define print_text(fmt, args...) \ #define print_text(color, fmt, args...) \
print_indent(8, fmt, ## args) print_indent(4, COLOR_OFF, "", "", color, fmt, ## args)
static void print_hexdump(const unsigned char *buf, uint16_t len) #define print_field(fmt, args...) \
print_indent(4, COLOR_OFF, "", "", COLOR_OFF, fmt, ## args)
#define print_attr(level, fmt, args...) \
print_indent(4 + (level) * 4, COLOR_OFF, "", "", COLOR_OFF, \
fmt, ## args)
#define print_attr_color(level, color, fmt, args...) \
print_indent(4 + (level) * 4, COLOR_OFF, "", "", color, \
fmt, ## args)
static void print_packet(struct timeval *tv, char ident,
const char *color, const char *label,
const char *text, const char *extra)
{
printf("%s%c %s: %s%s %s\n", use_color() ? color : "", ident, label,
text, use_color() ? COLOR_OFF : "" ,extra);
}
static void print_hexdump(unsigned int level,
const unsigned char *buf, uint16_t len)
{ {
static const char hexdigits[] = "0123456789abcdef"; static const char hexdigits[] = "0123456789abcdef";
char str[68]; char str[68];
@ -85,7 +127,7 @@ static void print_hexdump(const unsigned char *buf, uint16_t len)
str[47] = ' '; str[47] = ' ';
str[48] = ' '; str[48] = ' ';
str[65] = '\0'; str[65] = '\0';
print_text("%s", str); print_attr_color(level, COLOR_WHITE, "%s", str);
str[0] = ' '; str[0] = ' ';
} }
} }
@ -101,7 +143,7 @@ static void print_hexdump(const unsigned char *buf, uint16_t len)
str[47] = ' '; str[47] = ' ';
str[48] = ' '; str[48] = ' ';
str[65] = '\0'; str[65] = '\0';
print_text("%s", str); print_attr_color(level, COLOR_WHITE, "%s", str);
} }
} }
@ -614,21 +656,19 @@ static void print_value(int indent, const char *label, enum attr_type type,
switch (type) { switch (type) {
case ATTR_U16: case ATTR_U16:
val_u16 = *((uint16_t *) buf); val_u16 = *((uint16_t *) buf);
printf("%*c%s: %u (0x%04x)\n", indent, ' ', print_attr(indent, "%s: %u (0x%04x)", label, val_u16, val_u16);
label, val_u16, val_u16);
if (len != 2) if (len != 2)
printf("malformed packet\n"); printf("malformed packet\n");
break; break;
case ATTR_U32: case ATTR_U32:
val_u32 = *((uint32_t *) buf); val_u32 = *((uint32_t *) buf);
printf("%*c%s: %u (0x%08x)\n", indent, ' ', print_attr(indent, "%s: %u (0x%08x)", label, val_u32, val_u32);
label, val_u32, val_u32);
if (len != 4) if (len != 4)
printf("malformed packet\n"); printf("malformed packet\n");
break; break;
default: default:
printf("%*c%s: len %u\n", indent, ' ', label, len); print_attr(indent, "%s: len %u", label, len);
print_hexdump(buf, len); print_hexdump(indent + 1, buf, len);
break; break;
} }
} }
@ -687,51 +727,51 @@ static void print_attributes(int indent, const struct attr_entry *table,
switch (type) { switch (type) {
case ATTR_UNSPEC: case ATTR_UNSPEC:
printf("%*c%s: len %u\n", indent, ' ', str, print_attr(indent, "%s: len %u", str,
NLA_PAYLOAD(nla)); NLA_PAYLOAD(nla));
print_hexdump(NLA_DATA(nla), NLA_PAYLOAD(nla)); print_hexdump(indent + 1,
NLA_DATA(nla), NLA_PAYLOAD(nla));
break; break;
case ATTR_FLAG: case ATTR_FLAG:
printf("%*c%s: true\n", indent, ' ', str); print_attr(indent, "%s: true", str);
if (NLA_PAYLOAD(nla) != 0) if (NLA_PAYLOAD(nla) != 0)
printf("malformed packet\n"); printf("malformed packet\n");
break; break;
case ATTR_U8: case ATTR_U8:
val8 = *((uint8_t *) NLA_DATA(nla)); val8 = *((uint8_t *) NLA_DATA(nla));
printf("%*c%s: %u (0x%02x)\n", indent, ' ', str, print_attr(indent, "%s: %u (0x%02x)", str, val8, val8);
val8, val8);
if (NLA_PAYLOAD(nla) != 1) if (NLA_PAYLOAD(nla) != 1)
printf("malformed packet\n"); printf("malformed packet\n");
break; break;
case ATTR_U16: case ATTR_U16:
val16 = *((uint16_t *) NLA_DATA(nla)); val16 = *((uint16_t *) NLA_DATA(nla));
printf("%*c%s: %u (0x%04x)\n", indent, ' ', str, print_attr(indent, "%s: %u (0x%04x)", str,
val16, val16); val16, val16);
if (NLA_PAYLOAD(nla) != 2) if (NLA_PAYLOAD(nla) != 2)
printf("malformed packet\n"); printf("malformed packet\n");
break; break;
case ATTR_U32: case ATTR_U32:
val32 = *((uint32_t *) NLA_DATA(nla)); val32 = *((uint32_t *) NLA_DATA(nla));
printf("%*c%s: %u (0x%08x)\n", indent, ' ', str, print_attr(indent, "%s: %u (0x%08x)", str,
val32, val32); val32, val32);
if (NLA_PAYLOAD(nla) != 4) if (NLA_PAYLOAD(nla) != 4)
printf("malformed packet\n"); printf("malformed packet\n");
break; break;
case ATTR_U64: case ATTR_U64:
val64 = *((uint64_t *) NLA_DATA(nla)); val64 = *((uint64_t *) NLA_DATA(nla));
printf("%*c%s: %lu (0x%016lx)\n", indent, ' ', str, print_attr(indent, "%s: %lu (0x%016lx)", str,
val64, val64); val64, val64);
if (NLA_PAYLOAD(nla) != 8) if (NLA_PAYLOAD(nla) != 8)
printf("malformed packet\n"); printf("malformed packet\n");
break; break;
case ATTR_S32: case ATTR_S32:
val_s32 = *((int32_t *) NLA_DATA(nla)); val_s32 = *((int32_t *) NLA_DATA(nla));
printf("%*c%s: %d\n", indent, ' ', str, val_s32); print_attr(indent, "%s: %d", str, val_s32);
if (NLA_PAYLOAD(nla) != 4) if (NLA_PAYLOAD(nla) != 4)
printf("malformed packet\n"); printf("malformed packet\n");
break; break;
case ATTR_STRING: case ATTR_STRING:
printf("%*c%s: %s\n", indent, ' ', str, print_attr(indent, "%s: %s", str,
(char *) NLA_DATA(nla)); (char *) NLA_DATA(nla));
break; break;
case ATTR_ADDRESS: case ATTR_ADDRESS:
@ -740,38 +780,39 @@ static void print_attributes(int indent, const struct attr_entry *table,
"%02X:%02X:%02X:%02X:%02X:%02X", "%02X:%02X:%02X:%02X:%02X:%02X",
ptr[0], ptr[1], ptr[2], ptr[0], ptr[1], ptr[2],
ptr[3], ptr[4], ptr[5]); ptr[3], ptr[4], ptr[5]);
printf("%*c%s: %s\n", indent, ' ', str, addr); print_attr(indent, "%s: %s", str, addr);
if (NLA_PAYLOAD(nla) != 6) if (NLA_PAYLOAD(nla) != 6)
printf("malformed packet\n"); printf("malformed packet\n");
break; break;
case ATTR_BINARY: case ATTR_BINARY:
printf("%*c%s: len %u\n", indent, ' ', str, print_attr(indent, "%s: len %u", str,
NLA_PAYLOAD(nla)); NLA_PAYLOAD(nla));
print_hexdump(NLA_DATA(nla), NLA_PAYLOAD(nla)); print_hexdump(indent + 1,
NLA_DATA(nla), NLA_PAYLOAD(nla));
break; break;
case ATTR_NESTED: case ATTR_NESTED:
printf("%*c%s: len %u\n", indent, ' ', str, print_attr(indent, "%s: len %u", str,
NLA_PAYLOAD(nla)); NLA_PAYLOAD(nla));
if (!nested) if (!nested)
printf("missing table\n"); printf("missing table\n");
print_attributes(indent + 4, nested, print_attributes(indent + 1, nested,
NLA_DATA(nla), NLA_PAYLOAD(nla)); NLA_DATA(nla), NLA_PAYLOAD(nla));
break; break;
case ATTR_ARRAY: case ATTR_ARRAY:
printf("%*c%s: len %u\n", indent, ' ', str, print_attr(indent, "%s: len %u", str,
NLA_PAYLOAD(nla)); NLA_PAYLOAD(nla));
if (array_type == ATTR_UNSPEC) if (array_type == ATTR_UNSPEC)
printf("missing type\n"); printf("missing type\n");
print_array(indent + 4, array_type, print_array(indent + 1, array_type,
NLA_DATA(nla), NLA_PAYLOAD(nla)); NLA_DATA(nla), NLA_PAYLOAD(nla));
break; break;
case ATTR_FLAG_OR_U16: case ATTR_FLAG_OR_U16:
if (NLA_PAYLOAD(nla) == 0) if (NLA_PAYLOAD(nla) == 0)
printf("%*c%s: true\n", indent, ' ', str); print_attr(indent, "%s: true", str);
else if (NLA_PAYLOAD(nla) == 2) { else if (NLA_PAYLOAD(nla) == 2) {
val16 = *((uint16_t *) NLA_DATA(nla)); val16 = *((uint16_t *) NLA_DATA(nla));
printf("%*c%s: %u (0x%04x)\n", indent, ' ', print_attr(indent, "%s: %u (0x%04x)", str,
str, val16, val16); val16, val16);
} else } else
printf("malformed packet\n"); printf("malformed packet\n");
break; break;
@ -890,75 +931,98 @@ static const struct {
{ } { }
}; };
static void print_message(const struct nlmsghdr *nlmsg) static void print_message(enum msg_type type, uint16_t flags, int status,
uint8_t cmd, uint8_t version,
const void *data, uint32_t len)
{ {
const struct genlmsghdr *genlmsg; char extra_str[64];
const char *str; const char *label = "";
bool out; const char *color = COLOR_OFF;
int i; const char *cmd_str;
bool out = false;
int i, pos;
if (nlmsg->nlmsg_seq && (nlmsg->nlmsg_flags & NLM_F_REQUEST)) switch (type) {
case MSG_REQUEST:
label = "Request";
color = COLOR_REQUEST;
out = true; out = true;
else
out = false;
if (nlmsg->nlmsg_type < NLMSG_MIN_TYPE) {
switch (nlmsg->nlmsg_type) {
case NLMSG_NOOP:
str = "Noop";
break; break;
case NLMSG_ERROR: case MSG_RESPONSE:
str = "Complete"; label = "Response";
color = COLOR_RESPONSE;
break; break;
case NLMSG_DONE: case MSG_COMPLETE:
str = "Done"; label = "Complete";
color = COLOR_COMPLETE;
break; break;
case NLMSG_OVERRUN: case MSG_RESULT:
str = "Overrun"; label = "Result";
color = COLOR_RESULT;
break; break;
default: case MSG_EVENT:
str = "Reserved"; label = "Event";
color = COLOR_EVENT;
break; break;
} }
printf("%c %s (%u) flags 0x%04x len %u\n", cmd_str = "Reserved";
out ? '<' : '>', str,
nlmsg->nlmsg_type,
nlmsg->nlmsg_flags,
NLMSG_PAYLOAD(nlmsg, 0));
if (nlmsg->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = NLMSG_DATA(nlmsg);
printf("%*cStatus: %s (%d)\n", 4, ' ',
strerror(-err->error), -err->error);
print_hexdump(NLMSG_DATA(nlmsg) + 4,
NLMSG_PAYLOAD(nlmsg, 4));
} else
print_hexdump(NLMSG_DATA(nlmsg),
NLMSG_PAYLOAD(nlmsg, 0));
return;
}
genlmsg = NLMSG_DATA(nlmsg);
str = "Reserved";
for (i = 0; cmd_table[i].str; i++) { for (i = 0; cmd_table[i].str; i++) {
if (genlmsg->cmd == cmd_table[i].cmd) { if (cmd_table[i].cmd == cmd) {
str = cmd_table[i].str; cmd_str = cmd_table[i].str;
break; break;
} }
} }
printf("%c %s (%u) flags 0x%04x len %u\n", pos = sprintf(extra_str, "(0x%02x) len %u", cmd, len);
out ? '<' : '>', str,
genlmsg->cmd,
nlmsg->nlmsg_flags,
NLMSG_PAYLOAD(nlmsg, 0));
print_attributes(4, attr_table, NLMSG_DATA(nlmsg) + GENL_HDRLEN, if (flags) {
NLMSG_PAYLOAD(nlmsg, GENL_HDRLEN)); pos += sprintf(extra_str + pos, " [");
if (flags & NLM_F_MULTI) {
flags &= ~NLM_F_MULTI;
pos += sprintf(extra_str + pos, "multi%c",
flags ? ',' : ']');
}
if (flags & NLM_F_ACK) {
flags &= ~NLM_F_ACK;
pos += sprintf(extra_str + pos, "ack%c",
flags ? ',' : ']');
}
if (flags & NLM_F_ECHO) {
flags &= ~NLM_F_ECHO;
pos += sprintf(extra_str + pos, "echo%c",
flags ? ',' : ']');
}
if ((flags & NLM_F_DUMP) == NLM_F_DUMP) {
flags &= ~NLM_F_DUMP;
pos += sprintf(extra_str + pos, "dump%c",
flags ? ',' : ']');
}
if (flags)
pos += sprintf(extra_str + pos, "0x%x]", flags);
}
print_packet(NULL, out ? '<' : '>', color, label, cmd_str, extra_str);
switch (type) {
case MSG_REQUEST:
case MSG_RESULT:
case MSG_EVENT:
print_attributes(0, attr_table, data, len);
break;
case MSG_RESPONSE:
print_field("Status: %s (%d)", strerror(status), status);
break;
case MSG_COMPLETE:
print_field("Status: %d", status);
break;
}
} }
struct nlmon_req_match { struct nlmon_req_match {
@ -974,6 +1038,10 @@ static bool nlmon_req_match(const void *a, const void *b)
return (req->seq == match->seq && req->pid == match->pid); return (req->seq == match->seq && req->pid == match->pid);
} }
static void store_message(struct nlmon *nlmon, const struct nlmsghdr *nlmsg)
{
}
static void nlmon_message(struct nlmon *nlmon, const struct nlmsghdr *nlmsg) static void nlmon_message(struct nlmon *nlmon, const struct nlmsghdr *nlmsg)
{ {
struct nlmon_req *req; struct nlmon_req *req;
@ -987,8 +1055,29 @@ static void nlmon_message(struct nlmon *nlmon, const struct nlmsghdr *nlmsg)
req = l_queue_remove_if(nlmon->req_list, req = l_queue_remove_if(nlmon->req_list,
nlmon_req_match, &match); nlmon_req_match, &match);
if (req) { if (req) {
enum msg_type type;
struct nlmsgerr *err;
int status;
switch (nlmsg->nlmsg_type) {
case NLMSG_ERROR:
type = MSG_RESPONSE;
err = NLMSG_DATA(nlmsg);
status = -err->error;
break;
case NLMSG_DONE:
type = MSG_COMPLETE;
status = *((int *) NLMSG_DATA(nlmsg));
break;
default:
return;
}
store_message(nlmon, nlmsg);
print_message(type, nlmsg->nlmsg_flags, status,
req->cmd, req->version,
NULL, sizeof(status));
nlmon_req_free(req); nlmon_req_free(req);
print_message(nlmsg);
} }
return; return;
} }
@ -997,15 +1086,28 @@ static void nlmon_message(struct nlmon *nlmon, const struct nlmsghdr *nlmsg)
return; return;
if (nlmsg->nlmsg_flags & NLM_F_REQUEST) { if (nlmsg->nlmsg_flags & NLM_F_REQUEST) {
const struct genlmsghdr *genlmsg = NLMSG_DATA(nlmsg);
uint32_t flags = nlmsg->nlmsg_flags & ~NLM_F_REQUEST;
req = l_new(struct nlmon_req, 1); req = l_new(struct nlmon_req, 1);
req->seq = nlmsg->nlmsg_seq; req->seq = nlmsg->nlmsg_seq;
req->pid = nlmsg->nlmsg_pid; req->pid = nlmsg->nlmsg_pid;
req->flags = nlmsg->nlmsg_flags; req->flags = nlmsg->nlmsg_flags;
req->cmd = genlmsg->cmd;
req->version = genlmsg->version;
l_queue_push_tail(nlmon->req_list, req); l_queue_push_tail(nlmon->req_list, req);
print_message(nlmsg);
store_message(nlmon, nlmsg);
print_message(MSG_REQUEST, flags, 0,
req->cmd, req->version,
NLMSG_DATA(nlmsg) + GENL_HDRLEN,
NLMSG_PAYLOAD(nlmsg, GENL_HDRLEN));
} else { } else {
const struct genlmsghdr *genlmsg = NLMSG_DATA(nlmsg);
enum msg_type type = MSG_EVENT;
struct nlmon_req_match match = { struct nlmon_req_match match = {
.seq = nlmsg->nlmsg_seq, .seq = nlmsg->nlmsg_seq,
.pid = nlmsg->nlmsg_pid .pid = nlmsg->nlmsg_pid
@ -1017,9 +1119,14 @@ static void nlmon_message(struct nlmon *nlmon, const struct nlmsghdr *nlmsg)
l_queue_remove(nlmon->req_list, req); l_queue_remove(nlmon->req_list, req);
nlmon_req_free(req); nlmon_req_free(req);
} }
type = MSG_RESULT;
} }
print_message(nlmsg); store_message(nlmon, nlmsg);
print_message(type, nlmsg->nlmsg_flags, 0,
genlmsg->cmd, genlmsg->version,
NLMSG_DATA(nlmsg) + GENL_HDRLEN,
NLMSG_PAYLOAD(nlmsg, GENL_HDRLEN));
} }
} }
@ -1074,7 +1181,16 @@ static void genl_ctrl(struct nlmon *nlmon, const void *data, uint32_t len)
nlmon->id = id; nlmon->id = id;
} }
void nlmon_print(struct nlmon *nlmon, const void *data, uint32_t size) void nlmon_print_rtnl(struct nlmon *nlmon, const void *data, uint32_t size)
{
char str[16];
sprintf(str, "len %u", size);
print_packet(NULL, '*', COLOR_WHITE, "Route Netlink", str, "");
}
void nlmon_print_genl(struct nlmon *nlmon, const void *data, uint32_t size)
{ {
const struct nlmsghdr *nlmsg; const struct nlmsghdr *nlmsg;

View File

@ -29,4 +29,5 @@ void nlmon_close(struct nlmon *nlmon);
struct nlmon *nlmon_create(void); struct nlmon *nlmon_create(void);
void nlmon_destroy(struct nlmon *nlmon); void nlmon_destroy(struct nlmon *nlmon);
void nlmon_print(struct nlmon *nlmon, const void *data, uint32_t size); void nlmon_print_rtnl(struct nlmon *nlmon, const void *data, uint32_t size);
void nlmon_print_genl(struct nlmon *nlmon, const void *data, uint32_t size);