icmp4 action is used to replace the IPv4 packet been processed with an ICMPv4 packet initialized based on incoming IPv4 one. Ethernet and IPv4 fields not listed are not changed: - ip.proto = 1 (ICMPv4) - ip.frag = 0 (not a fragment) - ip.ttl = 255 - icmp4.type = 3 (destination unreachable) - icmp4.code = 1 (host unreachable) Prerequisite: ip4
Signed-off-by: Lorenzo Bianconi <[email protected]> --- include/ovn/actions.h | 7 +++++++ ovn/controller/pinctrl.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ ovn/lib/actions.c | 22 ++++++++++++++++++++ ovn/ovn-sb.xml | 5 +---- ovn/utilities/ovn-trace.c | 29 ++++++++++++++++++++++++++ tests/ovn.at | 10 +++++++++ 6 files changed, 122 insertions(+), 4 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 9554a395d..b1988d6aa 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -64,6 +64,7 @@ struct ovn_extend_table; OVNACT(CT_CLEAR, ovnact_null) \ OVNACT(CLONE, ovnact_nest) \ OVNACT(ARP, ovnact_nest) \ + OVNACT(ICMP4, ovnact_nest) \ OVNACT(ND_NA, ovnact_nest) \ OVNACT(GET_ARP, ovnact_get_mac_bind) \ OVNACT(PUT_ARP, ovnact_put_mac_bind) \ @@ -429,6 +430,12 @@ enum action_opcode { * The actions, in OpenFlow 1.3 format, follow the action_header. */ ACTION_OPCODE_ND_NS, + + /* "icmp4 { ...actions... }". + * + * The actions, in OpenFlow 1.3 format, follow the action_header. + */ + ACTION_OPCODE_ICMP4, }; /* Header. */ diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c index e34916f5c..f08fbab59 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -219,6 +219,55 @@ pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md, dp_packet_uninit(&packet); } +static void +pinctrl_handle_icmp4(const struct flow *ip_flow, const struct match *md, + struct ofpbuf *userdata) +{ + /* This action only works for IP packets, and the switch should only send + * us IP packets this way, but check here just to be sure. */ + if (ip_flow->dl_type != htons(ETH_TYPE_IP)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, + "ICMP4 action on non-IP packet (eth_type 0x%"PRIx16")", + ntohs(ip_flow->dl_type)); + return; + } + + uint64_t packet_stub[128 / 8]; + struct dp_packet packet; + + dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); + dp_packet_clear(&packet); + packet.packet_type = htonl(PT_ETH); + + struct eth_header *eh = dp_packet_put_zeros(&packet, sizeof *eh); + eh->eth_dst = ip_flow->dl_dst; + eh->eth_src = ip_flow->dl_src; + eh->eth_type = htons(ETH_TYPE_IP); + + struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh); + dp_packet_set_l3(&packet, nh); + nh->ip_ihl_ver = IP_IHL_VER(5, 4); + nh->ip_tot_len = htons(sizeof(struct ip_header) + + sizeof(struct icmp_header)); + nh->ip_proto = IPPROTO_ICMP; + nh->ip_frag_off = htons(IP_DF); + packet_set_ipv4(&packet, ip_flow->nw_src, ip_flow->nw_dst, + ip_flow->nw_tos, 255); + + struct icmp_header *ih = dp_packet_put_zeros(&packet, sizeof *ih); + dp_packet_set_l4(&packet, ih); + packet_set_icmp(&packet, ICMP4_DST_UNREACH, 1); + + if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) { + eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q), + ip_flow->vlans[0].tci); + } + + set_actions_and_enqueue_msg(&packet, md, userdata); + dp_packet_uninit(&packet); +} + static void pinctrl_handle_put_dhcp_opts( struct dp_packet *pkt_in, struct ofputil_packet_in *pin, @@ -1022,6 +1071,10 @@ process_packet_in(const struct ofp_header *msg, struct controller_ctx *ctx) pinctrl_handle_nd_ns(&headers, &pin.flow_metadata, &userdata); break; + case ACTION_OPCODE_ICMP4: + pinctrl_handle_icmp4(&headers, &pin.flow_metadata, &userdata); + break; + default: VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32, ntohl(ah->opcode)); diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index fde3bff00..fc5ace1f6 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -1141,6 +1141,12 @@ parse_ARP(struct action_context *ctx) parse_nested_action(ctx, OVNACT_ARP, "ip4"); } +static void +parse_ICMP4(struct action_context *ctx) +{ + parse_nested_action(ctx, OVNACT_ICMP4, "ip4"); +} + static void parse_ND_NA(struct action_context *ctx) { @@ -1174,6 +1180,12 @@ format_ARP(const struct ovnact_nest *nest, struct ds *s) format_nested_action(nest, "arp", s); } +static void +format_ICMP4(const struct ovnact_nest *nest, struct ds *s) +{ + format_nested_action(nest, "icmp4", s); +} + static void format_ND_NA(const struct ovnact_nest *nest, struct ds *s) { @@ -1224,6 +1236,14 @@ encode_ARP(const struct ovnact_nest *on, encode_nested_actions(on, ep, ACTION_OPCODE_ARP, ofpacts); } +static void +encode_ICMP4(const struct ovnact_nest *on, + const struct ovnact_encode_params *ep, + struct ofpbuf *ofpacts) +{ + encode_nested_actions(on, ep, ACTION_OPCODE_ICMP4, ofpacts); +} + static void encode_ND_NA(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, @@ -2247,6 +2267,8 @@ parse_action(struct action_context *ctx) parse_CLONE(ctx); } else if (lexer_match_id(ctx->lexer, "arp")) { parse_ARP(ctx); + } else if (lexer_match_id(ctx->lexer, "icmp4")) { + parse_ICMP4(ctx); } else if (lexer_match_id(ctx->lexer, "nd_na")) { parse_ND_NA(ctx); } else if (lexer_match_id(ctx->lexer, "nd_ns")) { diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index f000b166c..67da8a7f4 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -1725,14 +1725,11 @@ <ul> <li><code>ip.proto = 1</code> (ICMPv4)</li> <li><code>ip.frag = 0</code> (not a fragment)</li> + <li><code>ip.ttl = 255</code> (not a fragment)</li> <li><code>icmp4.type = 3</code> (destination unreachable)</li> <li><code>icmp4.code = 1</code> (host unreachable)</li> </ul> - <p> - Details TBD. - </p> - <p><b>Prerequisite:</b> <code>ip4</code></p> </dd> diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c index db39c4961..93094eea6 100644 --- a/ovn/utilities/ovn-trace.c +++ b/ovn/utilities/ovn-trace.c @@ -1535,6 +1535,30 @@ execute_nd_ns(const struct ovnact_nest *on, const struct ovntrace_datapath *dp, table_id, pipeline, &node->subs); } +static void +execute_icmp4(const struct ovnact_nest *on, + const struct ovntrace_datapath *dp, + const struct flow *uflow, uint8_t table_id, + enum ovnact_pipeline pipeline, struct ovs_list *super) +{ + struct flow icmp4_flow = *uflow; + + /* Update fields for ICMP. */ + icmp4_flow.dl_dst = uflow->dl_dst; + icmp4_flow.dl_src = uflow->dl_src; + icmp4_flow.nw_dst = uflow->nw_dst; + icmp4_flow.nw_src = uflow->nw_src; + icmp4_flow.nw_proto = IPPROTO_ICMP; + icmp4_flow.tp_src = htons(ICMP4_DST_UNREACH); /* icmp type */ + icmp4_flow.tp_dst = htons(1); /* icmp code */ + + struct ovntrace_node *node = ovntrace_node_append( + super, OVNTRACE_NODE_TRANSFORMATION, "icmp4"); + + trace_actions(on->nested, on->nested_len, dp, &icmp4_flow, + table_id, pipeline, &node->subs); +} + static void execute_get_mac_bind(const struct ovnact_get_mac_bind *bind, const struct ovntrace_datapath *dp, @@ -1893,6 +1917,11 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, case OVNACT_SET_METER: /* Nothing to do. */ break; + + case OVNACT_ICMP4: + execute_icmp4(ovnact_get_ICMP4(a), dp, uflow, table_id, pipeline, + super); + break; } } diff --git a/tests/ovn.at b/tests/ovn.at index 8ee3bf0b5..37e63810d 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1125,6 +1125,16 @@ reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = "1500", slla = ae:01:02:03:0 reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 10.0.0.4, slla = ae:01:02:03:04:10); Invalid value for "mtu" option +# icmp4 +icmp4 { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output; + encodes as controller(userdata=00.00.00.0a.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64) + has prereqs ip4 + +icmp4 { }; + formats as icmp4 { drop; }; + encodes as controller(userdata=00.00.00.0a.00.00.00.00) + has prereqs ip4 + # Contradictionary prerequisites (allowed but not useful): ip4.src = ip6.src[0..31]; encodes as move:NXM_NX_IPV6_SRC[0..31]->NXM_OF_IP_SRC[] -- 2.14.3 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
