@@ -181,6 +181,7 @@ enum ovn_stage {
181181 * logical router dropping packets with source IP address equals
182182 * one of the logical router's own IP addresses. */
183183#define REGBIT_EGRESS_LOOPBACK "reg9[1]"
184+ #define REGBIT_DISTRIBUTED_NAT "reg9[2]"
184185
185186/* Returns an "enum ovn_stage" built from the arguments. */
186187static enum ovn_stage
@@ -5009,6 +5010,66 @@ find_lrp_member_ip(const struct ovn_port *op, const char *ip_s)
50095010 return NULL ;
50105011}
50115012
5013+ static void
5014+ add_distributed_nat_routes (struct hmap * lflows , const struct ovn_port * op )
5015+ {
5016+ struct ds actions = DS_EMPTY_INITIALIZER ;
5017+ struct ds match = DS_EMPTY_INITIALIZER ;
5018+
5019+ if (!op -> od -> l3dgw_port ) {
5020+ return ;
5021+ }
5022+
5023+ if (!op -> peer || !op -> peer -> od -> nbs ) {
5024+ return ;
5025+ }
5026+
5027+ for (size_t i = 0 ; i < op -> od -> nbr -> n_nat ; i ++ ) {
5028+ const struct nbrec_nat * nat = op -> od -> nbr -> nat [i ];
5029+ bool found = false;
5030+
5031+ if (strcmp (nat -> type , "dnat_and_snat" ) ||
5032+ !nat -> external_mac || !nat -> external_ip ) {
5033+ continue ;
5034+ }
5035+
5036+ const struct ovn_datapath * peer_dp = op -> peer -> od ;
5037+ for (size_t j = 0 ; j < peer_dp -> nbs -> n_ports ; j ++ ) {
5038+ if (!strcmp (peer_dp -> nbs -> ports [j ]-> name , nat -> logical_port )) {
5039+ found = true;
5040+ break ;
5041+ }
5042+ }
5043+ if (!found ) {
5044+ continue ;
5045+ }
5046+
5047+ for (size_t j = 0 ; j < op -> od -> nbr -> n_nat ; j ++ ) {
5048+ const struct nbrec_nat * nat2 = op -> od -> nbr -> nat [j ];
5049+
5050+ if (nat == nat2 || strcmp (nat2 -> type , "dnat_and_snat" ) ||
5051+ !nat2 -> external_mac || !nat2 -> external_ip )
5052+ continue ;
5053+
5054+ ds_put_format (& match , "inport == %s && "
5055+ "ip4.src == %s && ip4.dst == %s" ,
5056+ op -> json_key , nat -> logical_ip , nat2 -> external_ip );
5057+ ds_put_format (& actions , "outport = %s; "
5058+ "eth.src = %s; eth.dst = %s; "
5059+ "reg0 = ip4.dst; reg1 = %s; "
5060+ REGBIT_DISTRIBUTED_NAT " = 1; "
5061+ REGBIT_NAT_REDIRECT " = 0; next;" ,
5062+ op -> od -> l3dgw_port -> json_key ,
5063+ op -> od -> l3dgw_port -> lrp_networks .ea_s ,
5064+ nat2 -> external_mac , nat -> external_ip );
5065+ ovn_lflow_add (lflows , op -> od , S_ROUTER_IN_IP_ROUTING , 400 ,
5066+ ds_cstr (& match ), ds_cstr (& actions ));
5067+ ds_clear (& match );
5068+ ds_clear (& actions );
5069+ }
5070+ }
5071+ }
5072+
50125073static void
50135074add_route (struct hmap * lflows , const struct ovn_port * op ,
50145075 const char * lrp_addr_s , const char * network_s , int plen ,
@@ -6392,6 +6453,41 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
63926453 * ingress pipeline with inport = outport. */
63936454 if (od -> l3dgw_port ) {
63946455 /* Distributed router. */
6456+ if (!strcmp (nat -> type , "dnat_and_snat" ) &&
6457+ nat -> external_mac && nat -> external_ip ) {
6458+ for (int j = 0 ; j < od -> nbr -> n_nat ; j ++ ) {
6459+ const struct nbrec_nat * nat2 = od -> nbr -> nat [j ];
6460+
6461+ if (nat2 == nat ||
6462+ strcmp (nat2 -> type , "dnat_and_snat" ) ||
6463+ !nat2 -> external_mac || !nat2 -> external_ip ) {
6464+ continue ;
6465+ }
6466+
6467+ ds_clear (& match );
6468+ ds_put_format (& match , "is_chassis_resident(\"%s\") && "
6469+ "ip4.src == %s && ip4.dst == %s" ,
6470+ nat -> logical_port , nat2 -> external_ip ,
6471+ nat -> external_ip );
6472+ ds_clear (& actions );
6473+ ds_put_format (& actions ,
6474+ "inport = outport; outport = \"\"; "
6475+ "flags = 0; flags.loopback = 1; "
6476+ REGBIT_EGRESS_LOOPBACK " = 1; "
6477+ "next(pipeline=ingress, table=0); " );
6478+ ovn_lflow_add (lflows , od , S_ROUTER_OUT_EGR_LOOP , 300 ,
6479+ ds_cstr (& match ), ds_cstr (& actions ));
6480+
6481+ ds_clear (& match );
6482+ ds_put_format (& match ,
6483+ "ip4.src == %s && ip4.dst == %s" ,
6484+ nat2 -> external_ip , nat -> external_ip );
6485+ ovn_lflow_add (lflows , od , S_ROUTER_OUT_EGR_LOOP , 200 ,
6486+ ds_cstr (& match ), "next;" );
6487+ ds_clear (& match );
6488+ }
6489+ }
6490+
63956491 ds_clear (& match );
63966492 ds_put_format (& match , "ip4.dst == %s && outport == %s" ,
63976493 nat -> external_ip ,
@@ -6463,6 +6559,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
64636559 ovn_lflow_add (lflows , od , S_ROUTER_IN_DNAT , 50 ,
64646560 "ip" , "flags.loopback = 1; ct_dnat;" );
64656561 } else {
6562+ ovn_lflow_add (lflows , od , S_ROUTER_IN_ARP_RESOLVE , 400 ,
6563+ REGBIT_DISTRIBUTED_NAT " == 1" , "next;" );
6564+
64666565 /* For NAT on a distributed router, add flows to Ingress
64676566 * IP Routing table, Ingress ARP Resolution table, and
64686567 * Ingress Gateway Redirect Table that are not specific to a
@@ -6698,6 +6797,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
66986797 continue ;
66996798 }
67006799
6800+ /* create logical flows for DVR floating IPs */
6801+ add_distributed_nat_routes (lflows , op );
6802+
67016803 for (int i = 0 ; i < op -> lrp_networks .n_ipv4_addrs ; i ++ ) {
67026804 add_route (lflows , op , op -> lrp_networks .ipv4_addrs [i ].addr_s ,
67036805 op -> lrp_networks .ipv4_addrs [i ].network_s ,
@@ -6938,6 +7040,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
69387040 continue ;
69397041 }
69407042 if (od -> l3dgw_port && od -> l3redirect_port ) {
7043+ ovn_lflow_add (lflows , od , S_ROUTER_IN_GW_REDIRECT , 300 ,
7044+ REGBIT_DISTRIBUTED_NAT " == 1" , "next;" );
7045+
69417046 /* For traffic with outport == l3dgw_port, if the
69427047 * packet did not match any higher priority redirect
69437048 * rule, then the traffic is redirected to the central
0 commit comments