diff --git a/include/ovn/actions.h b/include/ovn/actions.h index b1988d6aa..086ab19e0 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -65,6 +65,7 @@ struct ovn_extend_table; OVNACT(CLONE, ovnact_nest) \ OVNACT(ARP, ovnact_nest) \ OVNACT(ICMP4, ovnact_nest) \ + OVNACT(TCP_RESET, ovnact_nest) \ OVNACT(ND_NA, ovnact_nest) \ OVNACT(GET_ARP, ovnact_get_mac_bind) \ OVNACT(PUT_ARP, ovnact_put_mac_bind) \ @@ -436,6 +437,12 @@ enum action_opcode { * The actions, in OpenFlow 1.3 format, follow the action_header. */ ACTION_OPCODE_ICMP4, + + /* "tcp_reset { ...actions... }". + * + * The actions, in OpenFlow 1.3 format, follow the action_header. + */ + ACTION_OPCODE_TCP_RESET, }; /* Header. */ diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c index b4dbd8c29..a6c42e63b 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -268,6 +268,65 @@ pinctrl_handle_icmp4(const struct flow *ip_flow, const struct match *md, dp_packet_uninit(&packet); } +static void +pinctrl_handle_tcp_reset(const struct flow *ip_flow, struct dp_packet *pkt_in, + const struct match *md, struct ofpbuf *userdata) +{ + /* This action only works for TCP segments, and the switch should only send + * us TCP segments this way, but check here just to be sure. */ + if (ip_flow->nw_proto != IPPROTO_TCP) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "TCP_RESET action on non-TCP packet"); + 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(IP_HEADER_LEN + TCP_HEADER_LEN); + nh->ip_proto = IPPROTO_TCP; + 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 tcp_header *th = dp_packet_put_zeros(&packet, sizeof *th); + struct tcp_header *tcp_in = dp_packet_l4(pkt_in); + dp_packet_set_l4(&packet, th); + th->tcp_ctl = TCP_CTL(TCP_RST, 5); + if (ip_flow->tcp_flags & htons(TCP_ACK)) { + th->tcp_seq = tcp_in->tcp_ack; + } else { + uint32_t tcp_seq, ack_seq, tcp_len; + + tcp_seq = ntohl(get_16aligned_be32(&tcp_in->tcp_seq)); + tcp_len = TCP_OFFSET(tcp_in->tcp_ctl) * 4; + ack_seq = tcp_seq + dp_packet_l4_size(pkt_in) - tcp_len; + put_16aligned_be32(&th->tcp_ack, htonl(ack_seq)); + put_16aligned_be32(&th->tcp_seq, 0); + } + packet_set_tcp_port(&packet, ip_flow->tp_dst, ip_flow->tp_src); + + 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, @@ -1086,6 +1145,11 @@ process_packet_in(const struct ofp_header *msg, struct controller_ctx *ctx) pinctrl_handle_icmp4(&headers, &pin.flow_metadata, &userdata); break; + case ACTION_OPCODE_TCP_RESET: + pinctrl_handle_tcp_reset(&headers, &packet, &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 fc5ace1f6..1e0bcb7b7 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -1147,6 +1147,12 @@ parse_ICMP4(struct action_context *ctx) parse_nested_action(ctx, OVNACT_ICMP4, "ip4"); } +static void +parse_TCP_RESET(struct action_context *ctx) +{ + parse_nested_action(ctx, OVNACT_TCP_RESET, "tcp"); +} + static void parse_ND_NA(struct action_context *ctx) { @@ -1186,6 +1192,12 @@ format_ICMP4(const struct ovnact_nest *nest, struct ds *s) format_nested_action(nest, "icmp4", s); } +static void +format_TCP_RESET(const struct ovnact_nest *nest, struct ds *s) +{ + format_nested_action(nest, "tcp_reset", s); +} + static void format_ND_NA(const struct ovnact_nest *nest, struct ds *s) { @@ -1244,6 +1256,14 @@ encode_ICMP4(const struct ovnact_nest *on, encode_nested_actions(on, ep, ACTION_OPCODE_ICMP4, ofpacts); } +static void +encode_TCP_RESET(const struct ovnact_nest *on, + const struct ovnact_encode_params *ep, + struct ofpbuf *ofpacts) +{ + encode_nested_actions(on, ep, ACTION_OPCODE_TCP_RESET, ofpacts); +} + static void encode_ND_NA(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, @@ -2269,6 +2289,8 @@ parse_action(struct action_context *ctx) parse_ARP(ctx); } else if (lexer_match_id(ctx->lexer, "icmp4")) { parse_ICMP4(ctx); + } else if (lexer_match_id(ctx->lexer, "tcp_reset")) { + parse_TCP_RESET(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 6a8b818a3..23dcfb9cf 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -1752,11 +1752,7 @@ tcp.flags = RST;
Then, the action drops all TCP options and payload data, and - updates the TCP checksum. -
- -- Details TBD. + updates the TCP checksum. IP ttl is set to 255.
Prerequisite: tcp