diff options
author | Michael Brown | 2014-03-02 21:33:35 +0100 |
---|---|---|
committer | Michael Brown | 2014-03-02 21:33:35 +0100 |
commit | 11963c4f5f235c07380e4004082ca0bcf4aefa40 (patch) | |
tree | 85310531a78e1ed1387b3a80937cc4c508172151 /src/net | |
parent | [image] Ensure every image has a fully resolved URI (diff) | |
download | ipxe-11963c4f5f235c07380e4004082ca0bcf4aefa40.tar.gz ipxe-11963c4f5f235c07380e4004082ca0bcf4aefa40.tar.xz ipxe-11963c4f5f235c07380e4004082ca0bcf4aefa40.zip |
[tcpip] Add IP statistics collection as per RFC 4293
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/net')
-rw-r--r-- | src/net/fragment.c | 8 | ||||
-rw-r--r-- | src/net/ipv4.c | 55 | ||||
-rw-r--r-- | src/net/ipv6.c | 52 | ||||
-rw-r--r-- | src/net/tcpip.c | 8 |
4 files changed, 102 insertions, 21 deletions
diff --git a/src/net/fragment.c b/src/net/fragment.c index 3e1dfdf7..410915b3 100644 --- a/src/net/fragment.c +++ b/src/net/fragment.c @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <string.h> #include <ipxe/retry.h> #include <ipxe/timer.h> +#include <ipxe/ipstat.h> #include <ipxe/fragment.h> /** @file @@ -45,6 +46,7 @@ static void fragment_expired ( struct retry_timer *timer, int fail __unused ) { DBGC ( fragment, "FRAG %p expired\n", fragment ); free_iob ( fragment->iobuf ); list_del ( &fragment->list ); + fragment->fragments->stats->reasm_fails++; free ( fragment ); } @@ -89,6 +91,9 @@ struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments, size_t expected_offset; int more_frags; + /* Update statistics */ + fragments->stats->reasm_reqds++; + /* Find matching fragment reassembly buffer, if any */ fragment = fragment_find ( fragments, iobuf, *hdrlen ); @@ -115,6 +120,7 @@ struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments, fragment->iobuf = iobuf; fragment->hdrlen = *hdrlen; timer_init ( &fragment->timer, fragment_expired, NULL ); + fragment->fragments = fragments; DBGC ( fragment, "FRAG %p [0,%zd)\n", fragment, ( iob_len ( iobuf ) - *hdrlen ) ); @@ -157,6 +163,7 @@ struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments, *hdrlen = fragment->hdrlen; list_del ( &fragment->list ); free ( fragment ); + fragments->stats->reasm_oks++; return iobuf; } } @@ -167,6 +174,7 @@ struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments, return NULL; drop: + fragments->stats->reasm_fails++; free_iob ( iobuf ); return NULL; } diff --git a/src/net/ipv4.c b/src/net/ipv4.c index b57b2f83..d9a54ade 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -15,6 +15,7 @@ #include <ipxe/dhcp.h> #include <ipxe/settings.h> #include <ipxe/fragment.h> +#include <ipxe/ipstat.h> /** @file * @@ -30,6 +31,16 @@ static uint8_t next_ident_high = 0; /** List of IPv4 miniroutes */ struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes ); +/** IPv4 statistics */ +static struct ip_statistics ipv4_stats; + +/** IPv4 statistics family */ +struct ip_statistics_family +ipv4_stats_family __ip_statistics_family ( IP_STATISTICS_IPV4 ) = { + .version = 4, + .stats = &ipv4_stats, +}; + /** * Add IPv4 minirouting table entry * @@ -178,6 +189,7 @@ static struct fragment_reassembler ipv4_reassembler = { .is_fragment = ipv4_is_fragment, .fragment_offset = ipv4_fragment_offset, .more_fragments = ipv4_more_fragments, + .stats = &ipv4_stats, }; /** @@ -232,6 +244,9 @@ static int ipv4_tx ( struct io_buffer *iobuf, const void *ll_dest; int rc; + /* Update statistics */ + ipv4_stats.out_requests++; + /* Fill up the IP header, except source address */ memset ( iphdr, 0, sizeof ( *iphdr ) ); iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) ); @@ -255,6 +270,7 @@ static int ipv4_tx ( struct io_buffer *iobuf, if ( ! netdev ) { DBGC ( sin_dest->sin_addr, "IPv4 has no route to %s\n", inet_ntoa ( iphdr->dest ) ); + ipv4_stats.out_no_routes++; rc = -ENETUNREACH; goto err; } @@ -282,9 +298,11 @@ static int ipv4_tx ( struct io_buffer *iobuf, /* Calculate link-layer destination address, if possible */ if ( ( ( next_hop.s_addr ^ INADDR_BROADCAST ) & ~netmask.s_addr ) == 0){ /* Broadcast address */ + ipv4_stats.out_bcast_pkts++; ll_dest = netdev->ll_broadcast; } else if ( IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) { /* Multicast address */ + ipv4_stats.out_mcast_pkts++; if ( ( rc = netdev->ll_protocol->mc_hash ( AF_INET, &next_hop, ll_dest_buf ) ) !=0){ DBGC ( sin_dest->sin_addr, "IPv4 could not hash " @@ -298,6 +316,10 @@ static int ipv4_tx ( struct io_buffer *iobuf, ll_dest = NULL; } + /* Update statistics */ + ipv4_stats.out_transmits++; + ipv4_stats.out_octets += iob_len ( iobuf ); + /* Hand off to link layer (via ARP if applicable) */ if ( ll_dest ) { if ( ( rc = net_tx ( iobuf, netdev, &ipv4_protocol, ll_dest, @@ -389,43 +411,53 @@ static int ipv4_rx ( struct io_buffer *iobuf, uint16_t pshdr_csum; int rc; + /* Update statistics */ + ipv4_stats.in_receives++; + ipv4_stats.in_octets += iob_len ( iobuf ); + if ( flags & LL_BROADCAST ) { + ipv4_stats.in_bcast_pkts++; + } else if ( flags & LL_MULTICAST ) { + ipv4_stats.in_mcast_pkts++; + } + /* Sanity check the IPv4 header */ if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) { DBGC ( iphdr->src, "IPv4 packet too short at %zd bytes (min " "%zd bytes)\n", iob_len ( iobuf ), sizeof ( *iphdr ) ); - goto err; + goto err_header; } if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) { DBGC ( iphdr->src, "IPv4 version %#02x not supported\n", iphdr->verhdrlen ); - goto err; + goto err_header; } hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 ); if ( hdrlen < sizeof ( *iphdr ) ) { DBGC ( iphdr->src, "IPv4 header too short at %zd bytes (min " "%zd bytes)\n", hdrlen, sizeof ( *iphdr ) ); - goto err; + goto err_header; } if ( hdrlen > iob_len ( iobuf ) ) { DBGC ( iphdr->src, "IPv4 header too long at %zd bytes " "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) ); - goto err; + goto err_header; } if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) { DBGC ( iphdr->src, "IPv4 checksum incorrect (is %04x " "including checksum field, should be 0000)\n", csum ); - goto err; + goto err_header; } len = ntohs ( iphdr->len ); if ( len < hdrlen ) { DBGC ( iphdr->src, "IPv4 length too short at %zd bytes " "(header is %zd bytes)\n", len, hdrlen ); - goto err; + goto err_header; } if ( len > iob_len ( iobuf ) ) { DBGC ( iphdr->src, "IPv4 length too long at %zd bytes " "(packet is %zd bytes)\n", len, iob_len ( iobuf ) ); - goto err; + ipv4_stats.in_truncated_pkts++; + goto err_other; } /* Truncate packet to correct length */ @@ -443,7 +475,8 @@ static int ipv4_rx ( struct io_buffer *iobuf, ( ! ipv4_has_addr ( netdev, iphdr->dest ) ) ) { DBGC ( iphdr->src, "IPv4 discarding non-local unicast packet " "for %s\n", inet_ntoa ( iphdr->dest ) ); - goto err; + ipv4_stats.in_addr_errors++; + goto err_other; } /* Perform fragment reassembly if applicable */ @@ -470,7 +503,7 @@ static int ipv4_rx ( struct io_buffer *iobuf, pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM ); iob_pull ( iobuf, hdrlen ); if ( ( rc = tcpip_rx ( iobuf, netdev, iphdr->protocol, &src.st, - &dest.st, pshdr_csum ) ) != 0 ) { + &dest.st, pshdr_csum, &ipv4_stats ) ) != 0 ) { DBGC ( src.sin.sin_addr, "IPv4 received packet rejected by " "stack: %s\n", strerror ( rc ) ); return rc; @@ -478,7 +511,9 @@ static int ipv4_rx ( struct io_buffer *iobuf, return 0; - err: + err_header: + ipv4_stats.in_hdr_errors++; + err_other: free_iob ( iobuf ); return -EINVAL; } diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 621b4ff1..2802aef0 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/if_ether.h> #include <ipxe/crc32.h> #include <ipxe/fragment.h> +#include <ipxe/ipstat.h> #include <ipxe/ndp.h> #include <ipxe/ipv6.h> @@ -57,6 +58,16 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** List of IPv6 miniroutes */ struct list_head ipv6_miniroutes = LIST_HEAD_INIT ( ipv6_miniroutes ); +/** IPv6 statistics */ +static struct ip_statistics ipv6_stats; + +/** IPv6 statistics family */ +struct ip_statistics_family +ipv6_statistics_family __ip_statistics_family ( IP_STATISTICS_IPV6 ) = { + .version = 6, + .stats = &ipv6_stats, +}; + /** * Determine debugging colour for IPv6 debug messages * @@ -398,6 +409,7 @@ static struct fragment_reassembler ipv6_reassembler = { .is_fragment = ipv6_is_fragment, .fragment_offset = ipv6_fragment_offset, .more_fragments = ipv6_more_fragments, + .stats = &ipv6_stats, }; /** @@ -455,6 +467,9 @@ static int ipv6_tx ( struct io_buffer *iobuf, size_t len; int rc; + /* Update statistics */ + ipv6_stats.out_requests++; + /* Fill up the IPv6 header, except source address */ len = iob_len ( iobuf ); iphdr = iob_push ( iobuf, sizeof ( *iphdr ) ); @@ -475,6 +490,7 @@ static int ipv6_tx ( struct io_buffer *iobuf, if ( ! netdev ) { DBGC ( ipv6col ( &iphdr->dest ), "IPv6 has no route to %s\n", inet6_ntoa ( &iphdr->dest ) ); + ipv6_stats.out_no_routes++; rc = -ENETUNREACH; goto err; } @@ -498,6 +514,7 @@ static int ipv6_tx ( struct io_buffer *iobuf, /* Calculate link-layer destination address, if possible */ if ( IN6_IS_ADDR_MULTICAST ( next_hop ) ) { /* Multicast address */ + ipv6_stats.out_mcast_pkts++; if ( ( rc = netdev->ll_protocol->mc_hash ( AF_INET6, next_hop, ll_dest_buf ) ) !=0){ DBGC ( ipv6col ( &iphdr->dest ), "IPv6 could not hash " @@ -511,6 +528,10 @@ static int ipv6_tx ( struct io_buffer *iobuf, ll_dest = NULL; } + /* Update statistics */ + ipv6_stats.out_transmits++; + ipv6_stats.out_octets += iob_len ( iobuf ); + /* Hand off to link layer (via NDP if applicable) */ if ( ll_dest ) { if ( ( rc = net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest, @@ -568,20 +589,29 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, int next_header; int rc; + /* Update statistics */ + ipv6_stats.in_receives++; + ipv6_stats.in_octets += iob_len ( iobuf ); + if ( flags & LL_BROADCAST ) { + ipv6_stats.in_bcast_pkts++; + } else if ( flags & LL_MULTICAST ) { + ipv6_stats.in_mcast_pkts++; + } + /* Sanity check the IPv6 header */ if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) { DBGC ( ipv6col ( &iphdr->src ), "IPv6 packet too short at %zd " "bytes (min %zd bytes)\n", iob_len ( iobuf ), sizeof ( *iphdr ) ); rc = -EINVAL_LEN; - goto err; + goto err_header; } if ( ( iphdr->ver_tc_label & htonl ( IPV6_MASK_VER ) ) != htonl ( IPV6_VER ) ) { DBGC ( ipv6col ( &iphdr->src ), "IPv6 version %#08x not " "supported\n", ntohl ( iphdr->ver_tc_label ) ); rc = -ENOTSUP_VER; - goto err; + goto err_header; } /* Truncate packet to specified length */ @@ -589,8 +619,9 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, if ( len > iob_len ( iobuf ) ) { DBGC ( ipv6col ( &iphdr->src ), "IPv6 length too long at %zd " "bytes (packet is %zd bytes)\n", len, iob_len ( iobuf )); + ipv6_stats.in_truncated_pkts++; rc = -EINVAL_LEN; - goto err; + goto err_other; } iob_unput ( iobuf, ( iob_len ( iobuf ) - len - sizeof ( *iphdr ) ) ); hdrlen = sizeof ( *iphdr ); @@ -606,8 +637,9 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, ( ! ipv6_has_addr ( netdev, &iphdr->dest ) ) ) { DBGC ( ipv6col ( &iphdr->src ), "IPv6 discarding non-local " "unicast packet for %s\n", inet6_ntoa ( &iphdr->dest ) ); + ipv6_stats.in_addr_errors++; rc = -EPIPE; - goto err; + goto err_other; } /* Process any extension headers */ @@ -624,7 +656,7 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, "%zd bytes)\n", this_header, ( iob_len ( iobuf ) - hdrlen ), extlen ); rc = -EINVAL_LEN; - goto err; + goto err_header; } /* Determine size of extension header (if applicable) */ @@ -645,7 +677,7 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, "%zd bytes)\n", this_header, ( iob_len ( iobuf ) - hdrlen ), extlen ); rc = -EINVAL_LEN; - goto err; + goto err_header; } hdrlen += extlen; next_header = ext->common.next_header; @@ -662,7 +694,7 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, /* Check that all options can be ignored */ if ( ( rc = ipv6_check_options ( iphdr, &ext->options, extlen ) ) != 0 ) - goto err; + goto err_header; } else if ( this_header == IPV6_FRAGMENT ) { @@ -692,7 +724,7 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, pshdr_csum = ipv6_pshdr_chksum ( iphdr, iob_len ( iobuf ), next_header, TCPIP_EMPTY_CSUM ); if ( ( rc = tcpip_rx ( iobuf, netdev, next_header, &src.st, &dest.st, - pshdr_csum ) ) != 0 ) { + pshdr_csum, &ipv6_stats ) ) != 0 ) { DBGC ( ipv6col ( &src.sin6.sin6_addr ), "IPv6 received packet " "rejected by stack: %s\n", strerror ( rc ) ); return rc; @@ -700,7 +732,9 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, return 0; - err: + err_header: + ipv6_stats.in_hdr_errors++; + err_other: free_iob ( iobuf ); return rc; } diff --git a/src/net/tcpip.c b/src/net/tcpip.c index 0e467144..0b2adfd9 100644 --- a/src/net/tcpip.c +++ b/src/net/tcpip.c @@ -5,6 +5,7 @@ #include <byteswap.h> #include <ipxe/iobuf.h> #include <ipxe/tables.h> +#include <ipxe/ipstat.h> #include <ipxe/tcpip.h> /** @file @@ -25,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @v st_src Partially-filled source address * @v st_dest Partially-filled destination address * @v pshdr_csum Pseudo-header checksum + * @v stats IP statistics * @ret rc Return status code * * This function expects a transport-layer segment from the network @@ -35,20 +37,22 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ int tcpip_rx ( struct io_buffer *iobuf, struct net_device *netdev, uint8_t tcpip_proto, struct sockaddr_tcpip *st_src, - struct sockaddr_tcpip *st_dest, - uint16_t pshdr_csum ) { + struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum, + struct ip_statistics *stats ) { struct tcpip_protocol *tcpip; /* Hand off packet to the appropriate transport-layer protocol */ for_each_table_entry ( tcpip, TCPIP_PROTOCOLS ) { if ( tcpip->tcpip_proto == tcpip_proto ) { DBG ( "TCP/IP received %s packet\n", tcpip->name ); + stats->in_delivers++; return tcpip->rx ( iobuf, netdev, st_src, st_dest, pshdr_csum ); } } DBG ( "Unrecognised TCP/IP protocol %d\n", tcpip_proto ); + stats->in_unknown_protos++; free_iob ( iobuf ); return -EPROTONOSUPPORT; } |