summaryrefslogtreecommitdiffstats
path: root/src/vnet/dhcp/dhcp_client_detect.c
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2018-01-17 10:29:10 -0800
committerOle Trøan <otroan@employees.org>2018-01-23 13:36:48 +0000
commit54c6dc450031443663d40b836a8b0bffdcc2bdea (patch)
tree0d59479cda2cdaf14ecd72a6d72ef2ac3083e6ac /src/vnet/dhcp/dhcp_client_detect.c
parent9d42087149a6870965896be74dc6260f72d2cac9 (diff)
For DHCP client configuration control the setting of the broadcast flag in the
DISCOVER message sent. According to RFC2131: In the case of a client using DHCP for initial configuration (before the client's TCP/IP software has been completely configured), DHCP requires creative use of the client's TCP/IP software and liberal interpretation of RFC 1122. The TCP/IP software SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured; DHCP servers and BOOTP relay agents may not be able to deliver DHCP messages to clients that cannot accept hardware unicast datagrams before the TCP/IP software is configured. To work around some clients that cannot accept IP unicast datagrams before the TCP/IP software is configured as discussed in the previous paragraph, DHCP uses the 'flags' field [21]. The leftmost bit is defined as the BROADCAST (B) flag. The semantics of this flag are discussed in section 4.1 of this document. The remaining bits of the flags field are reserved for future use. They MUST be set to zero by clients and ignored by servers and relay agents. Figure 2 gives the format of the 'flags' field. this changes means VPP conforms to the: "SHOULD accept and forward to the IP layer any IP packets delivered to the client's hardware address before the IP address is configured" with the caveat that VPP allows DHCP packets destined to the stanard client DHCP port to be delivered. With this enhancement the control-plane is now able to choose the setting of the broadcast flag. Change-Id: Ia4eb2c9bb1e30c29f9192facc645e9533641955a Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'src/vnet/dhcp/dhcp_client_detect.c')
-rw-r--r--src/vnet/dhcp/dhcp_client_detect.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/vnet/dhcp/dhcp_client_detect.c b/src/vnet/dhcp/dhcp_client_detect.c
new file mode 100644
index 00000000000..1b916cdd356
--- /dev/null
+++ b/src/vnet/dhcp/dhcp_client_detect.c
@@ -0,0 +1,333 @@
+/*
+ * DHCP feature; applied as an input feature to select DHCP packets
+ *
+ * Copyright (c) 2013 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/dhcp/client.h>
+#include <vnet/udp/udp.h>
+
+#define foreach_dhcp_client_detect \
+ _(EXTRACT, "Extract")
+
+typedef enum
+{
+#define _(sym,str) DHCP_CLIENT_DETECT_ERROR_##sym,
+ foreach_dhcp_client_detect
+#undef _
+ DHCP_CLIENT_DETECT_N_ERROR,
+} dhcp_client_detect_error_t;
+
+static char *dhcp_client_detect_error_strings[] = {
+#define _(sym,string) string,
+ foreach_dhcp_client_detect
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) DHCP_CLIENT_DETECT_NEXT_##sym,
+ foreach_dhcp_client_detect
+#undef _
+ DHCP_CLIENT_DETECT_N_NEXT,
+} dhcp_client_detect_next_t;
+
+/**
+ * per-packet trace data
+ */
+typedef struct dhcp_client_detect_trace_t_
+{
+ /* per-pkt trace data */
+ u8 extracted;
+} dhcp_client_detect_trace_t;
+
+static uword
+dhcp_client_detect_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ dhcp_client_detect_next_t next_index;
+ u16 dhcp_client_port_network_order;
+ u32 n_left_from, *from, *to_next;
+ u32 extractions;
+
+ dhcp_client_port_network_order =
+ clib_net_to_host_u16 (UDP_DST_PORT_dhcp_to_client);
+ next_index = 0;
+ extractions = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ /*
+ * This loop is optimised not so we can really quickly process DHCp
+ * offers... but so we can quickly sift them out when the interface
+ * is also receving 'normal' packets
+ */
+ while (n_left_from >= 8 && n_left_to_next >= 4)
+ {
+ udp_header_t *udp0, *udp1, *udp2, *udp3;
+ ip4_header_t *ip0, *ip1, *ip2, *ip3;
+ vlib_buffer_t *b0, *b1, *b2, *b3;
+ u32 next0, next1, next2, next3;
+ u32 bi0, bi1, bi2, bi3;
+
+ next0 = next1 = next2 = next3 = ~0;
+ bi0 = to_next[0] = from[0];
+ bi1 = to_next[1] = from[1];
+ bi2 = to_next[2] = from[2];
+ bi3 = to_next[3] = from[3];
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t *p2, *p3, *p4, *p5;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+ p4 = vlib_get_buffer (vm, from[4]);
+ p5 = vlib_get_buffer (vm, from[5]);
+
+ vlib_prefetch_buffer_header (p2, STORE);
+ vlib_prefetch_buffer_header (p3, STORE);
+ vlib_prefetch_buffer_header (p4, STORE);
+ vlib_prefetch_buffer_header (p5, STORE);
+
+ CLIB_PREFETCH (p2->data, sizeof (ip0[0]) + sizeof (udp0[0]),
+ STORE);
+ CLIB_PREFETCH (p3->data, sizeof (ip0[0]) + sizeof (udp0[0]),
+ STORE);
+ CLIB_PREFETCH (p4->data, sizeof (ip0[0]) + sizeof (udp0[0]),
+ STORE);
+ CLIB_PREFETCH (p5->data, sizeof (ip0[0]) + sizeof (udp0[0]),
+ STORE);
+ }
+
+ from += 4;
+ to_next += 4;
+ n_left_from -= 4;
+ n_left_to_next -= 4;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+ b2 = vlib_get_buffer (vm, bi2);
+ b3 = vlib_get_buffer (vm, bi3);
+ ip0 = vlib_buffer_get_current (b0);
+ ip1 = vlib_buffer_get_current (b1);
+ ip2 = vlib_buffer_get_current (b2);
+ ip3 = vlib_buffer_get_current (b2);
+
+ vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ &next0, b0);
+ vnet_feature_next (vnet_buffer (b1)->sw_if_index[VLIB_TX],
+ &next1, b1);
+ vnet_feature_next (vnet_buffer (b2)->sw_if_index[VLIB_TX],
+ &next2, b2);
+ vnet_feature_next (vnet_buffer (b3)->sw_if_index[VLIB_TX],
+ &next3, b3);
+
+ if (ip0->protocol == IP_PROTOCOL_UDP)
+ {
+ udp0 = (udp_header_t *) (ip0 + 1);
+
+ if (dhcp_client_port_network_order == udp0->dst_port)
+ {
+ next0 = DHCP_CLIENT_DETECT_NEXT_EXTRACT;
+ extractions++;
+ }
+ }
+ if (ip1->protocol == IP_PROTOCOL_UDP)
+ {
+ udp1 = (udp_header_t *) (ip1 + 1);
+
+ if (dhcp_client_port_network_order == udp1->dst_port)
+ {
+ next1 = DHCP_CLIENT_DETECT_NEXT_EXTRACT;
+ extractions++;
+ }
+ }
+ if (ip2->protocol == IP_PROTOCOL_UDP)
+ {
+ udp2 = (udp_header_t *) (ip2 + 1);
+
+ if (dhcp_client_port_network_order == udp2->dst_port)
+ {
+ next2 = DHCP_CLIENT_DETECT_NEXT_EXTRACT;
+ extractions++;
+ }
+ }
+ if (ip3->protocol == IP_PROTOCOL_UDP)
+ {
+ udp3 = (udp_header_t *) (ip3 + 1);
+
+ if (dhcp_client_port_network_order == udp3->dst_port)
+ {
+ next3 = DHCP_CLIENT_DETECT_NEXT_EXTRACT;
+ extractions++;
+ }
+ }
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dhcp_client_detect_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->extracted = (next0 == DHCP_CLIENT_DETECT_NEXT_EXTRACT);
+ }
+ if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dhcp_client_detect_trace_t *t =
+ vlib_add_trace (vm, node, b1, sizeof (*t));
+ t->extracted = (next1 == DHCP_CLIENT_DETECT_NEXT_EXTRACT);
+ }
+ if (PREDICT_FALSE (b2->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dhcp_client_detect_trace_t *t =
+ vlib_add_trace (vm, node, b2, sizeof (*t));
+ t->extracted = (next2 == DHCP_CLIENT_DETECT_NEXT_EXTRACT);
+ }
+ if (PREDICT_FALSE (b3->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dhcp_client_detect_trace_t *t =
+ vlib_add_trace (vm, node, b3, sizeof (*t));
+ t->extracted = (next3 == DHCP_CLIENT_DETECT_NEXT_EXTRACT);
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x4 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, bi2, bi3,
+ next0, next1, next2, next3);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ udp_header_t *udp0;
+ vlib_buffer_t *b0;
+ ip4_header_t *ip0;
+ u32 next0 = ~0;
+ u32 bi0;
+
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ ip0 = vlib_buffer_get_current (b0);
+
+ /*
+ * when this feature is applied on an interface that is already
+ * accepting packets (because e.g. the interface has other addresses
+ * assigned) we are looking for the preverbial needle in the haystack
+ * so assume the packet is not the one we are looking for.
+ */
+ vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ &next0, b0);
+
+ /*
+ * all we are looking for here is DHCP/BOOTP packet-to-client
+ * UDO port.
+ */
+ if (ip0->protocol == IP_PROTOCOL_UDP)
+ {
+ udp0 = (udp_header_t *) (ip0 + 1);
+
+ if (dhcp_client_port_network_order == udp0->dst_port)
+ {
+ next0 = DHCP_CLIENT_DETECT_NEXT_EXTRACT;
+ extractions++;
+ }
+ }
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dhcp_client_detect_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->extracted = (next0 == DHCP_CLIENT_DETECT_NEXT_EXTRACT);
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ vlib_node_increment_counter (vm, node->node_index,
+ DHCP_CLIENT_DETECT_ERROR_EXTRACT, extractions);
+
+ return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_dhcp_client_detect_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ dhcp_client_detect_trace_t *t =
+ va_arg (*args, dhcp_client_detect_trace_t *);
+
+ s = format (s, "dhcp-client-detect: %s", (t->extracted ? "yes" : "no"));
+
+ return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dhcp_client_detect_node) = {
+ .function = dhcp_client_detect_node_fn,
+ .name = "ip4-dhcp-client-detect",
+ .vector_size = sizeof (u32),
+ .format_trace = format_dhcp_client_detect_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(dhcp_client_detect_error_strings),
+ .error_strings = dhcp_client_detect_error_strings,
+
+ .n_next_nodes = DHCP_CLIENT_DETECT_N_NEXT,
+ .next_nodes = {
+ /*
+ * Jump straight to the UDP dispatch node thus avoiding
+ * the RPF checks in ip4-local that will fail
+ */
+ [DHCP_CLIENT_DETECT_NEXT_EXTRACT] = "ip4-udp-lookup",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (dhcp_client_detect_node,
+ dhcp_client_detect_node_fn);
+
+VNET_FEATURE_INIT (ip4_dvr_reinject_feat_node, static) =
+{
+ .arc_name = "ip4-unicast",
+ .node_name = "ip4-dhcp-client-detect",
+ .runs_before = VNET_FEATURES ("ip4-drop"),
+};
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */