@@ -81,7 +81,8 @@ struct ovn_extend_table;
OVNACT(PUT_ND_RA_OPTS, ovnact_put_opts) \
OVNACT(ND_NS, ovnact_nest) \
OVNACT(SET_METER, ovnact_set_meter) \
- OVNACT(OVNFIELD_LOAD, ovnact_load)
+ OVNACT(OVNFIELD_LOAD, ovnact_load) \
+ OVNACT(CHECK_PKT_LARGER, ovnact_check_pkt_larger)
/* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
enum OVS_PACKED_ENUM ovnact_type {
@@ -311,6 +312,13 @@ struct ovnact_set_meter {
uint64_t burst; /* burst rate field, in kbps. */
};
+/* OVNACT_CHECK_IP_PKT_LARGER. */
+struct ovnact_check_pkt_larger {
+ struct ovnact ovnact;
+ uint16_t pkt_len;
+ struct expr_field dst; /* 1-bit destination field. */
+};
+
/* Internal use by the helpers below. */
void ovnact_init(struct ovnact *, enum ovnact_type, size_t len);
void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len);
@@ -435,6 +435,16 @@ pinctrl_handle_icmp(const struct flow *ip_flow, struct dp_packet *pkt_in,
eh->eth_src = ip_flow->dl_src;
if (get_dl_type(ip_flow) == htons(ETH_TYPE_IP)) {
+ struct ip_header *in_ip = dp_packet_l3(pkt_in);
+ uint16_t in_ip_len = ntohs(in_ip->ip_tot_len);
+ if (in_ip_len < IP_HEADER_LEN) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl,
+ "ICMP action on IP packet with invalid length (%u)",
+ in_ip_len);
+ return;
+ }
+
struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh);
eh->eth_type = htons(ETH_TYPE_IP);
@@ -452,6 +462,8 @@ pinctrl_handle_icmp(const struct flow *ip_flow, struct dp_packet *pkt_in,
packet_set_icmp(&packet, ICMP4_DST_UNREACH, 1);
uint8_t *n_ovnfield_acts = ofpbuf_try_pull(userdata,
sizeof *n_ovnfield_acts);
+ bool include_orig_ip_datagram = false;
+
if (n_ovnfield_acts && *n_ovnfield_acts) {
for (uint8_t i = 0; i < *n_ovnfield_acts; i++) {
struct ovnfield_act_header *oah =
@@ -465,12 +477,40 @@ pinctrl_handle_icmp(const struct flow *ip_flow, struct dp_packet *pkt_in,
ovs_be16 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu);
if (mtu) {
ih->icmp_fields.frag.mtu = *mtu;
+ include_orig_ip_datagram = true;
}
break;
}
}
}
}
+
+ if (include_orig_ip_datagram) {
+ /* RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes
+ * of header. MAY send more.
+ * RFC says return as much as we can without exceeding 576
+ * bytes.
+ * So, lets return as much as we can. */
+
+ /* Calculate available room to include the original IP + data. */
+ nh = dp_packet_l3(&packet);
+ uint16_t room = 576 - (sizeof *eh + ntohs(nh->ip_tot_len));
+ if (in_ip_len > room) {
+ in_ip_len = room;
+ }
+ dp_packet_put(&packet, in_ip, in_ip_len);
+
+ /* dp_packet_put may reallocate the buffer. Get the l3 and l4
+ * header pointers again. */
+ nh = dp_packet_l3(&packet);
+ ih = dp_packet_l4(&packet);
+ uint16_t ip_total_len = ntohs(nh->ip_tot_len) + in_ip_len;
+ nh->ip_tot_len = htons(ip_total_len);
+ ih->icmp_csum = 0;
+ ih->icmp_csum = csum(ih, sizeof *ih + in_ip_len);
+ nh->ip_csum = 0;
+ nh->ip_csum = csum(nh, sizeof *nh);
+ }
} else {
struct ip6_hdr *nh = dp_packet_put_zeros(&packet, sizeof *nh);
struct icmp6_error_header *ih;
@@ -2372,6 +2372,55 @@ encode_OVNFIELD_LOAD(const struct ovnact_load *load,
}
}
+static void
+parse_check_pkt_larger(struct action_context *ctx,
+ const struct expr_field *dst,
+ struct ovnact_check_pkt_larger *cipl)
+{
+ int pkt_len;
+
+ lexer_get(ctx->lexer); /* Skip check_pkt_len. */
+ if (!lexer_force_match(ctx->lexer, LEX_T_LPAREN)
+ || !lexer_get_int(ctx->lexer, &pkt_len)
+ || !lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
+ return;
+ }
+
+ /* Validate that the destination is a 1-bit, modifiable field. */
+ char *error = expr_type_check(dst, 1, true);
+ if (error) {
+ lexer_error(ctx->lexer, "%s", error);
+ free(error);
+ return;
+ }
+ cipl->dst = *dst;
+ cipl->pkt_len = pkt_len;
+}
+
+static void
+format_CHECK_PKT_LARGER(const struct ovnact_check_pkt_larger *cipl,
+ struct ds *s)
+{
+ expr_field_format(&cipl->dst, s);
+ ds_put_format(s, " = check_pkt_larger(%d);", cipl->pkt_len);
+}
+
+static void
+encode_CHECK_PKT_LARGER(const struct ovnact_check_pkt_larger *cipl,
+ const struct ovnact_encode_params *ep OVS_UNUSED,
+ struct ofpbuf *ofpacts)
+{
+ struct ofpact_check_pkt_larger *check_pkt_larger =
+ ofpact_put_CHECK_PKT_LARGER(ofpacts);
+ check_pkt_larger->pkt_len = cipl->pkt_len;
+ check_pkt_larger->dst = expr_resolve_field(&cipl->dst);
+}
+
+static void
+ovnact_check_pkt_larger_free(struct ovnact_check_pkt_larger *cipl OVS_UNUSED)
+{
+}
+
/* Parses an assignment or exchange or put_dhcp_opts action. */
static void
parse_set_action(struct action_context *ctx)
@@ -2401,6 +2450,10 @@ parse_set_action(struct action_context *ctx)
&& lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
parse_put_nd_ra_opts(ctx, &lhs,
ovnact_put_PUT_ND_RA_OPTS(ctx->ovnacts));
+ } else if (!strcmp(ctx->lexer->token.s, "check_pkt_larger")
+ && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+ parse_check_pkt_larger(ctx, &lhs,
+ ovnact_put_CHECK_PKT_LARGER(ctx->ovnacts));
} else {
parse_assignment_action(ctx, false, &lhs);
}
@@ -1766,6 +1766,27 @@
<p><b>Example:</b> <code>set_meter(100, 1000);</code></p>
</dd>
+
+ <dt><code><var>R</var> = check_pkt_larger(<var>L</var>)</code></dt>
+ <dd>
+ <p>
+ <b>Parameters</b>: packet length <var>L</var> to check for
+ in bytes.
+ </p>
+
+ <p>
+ <b>Result</b>: stored to a 1-bit subfield <var>R</var>.
+ </p>
+
+ <p>
+ This is a logical equivalent of the OpenFlow
+ <code>check_pkt_larger</code> action. If the packet is larger
+ than the length specified in <var>L</var>, it stores 1 in the
+ subfield <var>R</var>.
+ </p>
+
+ <p><b>Example: </b><code>reg0[6] = check_pkt_larger(1000);</code></p>
+ </dd>
</dl>
<dl>
@@ -2109,8 +2109,10 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
case OVNACT_OVNFIELD_LOAD:
break;
- }
+ case OVNACT_CHECK_PKT_LARGER:
+ break;
+ }
}
ds_destroy(&s);
}
@@ -1330,6 +1330,16 @@ ip4.src <-> ip6.src[0..31];
encodes as push:NXM_NX_IPV6_SRC[0..31],push:NXM_OF_IP_SRC[],pop:NXM_NX_IPV6_SRC[0..31],pop:NXM_OF_IP_SRC[]
has prereqs eth.type == 0x800 && eth.type == 0x86dd
+# check_pkt_larger
+reg0[0] = check_pkt_larger(1500);
+ encodes as check_pkt_larger(1500)->NXM_NX_XXREG0[96]
+
+reg0 = check_pkt_larger(1500);
+ Cannot use 32-bit field reg0[0..31] where 1-bit field is required.
+
+reg0 = check_pkt_larger(foo);
+ Syntax error at `foo' expecting `;'.
+
# Miscellaneous negative tests.
;
Syntax error at `;'.