aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Luke <chrisy@flirble.org>2016-04-25 14:26:55 -0400
committerDave Barach <openvpp@barachs.net>2016-04-28 11:37:30 +0000
commit194ebc58b6e362c7cc950ba2386567bfdbfbab55 (patch)
tree927dd4fe75586d31b1adf2a2e12c47228db0dddf
parent473bf23be85e861f95d69992b09b0b7d7a6efa2e (diff)
Track number of ethernet vlans in a frame
Adds flags to the packet buffer to track the number of VLANs in the current Ethernet frame. We use two bits to signify 0, 1 or 2 VLANs. The value 3 signififies an unknown quantity of VLANs, which includes "three or more" which is not widely supported. We place the bits in the vlib_buffer section; that is not the opaque section, so that all subordinate nodes can use it. For background, see the discussion thread at https://lists.fd.io/pipermail/vpp-dev/2016-March/000354.html The helper macro ethernet_buffer_header_size(buffer) uses these bits stored in "buffer" to calculate the Ethernet header size. The macro ethernet_buffer_set_vlan_count(buffer, count) sets the appropriate bit values based on the number in "count". By current frame we are referring to the case where a packet that arrives from the wire is carrying an encapsulated Ethernet packet. Once decapsulated that inner packet becomes the current frame. There are two places where this value is set; For most Ethernet frames this will be in the "ethernet-input" node when that node parses the Ethernet header. The second place is whenever vnet_update_l2_len() is used to update the layer 2 opaque data. Typically this function is used by nodes just before they send a packet into l2-input. These bits are zeroed in vlib_buffer_init_for_free_list() meaning that wherever the buffer comes from they have a reasonable value (eg, if ip4/ip6 generates the packet.) Primarily this VLAN counter is used by nodes below "ethernet- input" and "l2-input" to determine where the start of the current Ethernet header is. There is opaque data set by "ethernet-input" storing the offset of the current Ethernet header but, since this is opaque, it's not usable by downstream nodes. Previously several nodes have made assumptions regarding the location of the Ethernet header, including that it is always at the start of the packet buffer (incorrect when we have encapsulated packets) or that it is exactly sizeof(ethernet_header_t) away (incorrect when we have VLAN tags.) One notable case where this functionality is required is in ip6_neighbor when it generates a response to a received neighbor soliciation request; it reuses the incoming Ethernet header in-situ and thus needs to reliably know where that header begins. Also, at the suggestion of Dave Barach, this patch removes definition of HGSHM bits in the buffer flags since they are unused and unlikely to ever be. Change-Id: I00e4b9ced5ef814a776020c395d1774aba6185b3 Signed-off-by: Chris Luke <chrisy@flirble.org>
-rw-r--r--vlib/vlib/buffer.h3
-rw-r--r--vnet/vnet/buffer.h13
-rw-r--r--vnet/vnet/classify/ip_classify.c9
-rw-r--r--vnet/vnet/dhcpv6/proxy_node.c6
-rw-r--r--vnet/vnet/ethernet/ethernet.h44
-rw-r--r--vnet/vnet/ethernet/node.c8
-rw-r--r--vnet/vnet/ip/ip6_neighbor.c2
-rw-r--r--vnet/vnet/l2/l2_input.h4
-rw-r--r--vnet/vnet/l2/l2_vtr.h4
9 files changed, 84 insertions, 9 deletions
diff --git a/vlib/vlib/buffer.h b/vlib/vlib/buffer.h
index 07ed85d8c31..e11085b72a0 100644
--- a/vlib/vlib/buffer.h
+++ b/vlib/vlib/buffer.h
@@ -93,8 +93,7 @@ typedef struct {
#define VLIB_BUFFER_NEXT_PRESENT (1 << VLIB_BUFFER_LOG2_NEXT_PRESENT)
#define VLIB_BUFFER_IS_RECYCLED (1 << 2)
#define VLIB_BUFFER_TOTAL_LENGTH_VALID (1 << 3)
-#define VLIB_BUFFER_HGSHM_USER_INDEX_VALID (1 << 4)
-#define VLIB_BUFFER_REPL_FAIL (1 << 5)
+#define VLIB_BUFFER_REPL_FAIL (1 << 4)
/* User defined buffer flags. */
#define LOG2_VLIB_BUFFER_FLAG_USER(n) (32 - (n))
diff --git a/vnet/vnet/buffer.h b/vnet/vnet/buffer.h
index f7fd8e8f872..7153f3541b1 100644
--- a/vnet/vnet/buffer.h
+++ b/vnet/vnet/buffer.h
@@ -49,6 +49,19 @@
#define IP_BUFFER_L4_CHECKSUM_COMPUTED (1 << LOG2_IP_BUFFER_L4_CHECKSUM_COMPUTED)
#define IP_BUFFER_L4_CHECKSUM_CORRECT (1 << LOG2_IP_BUFFER_L4_CHECKSUM_CORRECT)
+/* VLAN header flags.
+ * These bits are zeroed in vlib_buffer_init_for_free_list()
+ * meaning wherever the buffer comes from they have a reasonable
+ * value (eg, if ip4/ip6 generates the packet.)
+ */
+#define LOG2_ETH_BUFFER_VLAN_2_DEEP LOG2_VLIB_BUFFER_FLAG_USER(3)
+#define LOG2_ETH_BUFFER_VLAN_1_DEEP LOG2_VLIB_BUFFER_FLAG_USER(4)
+#define ETH_BUFFER_VLAN_2_DEEP (1 << LOG2_ETH_BUFFER_VLAN_2_DEEP)
+#define ETH_BUFFER_VLAN_1_DEEP (1 << LOG2_ETH_BUFFER_VLAN_1_DEEP)
+#define ETH_BUFFER_VLAN_BITS (ETH_BUFFER_VLAN_1_DEEP | \
+ ETH_BUFFER_VLAN_2_DEEP)
+
+
#define foreach_buffer_opaque_union_subtype \
_(ethernet) \
_(ip) \
diff --git a/vnet/vnet/classify/ip_classify.c b/vnet/vnet/classify/ip_classify.c
index 55c08e0ceeb..8152f6ce353 100644
--- a/vnet/vnet/classify/ip_classify.c
+++ b/vnet/vnet/classify/ip_classify.c
@@ -104,11 +104,13 @@ ip_classify_inline (vlib_main_t * vm,
bi0 = from[0];
b0 = vlib_get_buffer (vm, bi0);
- h0 = b0->data;
+ h0 = (void *)vlib_buffer_get_current(b0) -
+ ethernet_buffer_header_size(b0);
bi1 = from[1];
b1 = vlib_get_buffer (vm, bi1);
- h1 = b1->data;
+ h1 = (void *)vlib_buffer_get_current(b1) -
+ ethernet_buffer_header_size(b1);
adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
adj0 = ip_get_adjacency (lm, adj_index0);
@@ -152,7 +154,8 @@ ip_classify_inline (vlib_main_t * vm,
bi0 = from[0];
b0 = vlib_get_buffer (vm, bi0);
- h0 = b0->data;
+ h0 = (void *)vlib_buffer_get_current(b0) -
+ ethernet_buffer_header_size(b0);
adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
adj0 = ip_get_adjacency (lm, adj_index0);
diff --git a/vnet/vnet/dhcpv6/proxy_node.c b/vnet/vnet/dhcpv6/proxy_node.c
index 01f5965c1b4..4dc746f6936 100644
--- a/vnet/vnet/dhcpv6/proxy_node.c
+++ b/vnet/vnet/dhcpv6/proxy_node.c
@@ -201,8 +201,7 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
b0 = vlib_get_buffer (vm, bi0);
h0 = vlib_buffer_get_current (b0);
- e_h0 = (ethernet_header_t *)b0->data;
- clib_memcpy(client_src_mac, e_h0->src_address, 6);
+
/* Send to DHCPV6 server via the configured FIB */
vnet_buffer(b0)->sw_if_index[VLIB_TX] =
dpm->server_fib_index;
@@ -212,6 +211,9 @@ dhcpv6_proxy_to_server_input (vlib_main_t * vm,
*/
u0 = (void *)h0 -(sizeof(*u0));
ip0 = (void *)u0 -(sizeof(*ip0));
+ e_h0 = (void *)ip0 - ethernet_buffer_header_size(b0);
+
+ clib_memcpy(client_src_mac, e_h0->src_address, 6);
switch (h0->u.msg_type) {
case DHCPV6_MSG_SOLICIT:
diff --git a/vnet/vnet/ethernet/ethernet.h b/vnet/vnet/ethernet/ethernet.h
index 492aa759d00..6f16527c0e3 100644
--- a/vnet/vnet/ethernet/ethernet.h
+++ b/vnet/vnet/ethernet/ethernet.h
@@ -308,6 +308,50 @@ ethernet_buffer_get_header (vlib_buffer_t * b)
+ vnet_buffer (b)->ethernet.start_of_ethernet_header);
}
+/** Returns the number of VLAN headers in the current Ethernet frame in the
+ * buffer. Returns 0, 1, 2 for the known header count. The value 3 indicates
+ * the number of headers is not known.
+ */
+#define ethernet_buffer_get_vlan_count(b) ( \
+ ((b)->flags & ETH_BUFFER_VLAN_BITS) >> LOG2_ETH_BUFFER_VLAN_1_DEEP \
+)
+
+/** Sets the number of VLAN headers in the current Ethernet frame in the
+ * buffer. Values 0, 1, 2 indicate the header count. The value 3 indicates
+ * the number of headers is not known.
+ */
+#define ethernet_buffer_set_vlan_count(b, v) ( \
+ (b)->flags = ((b)->flags & ~ETH_BUFFER_VLAN_BITS) | \
+ (((v) << LOG2_ETH_BUFFER_VLAN_1_DEEP) & ETH_BUFFER_VLAN_BITS) \
+)
+
+/** Adjusts the vlan count by the delta in 'v' */
+#define ethernet_buffer_adjust_vlan_count(b, v) ( \
+ ethernet_buffer_set_vlan_count(b, \
+ (word)ethernet_buffer_get_vlan_count(b) + (word)(v)) \
+)
+
+/** Adjusts the vlan count by the header size byte delta in 'v' */
+#define ethernet_buffer_adjust_vlan_count_by_bytes(b, v) ( \
+ (b)->flags = ((b)->flags & ~ETH_BUFFER_VLAN_BITS) | (( \
+ ((b)->flags & ETH_BUFFER_VLAN_BITS) + \
+ ((v) << (LOG2_ETH_BUFFER_VLAN_1_DEEP - 2)) \
+ ) & ETH_BUFFER_VLAN_BITS) \
+)
+
+/**
+ * Determine the size of the Ethernet headers of the current frame in
+ * the buffer. This uses the VLAN depth flags that are set by
+ * ethernet-input. Because these flags are stored in the vlib_buffer_t
+ * "flags" field this count is valid regardless of the node so long as it's
+ * checked downstream of ethernet-input; That is, the value is not stored in
+ * the opaque space.
+ */
+#define ethernet_buffer_header_size(b) ( \
+ ethernet_buffer_get_vlan_count((b)) * sizeof(ethernet_vlan_header_t) + \
+ sizeof(ethernet_header_t) \
+)
+
ethernet_main_t * ethernet_get_main (vlib_main_t * vm);
u32 ethernet_set_flags (vnet_main_t * vnm, u32 hw_if_index, u32 flags);
void ethernet_sw_interface_set_l2_mode (vnet_main_t * vnm, u32 sw_if_index, u32 l2);
diff --git a/vnet/vnet/ethernet/node.c b/vnet/vnet/ethernet/node.c
index 1ec49c4b805..7b9924f5cde 100644
--- a/vnet/vnet/ethernet/node.c
+++ b/vnet/vnet/ethernet/node.c
@@ -98,6 +98,7 @@ parse_header (ethernet_input_variant_t variant,
u16 * outer_id,
u16 * inner_id,
u32 * match_flags) {
+ u8 vlan_count;
if (variant == ETHERNET_INPUT_VARIANT_ETHERNET
|| variant == ETHERNET_INPUT_VARIANT_NOT_L2) {
@@ -129,6 +130,7 @@ parse_header (ethernet_input_variant_t variant,
*inner_id = 0;
*match_flags = SUBINT_CONFIG_VALID | SUBINT_CONFIG_MATCH_0_TAG;
+ vlan_count = 0;
// check for vlan encaps
if ((*type == ETHERNET_TYPE_VLAN) ||
@@ -150,6 +152,7 @@ parse_header (ethernet_input_variant_t variant,
*type = clib_net_to_host_u16(h0->type);
vlib_buffer_advance (b0, sizeof (h0[0]));
+ vlan_count = 1;
if (*type == ETHERNET_TYPE_VLAN) {
// Double tagged packet
@@ -164,13 +167,16 @@ parse_header (ethernet_input_variant_t variant,
*type = clib_net_to_host_u16(h0->type);
vlib_buffer_advance (b0, sizeof (h0[0]));
+ vlan_count = 2;
if (*type == ETHERNET_TYPE_VLAN) {
// More than double tagged packet
*match_flags = SUBINT_CONFIG_VALID | SUBINT_CONFIG_MATCH_3_TAG;
+ vlan_count = 3; // "unknown" number, aka, 3-or-more
}
}
}
+ ethernet_buffer_set_vlan_count(b0, vlan_count);
}
// Determine the subinterface for this packet, given the result of the
@@ -230,7 +236,7 @@ determine_next_node (ethernet_main_t * em,
*next0 = em->l2_next;
// record the L2 len and reset the buffer so the L2 header is preserved
vnet_buffer(b0)->l2.l2_len = b0->current_data;
- vlib_buffer_advance (b0, -(b0->current_data));
+ vlib_buffer_advance(b0, - ethernet_buffer_header_size(b0));
// check for common IP/MPLS ethertypes
} else if (type0 == ETHERNET_TYPE_IP4) {
diff --git a/vnet/vnet/ip/ip6_neighbor.c b/vnet/vnet/ip/ip6_neighbor.c
index 329cc6d7d67..19eb5a8d54d 100644
--- a/vnet/vnet/ip/ip6_neighbor.c
+++ b/vnet/vnet/ip/ip6_neighbor.c
@@ -847,7 +847,7 @@ icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm,
/* Reuse current MAC header, copy SMAC to DMAC and
* interface MAC to SMAC */
- vlib_buffer_reset (p0);
+ vlib_buffer_advance(p0, - ethernet_buffer_header_size(p0));
eth0 = vlib_buffer_get_current(p0);
clib_memcpy(eth0->dst_address, eth0->src_address, 6);
clib_memcpy(eth0->src_address, eth_if0->address, 6);
diff --git a/vnet/vnet/l2/l2_input.h b/vnet/vnet/l2/l2_input.h
index 1e5c1f0abc8..50649e79061 100644
--- a/vnet/vnet/l2/l2_input.h
+++ b/vnet/vnet/l2/l2_input.h
@@ -227,6 +227,7 @@ vnet_update_l2_len (vlib_buffer_t * b)
{
ethernet_header_t * eth;
u16 ethertype;
+ u8 vlan_count = 0;
/* point at currrent l2 hdr */
eth = vlib_buffer_get_current (b);
@@ -245,12 +246,15 @@ vnet_update_l2_len (vlib_buffer_t * b)
(ethertype == ETHERNET_TYPE_VLAN_9200)) {
ethernet_vlan_header_t * vlan;
vnet_buffer(b)->l2.l2_len += sizeof (*vlan);
+ vlan_count = 1;
vlan = (void *) (eth+1);
ethertype = clib_net_to_host_u16 (vlan->type);
if (ethertype == ETHERNET_TYPE_VLAN) {
vnet_buffer(b)->l2.l2_len += sizeof (*vlan);
+ vlan_count = 2;
}
}
+ ethernet_buffer_set_vlan_count(b, vlan_count);
}
/*
diff --git a/vnet/vnet/l2/l2_vtr.h b/vnet/vnet/l2/l2_vtr.h
index aef6c6d255e..ccd0920db52 100644
--- a/vnet/vnet/l2/l2_vtr.h
+++ b/vnet/vnet/l2/l2_vtr.h
@@ -96,6 +96,10 @@ l2_vtr_process (vlib_buffer_t * b0,
// Update l2_len
vnet_buffer(b0)->l2.l2_len += (word)config->push_bytes - (word)config->pop_bytes;
+ // Update vlan tag count
+ ethernet_buffer_adjust_vlan_count_by_bytes(b0,
+ (word)config->push_bytes - (word)config->pop_bytes);
+
// Update packet len
vlib_buffer_advance(b0, (word)config->pop_bytes - (word)config->push_bytes);