ovn: the implementation of icmp4 reject actions. The ovn-contoller will process the packet-in data packet and compose an ICMP4 packet, whose icmp4 data contains the IP header and first 8 bytes of original datagram's data.
Signed-off-by: nickcooper-zhangtonghao <nickcooper-zhangtong...@opencloud.tech> diff --git a/lib/packets.c b/lib/packets.c index a27264c..de90c9b 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -1251,6 +1251,9 @@ packet_format_tcp_flags(struct ds *s, uint16_t tcp_flags) #define ARP_PACKET_SIZE (2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + \ ARP_ETH_HEADER_LEN) +#define ICMP4_PACKET_SIZE (2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + \ + IP_HEADER_LEN + ICMP_HEADER_LEN) + /* Clears 'b' and replaces its contents by an ARP frame with the specified * 'arp_op', 'arp_sha', 'arp_tha', 'arp_spa', and 'arp_tpa'. The outer * Ethernet frame is initialized with Ethernet source 'arp_sha' and destination @@ -1299,6 +1302,24 @@ compose_arp__(struct dp_packet *b) dp_packet_set_l3(b, arp); } +void +compose_icmp4__(struct dp_packet *b) +{ + dp_packet_clear(b); + dp_packet_prealloc_tailroom(b, ICMP4_PACKET_SIZE); + dp_packet_reserve(b, 2 + VLAN_HEADER_LEN); + + struct eth_header *eth = dp_packet_put_zeros(b, sizeof *eth); + eth->eth_type = htons(ETH_TYPE_IP); + + struct ip_header *ip4 = dp_packet_put_zeros(b, sizeof *ip4); + struct icmp_header *icmp4 = dp_packet_put_zeros(b, sizeof *icmp4); + + dp_packet_reset_offsets(b); + dp_packet_set_l4(b, icmp4); + dp_packet_set_l3(b, ip4); +} + /* This function expect packet with ethernet header with correct * l3 pointer set. */ diff --git a/lib/packets.h b/lib/packets.h index 077ccfa..adfb02b 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -1067,6 +1067,7 @@ void compose_arp(struct dp_packet *, uint16_t arp_op, const struct eth_addr arp_sha, const struct eth_addr arp_tha, bool broadcast, ovs_be32 arp_spa, ovs_be32 arp_tpa); +void compose_icmp4__(struct dp_packet *); void compose_nd(struct dp_packet *, const struct eth_addr eth_src, struct in6_addr *, struct in6_addr *); void compose_na(struct dp_packet *, diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c index 1370301..3556409 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -72,6 +72,10 @@ static void send_garp_run(const struct ovsrec_bridge *, static void pinctrl_handle_na(const struct flow *ip_flow, const struct match *md, struct ofpbuf *userdata); +static void pinctrl_handle_icmp4(struct dp_packet *pkt_in, + const struct flow *ip_flow, + const struct match *md, + struct ofpbuf *userdata); static void reload_metadata(struct ofpbuf *ofpacts, const struct match *md); @@ -412,6 +416,10 @@ process_packet_in(const struct ofp_header *msg) case ACTION_OPCODE_NA: pinctrl_handle_na(&headers, &pin.flow_metadata, &userdata); break; + + case ACTION_OPCODE_ICMP4: + pinctrl_handle_icmp4(&packet, &headers, &pin.flow_metadata, &userdata); + break; default: VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32, @@ -1003,3 +1011,95 @@ exit: dp_packet_uninit(&packet); ofpbuf_uninit(&ofpacts); } + +static void +pinctrl_handle_icmp4(struct dp_packet *pkt_in, + const struct flow *ip_flow, + const struct match *md, + struct ofpbuf *userdata) +{ + /* This action only works for ICMP packets, and the switch should only send + * us ICMP packets this way, but check here just to be sure. */ + if (ip_flow->dl_type != htons(ETH_TYPE_IP) || + ip_flow->nw_proto != IPPROTO_ICMP) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "ICMP4 action on non-IP packet (Ethertype %"PRIx16") and (IP Protocol %"PRIx16")", + ntohs(ip_flow->dl_type), ntohs(ip_flow->nw_proto)); + return; + } + + /* Compose an ICMP4 packet. */ + struct icmp_header *icmp4; + struct ip_header *ip4; + struct eth_header *eth; + uint64_t packet_stub[128 / 8]; + struct dp_packet pkt_out; + + dp_packet_use_stub(&pkt_out, packet_stub, sizeof packet_stub); + compose_icmp4__(&pkt_out); + + eth = dp_packet_l2(&pkt_out); + eth->eth_dst = ip_flow->dl_dst; + eth->eth_src = ip_flow->dl_src; + + ip4 = dp_packet_l3(&pkt_out); + ip4->ip_ihl_ver = IP_IHL_VER(5, 4); + ip4->ip_tos = ip_flow->nw_tos; + ip4->ip_tot_len = htons(sizeof *ip4 + sizeof *icmp4 + sizeof *ip4 + 8); + ip4->ip_frag_off = htons(IP_DONT_FRAGMENT); + ip4->ip_ttl = MAXTTL; + ip4->ip_proto = IPPROTO_ICMP; + put_16aligned_be32(&ip4->ip_src, ip_flow->nw_src); + put_16aligned_be32(&ip4->ip_dst, ip_flow->nw_dst); + ip4->ip_csum = csum(ip4, sizeof *ip4); + + /* Format of the ICMP destination unreachable message packet is + * ---------------------------------------------------------------------------------------- + * | IP HEADER | ICMP HEADER, 1 Byte Type, 1 Byte Code, 2 Bytes CSUM | IP Header + 8 Bytes| + * ---------------------------------------------------------------------------------------- + */ + icmp4 = dp_packet_l4(&pkt_out); + icmp4->icmp_csum = csum(icmp4, sizeof *icmp4); + + /* IP header and first 8 bytes of original datagram's data'*/ + dp_packet_put(&pkt_out, dp_packet_l3(pkt_in), IP_HEADER_LEN +8); + + if (ip_flow->vlan_tci & htons(VLAN_CFI)) { + eth_push_vlan(&pkt_out, htons(ETH_TYPE_VLAN_8021Q), ip_flow->vlan_tci); + } + + /* Compose actions. + * + * First, copy metadata from 'md' into the packet-out via "set_field" + * actions, then add actions from 'userdata'. + */ + uint64_t ofpacts_stub[4096 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); + enum ofp_version version = rconn_get_version(swconn); + + reload_metadata(&ofpacts, md); + enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size, version, &ofpacts); + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "failed to parse icmp actions (%s)", + ofperr_to_string(error)); + goto exit; + } + + struct ofputil_packet_out po = { + .packet = dp_packet_data(&pkt_out), + .packet_len = dp_packet_size(&pkt_out), + .buffer_id = UINT32_MAX, + .in_port = OFPP_CONTROLLER, + .ofpacts = ofpacts.data, + .ofpacts_len = ofpacts.size, + }; + + enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); + queue_msg(ofputil_encode_packet_out(&po, proto)); + +exit: + dp_packet_uninit(&pkt_out); + ofpbuf_uninit(&ofpacts); +} diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index 3d10d61..a9b7c5a 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -249,7 +249,7 @@ put_controller_op(struct ofpbuf *ofpacts, enum action_opcode opcode) finish_controller_op(ofpacts, ofs); } -/* Implements the "arp" and "na" actions, which execute nested actions on a +/* Implements the "arp", "na" and "icmp4" actions, which execute nested actions on a * packet derived from the one being processed. */ static void parse_nested_action(struct action_context *ctx, enum action_opcode opcode, @@ -1068,6 +1068,8 @@ parse_action(struct action_context *ctx) parse_get_arp_action(ctx); } else if (lexer_match_id(ctx->lexer, "put_arp")) { parse_put_arp_action(ctx); + } else if (lexer_match_id(ctx->lexer, "icmp4")) { + parse_nested_action(ctx, ACTION_OPCODE_ICMP4, "ip4"); } else { action_syntax_error(ctx, "expecting action"); } diff --git a/ovn/lib/actions.h b/ovn/lib/actions.h index 48f0140..ed4ef5c 100644 --- a/ovn/lib/actions.h +++ b/ovn/lib/actions.h @@ -78,6 +78,12 @@ enum action_opcode { * The actions, in OpenFlow 1.3 format, follow the action_header. */ ACTION_OPCODE_NA, + + /* "icmp4 { ...actions... }". + * + * The actions, in OpenFlow 1.3 format, follow the action_header. + */ + ACTION_OPCODE_ICMP4, }; thanks, nick _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev