diff options
author | AkshayaNadahalli <anadahal@cisco.com> | 2016-12-01 16:33:51 +0530 |
---|---|---|
committer | Neale Ranns <nranns@cisco.com> | 2017-02-13 08:36:59 +0000 |
commit | fdd81af6afe6c782ad2c1a139210378badec626b (patch) | |
tree | b562b391bded3afc0f5b4d0f83eceb20f2c8e772 /src/plugins/ioam/encap | |
parent | fed79e83910459ed700615d2a5a24024f835a66c (diff) |
VPP-632 : InBand OAM Analyser
Refer to jira ticket for more details.
Change-Id: I6facb9ef8553a21464f9a2e612706f152badbb68
Signed-off-by: AkshayaNadahalli <anadahal@cisco.com>
Diffstat (limited to 'src/plugins/ioam/encap')
-rw-r--r-- | src/plugins/ioam/encap/ip6_ioam_e2e.c | 4 | ||||
-rw-r--r-- | src/plugins/ioam/encap/ip6_ioam_e2e.h | 17 | ||||
-rw-r--r-- | src/plugins/ioam/encap/ip6_ioam_pot.c | 21 | ||||
-rw-r--r-- | src/plugins/ioam/encap/ip6_ioam_pot.h | 40 | ||||
-rw-r--r-- | src/plugins/ioam/encap/ip6_ioam_seqno.c | 43 | ||||
-rw-r--r-- | src/plugins/ioam/encap/ip6_ioam_seqno.h | 42 | ||||
-rw-r--r-- | src/plugins/ioam/encap/ip6_ioam_seqno_analyse.c | 141 | ||||
-rw-r--r-- | src/plugins/ioam/encap/ip6_ioam_trace.c | 50 | ||||
-rw-r--r-- | src/plugins/ioam/encap/ip6_ioam_trace.h | 38 |
9 files changed, 128 insertions, 268 deletions
diff --git a/src/plugins/ioam/encap/ip6_ioam_e2e.c b/src/plugins/ioam/encap/ip6_ioam_e2e.c index 2831a351..cdaf740d 100644 --- a/src/plugins/ioam/encap/ip6_ioam_e2e.c +++ b/src/plugins/ioam/encap/ip6_ioam_e2e.c @@ -37,7 +37,7 @@ static u8 * ioam_e2e_trace_handler (u8 * s, if (e2e) { - seqno = clib_net_to_host_u32 (e2e->e2e_data); + seqno = clib_net_to_host_u32 (e2e->e2e_hdr.e2e_data); } s = format (s, "SeqNo = 0x%Lx", seqno); @@ -108,7 +108,7 @@ ioam_e2e_flow_handler (u32 ctx, u8 add) { pool_get(ioam_e2e_main.e2e_data, data); data->flow_ctx = ctx; - ioam_seqno_init_bitmap(&data->seqno_data); + ioam_seqno_init_data(&data->seqno_data); return ((u32) (data - ioam_e2e_main.e2e_data)); } diff --git a/src/plugins/ioam/encap/ip6_ioam_e2e.h b/src/plugins/ioam/encap/ip6_ioam_e2e.h index 18f35f80..fcec4a14 100644 --- a/src/plugins/ioam/encap/ip6_ioam_e2e.h +++ b/src/plugins/ioam/encap/ip6_ioam_e2e.h @@ -16,8 +16,16 @@ #ifndef __included_ip6_ioam_e2e_h__ #define __included_ip6_ioam_e2e_h__ +#include "../lib-e2e/e2e_util.h" #include "ip6_ioam_seqno.h" +/* *INDENT-OFF* */ +typedef CLIB_PACKED(struct { + ip6_hop_by_hop_option_t hdr; + ioam_e2e_packet_t e2e_hdr; +}) ioam_e2e_option_t; +/* *INDENT-ON* */ + typedef struct ioam_e2e_data_t_ { u32 flow_ctx; u32 pad; @@ -44,4 +52,13 @@ ioam_e2ec_get_seqno_data_from_flow_ctx (u32 flow_ctx) return &(data->seqno_data); } +static inline u32 +ioam_e2e_get_cur_seqno_from_flow_ctx (u32 flow_ctx) +{ + ioam_seqno_data *data = NULL; + + data = ioam_e2ec_get_seqno_data_from_flow_ctx(flow_ctx); + return data->seq_num; +} + #endif /* __included_ioam_e2e_h__ */ diff --git a/src/plugins/ioam/encap/ip6_ioam_pot.c b/src/plugins/ioam/encap/ip6_ioam_pot.c index 05f42c91..9a761233 100644 --- a/src/plugins/ioam/encap/ip6_ioam_pot.c +++ b/src/plugins/ioam/encap/ip6_ioam_pot.c @@ -16,26 +16,15 @@ #include <vnet/vnet.h> #include <vnet/pg/pg.h> #include <vppinfra/error.h> - #include <vnet/ip/ip6.h> -#include <vnet/ip/ip6_hop_by_hop.h> -#include <vnet/ip/ip6_hop_by_hop_packet.h> #include <vppinfra/hash.h> #include <vppinfra/error.h> #include <vppinfra/elog.h> +#include <ioam/encap/ip6_ioam_pot.h> #include <ioam/lib-pot/pot_util.h> -typedef CLIB_PACKED(struct { - ip6_hop_by_hop_option_t hdr; - u8 pot_type; -#define PROFILE_ID_MASK 0xF - u8 reserved_profile_id; /* 4 bits reserved, 4 bits to carry profile id */ - u64 random; - u64 cumulative; -}) ioam_pot_option_t; - #define foreach_ip6_hop_by_hop_ioam_pot_stats \ _(PROCESSED, "Pkts with ip6 hop-by-hop pot options") \ _(PROFILE_MISS, "Pkts with ip6 hop-by-hop pot options but no profile set") \ @@ -180,19 +169,19 @@ ip6_hbh_ioam_proof_of_transit_pop_handler (vlib_buffer_t *b, ip6_header_t *ip, int rv = 0; pot_profile *pot_profile = 0; u8 result = 0; - + pot0 = (ioam_pot_option_t *) opt0; random = clib_net_to_host_u64(pot0->random); cumulative = clib_net_to_host_u64(pot0->cumulative); pot_profile = pot_profile_get_active(); result = pot_validate (pot_profile, cumulative, random); - - if (result == 1) + + if (result == 1) { ip6_ioam_stats_increment_counter (IP6_IOAM_POT_PASSED, 1); } - else + else { ip6_ioam_stats_increment_counter (IP6_IOAM_POT_FAILED, 1); } diff --git a/src/plugins/ioam/encap/ip6_ioam_pot.h b/src/plugins/ioam/encap/ip6_ioam_pot.h new file mode 100644 index 00000000..01ce4ac5 --- /dev/null +++ b/src/plugins/ioam/encap/ip6_ioam_pot.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_POT_H_ +#define PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_POT_H_ + +#include <vnet/ip/ip6_hop_by_hop_packet.h> + +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct { + ip6_hop_by_hop_option_t hdr; + u8 pot_type; + #define PROFILE_ID_MASK 0xF + u8 reserved_profile_id; /* 4 bits reserved, 4 bits to carry profile id */ + u64 random; + u64 cumulative; +}) ioam_pot_option_t; +/* *INDENT-ON* */ + +#endif /* PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_POT_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/ioam/encap/ip6_ioam_seqno.c b/src/plugins/ioam/encap/ip6_ioam_seqno.c index 0b4d4192..01977123 100644 --- a/src/plugins/ioam/encap/ip6_ioam_seqno.c +++ b/src/plugins/ioam/encap/ip6_ioam_seqno.c @@ -28,20 +28,6 @@ #include "ip6_ioam_seqno.h" #include "ip6_ioam_e2e.h" -ioam_seqno_data_main_t ioam_seqno_main; - -void ioam_seqno_init_bitmap (ioam_seqno_data *data) -{ - seqno_bitmap *bitmap = &data->seqno_rx.bitmap; - bitmap->window_size = SEQNO_WINDOW_SIZE; - bitmap->array_size = SEQNO_WINDOW_ARRAY_SIZE; - bitmap->mask = 32 * SEQNO_WINDOW_ARRAY_SIZE - 1; - bitmap->array[0] = 0x00000000;/* pretend we haven seen sequence numbers 0*/ - bitmap->highest = 0; - - data->seq_num = 0; - return ; -} /* * This Routine gets called from IPv6 hop-by-hop option handling. @@ -60,7 +46,7 @@ ioam_seqno_encap_handler (vlib_buffer_t *b, ip6_header_t *ip, data = ioam_e2ec_get_seqno_data_from_flow_ctx(opaque_index); e2e = (ioam_e2e_option_t *) opt; - e2e->e2e_data = clib_host_to_net_u32(++data->seq_num); + e2e->e2e_hdr.e2e_data = clib_host_to_net_u32(++data->seq_num); return (rv); } @@ -79,31 +65,8 @@ ioam_seqno_decap_handler (vlib_buffer_t *b, ip6_header_t *ip, data = ioam_e2ec_get_seqno_data_from_flow_ctx(opaque_index); e2e = (ioam_e2e_option_t *) opt; - ioam_analyze_seqno(&data->seqno_rx, (u64) clib_net_to_host_u32(e2e->e2e_data)); + ioam_analyze_seqno(&data->seqno_rx, + (u64) clib_net_to_host_u32(e2e->e2e_hdr.e2e_data)); return (rv); } - -u8 * -show_ioam_seqno_cmd_fn (u8 *s, ioam_seqno_data *seqno_data, u8 enc) -{ - seqno_rx_info *rx; - - s = format(s, "SeqNo Data:\n"); - if (enc) - { - s = format(s, " Current Seq. Number : %llu\n", seqno_data->seq_num); - } - else - { - rx = &seqno_data->seqno_rx; - s = format(s, " Highest Seq. Number : %llu\n", rx->bitmap.highest); - s = format(s, " Packets received : %llu\n", rx->rx_packets); - s = format(s, " Lost packets : %llu\n", rx->lost_packets); - s = format(s, " Reordered packets : %llu\n", rx->reordered_packets); - s = format(s, " Duplicate packets : %llu\n", rx->dup_packets); - } - - format(s, "\n"); - return s; -} diff --git a/src/plugins/ioam/encap/ip6_ioam_seqno.h b/src/plugins/ioam/encap/ip6_ioam_seqno.h index 13a84db0..5c140246 100644 --- a/src/plugins/ioam/encap/ip6_ioam_seqno.h +++ b/src/plugins/ioam/encap/ip6_ioam_seqno.h @@ -18,42 +18,7 @@ #include <vnet/ip/ip6_packet.h> #include <vnet/ip/ip6_hop_by_hop.h> - -#define SEQ_CHECK_VALUE 0x80000000 /* for seq number wraparound detection */ - -#define SEQNO_WINDOW_SIZE 2048 -#define SEQNO_WINDOW_ARRAY_SIZE 64 - -typedef struct seqno_bitmap_ { - u32 window_size; - u32 array_size; - u32 mask; - u32 pad; - u64 highest; - u64 array[SEQNO_WINDOW_ARRAY_SIZE]; /* Will be alloc to array_size */ -} seqno_bitmap; - -typedef struct seqno_rx_info_ { - u64 rx_packets; - u64 lost_packets; - u64 reordered_packets; - u64 dup_packets; - seqno_bitmap bitmap; -} seqno_rx_info; - -/* This structure is 64-byte aligned */ -typedef struct ioam_seqno_data_ { - union { - u32 seq_num; /* Useful only for encap node */ - seqno_rx_info seqno_rx; - }; -} ioam_seqno_data; - -typedef struct ioam_seqno_data_main_t_ { - ioam_seqno_data *seqno_data; -} ioam_seqno_data_main_t; - -void ioam_seqno_init_bitmap(ioam_seqno_data *data); +#include <ioam/lib-e2e/e2e_util.h> int ioam_seqno_encap_handler(vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt); @@ -62,9 +27,4 @@ int ioam_seqno_decap_handler(vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt); -void ioam_analyze_seqno(seqno_rx_info *ppc_rx, u64 seqno); - -u8 * -show_ioam_seqno_cmd_fn(u8 *s, ioam_seqno_data *seqno_data, u8 enc); - #endif diff --git a/src/plugins/ioam/encap/ip6_ioam_seqno_analyse.c b/src/plugins/ioam/encap/ip6_ioam_seqno_analyse.c deleted file mode 100644 index 4638871c..00000000 --- a/src/plugins/ioam/encap/ip6_ioam_seqno_analyse.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <vnet/vnet.h> -#include "ip6_ioam_seqno.h" - -static inline void BIT_SET (u64 *p, u32 n) -{ - p[ n>>5 ] |= (1 << (n&31)); -} - -static inline int BIT_TEST (u64 *p, u32 n) -{ - return p[ n>>5 ] & (1 << (n&31)); -} - -static void BIT_CLEAR (u64 *p, u64 start, int num_bits, u32 mask) -{ - int n, t; - int start_index = (start >> 5); - int mask_index = (mask >> 5); - - start_index &= mask_index; - if (start & 0x1f) - { - int start_bit = (start & 0x1f); - - n = (1 << start_bit)-1; - t = start_bit + num_bits; - if (t < 32) - { - n |= ~((1 << t)-1); - p[ start_index ] &= n; - return; - } - p[ start_index ] &= n; - start_index = (start_index + 1) & mask_index; - num_bits -= (32 - start_bit); - } - while (num_bits >= 32) - { - p[ start_index ] = 0; - start_index = (start_index + 1) & mask_index; - num_bits -= 32; - } - n = ~((1 << num_bits) - 1); - p[ start_index ] &= n; -} - -static inline u8 seqno_check_wraparound(u32 a, u32 b) -{ - if ((a != b) && (a > b) && ((a - b) > SEQ_CHECK_VALUE)) - { - return 1; - } - return 0; -} - -/* - * Function to analyze the PPC value recevied. - * - Updates the bitmap with received sequence number - * - counts the received/lost/duplicate/reordered packets - */ -void ioam_analyze_seqno (seqno_rx_info *seqno_rx, u64 seqno) -{ - int diff; - static int peer_dead_count; - seqno_bitmap *bitmap = &seqno_rx->bitmap; - - seqno_rx->rx_packets++; - - if (seqno > bitmap->highest) - { /* new larger sequence number */ - peer_dead_count = 0; - diff = seqno - bitmap->highest; - if (diff < bitmap->window_size) - { - if (diff > 1) - { /* diff==1 is *such* a common case it's a win to optimize it */ - BIT_CLEAR(bitmap->array, bitmap->highest+1, diff-1, bitmap->mask); - seqno_rx->lost_packets += diff -1; - } - } - else - { - seqno_rx->lost_packets += diff -1; - memset( bitmap->array, 0, bitmap->array_size * sizeof(u64) ); - } - BIT_SET(bitmap->array, seqno & bitmap->mask); - bitmap->highest = seqno; - return; - } - - /* we've seen a bigger seq number before */ - diff = bitmap->highest - seqno; - if (diff >= bitmap->window_size) - { - if (seqno_check_wraparound(bitmap->highest, seqno)) - { - memset( bitmap->array, 0, bitmap->array_size * sizeof(u64)); - BIT_SET(bitmap->array, seqno & bitmap->mask); - bitmap->highest = seqno; - return; - } - else - { - peer_dead_count++; - if (peer_dead_count > 25) - { - peer_dead_count = 0; - memset( bitmap->array, 0, bitmap->array_size * sizeof(u64) ); - BIT_SET(bitmap->array, seqno & bitmap->mask); - bitmap->highest = seqno; - } - //ppc_rx->reordered_packets++; - } - return; - } - - if (BIT_TEST(bitmap->array, seqno & bitmap->mask)) - { - seqno_rx->dup_packets++; - return; /* Already seen */ - } - seqno_rx->reordered_packets++; - seqno_rx->lost_packets--; - BIT_SET(bitmap->array, seqno & bitmap->mask); - return; -} diff --git a/src/plugins/ioam/encap/ip6_ioam_trace.c b/src/plugins/ioam/encap/ip6_ioam_trace.c index a836dfe8..6972ba4b 100644 --- a/src/plugins/ioam/encap/ip6_ioam_trace.c +++ b/src/plugins/ioam/encap/ip6_ioam_trace.c @@ -27,7 +27,7 @@ #include <vppinfra/elog.h> #include <vnet/plugin/plugin.h> -#include <ioam/lib-trace/trace_util.h> +#include <ioam/encap/ip6_ioam_trace.h> /* Timestamp precision multipliers for seconds, milliseconds, microseconds * and nanoseconds respectively. @@ -40,15 +40,6 @@ typedef union u32 as_u32[2]; } time_u64_t; -/* *INDENT-OFF* */ -typedef CLIB_PACKED(struct { - ip6_hop_by_hop_option_t hdr; - u8 ioam_trace_type; - u8 data_list_elts_left; - u32 elts[0]; /* Variable type. So keep it generic */ -}) ioam_trace_option_t; -/* *INDENT-ON* */ - extern ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main; extern ip6_main_t ip6_main; @@ -201,8 +192,9 @@ ip6_hop_by_hop_ioam_trace_rewrite_handler (u8 * rewrite_string, HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE; trace_option->hdr.length = 2 /*ioam_trace_type,data_list_elts_left */ + trace_option_elts * trace_data_size; - trace_option->ioam_trace_type = profile->trace_type & TRACE_TYPE_MASK; - trace_option->data_list_elts_left = trace_option_elts; + trace_option->trace_hdr.ioam_trace_type = + profile->trace_type & TRACE_TYPE_MASK; + trace_option->trace_hdr.data_list_elts_left = trace_option_elts; *rewrite_size = sizeof (ioam_trace_option_t) + (trace_option_elts * trace_data_size); @@ -238,24 +230,24 @@ ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip, time_u64.as_u64 = 0; - if (PREDICT_TRUE (trace->data_list_elts_left)) + if (PREDICT_TRUE (trace->trace_hdr.data_list_elts_left)) { - trace->data_list_elts_left--; + trace->trace_hdr.data_list_elts_left--; /* fetch_trace_data_size returns in bytes. Convert it to 4-bytes * to skip to this node's location. */ elt_index = - trace->data_list_elts_left * - fetch_trace_data_size (trace->ioam_trace_type) / 4; - elt = &trace->elts[elt_index]; - if (trace->ioam_trace_type & BIT_TTL_NODEID) + trace->trace_hdr.data_list_elts_left * + fetch_trace_data_size (trace->trace_hdr.ioam_trace_type) / 4; + elt = &trace->trace_hdr.elts[elt_index]; + if (trace->trace_hdr.ioam_trace_type & BIT_TTL_NODEID) { *elt = clib_host_to_net_u32 ((ip->hop_limit << 24) | profile->node_id); elt++; } - if (trace->ioam_trace_type & BIT_ING_INTERFACE) + if (trace->trace_hdr.ioam_trace_type & BIT_ING_INTERFACE) { *elt = (vnet_buffer (b)->sw_if_index[VLIB_RX] & 0xFFFF) << 16 | @@ -264,7 +256,7 @@ ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip, elt++; } - if (trace->ioam_trace_type & BIT_TIMESTAMP) + if (trace->trace_hdr.ioam_trace_type & BIT_TIMESTAMP) { /* Send least significant 32 bits */ f64 time_f64 = @@ -276,7 +268,7 @@ ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t * b, ip6_header_t * ip, elt++; } - if (trace->ioam_trace_type & BIT_APPDATA) + if (trace->trace_hdr.ioam_trace_type & BIT_APPDATA) { /* $$$ set elt0->app_data */ *elt = clib_host_to_net_u32 (profile->app_data); @@ -302,17 +294,19 @@ ip6_hbh_ioam_trace_data_list_trace_handler (u8 * s, trace = (ioam_trace_option_t *) opt; s = - format (s, " Trace Type 0x%x , %d elts left\n", trace->ioam_trace_type, - trace->data_list_elts_left); + format (s, " Trace Type 0x%x , %d elts left\n", + trace->trace_hdr.ioam_trace_type, + trace->trace_hdr.data_list_elts_left); trace_data_size_in_words = - fetch_trace_data_size (trace->ioam_trace_type) / 4; - elt = &trace->elts[0]; - while ((u8 *) elt < ((u8 *) (&trace->elts[0]) + trace->hdr.length - 2 - /* -2 accounts for ioam_trace_type,elts_left */ )) + fetch_trace_data_size (trace->trace_hdr.ioam_trace_type) / 4; + elt = &trace->trace_hdr.elts[0]; + while ((u8 *) elt < + ((u8 *) (&trace->trace_hdr.elts[0]) + trace->hdr.length - 2 + /* -2 accounts for ioam_trace_type,elts_left */ )) { s = format (s, " [%d] %U\n", elt_index, format_ioam_data_list_element, - elt, &trace->ioam_trace_type); + elt, &trace->trace_hdr.ioam_trace_type); elt_index++; elt += trace_data_size_in_words; } diff --git a/src/plugins/ioam/encap/ip6_ioam_trace.h b/src/plugins/ioam/encap/ip6_ioam_trace.h new file mode 100644 index 00000000..b332b319 --- /dev/null +++ b/src/plugins/ioam/encap/ip6_ioam_trace.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_TRACE_H_ +#define PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_TRACE_H_ + +#include <vnet/ip/ip6_hop_by_hop_packet.h> +#include <ioam/lib-trace/trace_util.h> + +/* *INDENT-OFF* */ +typedef CLIB_PACKED(struct { + ip6_hop_by_hop_option_t hdr; + ioam_trace_hdr_t trace_hdr; +}) ioam_trace_option_t; +/* *INDENT-ON* */ + + +#endif /* PLUGINS_IOAM_PLUGIN_IOAM_ENCAP_IP6_IOAM_TRACE_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |