ovn-northd: Fix memory leak in logical router flow generation.
[cascardo/ovs.git] / ovn / northd / ovn-northd.c
index e6e9f3e..282f8ae 100644 (file)
@@ -111,6 +111,12 @@ enum ovn_stage {
 #undef PIPELINE_STAGE
 };
 
+/* Due to various hard-coded priorities need to implement ACLs, the
+ * northbound database supports a smaller range of ACL priorities than
+ * are available to logical flows.  This value is added to an ACL
+ * priority to determine the ACL's logical flow priority. */
+#define OVN_ACL_PRI_OFFSET 1000
+
 /* Returns an "enum ovn_stage" built from the arguments. */
 static enum ovn_stage
 ovn_stage_build(enum ovn_datapath_type dp_type, enum ovn_pipeline pipeline,
@@ -237,7 +243,8 @@ struct ovn_datapath {
     ovs_be32 gateway;
 
     /* Logical switch data. */
-    struct ovn_port *router_port;
+    struct ovn_port **router_ports;
+    size_t n_router_ports;
 
     struct hmap port_tnlids;
     uint32_t port_key_hint;
@@ -271,6 +278,7 @@ ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od)
          * use it. */
         hmap_remove(datapaths, &od->key_node);
         destroy_tnlids(&od->port_tnlids);
+        free(od->router_ports);
         free(od);
     }
 }
@@ -634,7 +642,10 @@ join_logical_ports(struct northd_context *ctx,
 
             peer->peer = op;
             op->peer = peer;
-            op->od->router_port = op;
+            op->od->router_ports = xrealloc(
+                op->od->router_ports,
+                sizeof *op->od->router_ports * (op->od->n_router_ports + 1));
+            op->od->router_ports[op->od->n_router_ports++] = op;
         } else if (op->nbr && op->nbr->peer) {
             char peer_name[UUID_LEN + 1];
             snprintf(peer_name, sizeof peer_name, UUID_FMT,
@@ -1051,7 +1062,8 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
              * may and then its return traffic would not have an
              * associated conntrack entry and would return "+invalid". */
             const char *actions = has_stateful ? "ct_commit; next;" : "next;";
-            ovn_lflow_add(lflows, od, stage, acl->priority,
+            ovn_lflow_add(lflows, od, stage,
+                          acl->priority + OVN_ACL_PRI_OFFSET,
                           acl->match, actions);
         } else if (!strcmp(acl->action, "allow-related")) {
             struct ds match = DS_EMPTY_INITIALIZER;
@@ -1060,17 +1072,20 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
              * other traffic related to this entry to flow due to the
              * 65535 priority flow defined earlier. */
             ds_put_format(&match, "ct.new && (%s)", acl->match);
-            ovn_lflow_add(lflows, od, stage, acl->priority,
+            ovn_lflow_add(lflows, od, stage,
+                          acl->priority + OVN_ACL_PRI_OFFSET,
                           ds_cstr(&match), "ct_commit; next;");
 
             ds_destroy(&match);
         } else if (!strcmp(acl->action, "drop")) {
-            ovn_lflow_add(lflows, od, stage, acl->priority,
+            ovn_lflow_add(lflows, od, stage,
+                          acl->priority + OVN_ACL_PRI_OFFSET,
                           acl->match, "drop;");
         } else if (!strcmp(acl->action, "reject")) {
             /* xxx Need to support "reject". */
             VLOG_INFO("reject is not a supported action");
-            ovn_lflow_add(lflows, od, stage, acl->priority,
+            ovn_lflow_add(lflows, od, stage,
+                          acl->priority + OVN_ACL_PRI_OFFSET,
                           acl->match, "drop;");
         }
     }
@@ -1313,6 +1328,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             ETH_ADDR_ARGS(op->mac), op->json_key);
         ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
                       match, "next;");
+        free(match);
     }
 
     /* Logical router ingress table 1: IP Input. */
@@ -1431,18 +1447,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
     HMAP_FOR_EACH (op, key_node, ports) {
         if (op->nbr) {
             /* XXX ARP for neighboring router */
-        } else if (op->od->router_port) {
-            const char *peer_name = smap_get(
-                &op->od->router_port->nbs->options, "router-port");
-            if (!peer_name) {
-                continue;
-            }
-
-            struct ovn_port *peer = ovn_port_find(ports, peer_name);
-            if (!peer || !peer->nbr) {
-                continue;
-            }
-
+        } else if (op->od->n_router_ports) {
             for (size_t i = 0; i < op->nbs->n_addresses; i++) {
                 struct eth_addr ea;
                 ovs_be32 ip;
@@ -1450,18 +1455,41 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                 if (ovs_scan(op->nbs->addresses[i],
                              ETH_ADDR_SCAN_FMT" "IP_SCAN_FMT,
                              ETH_ADDR_SCAN_ARGS(ea), IP_SCAN_ARGS(&ip))) {
-                    char *match = xasprintf("reg0 == "IP_FMT, IP_ARGS(ip));
-                    char *actions = xasprintf("eth.src = "ETH_ADDR_FMT"; "
-                                              "eth.dst = "ETH_ADDR_FMT"; "
-                                              "outport = %s; "
-                                              "output;",
-                                              ETH_ADDR_ARGS(peer->mac),
-                                              ETH_ADDR_ARGS(ea),
-                                              peer->json_key);
-                    ovn_lflow_add(lflows, peer->od,
-                                  S_ROUTER_IN_ARP, 200, match, actions);
-                    free(actions);
-                    free(match);
+                    for (size_t j = 0; j < op->od->n_router_ports; j++) {
+                        /* Get the Logical_Router_Port that the Logical_Port is
+                         * connected to, as 'peer'. */
+                        const char *peer_name = smap_get(
+                            &op->od->router_ports[j]->nbs->options,
+                            "router-port");
+                        if (!peer_name) {
+                            continue;
+                        }
+
+                        struct ovn_port *peer
+                            = ovn_port_find(ports, peer_name);
+                        if (!peer || !peer->nbr) {
+                            continue;
+                        }
+
+                        /* Make sure that 'ip' is in 'peer''s network. */
+                        if ((ip ^ peer->network) & peer->mask) {
+                            continue;
+                        }
+
+                        char *match = xasprintf("reg0 == "IP_FMT, IP_ARGS(ip));
+                        char *actions = xasprintf("eth.src = "ETH_ADDR_FMT"; "
+                                                  "eth.dst = "ETH_ADDR_FMT"; "
+                                                  "outport = %s; "
+                                                  "output;",
+                                                  ETH_ADDR_ARGS(peer->mac),
+                                                  ETH_ADDR_ARGS(ea),
+                                                  peer->json_key);
+                        ovn_lflow_add(lflows, peer->od,
+                                      S_ROUTER_IN_ARP, 200, match, actions);
+                        free(actions);
+                        free(match);
+                        break;
+                    }
                 }
             }
         }