// -*- mode: c++; c-basic-offset: 4 -*-
/*
* ipsumdump_general.{cc,hh} -- general IP summary dump unparsers
* Eddie Kohler
*
* Copyright (c) 2002 International Computer Science Institute
* Copyright (c) 2004 Regents of the University of California
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#include "ipsumdumpinfo.hh"
#include <click/packet.hh>
#include <click/packet_anno.hh>
#include <clicknet/ip.h>
#include <clicknet/tcp.h>
#include <clicknet/udp.h>
#include <click/confparse.hh>
CLICK_DECLS
enum { T_IP_SRC, T_IP_DST, T_IP_TOS, T_IP_TTL, T_IP_FRAG, T_IP_FRAGOFF,
T_IP_ID, T_IP_SUM, T_IP_PROTO, T_IP_OPT, T_IP_LEN, T_IP_CAPTURE_LEN,
T_SPORT, T_DPORT, T_PAYLOAD_LEN, T_PAYLOAD };
namespace IPSummaryDump {
void ip_prepare(PacketDesc& d)
{
Packet* p = d.p;
d.iph = p->ip_header();
d.tcph = p->tcp_header();
d.udph = p->udp_header();
#define BAD(msg, hdr) do { if (d.bad_sa && !*d.bad_sa) *d.bad_sa << "!bad " << msg << '\n'; hdr = 0; } while (0)
#define BAD2(msg, val, hdr) do { if (d.bad_sa && !*d.bad_sa) *d.bad_sa << "!bad " << msg << val << '\n'; hdr = 0; } while (0)
// check IP header
if (!d.iph)
/* nada */;
else if (p->network_length() < (int) offsetof(click_ip, ip_id))
BAD("truncated IP header", d.iph);
else if (d.iph->ip_v != 4)
BAD2("IP version ", d.iph->ip_v, d.iph);
else if (d.iph->ip_hl < (sizeof(click_ip) >> 2))
BAD2("IP header length ", d.iph->ip_hl, d.iph);
else if (ntohs(d.iph->ip_len) < (d.iph->ip_hl << 2))
BAD2("IP length ", ntohs(d.iph->ip_len), d.iph);
else {
// truncate packet length to IP length if necessary
int ip_len = ntohs(d.iph->ip_len);
if (p->network_length() > ip_len) {
SET_EXTRA_LENGTH_ANNO(p, EXTRA_LENGTH_ANNO(p) + p->network_length() - ip_len);
p->take(p->network_length() - ip_len);
} else if (d.careful_trunc && p->network_length() + EXTRA_LENGTH_ANNO(p) < (uint32_t) ip_len) {
/* This doesn't actually kill the IP header. */
int scratch;
BAD2("truncated IP missing ", (ntohs(d.iph->ip_len) - p->network_length() - EXTRA_LENGTH_ANNO(p)), scratch);
}
}
// check TCP header
if (!d.iph || !d.tcph
|| p->network_length() <= (int)(d.iph->ip_hl << 2)
|| d.iph->ip_p != IP_PROTO_TCP
|| !IP_FIRSTFRAG(d.iph))
d.tcph = 0;
else if (p->transport_length() > 12
&& d.tcph->th_off < (sizeof(click_tcp) >> 2))
BAD2("TCP header length ", d.tcph->th_off, d.tcph);
// check UDP header
if (!d.iph || !d.udph
|| p->network_length() <= (int)(d.iph->ip_hl << 2)
|| d.iph->ip_p != IP_PROTO_UDP
|| !IP_FIRSTFRAG(d.iph))
d.udph = 0;
#undef BAD
#undef BAD2
// Adjust extra length, since we calculate lengths here based on ip_len.
if (d.iph && EXTRA_LENGTH_ANNO(p) > 0) {
int32_t full_len = p->length() + EXTRA_LENGTH_ANNO(p);
if (ntohs(d.iph->ip_len) + 8 >= full_len - p->network_header_offset())
SET_EXTRA_LENGTH_ANNO(p, 0);
else {
full_len = full_len - ntohs(d.iph->ip_len);
SET_EXTRA_LENGTH_ANNO(p, full_len);
}
}
}
static bool ip_extract(PacketDesc& d, int thunk)
{
int network_length = d.p->network_length();
switch (thunk & ~B_TYPEMASK) {
// IP header properties
#define CHECK(l) do { if (!d.iph || network_length < (l)) return field_missing(d, MISSING_IP, "IP", (l)); } while (0)
case T_IP_SRC:
CHECK(16);
d.v = d.iph->ip_src.s_addr;
return true;
case T_IP_DST:
CHECK(20);
d.v = d.iph->ip_dst.s_addr;
return true;
case T_IP_TOS:
CHECK(2);
d.v = d.iph->ip_tos;
return true;
case T_IP_TTL:
CHECK(9);
d.v = d.iph->ip_ttl;
return true;
case T_IP_FRAG:
CHECK(8);
d.v = (IP_ISFRAG(d.iph) ? (IP_FIRSTFRAG(d.iph) ? 'F' : 'f') : '.');
return true;
case T_IP_FRAGOFF:
CHECK(8);
d.v = ntohs(d.iph->ip_off);
return true;
case T_IP_ID:
CHECK(6);
d.v = ntohs(d.iph->ip_id);
return true;
case T_IP_SUM:
CHECK(12);
d.v = ntohs(d.iph->ip_sum);
return true;
case T_IP_PROTO:
CHECK(10);
d.v = d.iph->ip_p;
return true;
case T_IP_OPT:
if (!d.iph || (d.iph->ip_hl > 5 && network_length < (int)(d.iph->ip_hl << 2)))
return field_missing(d, MISSING_IP, "IP", (d.iph ? d.iph->ip_hl << 2 : 20));
if (d.iph->ip_hl <= 5)
d.vptr = 0, d.v2 = 0;
else {
d.vptr = (const uint8_t *) (d.iph + 1);
d.v2 = (d.iph->ip_hl << 2) - sizeof(click_ip);
}
return true;
case T_IP_LEN:
if (d.iph)
d.v = ntohs(d.iph->ip_len) + (d.force_extra_length ? EXTRA_LENGTH_ANNO(d.p) : 0);
else
d.v = d.p->length() + EXTRA_LENGTH_ANNO(d.p);
return true;
case T_IP_CAPTURE_LEN: {
uint32_t allow_len = (d.iph ? network_length : d.p->length());
uint32_t len = (d.iph ? ntohs(d.iph->ip_len) : allow_len);
d.v = (len < allow_len ? len : allow_len);
return true;
}
#undef CHECK
default:
return false;
}
}
static void ip_outa(const PacketDesc& d, int thunk)
{
switch (thunk & ~B_TYPEMASK) {
case T_IP_SRC:
case T_IP_DST:
*d.sa << IPAddress(d.v);
break;
case T_IP_FRAG:
*d.sa << (char) d.v;
break;
case T_IP_FRAGOFF:
*d.sa << ((d.v & IP_OFFMASK) << 3);
if (d.v & IP_MF)
*d.sa << '+';
break;
case T_IP_PROTO:
switch (d.v) {
case IP_PROTO_TCP: *d.sa << 'T'; break;
case IP_PROTO_UDP: *d.sa << 'U'; break;
case IP_PROTO_ICMP: *d.sa << 'I'; break;
default: *d.sa << d.v; break;
}
break;
case T_IP_OPT:
if (!d.vptr)
*d.sa << '.';
else
unparse_ip_opt(*d.sa, d.vptr, d.v2, DO_IPOPT_ALL_NOPAD);
break;
}
}
static void ip_outb(const PacketDesc& d, bool ok, int thunk)
{
if ((thunk & ~B_TYPEMASK) == T_IP_OPT) {
if (!ok || !d.vptr)
*d.sa << '\0';
else
unparse_ip_opt_binary(*d.sa, d.vptr, d.v2, DO_IPOPT_ALL);
}
}
static const uint8_t* ip_inb(PacketDesc& d, const uint8_t *s, const uint8_t *ends, int thunk)
{
if ((thunk & ~B_TYPEMASK) == T_IP_OPT && s + s[0] + 1 <= ends) {
d.vptr = s + 1;
d.v2 = s[0];
return s + s[0] + 1;
} else
return ends;
}
static bool transport_extract(PacketDesc& d, int thunk)
{
Packet* p = d.p;
switch (thunk & ~B_TYPEMASK) {
// TCP/UDP header properties
#define CHECK(l) do { if ((!d.tcph && !d.udph) || p->transport_length() < (l)) return field_missing(d, MISSING_IP_TRANSPORT, "transport", (l)); } while (0)
case T_SPORT:
CHECK(2);
d.v = ntohs(p->udp_header()->uh_sport);
return true;
case T_DPORT:
CHECK(4);
d.v = ntohs(p->udp_header()->uh_dport);
return true;
#undef CHECK
case T_PAYLOAD_LEN:
if (d.iph) {
d.v = ntohs(d.iph->ip_len);
int32_t off = p->network_header_length();
if (d.tcph && p->transport_length() >= 13
&& off + (d.tcph->th_off << 2) <= d.v)
off += (d.tcph->th_off << 2);
else if (d.udph)
off += sizeof(click_udp);
else if (IP_FIRSTFRAG(d.iph) && (d.iph->ip_p == IP_PROTO_TCP || d.iph->ip_p == IP_PROTO_UDP))
off = d.v;
d.v -= off;
d.v += (d.force_extra_length ? EXTRA_LENGTH_ANNO(p) : 0);
} else
d.v = p->length() + EXTRA_LENGTH_ANNO(p);
return true;
case T_PAYLOAD:
return true;
default:
return false;
}
}
static void transport_outa(const PacketDesc& d, int thunk)
{
switch (thunk & ~B_TYPEMASK) {
case T_PAYLOAD: {
int32_t off;
uint32_t len;
if (d.iph) {
len = ntohs(d.iph->ip_len);
off = d.p->transport_header_offset();
if (d.tcph && d.p->transport_length() >= 13
&& off + (d.tcph->th_off << 2) <= len)
off += (d.tcph->th_off << 2);
else if (d.udph)
off += sizeof(click_udp);
len = len - off + d.p->network_header_offset();
if (len + off > d.p->length()) // EXTRA_LENGTH?
len = d.p->length() - off;
} else {
off = 0;
len = d.p->length();
}
String s = String::stable_string((const char *)(d.p->data() + off), len);
*d.sa << cp_quote(s);
break;
}
}
}
#define U DO_IPOPT_UNKNOWN
static int ip_opt_mask_mapping[] = {
DO_IPOPT_PADDING, DO_IPOPT_PADDING, // EOL, NOP
U, U, U, U, U, // 2, 3, 4, 5, 6
DO_IPOPT_ROUTE, // RR
U, U, U, U, U, U, U, U, U, U, U, U, U, // 8-20
U, U, U, U, U, U, U, U, U, U, // 21-30
U, U, U, U, U, U, U, U, U, U, // 31-40
U, U, U, U, U, U, U, U, U, U, // 41-50
U, U, U, U, U, U, U, U, U, U, // 51-60
U, U, U, U, U, U, U, // 61-67
DO_IPOPT_TS, U, U, // TS, 69-70
U, U, U, U, U, U, U, U, U, U, // 71-80
U, U, U, U, U, U, U, U, U, U, // 81-90
U, U, U, U, U, U, U, U, U, U, // 91-100
U, U, U, U, U, U, U, U, U, U, // 101-110
U, U, U, U, U, U, U, U, U, U, // 111-120
U, U, U, U, U, U, U, U, U, // 121-129
DO_IPOPT_UNKNOWN, DO_IPOPT_ROUTE, // SECURITY, LSRR
U, U, U, U, // 132-135
DO_IPOPT_UNKNOWN, DO_IPOPT_ROUTE, // SATID, SSRR
U, U, U, // 138-140
U, U, U, U, U, U, U, // 141-147
DO_IPOPT_UNKNOWN // RA
};
#undef U
void unparse_ip_opt(StringAccum& sa, const uint8_t* opt, int opt_len, int mask)
{
int initial_sa_len = sa.length();
const uint8_t *end_opt = opt + opt_len;
const char *sep = "";
while (opt < end_opt)
switch (*opt) {
case IPOPT_EOL:
if (mask & DO_IPOPT_PADDING)
sa << sep << "eol";
goto done;
case IPOPT_NOP:
if (mask & DO_IPOPT_PADDING) {
sa << sep << "nop";
sep = ";";
}
opt++;
break;
case IPOPT_RR:
if (opt + opt[1] > end_opt || opt[1] < 3 || opt[2] < 4)
goto bad_opt;
if (!(mask & DO_IPOPT_ROUTE))
goto unknown;
sa << sep << "rr";
goto print_route;
case IPOPT_LSRR:
if (opt + opt[1] > end_opt || opt[1] < 3 || opt[2] < 4)
goto bad_opt;
if (!(mask & DO_IPOPT_ROUTE))
goto unknown;
sa << sep << "lsrr";
goto print_route;
case IPOPT_SSRR:
if (opt + opt[1] > end_opt || opt[1] < 3 || opt[2] < 4)
goto bad_opt;
if (!(mask & DO_IPOPT_ROUTE))
goto unknown;
sa << sep << "ssrr";
goto print_route;
print_route: {
const uint8_t *o = opt + 3, *ol = opt + opt[1], *op = opt + opt[2] - 1;
sep = "";
sa << '{';
for (; o + 4 <= ol; o += 4) {
if (o == op) {
if (opt[0] == IPOPT_RR)
break;
sep = "^";
}
sa << sep << (int)o[0] << '.' << (int)o[1] << '.' << (int)o[2] << '.' << (int)o[3];
sep = ",";
}
if (o == ol && o == op && opt[0] != IPOPT_RR)
sa << '^';
sa << '}';
if (o + 4 <= ol && o == op && opt[0] == IPOPT_RR)
sa << '+' << (ol - o) / 4;
opt = ol;
sep = ";";
break;
}
case IPOPT_TS: {
if (opt + opt[1] > end_opt || opt[1] < 4 || opt[2] < 5)
goto bad_opt;
const uint8_t *o = opt + 4, *ol = opt + opt[1], *op = opt + opt[2] - 1;
int flag = opt[3] & 0xF;
int size = (flag >= 1 && flag <= 3 ? 8 : 4);
sa << sep << "ts";
if (flag == 1)
sa << ".ip";
else if (flag == 3)
sa << ".preip";
else if (flag != 0)
sa << "." << flag;
sa << '{';
sep = "";
for (; o + size <= ol; o += 4) {
if (o == op) {
if (flag != 3)
break;
sep = "^";
}
if (flag != 0) {
sa << sep << (int)o[0] << '.' << (int)o[1] << '.' << (int)o[2] << '.' << (int)o[3] << '=';
o += 4;
} else
sa << sep;
if (flag == 3 && o > op)
sa.pop_back();
else {
uint32_t v = (o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3];
if (v & 0x80000000U)
sa << '!';
sa << (v & 0x7FFFFFFFU);
}
sep = ",";
}
if (o == ol && o == op)
sa << '^';
sa << '}';
if (o + size <= ol && o == op)
sa << '+' << (ol - o) / size;
if (opt[3] & 0xF0)
sa << '+' << '+' << (opt[3] >> 4);
opt = ol;
sep = ";";
break;
}
default: {
if (opt + opt[1] > end_opt || opt[1] < 2)
goto bad_opt;
if (!(mask & DO_IPOPT_UNKNOWN))
goto unknown;
sa << sep << (int)(opt[0]);
const uint8_t *end_this_opt = opt + opt[1];
char opt_sep = '=';
for (opt += 2; opt < end_this_opt; opt++) {
sa << opt_sep << (int)(*opt);
opt_sep = ':';
}
sep = ";";
break;
}
unknown:
opt += (opt[1] >= 2 ? opt[1] : 128);
break;
}
done:
if (sa.length() == initial_sa_len)
sa << '.';
return;
bad_opt:
sa.set_length(initial_sa_len);
sa << '?';
}
void unparse_ip_opt(StringAccum& sa, const click_ip* iph, int mask)
{
unparse_ip_opt(sa, reinterpret_cast<const uint8_t *>(iph + 1), (iph->ip_hl << 2) - sizeof(click_ip), mask);
}
void unparse_ip_opt_binary(StringAccum& sa, const uint8_t *opt, int opt_len, int mask)
{
if (mask == (int)DO_IPOPT_ALL) {
// store all options
sa.append((char)opt_len);
sa.append(opt, opt_len);
}
const uint8_t *end_opt = opt + opt_len;
int initial_sa_len = sa.length();
sa.append('\0');
while (opt < end_opt) {
// one-byte options
if (*opt == IPOPT_EOL) {
if (mask & DO_IPOPT_PADDING)
sa.append(opt, 1);
goto done;
} else if (*opt == IPOPT_NOP) {
if (mask & DO_IPOPT_PADDING)
sa.append(opt, 1);
opt++;
continue;
}
// quit copying options if you encounter something obviously invalid
if (opt[1] < 2 || opt + opt[1] > end_opt)
break;
int this_content = (*opt > IPOPT_RA ? (int)DO_IPOPT_UNKNOWN : ip_opt_mask_mapping[*opt]);
if (mask & this_content)
sa.append(opt, opt[1]);
opt += opt[1];
}
done:
sa[initial_sa_len] = sa.length() - initial_sa_len - 1;
}
void unparse_ip_opt_binary(StringAccum& sa, const click_ip *iph, int mask)
{
unparse_ip_opt_binary(sa, reinterpret_cast<const uint8_t *>(iph + 1), (iph->ip_hl << 2) - sizeof(click_ip), mask);
}
void ip_register_unparsers()
{
register_unparser("ip_src", T_IP_SRC | B_4NET, ip_prepare, ip_extract, ip_outa, outb, inb);
register_unparser("ip_dst", T_IP_DST | B_4NET, ip_prepare, ip_extract, ip_outa, outb, inb);
register_unparser("ip_tos", T_IP_TOS | B_1, ip_prepare, ip_extract, num_outa, outb, inb);
register_unparser("ip_ttl", T_IP_TTL | B_1, ip_prepare, ip_extract, num_outa, outb, inb);
register_unparser("ip_frag", T_IP_FRAG | B_1, ip_prepare, ip_extract, ip_outa, outb, inb);
register_unparser("ip_fragoff", T_IP_FRAGOFF | B_2, ip_prepare, ip_extract, ip_outa, outb, inb);
register_unparser("ip_id", T_IP_ID | B_2, ip_prepare, ip_extract, num_outa, outb, inb);
register_unparser("ip_sum", T_IP_SUM | B_2, ip_prepare, ip_extract, num_outa, outb, inb);
register_unparser("ip_proto", T_IP_PROTO | B_1, ip_prepare, ip_extract, ip_outa, outb, inb);
register_unparser("ip_len", T_IP_LEN | B_4, ip_prepare, ip_extract, num_outa, outb, inb);
register_unparser("ip_capture_len", T_IP_CAPTURE_LEN | B_4, ip_prepare, ip_extract, num_outa, outb, inb);
register_unparser("ip_opt", T_IP_OPT | B_SPECIAL, ip_prepare, ip_extract, ip_outa, ip_outb, ip_inb);
register_unparser("sport", T_SPORT | B_2, ip_prepare, transport_extract, num_outa, outb, inb);
register_unparser("dport", T_DPORT | B_2, ip_prepare, transport_extract, num_outa, outb, inb);
register_unparser("payload_len", T_PAYLOAD_LEN | B_4, ip_prepare, transport_extract, num_outa, outb, inb);
register_unparser("payload", T_PAYLOAD | B_NOTALLOWED, ip_prepare, transport_extract, transport_outa, 0, 0);
register_synonym("length", "ip_len");
register_synonym("ip_p", "ip_proto");
register_synonym("payload_length", "payload_len");
}
}
ELEMENT_REQUIRES(userlevel IPSummaryDump)
ELEMENT_PROVIDES(IPSummaryDump_IP)
CLICK_ENDDECLS
syntax highlighted by Code2HTML, v. 0.9.1