On Tue, Jan 24, 2023 at 08:21:28PM +0100, Adrián Moreno wrote:
> From: Adrian Moreno <[email protected]>
>
> IPFIX templates have to be sent for each Observation Domain ID.
> Currently, a timer is kept at each dpif_ipfix_exporter to send them.
> This works fine for per-bridge sampling where there is only one
> Observation Domain ID per exporter. However, this is does not work for
> per-flow sampling where more than one Observation Domain IDs can be
> specified by the controller. In this case, ovs-vswitchd will only send
> template information for one (arbitrary) DomainID.
>
> Fix per-flow sampling by using an hmap to keep a timer for each
> Observation Domain ID.
>
> Signed-off-by: Adrian Moreno <[email protected]>
>
> ---
> - v2:
> - Fixed a potential race condition in unit test.
> - v1:
> - Added unit test.
> - Drop domain_id from "struct dpif_ipfix_domain" since the hashmap
> already uses it as index.
> - Added OVS_REQUES(mutex) to dpif_ipfix_exporter_find_domain.
> ---
> ofproto/ofproto-dpif-ipfix.c | 127 ++++++++++++++++++++++++++++-------
> tests/system-traffic.at | 52 ++++++++++++++
> 2 files changed, 156 insertions(+), 23 deletions(-)
>
> diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
> index 742eed399..fa71fda0f 100644
> --- a/ofproto/ofproto-dpif-ipfix.c
> +++ b/ofproto/ofproto-dpif-ipfix.c
> @@ -124,11 +124,18 @@ struct dpif_ipfix_port {
> uint32_t ifindex;
> };
>
> +struct dpif_ipfix_domain {
> + struct hmap_node hmap_node; /* In struct dpif_ipfix_exporter's domains.
> */
> + time_t last_template_set_time;
> +};
> +
> struct dpif_ipfix_exporter {
> uint32_t exporter_id; /* Exporting Process identifier */
> struct collectors *collectors;
> uint32_t seq_number;
> - time_t last_template_set_time;
> + struct hmap domains; /* Contains struct dpif_ipfix_domain indexed by
> + observation domain id. */
> + time_t last_stats_sent_time;
> struct hmap cache_flow_key_map; /* ipfix_flow_cache_entry. */
> struct ovs_list cache_flow_start_timestamp_list; /*
> ipfix_flow_cache_entry. */
> uint32_t cache_active_timeout; /* In seconds. */
I'm not sure if it is important, but this change bumps the
cache_flow_key_map from the 1st to 2nd cacheline (on x86_64).
This can be avoided by moving seq_number above collectors,
as there is a 32-bit hole there.
Possibly there is further scope for making this structure more
cache-fiendly. But again, I'm not sure if it is important.
Before:
$ pahole ofproto/libofproto_la-ofproto-dpif-ipfix.o
...
struct dpif_ipfix_exporter {
uint32_t exporter_id; /* 0 4 */
/* XXX 4 bytes hole, try to pack */
struct collectors * collectors; /* 8 8 */
uint32_t seq_number; /* 16 4 */
/* XXX 4 bytes hole, try to pack */
time_t last_template_set_time; /* 24 8 */
struct hmap cache_flow_key_map; /* 32 32 */
/* --- cacheline 1 boundary (64 bytes) --- */
struct ovs_list cache_flow_start_timestamp_list; /* 64
16 */
uint32_t cache_active_timeout; /* 80 4 */
uint32_t cache_max_flows; /* 84 4 */
char * virtual_obs_id; /* 88 8 */
uint8_t virtual_obs_len; /* 96 1 */
/* XXX 7 bytes hole, try to pack */
ofproto_ipfix_stats ofproto_stats; /* 104 88 */
/* --- cacheline 3 boundary (192 bytes) --- */
struct dpif_ipfix_global_stats ipfix_global_stats; /* 192 152 */
/* size: 344, cachelines: 6, members: 12 */
/* sum members: 329, holes: 3, sum holes: 15 */
/* last cacheline: 24 bytes */
}
...
After:
$ pahole ofproto/libofproto_la-ofproto-dpif-ipfix.o
...
struct dpif_ipfix_exporter {
uint32_t exporter_id; /* 0 4 */
/* XXX 4 bytes hole, try to pack */
struct collectors * collectors; /* 8 8 */
uint32_t seq_number; /* 16 4 */
/* XXX 4 bytes hole, try to pack */
struct hmap domains; /* 24 32 */
time_t last_stats_sent_time; /* 56 8 */
/* --- cacheline 1 boundary (64 bytes) --- */
struct hmap cache_flow_key_map; /* 64 32 */
struct ovs_list cache_flow_start_timestamp_list; /* 96
16 */
uint32_t cache_active_timeout; /* 112 4 */
uint32_t cache_max_flows; /* 116 4 */
char * virtual_obs_id; /* 120 8 */
/* --- cacheline 2 boundary (128 bytes) --- */
uint8_t virtual_obs_len; /* 128 1 */
/* XXX 7 bytes hole, try to pack */
ofproto_ipfix_stats ofproto_stats; /* 136 88 */
/* --- cacheline 3 boundary (192 bytes) was 32 bytes ago --- */
struct dpif_ipfix_global_stats ipfix_global_stats; /* 224 152 */
/* size: 376, cachelines: 6, members: 13 */
/* sum members: 361, holes: 3, sum holes: 15 */
/* last cacheline: 56 bytes */
};
...
Proposal:
$ git diff
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index fa71fda0f276..1701d91e8a72 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -131,8 +131,8 @@ struct dpif_ipfix_domain {
struct dpif_ipfix_exporter {
uint32_t exporter_id; /* Exporting Process identifier */
- struct collectors *collectors;
uint32_t seq_number;
+ struct collectors *collectors;
struct hmap domains; /* Contains struct dpif_ipfix_domain indexed by
observation domain id. */
time_t last_stats_sent_time;
$ pahole ofproto/libofproto_la-ofproto-dpif-ipfix.o
...
struct dpif_ipfix_exporter {
uint32_t exporter_id; /* 0 4 */
uint32_t seq_number; /* 4 4 */
struct collectors * collectors; /* 8 8 */
struct hmap domains; /* 16 32 */
time_t last_stats_sent_time; /* 48 8 */
struct hmap cache_flow_key_map; /* 56 32 */
/* --- cacheline 1 boundary (64 bytes) was 24 bytes ago --- */
struct ovs_list cache_flow_start_timestamp_list; /* 88
16 */
uint32_t cache_active_timeout; /* 104 4 */
uint32_t cache_max_flows; /* 108 4 */
char * virtual_obs_id; /* 112 8 */
uint8_t virtual_obs_len; /* 120 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 2 boundary (128 bytes) --- */
ofproto_ipfix_stats ofproto_stats; /* 128 88 */
/* --- cacheline 3 boundary (192 bytes) was 24 bytes ago --- */
struct dpif_ipfix_global_stats ipfix_global_stats; /* 216 152 */
/* size: 368, cachelines: 6, members: 13 */
/* sum members: 361, holes: 1, sum holes: 7 */
/* last cacheline: 48 bytes */
};
...
> diff --git a/tests/system-traffic.at b/tests/system-traffic.at
> index 08c78ff57..1421ab0ab 100644
> --- a/tests/system-traffic.at
> +++ b/tests/system-traffic.at
> @@ -7488,3 +7488,55 @@ OVS_WAIT_UNTIL([cat p2.pcap | grep -E "0x0050: *0000
> *0000 *5002 *2000 *b85e *00
>
> OVS_TRAFFIC_VSWITCHD_STOP
> AT_CLEANUP
> +
> +AT_SETUP([ipfix - muliple domains])
> +AT_SKIP_IF([test $HAVE_TCPDUMP = no])
> +OVS_TRAFFIC_VSWITCHD_START()
> +
> +ADD_NAMESPACES(at_ns0, at_ns1)
> +
> +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
> +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
> +
> +dnl Create a dummy IPFIX collector just in localhost so that port seems open.
> +OVS_DAEMONIZE([nc -l -u 127.0.0.1 5555 > /dev/null], [nc.pid])
> +
> +OVS_DAEMONIZE([tcpdump -n -i lo udp port 5555 -w ipfix.pcap], [tcpdump.pid])
> +sleep 1
> +
> +dnl Configure a Flow Sample Collector Set with id = 1.
> +AT_CHECK([ovs-vsctl -- --id=@br get Bridge br0 \
> + -- --id=@i create IPFIX targets=\"127.0.0.1:5555\" \
> + -- create Flow_Sample_Collector_Set bridge=@br id=1
> ipfix=@i],
> + [ignore], [ignore])
> +
> +dnl Push flows that will allow communication between the network namespaces
> +dnl and sample all ICMP traffic with multiple observation domain IDs.
> +AT_DATA([flows.txt], [dnl
> +table=0,priority=100,in_port=ovs-p0,ip,icmp
> actions=sample(probability=65535,collector_set_id=1,obs_domain_id=1),NORMAL
> +table=0,priority=100,in_port=ovs-p1,ip,icmp
> actions=sample(probability=65535,collector_set_id=1,obs_domain_id=2),NORMAL
> +table=0,priority=0,actions=NORMAL
> +])
> +AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
> +
> +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING],
> [0], [dnl
> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> +])
> +
> +sleep 1
> +kill $(cat tcpdump.pid)
> +wait $(cat tcpdump.pid) 2> /dev/null
> +
> +dnl Check templates (Flow ID = 0x02) where sent to ObservationDomain 1.
> +AT_CHECK([test $(tcpdump -r ipfix.pcap --count "udp port 5555 and
> udp[[20:4]] = 0x00000001 and udp[[24:2]] = 0x02" 2>/dev/null | tr -d
> "packets") -gt 0])
> +dnl Check that exactly 3 samples (Flow ID != 0x02) where sent to
> ObservationDomain 1.
> +AT_CHECK([test $(tcpdump -r ipfix.pcap --count "udp port 5555 and
> udp[[20:4]] = 0x00000001 and udp[[24:2]] != 0x02" 2>/dev/null | tr -d
> "packets") -eq 3])
> +
> +dnl Check templates (Flow ID = 0x02) where sent to ObservationDomain 2.
> +AT_CHECK([test $(tcpdump -r ipfix.pcap --count "udp port 5555 and
> udp[[20:4]] = 0x00000002 and udp[[24:2]] = 0x02" 2>/dev/null | tr -d
> "packets") -gt 0])
> +dnl Check that exactly 3 samples (Flow ID != 0x02) where sent to
> ObservationDomain 2.
> +AT_CHECK([test $(tcpdump -r ipfix.pcap --count "udp port 5555 and
> udp[[20:4]] = 0x00000002 and udp[[24:2]] != 0x02" 2>/dev/null | tr -d
> "packets") -eq 3])
> +
> +OVS_TRAFFIC_VSWITCHD_STOP
> +AT_CLEANUP
> +
nit: git complains, I think it is about the trailing blank line above.
$ git am a.patch
Applying: ofproto-ipfix: use per-domain template timeouts
.git/rebase-apply/patch:366: new blank line at EOF.
+
warning: 1 line adds whitespace errors.
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev