#include <sys/time.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "interface.h"
#include "addrtoname.h"
#include "extract.h"
#ifndef roundup
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
#endif
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
struct nsh_header {
uint32_t base;
#define NSH_VER_SHIFT 30
#define NSH_VER_MASK (0x03 << NSH_VER_SHIFT)
#define NSH_VER_0 0x0
#define NSH_VER_RESERVED (0x01 << NSH_VER_SHIFT)
#define NSH_OAM_SHIFT 29
#define NSH_OAM_MASK (0x01 << NSH_OAM_SHIFT)
#define NSH_TTL_SHIFT 22
#define NSH_TTL_MASK (0x3f << NSH_TTL_SHIFT)
#define NSH_LEN_SHIFT 16
#define NSH_LEN_MASK (0x3f << NSH_LEN_SHIFT)
#define NSH_LEN_FACTOR 4
#define NSH_MDTYPE_SHIFT 8
#define NSH_MDTYPE_MASK (0x0f << NSH_MDTYPE_SHIFT)
#define NSH_PROTO_SHIFT 0
#define NSH_PROTO_MASK (0xff << NSH_PROTO_SHIFT)
uint32_t sp;
#define NSH_SPI_SHIFT 8
#define NSH_SPI_MASK (0xffffff << NSH_SPI_SHIFT)
#define NSH_SI_SHIFT 0
#define NSH_SI_MASK (0xff << NSH_SI_SHIFT)
};
#define NSH_PROTO_IPV4 0x01
#define NSH_PROTO_IPV6 0x02
#define NSH_PROTO_ETHERNET 0x03
#define NSH_PROTO_NSH 0x04
#define NSH_PROTO_MPLS 0x05
#define NSH_PROTO_EXP1 0xfe
#define NSH_PROTO_EXP2 0xff
#define NSH_MDTYPE_RESERVED 0x0
#define NSH_MDTYPE_1 0x1
#define NSH_MDTYPE_2 0x2
#define NSH_MDTYPE_EXP 0xf
struct nsh_context_header {
uint32_t ch[4];
};
struct nsh_md_header {
uint16_t class;
uint8_t type;
uint8_t len;
#define NSH_MD_LEN_MASK 0x7f
};
static void nsh_print_bytes(const void *, u_int);
static void nsh_print_mdtype1(const u_char *, u_int);
static void nsh_print_mdtype2(const u_char *, u_int);
void
nsh_print(const u_char *p, u_int length)
{
struct nsh_header nsh;
uint32_t field, len, proto;
int l = snapend - p;
printf("NSH");
if (l < sizeof(nsh))
goto trunc;
if (length < sizeof(nsh)) {
printf(" encapsulation truncated");
return;
}
nsh.base = EXTRACT_32BITS(p);
nsh.sp = EXTRACT_32BITS(p + sizeof(nsh.base));
field = (nsh.base & NSH_VER_MASK) >> NSH_VER_SHIFT;
switch (field) {
case NSH_VER_0:
break;
case NSH_VER_RESERVED:
printf(" Reserved version");
return;
default:
printf(" Unknown version %u", field);
return;
}
field = (nsh.sp & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
printf(" spi %u", field);
field = (nsh.sp & NSH_SI_MASK) >> NSH_SI_SHIFT;
printf(" si %u", field);
len = ((nsh.base & NSH_LEN_MASK) >> NSH_LEN_SHIFT) * NSH_LEN_FACTOR;
if (vflag > 1) {
field = (nsh.base & NSH_TTL_MASK) >> NSH_TTL_SHIFT;
printf(" (ttl %u, len %u)", field, len);
}
if (l < len)
goto trunc;
if (length < len) {
printf(" encapsulation truncated");
return;
}
p += sizeof(nsh);
l -= sizeof(nsh);
len -= sizeof(nsh);
field = (nsh.base & NSH_MDTYPE_MASK) >> NSH_MDTYPE_SHIFT;
switch (field) {
case NSH_MDTYPE_RESERVED:
printf(" md-type-reserved");
break;
case NSH_MDTYPE_1:
printf(" md1");
if (vflag)
nsh_print_mdtype1(p, len);
break;
case NSH_MDTYPE_2:
printf(" md2");
if (vflag)
nsh_print_mdtype2(p, len);
break;
case NSH_MDTYPE_EXP:
printf(" mdtype-experimentation");
break;
default:
printf(" mdtype-unknown-0x%02x", field);
break;
}
printf("%s", vflag ? "\n " : ": ");
p += len;
l -= len;
length -= len;
proto = (nsh.base & NSH_PROTO_MASK) >> NSH_PROTO_SHIFT;
if (nsh.base & NSH_OAM_MASK)
printf("NSH OAM (proto 0x%0x, len %u)", proto, length);
else {
switch (field) {
case NSH_PROTO_IPV4:
ip_print(p, length);
return;
case NSH_PROTO_IPV6:
ip_print(p, length);
return;
case NSH_PROTO_ETHERNET:
ether_tryprint(p, length, 0);
return;
case NSH_PROTO_NSH:
nsh_print(p, length);
return;
case NSH_PROTO_MPLS:
mpls_print(p, length);
return;
case NSH_PROTO_EXP1:
printf("NSH Experiment 1");
break;
case NSH_PROTO_EXP2:
printf("NSH Experiment 2");
break;
default:
printf("nsh-unknown-proto-0x%02x", field);
break;
}
}
if (vflag)
default_print(p, l);
return;
trunc:
printf(" [|nsh]");
}
static void
nsh_print_mdtype1(const u_char *p, u_int len)
{
const struct nsh_context_header *ctx;
size_t i;
if (len != sizeof(*ctx))
printf("nsh-mdtype1-length-%u (not %zu)", len, sizeof(*ctx));
printf("\n\tcontext");
ctx = (const struct nsh_context_header *)p;
for (i = 0; i < nitems(ctx->ch); i++) {
printf(" ");
nsh_print_bytes(&ctx->ch[i], sizeof(ctx->ch[i]));
}
}
static void
nsh_print_mdtype2(const u_char *p, u_int l)
{
if (l == 0)
return;
do {
struct nsh_md_header h;
uint8_t len;
if (l < sizeof(h))
goto trunc;
memcpy(&h, p, sizeof(h));
p += sizeof(h);
l -= sizeof(h);
h.class = ntohs(h.class);
len = h.len & NSH_MD_LEN_MASK;
printf("\n\tmd class %u type %u", h.class, h.type);
if (len > 0) {
printf(" ");
nsh_print_bytes(p, len);
}
len = roundup(len, 4);
if (l < len)
goto trunc;
p += len;
l -= len;
} while (l > 0);
return;
trunc:
printf("[|nsh md]");
}
static void
nsh_print_bytes(const void *b, u_int l)
{
const uint8_t *p = b;
u_int i;
for (i = 0; i < l; i++) {
int ch = p[i];
#if 0
if (isprint(ch) && !isspace(ch))
putchar(ch);
else {
switch (ch) {
case '\\':
printf("\\\\");
break;
case '\0':
printf("\\0");
break;
default:
printf("\\x%02x", ch);
break;
}
}
#else
printf("%02x", ch);
#endif
}
}