+ u32 next_index;
+
+ *next = error != IP4_ERROR_UNKNOWN_PROTOCOL ? IP_LOCAL_NEXT_DROP : *next;
+ b->error = error ? error_node->errors[error] : 0;
+ if (head_of_feature_arc)
+ {
+ next_index = *next;
+ if (PREDICT_TRUE (error == (u8) IP4_ERROR_UNKNOWN_PROTOCOL))
+ {
+ vnet_feature_arc_start (
+ arc_index, vnet_buffer (b)->ip.rx_sw_if_index, &next_index, b);
+ *next = next_index;
+ }
+ }
+}
+
+typedef struct
+{
+ /* The src and fib-index together determine if packet n is the same as n-1 */
+ ip4_address_t src;
+ u32 fib_index;
+ u32 lbi;
+ u8 error;
+ u8 first;
+} ip4_local_last_check_t;
+
+static inline void
+ip4_local_check_src (vlib_buffer_t *b, ip4_header_t *ip0,
+ ip4_local_last_check_t *last_check, u8 *error0,
+ int is_receive_dpo)
+{
+ const dpo_id_t *dpo0;
+ load_balance_t *lb0;
+ u32 lbi0;
+
+ vnet_buffer (b)->ip.fib_index =
+ vnet_buffer (b)->sw_if_index[VLIB_TX] != ~0 ?
+ vnet_buffer (b)->sw_if_index[VLIB_TX] : vnet_buffer (b)->ip.fib_index;
+
+ vnet_buffer (b)->ip.rx_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
+ if (is_receive_dpo)
+ {
+ receive_dpo_t *rd;
+ rd = receive_dpo_get (vnet_buffer (b)->ip.adj_index[VLIB_TX]);
+ if (rd->rd_sw_if_index != ~0)
+ vnet_buffer (b)->ip.rx_sw_if_index = rd->rd_sw_if_index;
+ }
+
+ /*
+ * vnet_buffer()->ip.adj_index[VLIB_RX] will be set to the index of the
+ * adjacency for the destination address (the local interface address).
+ * vnet_buffer()->ip.adj_index[VLIB_TX] will be set to the index of the
+ * adjacency for the source address (the remote sender's address)
+ */
+ if (PREDICT_TRUE ((last_check->src.as_u32 != ip0->src_address.as_u32)) ||
+ (last_check->fib_index != vnet_buffer (b)->ip.fib_index) ||
+ last_check->first)
+ {
+ lbi0 = ip4_fib_forwarding_lookup (vnet_buffer (b)->ip.fib_index,
+ &ip0->src_address);
+
+ vnet_buffer (b)->ip.adj_index[VLIB_RX] =
+ vnet_buffer (b)->ip.adj_index[VLIB_TX];
+ vnet_buffer (b)->ip.adj_index[VLIB_TX] = lbi0;
+
+ lb0 = load_balance_get (lbi0);
+ dpo0 = load_balance_get_bucket_i (lb0, 0);
+
+ /*
+ * Must have a route to source otherwise we drop the packet.
+ * ip4 broadcasts are accepted, e.g. to make dhcp client work
+ *
+ * The checks are:
+ * - the source is a recieve => it's from us => bogus, do this
+ * first since it sets a different error code.
+ * - uRPF check for any route to source - accept if passes.
+ * - allow packets destined to the broadcast address from unknown sources
+ */
+
+ *error0 = ((*error0 == IP4_ERROR_UNKNOWN_PROTOCOL
+ && dpo0->dpoi_type == DPO_RECEIVE) ?
+ IP4_ERROR_SPOOFED_LOCAL_PACKETS : *error0);
+ *error0 = ((*error0 == IP4_ERROR_UNKNOWN_PROTOCOL
+ && !fib_urpf_check_size (lb0->lb_urpf)
+ && ip0->dst_address.as_u32 != 0xFFFFFFFF) ?
+ IP4_ERROR_SRC_LOOKUP_MISS : *error0);
+
+ last_check->src.as_u32 = ip0->src_address.as_u32;
+ last_check->lbi = lbi0;
+ last_check->error = *error0;
+ last_check->first = 0;
+ last_check->fib_index = vnet_buffer (b)->ip.fib_index;
+ }
+ else
+ {
+ vnet_buffer (b)->ip.adj_index[VLIB_RX] =
+ vnet_buffer (b)->ip.adj_index[VLIB_TX];
+ vnet_buffer (b)->ip.adj_index[VLIB_TX] = last_check->lbi;
+ *error0 = last_check->error;
+ }
+}
+
+static inline void
+ip4_local_check_src_x2 (vlib_buffer_t **b, ip4_header_t **ip,
+ ip4_local_last_check_t *last_check, u8 *error,
+ int is_receive_dpo)
+{
+ const dpo_id_t *dpo[2];
+ load_balance_t *lb[2];
+ u32 not_last_hit;
+ u32 lbi[2];
+
+ not_last_hit = last_check->first;
+ not_last_hit |= ip[0]->src_address.as_u32 ^ last_check->src.as_u32;
+ not_last_hit |= ip[1]->src_address.as_u32 ^ last_check->src.as_u32;
+
+ vnet_buffer (b[0])->ip.fib_index =
+ vnet_buffer (b[0])->sw_if_index[VLIB_TX] != ~0 ?
+ vnet_buffer (b[0])->sw_if_index[VLIB_TX] :
+ vnet_buffer (b[0])->ip.fib_index;
+
+ vnet_buffer (b[1])->ip.fib_index =
+ vnet_buffer (b[1])->sw_if_index[VLIB_TX] != ~0 ?
+ vnet_buffer (b[1])->sw_if_index[VLIB_TX] :
+ vnet_buffer (b[1])->ip.fib_index;
+
+ not_last_hit |= vnet_buffer (b[0])->ip.fib_index ^ last_check->fib_index;
+ not_last_hit |= vnet_buffer (b[1])->ip.fib_index ^ last_check->fib_index;
+
+ vnet_buffer (b[0])->ip.rx_sw_if_index =
+ vnet_buffer (b[0])->sw_if_index[VLIB_RX];
+ vnet_buffer (b[1])->ip.rx_sw_if_index =
+ vnet_buffer (b[1])->sw_if_index[VLIB_RX];
+ if (is_receive_dpo)
+ {
+ const receive_dpo_t *rd0, *rd1;
+ rd0 = receive_dpo_get (vnet_buffer (b[0])->ip.adj_index[VLIB_TX]);
+ rd1 = receive_dpo_get (vnet_buffer (b[1])->ip.adj_index[VLIB_TX]);
+ if (rd0->rd_sw_if_index != ~0)
+ vnet_buffer (b[0])->ip.rx_sw_if_index = rd0->rd_sw_if_index;
+ if (rd1->rd_sw_if_index != ~0)
+ vnet_buffer (b[1])->ip.rx_sw_if_index = rd1->rd_sw_if_index;
+ }
+
+ /*
+ * vnet_buffer()->ip.adj_index[VLIB_RX] will be set to the index of the
+ * adjacency for the destination address (the local interface address).
+ * vnet_buffer()->ip.adj_index[VLIB_TX] will be set to the index of the
+ * adjacency for the source address (the remote sender's address)
+ */
+ if (PREDICT_TRUE (not_last_hit))
+ {
+ ip4_fib_forwarding_lookup_x2 (
+ vnet_buffer (b[0])->ip.fib_index, vnet_buffer (b[1])->ip.fib_index,
+ &ip[0]->src_address, &ip[1]->src_address, &lbi[0], &lbi[1]);
+
+ vnet_buffer (b[0])->ip.adj_index[VLIB_RX] =
+ vnet_buffer (b[0])->ip.adj_index[VLIB_TX];
+ vnet_buffer (b[0])->ip.adj_index[VLIB_TX] = lbi[0];
+
+ vnet_buffer (b[1])->ip.adj_index[VLIB_RX] =
+ vnet_buffer (b[1])->ip.adj_index[VLIB_TX];
+ vnet_buffer (b[1])->ip.adj_index[VLIB_TX] = lbi[1];
+
+ lb[0] = load_balance_get (lbi[0]);
+ lb[1] = load_balance_get (lbi[1]);
+
+ dpo[0] = load_balance_get_bucket_i (lb[0], 0);
+ dpo[1] = load_balance_get_bucket_i (lb[1], 0);
+
+ error[0] = ((error[0] == IP4_ERROR_UNKNOWN_PROTOCOL &&
+ dpo[0]->dpoi_type == DPO_RECEIVE) ?
+ IP4_ERROR_SPOOFED_LOCAL_PACKETS : error[0]);
+ error[0] = ((error[0] == IP4_ERROR_UNKNOWN_PROTOCOL &&
+ !fib_urpf_check_size (lb[0]->lb_urpf) &&
+ ip[0]->dst_address.as_u32 != 0xFFFFFFFF)
+ ? IP4_ERROR_SRC_LOOKUP_MISS : error[0]);
+
+ error[1] = ((error[1] == IP4_ERROR_UNKNOWN_PROTOCOL &&
+ dpo[1]->dpoi_type == DPO_RECEIVE) ?
+ IP4_ERROR_SPOOFED_LOCAL_PACKETS : error[1]);
+ error[1] = ((error[1] == IP4_ERROR_UNKNOWN_PROTOCOL &&
+ !fib_urpf_check_size (lb[1]->lb_urpf) &&
+ ip[1]->dst_address.as_u32 != 0xFFFFFFFF)
+ ? IP4_ERROR_SRC_LOOKUP_MISS : error[1]);
+
+ last_check->src.as_u32 = ip[1]->src_address.as_u32;
+ last_check->lbi = lbi[1];
+ last_check->error = error[1];
+ last_check->first = 0;
+ last_check->fib_index = vnet_buffer (b[1])->ip.fib_index;
+ }
+ else
+ {
+ vnet_buffer (b[0])->ip.adj_index[VLIB_RX] =
+ vnet_buffer (b[0])->ip.adj_index[VLIB_TX];
+ vnet_buffer (b[0])->ip.adj_index[VLIB_TX] = last_check->lbi;
+
+ vnet_buffer (b[1])->ip.adj_index[VLIB_RX] =
+ vnet_buffer (b[1])->ip.adj_index[VLIB_TX];
+ vnet_buffer (b[1])->ip.adj_index[VLIB_TX] = last_check->lbi;
+
+ error[0] = last_check->error;
+ error[1] = last_check->error;
+ }
+}
+
+enum ip_local_packet_type_e
+{
+ IP_LOCAL_PACKET_TYPE_L4,
+ IP_LOCAL_PACKET_TYPE_NAT,
+ IP_LOCAL_PACKET_TYPE_FRAG,
+};
+
+/**
+ * Determine packet type and next node.
+ *
+ * The expectation is that all packets that are not L4 will skip
+ * checksums and source checks.
+ */
+always_inline u8
+ip4_local_classify (vlib_buffer_t * b, ip4_header_t * ip, u16 * next)
+{
+ ip_lookup_main_t *lm = &ip4_main.lookup_main;
+
+ if (PREDICT_FALSE (ip4_is_fragment (ip)))
+ {
+ *next = IP_LOCAL_NEXT_REASSEMBLY;
+ return IP_LOCAL_PACKET_TYPE_FRAG;
+ }
+ if (PREDICT_FALSE (b->flags & VNET_BUFFER_F_IS_NATED))
+ {
+ *next = lm->local_next_by_ip_protocol[ip->protocol];
+ return IP_LOCAL_PACKET_TYPE_NAT;
+ }
+
+ *next = lm->local_next_by_ip_protocol[ip->protocol];
+ return IP_LOCAL_PACKET_TYPE_L4;
+}
+
+static inline uword
+ip4_local_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vlib_frame_t *frame, int head_of_feature_arc,
+ int is_receive_dpo)
+{
+ u32 *from, n_left_from;
+ vlib_node_runtime_t *error_node =
+ vlib_node_get_runtime (vm, ip4_local_node.index);
+ u16 nexts[VLIB_FRAME_SIZE], *next;
+ vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
+ ip4_header_t *ip[2];
+ u8 error[2], pt[2];
+
+ ip4_local_last_check_t last_check = {
+ /*
+ * 0.0.0.0 can appear as the source address of an IP packet,
+ * as can any other address, hence the need to use the 'first'
+ * member to make sure the .lbi is initialised for the first
+ * packet.
+ */
+ .src = { .as_u32 = 0 },
+ .lbi = ~0,
+ .error = IP4_ERROR_UNKNOWN_PROTOCOL,
+ .first = 1,
+ .fib_index = 0,
+ };