ovn: Add stateful ACL support.
[cascardo/ovs.git] / ovn / controller-vtep / gateway.c
1 /* Copyright (c) 2015 Nicira, Inc.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <config.h>
17 #include "gateway.h"
18
19 #include "lib/poll-loop.h"
20 #include "lib/simap.h"
21 #include "lib/sset.h"
22 #include "lib/util.h"
23 #include "openvswitch/vlog.h"
24 #include "ovn/lib/ovn-sb-idl.h"
25 #include "vtep/vtep-idl.h"
26 #include "ovn-controller-vtep.h"
27
28 VLOG_DEFINE_THIS_MODULE(gateway);
29
30 /*
31  * Registers the physical switches in vtep to ovnsb as chassis.  For each
32  * physical switch in the vtep database, finds all vtep logical switches that
33  * are associated with the physical switch, and updates the corresponding
34  * chassis's 'vtep_logical_switches' column.
35  *
36  */
37
38 /* Global revalidation sequence number, incremented at each call to
39  * 'revalidate_gateway()'. */
40 static unsigned int gw_reval_seq;
41
42 /* Maps all chassis created by the gateway module to their own reval_seq. */
43 static struct simap gw_chassis_map = SIMAP_INITIALIZER(&gw_chassis_map);
44
45 /* Creates and returns a new instance of 'struct sbrec_chassis'. */
46 static const struct sbrec_chassis *
47 create_chassis_rec(struct ovsdb_idl_txn *txn, const char *name,
48                    const char *encap_ip)
49 {
50     const struct sbrec_chassis *chassis_rec;
51     struct sbrec_encap *encap_rec;
52
53     VLOG_INFO("add Chassis row for VTEP physical switch (%s)", name);
54
55     chassis_rec = sbrec_chassis_insert(txn);
56     sbrec_chassis_set_name(chassis_rec, name);
57     encap_rec = sbrec_encap_insert(txn);
58     sbrec_encap_set_type(encap_rec, OVN_SB_ENCAP_TYPE);
59     sbrec_encap_set_ip(encap_rec, encap_ip);
60     sbrec_chassis_set_encaps(chassis_rec, &encap_rec, 1);
61
62     return chassis_rec;
63 }
64
65 /* Revalidates chassis in ovnsb against vtep database.  Creates chassis for
66  * new vtep physical switch.  And removes chassis which no longer have
67  * physical switch in vtep.
68  *
69  * xxx: Support multiple tunnel encaps.
70  *
71  * */
72 static void
73 revalidate_gateway(struct controller_vtep_ctx *ctx)
74 {
75     const struct vteprec_physical_switch *pswitch;
76
77     /* Increments the global revalidation sequence number. */
78     gw_reval_seq++;
79
80     ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
81                               "ovn-controller-vtep: updating vtep chassis");
82
83     VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
84         const struct sbrec_chassis *chassis_rec;
85         struct simap_node *gw_node;
86         const char *encap_ip;
87
88         encap_ip = pswitch->n_tunnel_ips ? pswitch->tunnel_ips[0] : "";
89         gw_node = simap_find(&gw_chassis_map, pswitch->name);
90         chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
91         if (chassis_rec) {
92             if (!gw_node &&
93                 (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)
94                  || strcmp(chassis_rec->encaps[0]->ip, encap_ip))) {
95                 VLOG_WARN("Chassis config changing on startup, make sure "
96                           "multiple chassis are not configured : %s/%s->%s/%s",
97                           chassis_rec->encaps[0]->type,
98                           chassis_rec->encaps[0]->ip,
99                           OVN_SB_ENCAP_TYPE, encap_ip);
100             }
101             /* Updates chassis's encap if anything changed. */
102             if (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)) {
103                 VLOG_WARN("Chassis for VTEP physical switch (%s) can only have "
104                           "encap type \"%s\"", pswitch->name, OVN_SB_ENCAP_TYPE);
105                 sbrec_encap_set_type(chassis_rec->encaps[0], OVN_SB_ENCAP_TYPE);
106             }
107             if (strcmp(chassis_rec->encaps[0]->ip, encap_ip)) {
108                 sbrec_encap_set_ip(chassis_rec->encaps[0], encap_ip);
109             }
110         } else {
111             if (gw_node) {
112                 VLOG_WARN("Chassis for VTEP physical switch (%s) disappears, "
113                           "maybe deleted by ovn-sbctl, adding it back",
114                           pswitch->name);
115             }
116             /* Creates a new chassis for the VTEP physical switch. */
117             create_chassis_rec(ctx->ovnsb_idl_txn, pswitch->name, encap_ip);
118         }
119         /* Updates or creates the simap node for 'pswitch->name'. */
120         simap_put(&gw_chassis_map, pswitch->name, gw_reval_seq);
121     }
122
123     struct simap_node *iter, *next;
124     /* For 'gw_node' in 'gw_chassis_map' whose data is not
125      * 'gw_reval_seq', it means the corresponding physical switch no
126      * longer exist.  So, garbage collects them. */
127     SIMAP_FOR_EACH_SAFE (iter, next, &gw_chassis_map) {
128         if (iter->data != gw_reval_seq) {
129             const struct sbrec_chassis *chassis_rec;
130
131             chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, iter->name);
132             if (chassis_rec) {
133                 sbrec_chassis_delete(chassis_rec);
134             }
135             simap_delete(&gw_chassis_map, iter);
136         }
137     }
138 }
139
140 /* Updates the 'vtep_logical_switches' column in the Chassis table based
141  * on vtep database configuration. */
142 static void
143 update_vtep_logical_switches(struct controller_vtep_ctx *ctx)
144 {
145     const struct vteprec_physical_switch *pswitch;
146
147     ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: "
148                               "updating chassis's vtep_logical_switches");
149
150     VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
151         const struct sbrec_chassis *chassis_rec =
152             get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
153         struct sset lswitches = SSET_INITIALIZER(&lswitches);
154         size_t i;
155
156         for (i = 0; i < pswitch->n_ports; i++) {
157             const struct vteprec_physical_port *port = pswitch->ports[i];
158             size_t j;
159
160             for (j = 0; j < port->n_vlan_bindings; j++) {
161                 const struct vteprec_logical_switch *vtep_lswitch;
162
163                 vtep_lswitch = port->value_vlan_bindings[j];
164                 /* If not already in 'lswitches', records it. */
165                 if (!sset_find(&lswitches, vtep_lswitch->name)) {
166                     sset_add(&lswitches, vtep_lswitch->name);
167                 }
168             }
169         }
170
171         const char **ls_arr = sset_array(&lswitches);
172         sbrec_chassis_set_vtep_logical_switches(chassis_rec, ls_arr,
173                                                 sset_count(&lswitches));
174         free(ls_arr);
175         sset_destroy(&lswitches);
176     }
177 }
178
179 \f
180 void
181 gateway_run(struct controller_vtep_ctx *ctx)
182 {
183     if (!ctx->ovnsb_idl_txn) {
184         return;
185     }
186
187     revalidate_gateway(ctx);
188     update_vtep_logical_switches(ctx);
189 }
190
191 /* Destroys the chassis table entries for vtep physical switches.
192  * Returns true when done (i.e. there is no change made to 'ctx->ovnsb_idl'),
193  * otherwise returns false. */
194 bool
195 gateway_cleanup(struct controller_vtep_ctx *ctx)
196 {
197     static bool simap_destroyed = false;
198     const struct vteprec_physical_switch *pswitch;
199
200     if (!ctx->ovnsb_idl_txn) {
201         return false;
202     }
203
204     bool all_done = true;
205     ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: "
206                               "unregistering vtep chassis");
207     VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
208         const struct sbrec_chassis *chassis_rec;
209
210         chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
211         if (!chassis_rec) {
212             continue;
213         }
214         all_done = false;
215         sbrec_chassis_delete(chassis_rec);
216     }
217     if (!simap_destroyed) {
218         simap_destroy(&gw_chassis_map);
219         simap_destroyed = true;
220     }
221
222     return all_done;
223 }