]> git.proxmox.com Git - mirror_ovs.git/blame - ovn/controller/lflow.c
ovn-controller: Incremental processing for address-set changes.
[mirror_ovs.git] / ovn / controller / lflow.c
CommitLineData
4c99cb18 1/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
678729a2
BP
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>
48605550 17#include "lflow.h"
cda91769 18#include "coverage.h"
8256e80b 19#include "ha-chassis.h"
bce7cf45 20#include "lport.h"
b4e87a48 21#include "ofctrl.h"
b598f214
BW
22#include "openvswitch/dynamic-string.h"
23#include "openvswitch/ofp-actions.h"
64c96779 24#include "openvswitch/ofpbuf.h"
678729a2 25#include "openvswitch/vlog.h"
bce7cf45 26#include "ovn-controller.h"
8b2ed684
AR
27#include "ovn/actions.h"
28#include "ovn/expr.h"
16936e4d 29#include "ovn/lib/ovn-l7.h"
678729a2 30#include "ovn/lib/ovn-sb-idl.h"
66d89287 31#include "ovn/lib/extend-table.h"
aa68cf38 32#include "packets.h"
70c7cfef 33#include "physical.h"
678729a2 34#include "simap.h"
ea382567 35#include "sset.h"
678729a2 36
48605550 37VLOG_DEFINE_THIS_MODULE(lflow);
cda91769
JS
38
39COVERAGE_DEFINE(lflow_run);
678729a2
BP
40\f
41/* Symbol table. */
42
48605550 43/* Contains "struct expr_symbol"s for fields supported by OVN lflows. */
678729a2
BP
44static struct shash symtab;
45
bce7cf45
BP
46void
47lflow_init(void)
678729a2 48{
7700eea0 49 ovn_init_symtab(&symtab);
ea382567 50}
678729a2 51\f
bce7cf45 52struct lookup_port_aux {
d14e007c
BP
53 struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
54 struct ovsdb_idl_index *sbrec_port_binding_by_name;
bce7cf45 55 const struct sbrec_datapath_binding *dp;
678729a2
BP
56};
57
ba8d3816 58struct condition_aux {
d14e007c 59 struct ovsdb_idl_index *sbrec_port_binding_by_name;
ba8d3816 60 const struct sbrec_chassis *chassis;
508b7f96 61 const struct sset *active_tunnels;
ba8d3816
MS
62};
63
0bd4d85c 64static bool consider_logical_flow(
d14e007c
BP
65 struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
66 struct ovsdb_idl_index *sbrec_port_binding_by_name,
d14e007c
BP
67 const struct sbrec_logical_flow *,
68 const struct hmap *local_datapaths,
69 const struct sbrec_chassis *,
70 struct hmap *dhcp_opts,
71 struct hmap *dhcpv6_opts,
72 struct hmap *nd_ra_opts,
73 const struct shash *addr_sets,
74 const struct shash *port_groups,
75 const struct sset *active_tunnels,
76 const struct sset *local_lport_ids,
0bd4d85c 77 struct ovn_desired_flow_table *,
d14e007c 78 struct ovn_extend_table *group_table,
0bd4d85c 79 struct ovn_extend_table *meter_table,
43e6900a 80 struct lflow_resource_ref *lfrr,
0bd4d85c 81 uint32_t *conj_id_ofs);
a53d69c9 82
bce7cf45
BP
83static bool
84lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
678729a2 85{
bce7cf45 86 const struct lookup_port_aux *aux = aux_;
678729a2 87
bce7cf45 88 const struct sbrec_port_binding *pb
d14e007c 89 = lport_lookup_by_name(aux->sbrec_port_binding_by_name, port_name);
bce7cf45
BP
90 if (pb && pb->datapath == aux->dp) {
91 *portp = pb->tunnel_key;
92 return true;
678729a2
BP
93 }
94
d14e007c
BP
95 const struct sbrec_multicast_group *mg = mcgroup_lookup_by_dp_name(
96 aux->sbrec_multicast_group_by_name_datapath, aux->dp, port_name);
bce7cf45
BP
97 if (mg) {
98 *portp = mg->tunnel_key;
99 return true;
678729a2
BP
100 }
101
bce7cf45 102 return false;
678729a2
BP
103}
104
ba8d3816
MS
105static bool
106is_chassis_resident_cb(const void *c_aux_, const char *port_name)
107{
108 const struct condition_aux *c_aux = c_aux_;
109
110 const struct sbrec_port_binding *pb
d14e007c 111 = lport_lookup_by_name(c_aux->sbrec_port_binding_by_name, port_name);
508b7f96 112 if (!pb) {
113 return false;
114 }
115 if (strcmp(pb->type, "chassisredirect")) {
116 /* for non-chassisredirect ports */
117 return pb->chassis && pb->chassis == c_aux->chassis;
1da17a0b 118 } else {
8256e80b
NS
119 if (ha_chassis_group_contains(pb->ha_chassis_group,
120 c_aux->chassis)) {
121 bool active = ha_chassis_group_is_active(pb->ha_chassis_group,
122 c_aux->active_tunnels,
123 c_aux->chassis);
508b7f96 124 return active;
125 }
126 return false;
1da17a0b 127 }
ba8d3816
MS
128}
129
f1c16a85 130static bool
bce7cf45 131is_switch(const struct sbrec_datapath_binding *ldp)
f1c16a85 132{
bce7cf45
BP
133 return smap_get(&ldp->external_ids, "logical-switch") != NULL;
134
f1c16a85
BP
135}
136
43e6900a
HZ
137void
138lflow_resource_init(struct lflow_resource_ref *lfrr)
139{
140 hmap_init(&lfrr->ref_lflow_table);
141 hmap_init(&lfrr->lflow_ref_table);
142}
143
144void
145lflow_resource_destroy(struct lflow_resource_ref *lfrr)
146{
147 struct ref_lflow_node *rlfn, *rlfn_next;
148 HMAP_FOR_EACH_SAFE (rlfn, rlfn_next, node, &lfrr->ref_lflow_table) {
149 free(rlfn->ref_name);
150 struct lflow_ref_list_node *lrln, *next;
151 LIST_FOR_EACH_SAFE (lrln, next, ref_list, &rlfn->ref_lflow_head) {
152 ovs_list_remove(&lrln->ref_list);
153 ovs_list_remove(&lrln->lflow_list);
154 free(lrln);
155 }
156 hmap_remove(&lfrr->ref_lflow_table, &rlfn->node);
157 free(rlfn);
158 }
159 hmap_destroy(&lfrr->ref_lflow_table);
160
161 struct lflow_ref_node *lfrn, *lfrn_next;
162 HMAP_FOR_EACH_SAFE (lfrn, lfrn_next, node, &lfrr->lflow_ref_table) {
163 hmap_remove(&lfrr->lflow_ref_table, &lfrn->node);
164 free(lfrn);
165 }
166 hmap_destroy(&lfrr->lflow_ref_table);
167}
168
169void
170lflow_resource_clear(struct lflow_resource_ref *lfrr)
171{
172 lflow_resource_destroy(lfrr);
173 lflow_resource_init(lfrr);
174}
175
176static struct ref_lflow_node*
177ref_lflow_lookup(struct hmap *ref_lflow_table,
178 enum ref_type type, const char *ref_name)
179{
180 struct ref_lflow_node *rlfn;
181
182 HMAP_FOR_EACH_WITH_HASH (rlfn, node, hash_string(ref_name, type),
183 ref_lflow_table) {
184 if (rlfn->type == type && !strcmp(rlfn->ref_name, ref_name)) {
185 return rlfn;
186 }
187 }
188 return NULL;
189}
190
191static struct lflow_ref_node*
192lflow_ref_lookup(struct hmap *lflow_ref_table,
193 const struct uuid *lflow_uuid)
194{
195 struct lflow_ref_node *lfrn;
196
197 HMAP_FOR_EACH_WITH_HASH (lfrn, node, uuid_hash(lflow_uuid),
198 lflow_ref_table) {
199 if (uuid_equals(&lfrn->lflow_uuid, lflow_uuid)) {
200 return lfrn;
201 }
202 }
203 return NULL;
204}
205
206static void
207lflow_resource_add(struct lflow_resource_ref *lfrr, enum ref_type type,
208 const char *ref_name, const struct uuid *lflow_uuid)
209{
210 struct ref_lflow_node *rlfn = ref_lflow_lookup(&lfrr->ref_lflow_table,
211 type, ref_name);
212 if (!rlfn) {
213 rlfn = xzalloc(sizeof *rlfn);
214 rlfn->node.hash = hash_string(ref_name, type);
215 rlfn->type = type;
216 rlfn->ref_name = xstrdup(ref_name);
217 ovs_list_init(&rlfn->ref_lflow_head);
218 hmap_insert(&lfrr->ref_lflow_table, &rlfn->node, rlfn->node.hash);
219 }
220
221 struct lflow_ref_node *lfrn = lflow_ref_lookup(&lfrr->lflow_ref_table,
222 lflow_uuid);
223 if (!lfrn) {
224 lfrn = xzalloc(sizeof *lfrn);
225 lfrn->node.hash = uuid_hash(lflow_uuid);
226 lfrn->lflow_uuid = *lflow_uuid;
227 ovs_list_init(&lfrn->lflow_ref_head);
228 hmap_insert(&lfrr->lflow_ref_table, &lfrn->node, lfrn->node.hash);
229 }
230
231 struct lflow_ref_list_node *lrln = xzalloc(sizeof *lrln);
232 lrln->type = type;
233 lrln->ref_name = xstrdup(ref_name);
234 lrln->lflow_uuid = *lflow_uuid;
235 ovs_list_push_back(&rlfn->ref_lflow_head, &lrln->ref_list);
236 ovs_list_push_back(&lfrn->lflow_ref_head, &lrln->lflow_list);
237}
238
239static void
240lflow_resource_destroy_lflow(struct lflow_resource_ref *lfrr,
241 const struct uuid *lflow_uuid)
242{
243 struct lflow_ref_node *lfrn = lflow_ref_lookup(&lfrr->lflow_ref_table,
244 lflow_uuid);
245 if (!lfrn) {
246 return;
247 }
248
249 hmap_remove(&lfrr->lflow_ref_table, &lfrn->node);
250 struct lflow_ref_list_node *lrln, *next;
251 LIST_FOR_EACH_SAFE (lrln, next, lflow_list, &lfrn->lflow_ref_head) {
252 ovs_list_remove(&lrln->ref_list);
253 ovs_list_remove(&lrln->lflow_list);
254 free(lrln);
255 }
256 free(lfrn);
257}
258
fdbdb595 259/* Adds the logical flows from the Logical_Flow table to flow tables. */
0bac7164 260static void
0eb1e37c 261add_logical_flows(
d14e007c
BP
262 struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
263 struct ovsdb_idl_index *sbrec_port_binding_by_name,
0eb1e37c
BP
264 const struct sbrec_dhcp_options_table *dhcp_options_table,
265 const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
266 const struct sbrec_logical_flow_table *logical_flow_table,
0eb1e37c
BP
267 const struct hmap *local_datapaths,
268 const struct sbrec_chassis *chassis,
269 const struct shash *addr_sets,
270 const struct shash *port_groups,
271 const struct sset *active_tunnels,
272 const struct sset *local_lport_ids,
0bd4d85c 273 struct ovn_desired_flow_table *flow_table,
0eb1e37c 274 struct ovn_extend_table *group_table,
0bd4d85c 275 struct ovn_extend_table *meter_table,
43e6900a 276 struct lflow_resource_ref *lfrr,
0bd4d85c 277 uint32_t *conj_id_ofs)
678729a2 278{
70c7cfef
RM
279 const struct sbrec_logical_flow *lflow;
280
42814145 281 struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
01cfdb2f 282 struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
42814145 283 const struct sbrec_dhcp_options *dhcp_opt_row;
0eb1e37c 284 SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row, dhcp_options_table) {
42814145
NS
285 dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
286 dhcp_opt_row->type);
287 }
288
01cfdb2f
NS
289
290 const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
0eb1e37c
BP
291 SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH (dhcpv6_opt_row,
292 dhcpv6_options_table) {
01cfdb2f
NS
293 dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name, dhcpv6_opt_row->code,
294 dhcpv6_opt_row->type);
295 }
296
52ed5fcc
NS
297 struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
298 nd_ra_opts_init(&nd_ra_opts);
299
0eb1e37c 300 SBREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, logical_flow_table) {
0bd4d85c
HZ
301 if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
302 sbrec_port_binding_by_name,
303 lflow, local_datapaths,
304 chassis, &dhcp_opts, &dhcpv6_opts,
305 &nd_ra_opts, addr_sets, port_groups,
306 active_tunnels, local_lport_ids,
307 flow_table, group_table, meter_table,
43e6900a 308 lfrr, conj_id_ofs)) {
0bd4d85c
HZ
309 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
310 VLOG_ERR_RL(&rl, "Conjunction id overflow when processing lflow "
311 UUID_FMT, UUID_ARGS(&lflow->header_.uuid));
312 }
a53d69c9 313 }
bce7cf45 314
a53d69c9 315 dhcp_opts_destroy(&dhcp_opts);
01cfdb2f 316 dhcp_opts_destroy(&dhcpv6_opts);
52ed5fcc 317 nd_ra_opts_destroy(&nd_ra_opts);
a53d69c9 318}
b1e04512 319
e0b9e339
HZ
320bool
321lflow_handle_changed_flows(
322 struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
323 struct ovsdb_idl_index *sbrec_port_binding_by_name,
324 const struct sbrec_dhcp_options_table *dhcp_options_table,
325 const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
326 const struct sbrec_logical_flow_table *logical_flow_table,
327 const struct hmap *local_datapaths,
328 const struct sbrec_chassis *chassis,
329 const struct shash *addr_sets,
330 const struct shash *port_groups,
331 const struct sset *active_tunnels,
332 const struct sset *local_lport_ids,
333 struct ovn_desired_flow_table *flow_table,
334 struct ovn_extend_table *group_table,
335 struct ovn_extend_table *meter_table,
43e6900a 336 struct lflow_resource_ref *lfrr,
e0b9e339
HZ
337 uint32_t *conj_id_ofs)
338{
339 bool ret = true;
340 const struct sbrec_logical_flow *lflow;
341
342 struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
343 struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
344 const struct sbrec_dhcp_options *dhcp_opt_row;
345 SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row, dhcp_options_table) {
346 dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
347 dhcp_opt_row->type);
348 }
349
350
351 const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
352 SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH (dhcpv6_opt_row,
353 dhcpv6_options_table) {
354 dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name, dhcpv6_opt_row->code,
355 dhcpv6_opt_row->type);
356 }
357
358 struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
359 nd_ra_opts_init(&nd_ra_opts);
360
361 /* Handle removed flows first, and then other flows, so that when
362 * the flows being added and removed have same match conditions
363 * can be processed in the proper order */
364 SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow, logical_flow_table) {
365 /* Remove any flows that should be removed. */
366 if (sbrec_logical_flow_is_deleted(lflow)) {
367 VLOG_DBG("handle deleted lflow "UUID_FMT,
368 UUID_ARGS(&lflow->header_.uuid));
369 ofctrl_remove_flows(flow_table, &lflow->header_.uuid);
43e6900a
HZ
370 /* Delete entries from lflow resource reference. */
371 lflow_resource_destroy_lflow(lfrr, &lflow->header_.uuid);
e0b9e339
HZ
372 }
373 }
374 SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow, logical_flow_table) {
375 if (!sbrec_logical_flow_is_deleted(lflow)) {
376 /* Now, add/modify existing flows. If the logical
377 * flow is a modification, just remove the flows
378 * for this row, and then add new flows. */
379 if (!sbrec_logical_flow_is_new(lflow)) {
380 VLOG_DBG("handle updated lflow "UUID_FMT,
381 UUID_ARGS(&lflow->header_.uuid));
382 ofctrl_remove_flows(flow_table, &lflow->header_.uuid);
43e6900a
HZ
383 /* Delete entries from lflow resource reference. */
384 lflow_resource_destroy_lflow(lfrr, &lflow->header_.uuid);
e0b9e339
HZ
385 }
386 VLOG_DBG("handle new lflow "UUID_FMT,
387 UUID_ARGS(&lflow->header_.uuid));
388 if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
389 sbrec_port_binding_by_name,
390 lflow, local_datapaths,
391 chassis, &dhcp_opts, &dhcpv6_opts,
392 &nd_ra_opts, addr_sets, port_groups,
393 active_tunnels, local_lport_ids,
394 flow_table, group_table, meter_table,
43e6900a 395 lfrr, conj_id_ofs)) {
e0b9e339
HZ
396 ret = false;
397 break;
398 }
399 }
400 }
401 dhcp_opts_destroy(&dhcp_opts);
402 dhcp_opts_destroy(&dhcpv6_opts);
403 nd_ra_opts_destroy(&nd_ra_opts);
404 return ret;
405}
406
37c951b2
HZ
407bool
408lflow_handle_changed_ref(
409 enum ref_type ref_type,
410 const char *ref_name,
411 struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
412 struct ovsdb_idl_index *sbrec_port_binding_by_name,
413 const struct sbrec_dhcp_options_table *dhcp_options_table,
414 const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
415 const struct sbrec_logical_flow_table *logical_flow_table,
416 const struct hmap *local_datapaths,
417 const struct sbrec_chassis *chassis,
418 const struct shash *addr_sets,
419 const struct shash *port_groups,
420 const struct sset *active_tunnels,
421 const struct sset *local_lport_ids,
422 struct ovn_desired_flow_table *flow_table,
423 struct ovn_extend_table *group_table,
424 struct ovn_extend_table *meter_table,
425 struct lflow_resource_ref *lfrr,
426 uint32_t *conj_id_ofs,
427 bool *changed)
428{
429 struct ref_lflow_node *rlfn = ref_lflow_lookup(&lfrr->ref_lflow_table,
430 ref_type, ref_name);
431 if (!rlfn) {
432 *changed = false;
433 return true;
434 }
435 VLOG_DBG("Handle changed lflow reference for resource type: %d,"
436 " name: %s.", ref_type, ref_name);
437 *changed = false;
438 bool ret = true;
439
440 hmap_remove(&lfrr->ref_lflow_table, &rlfn->node);
441
442 struct lflow_ref_list_node *lrln, *next;
443 /* Detach the rlfn->ref_lflow_head nodes from the lfrr table and clean
444 * up all other nodes related to the lflows that uses the resource,
445 * so that the old nodes won't interfere with updating the lfrr table
446 * when reparsing the lflows. */
447 LIST_FOR_EACH (lrln, ref_list, &rlfn->ref_lflow_head) {
448 ovs_list_remove(&lrln->lflow_list);
449 lflow_resource_destroy_lflow(lfrr, &lrln->lflow_uuid);
450 }
451
452 struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
453 struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
454 const struct sbrec_dhcp_options *dhcp_opt_row;
455 SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row, dhcp_options_table) {
456 dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
457 dhcp_opt_row->type);
458 }
459
460 const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
461 SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH(dhcpv6_opt_row, dhcpv6_options_table) {
462 dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name, dhcpv6_opt_row->code,
463 dhcpv6_opt_row->type);
464 }
465
466 struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
467 nd_ra_opts_init(&nd_ra_opts);
468
469 /* Re-parse the related lflows. */
470 LIST_FOR_EACH (lrln, ref_list, &rlfn->ref_lflow_head) {
471 const struct sbrec_logical_flow *lflow =
472 sbrec_logical_flow_table_get_for_uuid(logical_flow_table,
473 &lrln->lflow_uuid);
474 if (!lflow) {
475 VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d,"
476 " name: %s - not found.",
477 UUID_ARGS(&lrln->lflow_uuid),
478 ref_type, ref_name);
479 continue;
480 }
481 VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d,"
482 " name: %s.",
483 UUID_ARGS(&lrln->lflow_uuid),
484 ref_type, ref_name);
485 ofctrl_remove_flows(flow_table, &lrln->lflow_uuid);
486 if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
487 sbrec_port_binding_by_name,
488 lflow, local_datapaths,
489 chassis, &dhcp_opts, &dhcpv6_opts,
490 &nd_ra_opts, addr_sets, port_groups,
491 active_tunnels, local_lport_ids,
492 flow_table, group_table, meter_table,
493 lfrr, conj_id_ofs)) {
494 ret = false;
495 break;
496 }
497 *changed = true;
498 }
499
500 LIST_FOR_EACH_SAFE (lrln, next, ref_list, &rlfn->ref_lflow_head) {
501 ovs_list_remove(&lrln->ref_list);
502 free(lrln);
503 }
504 free(rlfn);
505
506 dhcp_opts_destroy(&dhcp_opts);
507 dhcp_opts_destroy(&dhcpv6_opts);
508 nd_ra_opts_destroy(&nd_ra_opts);
509 return ret;
510}
511
0bd4d85c
HZ
512static bool
513update_conj_id_ofs(uint32_t *conj_id_ofs, uint32_t n_conjs)
514{
515 if (*conj_id_ofs + n_conjs < *conj_id_ofs) {
516 /* overflow */
517 return false;
518 }
519 *conj_id_ofs += n_conjs;
520 return true;
521}
522
523static bool
d14e007c
BP
524consider_logical_flow(
525 struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
526 struct ovsdb_idl_index *sbrec_port_binding_by_name,
d14e007c
BP
527 const struct sbrec_logical_flow *lflow,
528 const struct hmap *local_datapaths,
529 const struct sbrec_chassis *chassis,
530 struct hmap *dhcp_opts,
531 struct hmap *dhcpv6_opts,
532 struct hmap *nd_ra_opts,
533 const struct shash *addr_sets,
534 const struct shash *port_groups,
535 const struct sset *active_tunnels,
536 const struct sset *local_lport_ids,
0bd4d85c 537 struct ovn_desired_flow_table *flow_table,
d14e007c 538 struct ovn_extend_table *group_table,
0bd4d85c 539 struct ovn_extend_table *meter_table,
43e6900a 540 struct lflow_resource_ref *lfrr,
0bd4d85c 541 uint32_t *conj_id_ofs)
a53d69c9
RM
542{
543 /* Determine translation of logical table IDs to physical table IDs. */
544 bool ingress = !strcmp(lflow->pipeline, "ingress");
558ec83d 545
a53d69c9
RM
546 const struct sbrec_datapath_binding *ldp = lflow->logical_datapath;
547 if (!ldp) {
0bd4d85c
HZ
548 VLOG_DBG("lflow "UUID_FMT" has no datapath binding, skip",
549 UUID_ARGS(&lflow->header_.uuid));
550 return true;
a53d69c9 551 }
1ea9b847 552 if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) {
0bd4d85c
HZ
553 VLOG_DBG("lflow "UUID_FMT" is not for local datapath, skip",
554 UUID_ARGS(&lflow->header_.uuid));
555 return true;
a53d69c9 556 }
678729a2 557
a53d69c9
RM
558 /* Determine translation of logical table IDs to physical table IDs. */
559 uint8_t first_ptable = (ingress
560 ? OFTABLE_LOG_INGRESS_PIPELINE
561 : OFTABLE_LOG_EGRESS_PIPELINE);
562 uint8_t ptable = first_ptable + lflow->table_id;
563 uint8_t output_ptable = (ingress
564 ? OFTABLE_REMOTE_OUTPUT
bf143492 565 : OFTABLE_SAVE_INPORT);
a53d69c9 566
d5a76da4 567 /* Parse OVN logical actions.
a53d69c9
RM
568 *
569 * XXX Deny changes to 'outport' in egress pipeline. */
d5a76da4
BP
570 uint64_t ovnacts_stub[1024 / 8];
571 struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(ovnacts_stub);
572 struct ovnact_parse_params pp = {
573 .symtab = &symtab,
89231681
JP
574 .dhcp_opts = dhcp_opts,
575 .dhcpv6_opts = dhcpv6_opts,
52ed5fcc 576 .nd_ra_opts = nd_ra_opts,
d5a76da4 577
4c99cb18 578 .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
d5a76da4
BP
579 .n_tables = LOG_PIPELINE_LEN,
580 .cur_ltable = lflow->table_id,
581 };
a53d69c9
RM
582 struct expr *prereqs;
583 char *error;
584
d5a76da4
BP
585 error = ovnacts_parse_string(lflow->actions, &pp, &ovnacts, &prereqs);
586 if (error) {
587 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
588 VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s",
589 lflow->actions, error);
590 free(error);
ce81a73f 591 ovnacts_free(ovnacts.data, ovnacts.size);
d5a76da4 592 ofpbuf_uninit(&ovnacts);
0bd4d85c 593 return true;
d5a76da4
BP
594 }
595
a53d69c9
RM
596 /* Translate OVN match into table of OpenFlow matches. */
597 struct hmap matches;
598 struct expr *expr;
599
43e6900a 600 struct sset addr_sets_ref = SSET_INITIALIZER(&addr_sets_ref);
3d2848ba 601 expr = expr_parse_string(lflow->match, &symtab, addr_sets, port_groups,
43e6900a
HZ
602 &addr_sets_ref, &error);
603 const char *addr_set_name;
604 SSET_FOR_EACH (addr_set_name, &addr_sets_ref) {
605 lflow_resource_add(lfrr, REF_TYPE_ADDRSET, addr_set_name,
606 &lflow->header_.uuid);
607 }
608 sset_destroy(&addr_sets_ref);
609
a53d69c9
RM
610 if (!error) {
611 if (prereqs) {
612 expr = expr_combine(EXPR_T_AND, expr, prereqs);
613 prereqs = NULL;
678729a2 614 }
a53d69c9
RM
615 expr = expr_annotate(expr, &symtab, &error);
616 }
617 if (error) {
618 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
619 VLOG_WARN_RL(&rl, "error parsing match \"%s\": %s",
620 lflow->match, error);
621 expr_destroy(prereqs);
a53d69c9 622 free(error);
e50ed58a
GL
623 ovnacts_free(ovnacts.data, ovnacts.size);
624 ofpbuf_uninit(&ovnacts);
0bd4d85c 625 return true;
a53d69c9 626 }
678729a2 627
e50ed58a 628 struct lookup_port_aux aux = {
d14e007c
BP
629 .sbrec_multicast_group_by_name_datapath
630 = sbrec_multicast_group_by_name_datapath,
631 .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
e50ed58a
GL
632 .dp = lflow->logical_datapath
633 };
d14e007c
BP
634 struct condition_aux cond_aux = {
635 .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
636 .chassis = chassis,
637 .active_tunnels = active_tunnels,
d14e007c 638 };
ba8d3816 639 expr = expr_simplify(expr, is_chassis_resident_cb, &cond_aux);
a53d69c9
RM
640 expr = expr_normalize(expr);
641 uint32_t n_conjs = expr_to_matches(expr, lookup_port_cb, &aux,
642 &matches);
643 expr_destroy(expr);
644
e50ed58a 645 if (hmap_is_empty(&matches)) {
0bd4d85c
HZ
646 VLOG_DBG("lflow "UUID_FMT" matches are empty, skip",
647 UUID_ARGS(&lflow->header_.uuid));
e50ed58a
GL
648 ovnacts_free(ovnacts.data, ovnacts.size);
649 ofpbuf_uninit(&ovnacts);
650 expr_matches_destroy(&matches);
0bd4d85c 651 return true;
e50ed58a
GL
652 }
653
654 /* Encode OVN logical actions into OpenFlow. */
655 uint64_t ofpacts_stub[1024 / 8];
656 struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
657 struct ovnact_encode_params ep = {
658 .lookup_port = lookup_port_cb,
659 .aux = &aux,
660 .is_switch = is_switch(ldp),
e50ed58a
GL
661 .group_table = group_table,
662 .meter_table = meter_table,
0bd4d85c 663 .lflow_uuid = lflow->header_.uuid,
e50ed58a
GL
664
665 .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
666 .ingress_ptable = OFTABLE_LOG_INGRESS_PIPELINE,
667 .egress_ptable = OFTABLE_LOG_EGRESS_PIPELINE,
668 .output_ptable = output_ptable,
669 .mac_bind_ptable = OFTABLE_MAC_BINDING,
670 };
671 ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
672 ovnacts_free(ovnacts.data, ovnacts.size);
673 ofpbuf_uninit(&ovnacts);
674
a53d69c9
RM
675 /* Prepare the OpenFlow matches for adding to the flow table. */
676 struct expr_match *m;
677 HMAP_FOR_EACH (m, hmap_node, &matches) {
678 match_set_metadata(&m->match,
679 htonll(lflow->logical_datapath->tunnel_key));
680 if (m->match.wc.masks.conj_id) {
89231681 681 m->match.flow.conj_id += *conj_id_ofs;
a53d69c9 682 }
29e40807
RB
683 if (is_switch(ldp)) {
684 unsigned int reg_index
685 = (ingress ? MFF_LOG_INPORT : MFF_LOG_OUTPORT) - MFF_REG0;
686 int64_t port_id = m->match.flow.regs[reg_index];
687 if (port_id) {
688 int64_t dp_id = lflow->logical_datapath->tunnel_key;
689 char buf[16];
690 snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64, dp_id, port_id);
691 if (!sset_contains(local_lport_ids, buf)) {
0bd4d85c
HZ
692 VLOG_DBG("lflow "UUID_FMT
693 " port %s in match is not local, skip",
694 UUID_ARGS(&lflow->header_.uuid),
695 buf);
29e40807
RB
696 continue;
697 }
698 }
699 }
a53d69c9 700 if (!m->n) {
c80eac1f 701 ofctrl_add_flow(flow_table, ptable, lflow->priority,
0bd4d85c
HZ
702 lflow->header_.uuid.parts[0], &m->match, &ofpacts,
703 &lflow->header_.uuid);
a53d69c9
RM
704 } else {
705 uint64_t conj_stubs[64 / 8];
706 struct ofpbuf conj;
707
708 ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs);
709 for (int i = 0; i < m->n; i++) {
710 const struct cls_conjunction *src = &m->conjunctions[i];
711 struct ofpact_conjunction *dst;
712
713 dst = ofpact_put_CONJUNCTION(&conj);
89231681 714 dst->id = src->id + *conj_id_ofs;
a53d69c9
RM
715 dst->clause = src->clause;
716 dst->n_clauses = src->n_clauses;
678729a2 717 }
c80eac1f 718 ofctrl_add_flow(flow_table, ptable, lflow->priority, 0, &m->match,
0bd4d85c 719 &conj, &lflow->header_.uuid);
a53d69c9 720 ofpbuf_uninit(&conj);
678729a2 721 }
678729a2 722 }
42814145 723
a53d69c9
RM
724 /* Clean up. */
725 expr_matches_destroy(&matches);
726 ofpbuf_uninit(&ofpacts);
0bd4d85c 727 return update_conj_id_ofs(conj_id_ofs, n_conjs);
678729a2
BP
728}
729
0bac7164
BP
730static void
731put_load(const uint8_t *data, size_t len,
732 enum mf_field_id dst, int ofs, int n_bits,
733 struct ofpbuf *ofpacts)
734{
128684a6
JR
735 struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
736 mf_from_id(dst), NULL,
737 NULL);
738 bitwise_copy(data, len, 0, sf->value, sf->field->n_bytes, ofs, n_bits);
739 bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits);
0bac7164
BP
740}
741
7ecefc49 742static void
d14e007c 743consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
7ecefc49 744 const struct sbrec_mac_binding *b,
0bd4d85c 745 struct ovn_desired_flow_table *flow_table)
7ecefc49
RM
746{
747 const struct sbrec_port_binding *pb
d14e007c 748 = lport_lookup_by_name(sbrec_port_binding_by_name, b->logical_port);
7ecefc49
RM
749 if (!pb) {
750 return;
751 }
752
753 struct eth_addr mac;
754 if (!eth_addr_from_string(b->mac, &mac)) {
755 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
756 VLOG_WARN_RL(&rl, "bad 'mac' %s", b->mac);
757 return;
758 }
759
b81e5900 760 struct match match = MATCH_CATCHALL_INITIALIZER;
c34a87b6
JP
761 if (strchr(b->ip, '.')) {
762 ovs_be32 ip;
763 if (!ip_parse(b->ip, &ip)) {
764 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
765 VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
766 return;
767 }
b81e5900 768 match_set_reg(&match, 0, ntohl(ip));
c34a87b6
JP
769 } else {
770 struct in6_addr ip6;
771 if (!ipv6_parse(b->ip, &ip6)) {
772 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
773 VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
774 return;
775 }
776 ovs_be128 value;
777 memcpy(&value, &ip6, sizeof(value));
b81e5900 778 match_set_xxreg(&match, 0, ntoh128(value));
7ecefc49
RM
779 }
780
b81e5900
CSV
781 match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
782 match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
7ecefc49 783
b81e5900
CSV
784 uint64_t stub[1024 / 8];
785 struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
786 put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
0bd4d85c
HZ
787 ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &match, &ofpacts,
788 &b->header_.uuid);
b81e5900 789 ofpbuf_uninit(&ofpacts);
7ecefc49
RM
790}
791
fdbdb595 792/* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN
f4557c09 793 * southbound database. */
0bac7164 794static void
d14e007c 795add_neighbor_flows(struct ovsdb_idl_index *sbrec_port_binding_by_name,
0eb1e37c 796 const struct sbrec_mac_binding_table *mac_binding_table,
0bd4d85c 797 struct ovn_desired_flow_table *flow_table)
0bac7164 798{
0bac7164 799 const struct sbrec_mac_binding *b;
0eb1e37c 800 SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) {
d14e007c 801 consider_neighbor_flow(sbrec_port_binding_by_name, b, flow_table);
0bac7164 802 }
0bac7164
BP
803}
804\f
805/* Translates logical flows in the Logical_Flow table in the OVN_SB database
806 * into OpenFlow flows. See ovn-architecture(7) for more information. */
807void
8256e80b 808lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
d14e007c 809 struct ovsdb_idl_index *sbrec_port_binding_by_name,
0eb1e37c
BP
810 const struct sbrec_dhcp_options_table *dhcp_options_table,
811 const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
812 const struct sbrec_logical_flow_table *logical_flow_table,
813 const struct sbrec_mac_binding_table *mac_binding_table,
ba8d3816 814 const struct sbrec_chassis *chassis,
0bac7164 815 const struct hmap *local_datapaths,
d49a3578 816 const struct shash *addr_sets,
3d2848ba 817 const struct shash *port_groups,
83293ddf
BP
818 const struct sset *active_tunnels,
819 const struct sset *local_lport_ids,
0bd4d85c 820 struct ovn_desired_flow_table *flow_table,
83293ddf 821 struct ovn_extend_table *group_table,
0bd4d85c 822 struct ovn_extend_table *meter_table,
43e6900a 823 struct lflow_resource_ref *lfrr,
0bd4d85c 824 uint32_t *conj_id_ofs)
0bac7164 825{
cda91769
JS
826 COVERAGE_INC(lflow_run);
827
8256e80b 828 add_logical_flows(sbrec_multicast_group_by_name_datapath,
d14e007c 829 sbrec_port_binding_by_name, dhcp_options_table,
789ddafa 830 dhcpv6_options_table, logical_flow_table,
d14e007c
BP
831 local_datapaths, chassis, addr_sets, port_groups,
832 active_tunnels, local_lport_ids, flow_table, group_table,
43e6900a 833 meter_table, lfrr, conj_id_ofs);
d14e007c
BP
834 add_neighbor_flows(sbrec_port_binding_by_name, mac_binding_table,
835 flow_table);
0bac7164
BP
836}
837
678729a2 838void
48605550 839lflow_destroy(void)
678729a2
BP
840{
841 expr_symtab_destroy(&symtab);
c775160f 842 shash_destroy(&symtab);
086470cd 843 ovn_destroy_ovnfields();
678729a2 844}