Check the sample, flow, and counter lengths.

When dissecting a sample, or a flow or counter within the sample, make
sure we don't run past the length in the sample/flow/counter header.

This also means we count the sample length down as we process the
contents of the sample, so we only add what, if anything, remains of the
sample to the pointer into the packet, and subtract that from the
remaining data length in the packet, which means we properly handle
packets with multiple samples.
This commit is contained in:
Guy Harris 2011-04-04 19:03:56 -07:00
parent 8a52e408e9
commit a9876473a2

View File

@ -274,7 +274,8 @@ sflow_print(const u_char *pptr, u_int len) {
const u_char *tptr; const u_char *tptr;
int tlen; int tlen;
u_int32_t sflow_sample_type, sflow_sample_len; u_int32_t sflow_sample_type, sflow_sample_len;
int nsamples, nrecords, counter_len, counter_type, flow_len, flow_type; u_int32_t nsamples, nrecords;
u_int32_t counter_len, counter_type, flow_len, flow_type;
tptr=pptr; tptr=pptr;
tlen = len; tlen = len;
@ -313,16 +314,18 @@ sflow_print(const u_char *pptr, u_int len) {
len); len);
/* skip Common header */ /* skip Common header */
tptr+=sizeof(const struct sflow_datagram_t); tptr += sizeof(const struct sflow_datagram_t);
tlen-=sizeof(const struct sflow_datagram_t); tlen -= sizeof(const struct sflow_datagram_t);
while (nsamples > 0 && tlen > 0) { while (nsamples > 0 && tlen > 0) {
sflow_sample = (const struct sflow_sample_header *)tptr; sflow_sample = (const struct sflow_sample_header *)tptr;
TCHECK(*sflow_sample);
sflow_sample_type = (EXTRACT_32BITS(sflow_sample->format)&0x0FFF); sflow_sample_type = (EXTRACT_32BITS(sflow_sample->format)&0x0FFF);
sflow_sample_len = EXTRACT_32BITS(sflow_sample->len); sflow_sample_len = EXTRACT_32BITS(sflow_sample->len);
tptr+=sizeof(struct sflow_sample_header); tptr += sizeof(struct sflow_sample_header);
tlen-=sizeof(struct sflow_sample_header); tlen -= sizeof(struct sflow_sample_header);
printf("\n\t%s (%u), length %u,", printf("\n\t%s (%u), length %u,",
tok2str(sflow_format_values, "Unknown", sflow_sample_type), tok2str(sflow_format_values, "Unknown", sflow_sample_type),
@ -335,8 +338,7 @@ sflow_print(const u_char *pptr, u_int len) {
} }
/* did we capture enough for fully decoding the sample ? */ /* did we capture enough for fully decoding the sample ? */
if (!TTEST2(*tptr, sflow_sample_len)) TCHECK2(*tptr, sflow_sample_len);
goto trunc;
switch(sflow_sample_type) { switch(sflow_sample_type) {
case SFLOW_FLOW_SAMPLE: /* XXX */ case SFLOW_FLOW_SAMPLE: /* XXX */
@ -346,6 +348,10 @@ sflow_print(const u_char *pptr, u_int len) {
break; break;
case SFLOW_EXPANDED_FLOW_SAMPLE: case SFLOW_EXPANDED_FLOW_SAMPLE:
/* does the header run past the end of the sample? */
if (sflow_sample_len < sizeof(struct sflow_expanded_flow_sample_t))
goto trunc;
sflow_expanded_flow_sample = (const struct sflow_expanded_flow_sample_t *)tptr; sflow_expanded_flow_sample = (const struct sflow_expanded_flow_sample_t *)tptr;
nrecords = EXTRACT_32BITS(sflow_expanded_flow_sample->records); nrecords = EXTRACT_32BITS(sflow_expanded_flow_sample->records);
@ -358,12 +364,15 @@ sflow_print(const u_char *pptr, u_int len) {
EXTRACT_32BITS(sflow_expanded_flow_sample->drops), EXTRACT_32BITS(sflow_expanded_flow_sample->drops),
EXTRACT_32BITS(sflow_expanded_flow_sample->records)); EXTRACT_32BITS(sflow_expanded_flow_sample->records));
tptr+= sizeof(struct sflow_expanded_flow_sample_t); tptr += sizeof(struct sflow_expanded_flow_sample_t);
tlen-= sizeof(struct sflow_expanded_flow_sample_t); tlen -= sizeof(struct sflow_expanded_flow_sample_t);
sflow_sample_len-= sizeof(struct sflow_expanded_flow_sample_t);
while ( nrecords > 0 && tlen > 0) { while ( nrecords > 0 && tlen > 0) {
if (sflow_sample_len < 8)
/* decode Flow record - 2 bytes */ goto trunc;
/* decode Flow record */
flow_type = EXTRACT_32BITS(tptr)&0x0FFF; flow_type = EXTRACT_32BITS(tptr)&0x0FFF;
flow_len = EXTRACT_32BITS(tptr+4); flow_len = EXTRACT_32BITS(tptr+4);
printf("\n\t %s (%u) length %u", printf("\n\t %s (%u) length %u",
@ -373,13 +382,17 @@ sflow_print(const u_char *pptr, u_int len) {
tptr += 8; tptr += 8;
tlen -= 8; tlen -= 8;
sflow_sample_len -= 8;
/* did we capture enough for fully decoding the flow ? */ /* does the flow run past the end of the sample? */
if (!TTEST2(*tptr, flow_len)) if (sflow_sample_len < flow_len)
goto trunc; goto trunc;
switch(flow_type) { switch(flow_type) {
case SFLOW_FLOW_RAW_PACKET: case SFLOW_FLOW_RAW_PACKET:
if (flow_len < sizeof (struct sflow_expanded_flow_raw_t))
goto trunc;
sflow_flow_raw = (const struct sflow_expanded_flow_raw_t *)tptr; sflow_flow_raw = (const struct sflow_expanded_flow_raw_t *)tptr;
printf("\n\t protocol %s (%u), length %u, stripped bytes %u, header_size %u", printf("\n\t protocol %s (%u), length %u, stripped bytes %u, header_size %u",
tok2str(sflow_flow_raw_protocol_values,"Unknown",EXTRACT_32BITS(sflow_flow_raw->protocol)), tok2str(sflow_flow_raw_protocol_values,"Unknown",EXTRACT_32BITS(sflow_flow_raw->protocol)),
@ -408,6 +421,7 @@ sflow_print(const u_char *pptr, u_int len) {
case SFLOW_FLOW_EXTENDED_MPLS_LVP_FEC: case SFLOW_FLOW_EXTENDED_MPLS_LVP_FEC:
case SFLOW_FLOW_EXTENDED_VLAN_TUNNEL: case SFLOW_FLOW_EXTENDED_VLAN_TUNNEL:
break; break;
default: default:
if (vflag <= 1) if (vflag <= 1)
print_unknown_data(tptr, "\n\t ", flow_len); print_unknown_data(tptr, "\n\t ", flow_len);
@ -416,11 +430,16 @@ sflow_print(const u_char *pptr, u_int len) {
} }
tptr += flow_len; tptr += flow_len;
tlen -= flow_len; tlen -= flow_len;
sflow_sample_len -= flow_len;
nrecords--; nrecords--;
} }
break; break;
case SFLOW_EXPANDED_COUNTER_SAMPLE: case SFLOW_EXPANDED_COUNTER_SAMPLE:
/* does the header run past the end of the sample? */
if (sflow_sample_len < sizeof(struct sflow_expanded_counter_sample_t))
goto trunc;
sflow_expanded_counter_sample = (const struct sflow_expanded_counter_sample_t *)tptr; sflow_expanded_counter_sample = (const struct sflow_expanded_counter_sample_t *)tptr;
nrecords = EXTRACT_32BITS(sflow_expanded_counter_sample->records); nrecords = EXTRACT_32BITS(sflow_expanded_counter_sample->records);
@ -430,12 +449,15 @@ sflow_print(const u_char *pptr, u_int len) {
EXTRACT_32BITS(sflow_expanded_counter_sample->index), EXTRACT_32BITS(sflow_expanded_counter_sample->index),
nrecords); nrecords);
tptr+= sizeof(struct sflow_expanded_counter_sample_t); tptr += sizeof(struct sflow_expanded_counter_sample_t);
tlen-= sizeof(struct sflow_expanded_counter_sample_t); tlen -= sizeof(struct sflow_expanded_counter_sample_t);
sflow_sample_len -= sizeof(struct sflow_expanded_counter_sample_t);
while ( nrecords > 0 && tlen > 0) { while ( nrecords > 0 && tlen > 0) {
if (sflow_sample_len < 8)
goto trunc;
/* decode counter record - 2 bytes */ /* decode counter record */
counter_type = EXTRACT_32BITS(tptr)&0x0FFF; counter_type = EXTRACT_32BITS(tptr)&0x0FFF;
counter_len = EXTRACT_32BITS(tptr+4); counter_len = EXTRACT_32BITS(tptr+4);
printf("\n\t %s (%u) length %u", printf("\n\t %s (%u) length %u",
@ -445,13 +467,17 @@ sflow_print(const u_char *pptr, u_int len) {
tptr += 8; tptr += 8;
tlen -= 8; tlen -= 8;
sflow_sample_len -= 8;
/* did we capture enough for fully decoding the counter ? */ /* does the counter run past the end of the sample? */
if (!TTEST2(*tptr, counter_len)) if (sflow_sample_len < counter_len)
goto trunc; goto trunc;
switch(counter_type) { switch(counter_type) {
case SFLOW_COUNTER_GENERIC: case SFLOW_COUNTER_GENERIC:
if (counter_len < sizeof (struct sflow_generic_counter_t))
goto trunc;
sflow_gen_counter = (const struct sflow_generic_counter_t *)tptr; sflow_gen_counter = (const struct sflow_generic_counter_t *)tptr;
printf("\n\t ifindex %u, iftype %u, ifspeed %u, ifdirection %u (%s)", printf("\n\t ifindex %u, iftype %u, ifspeed %u, ifdirection %u (%s)",
EXTRACT_32BITS(sflow_gen_counter->ifindex), EXTRACT_32BITS(sflow_gen_counter->ifindex),
@ -485,7 +511,11 @@ sflow_print(const u_char *pptr, u_int len) {
EXTRACT_32BITS(sflow_gen_counter->ifouterrors), EXTRACT_32BITS(sflow_gen_counter->ifouterrors),
EXTRACT_32BITS(sflow_gen_counter->ifpromiscmode)); EXTRACT_32BITS(sflow_gen_counter->ifpromiscmode));
break; break;
case SFLOW_COUNTER_ETHERNET: case SFLOW_COUNTER_ETHERNET:
if (counter_len < sizeof (struct sflow_ethernet_counter_t))
goto trunc;
sflow_eth_counter = (const struct sflow_ethernet_counter_t *)tptr; sflow_eth_counter = (const struct sflow_ethernet_counter_t *)tptr;
printf("\n\t align errors %u, fcs errors %u, single collision %u, multiple collision %u, test error %u", printf("\n\t align errors %u, fcs errors %u, single collision %u, multiple collision %u, test error %u",
EXTRACT_32BITS(sflow_eth_counter->alignerrors), EXTRACT_32BITS(sflow_eth_counter->alignerrors),
@ -504,9 +534,14 @@ sflow_print(const u_char *pptr, u_int len) {
EXTRACT_32BITS(sflow_eth_counter->mac_receive_errors), EXTRACT_32BITS(sflow_eth_counter->mac_receive_errors),
EXTRACT_32BITS(sflow_eth_counter->symbol_errors)); EXTRACT_32BITS(sflow_eth_counter->symbol_errors));
break; break;
case SFLOW_COUNTER_TOKEN_RING: /* XXX */ case SFLOW_COUNTER_TOKEN_RING: /* XXX */
break; break;
case SFLOW_COUNTER_BASEVG: case SFLOW_COUNTER_BASEVG:
if (counter_len < sizeof (struct sflow_100basevg_counter_t))
goto trunc;
sflow_100basevg_counter = (const struct sflow_100basevg_counter_t *)tptr; sflow_100basevg_counter = (const struct sflow_100basevg_counter_t *)tptr;
printf("\n\t in high prio frames %u, in high prio octets %" PRIu64, printf("\n\t in high prio frames %u, in high prio octets %" PRIu64,
EXTRACT_32BITS(sflow_100basevg_counter->in_highpriority_frames), EXTRACT_32BITS(sflow_100basevg_counter->in_highpriority_frames),
@ -531,7 +566,11 @@ sflow_print(const u_char *pptr, u_int len) {
EXTRACT_64BITS(sflow_100basevg_counter->hc_in_normpriority_octets), EXTRACT_64BITS(sflow_100basevg_counter->hc_in_normpriority_octets),
EXTRACT_64BITS(sflow_100basevg_counter->hc_out_highpriority_octets)); EXTRACT_64BITS(sflow_100basevg_counter->hc_out_highpriority_octets));
break; break;
case SFLOW_COUNTER_VLAN: case SFLOW_COUNTER_VLAN:
if (counter_len < sizeof (struct sflow_vlan_counter_t))
goto trunc;
sflow_vlan_counter = (const struct sflow_vlan_counter_t *)tptr; sflow_vlan_counter = (const struct sflow_vlan_counter_t *)tptr;
printf("\n\t vlan_id %u, octets %" PRIu64 printf("\n\t vlan_id %u, octets %" PRIu64
", unicast_pkt %u, multicast_pkt %u, broadcast_pkt %u, discards %u", ", unicast_pkt %u, multicast_pkt %u, broadcast_pkt %u, discards %u",
@ -542,8 +581,10 @@ sflow_print(const u_char *pptr, u_int len) {
EXTRACT_32BITS(sflow_vlan_counter->broadcast_pkt), EXTRACT_32BITS(sflow_vlan_counter->broadcast_pkt),
EXTRACT_32BITS(sflow_vlan_counter->discards)); EXTRACT_32BITS(sflow_vlan_counter->discards));
break; break;
case SFLOW_COUNTER_PROCESSOR: /* XXX */ case SFLOW_COUNTER_PROCESSOR: /* XXX */
break; break;
default: default:
if (vflag <= 1) if (vflag <= 1)
print_unknown_data(tptr, "\n\t\t", counter_len); print_unknown_data(tptr, "\n\t\t", counter_len);
@ -551,9 +592,11 @@ sflow_print(const u_char *pptr, u_int len) {
} }
tptr += counter_len; tptr += counter_len;
tlen -= counter_len; tlen -= counter_len;
sflow_sample_len -= counter_len;
nrecords--; nrecords--;
} }
break; break;
default: default:
if (vflag <= 1) if (vflag <= 1)
print_unknown_data(tptr, "\n\t ", sflow_sample_len); print_unknown_data(tptr, "\n\t ", sflow_sample_len);