]>
Commit | Line | Data |
---|---|---|
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 | 37 | VLOG_DEFINE_THIS_MODULE(lflow); |
cda91769 JS |
38 | |
39 | COVERAGE_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 |
44 | static struct shash symtab; |
45 | ||
bce7cf45 BP |
46 | void |
47 | lflow_init(void) | |
678729a2 | 48 | { |
7700eea0 | 49 | ovn_init_symtab(&symtab); |
ea382567 | 50 | } |
678729a2 | 51 | \f |
bce7cf45 | 52 | struct 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 | 58 | struct 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 | 64 | static 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 |
83 | static bool |
84 | lookup_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 |
105 | static bool |
106 | is_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 | 130 | static bool |
bce7cf45 | 131 | is_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 |
137 | void |
138 | lflow_resource_init(struct lflow_resource_ref *lfrr) | |
139 | { | |
140 | hmap_init(&lfrr->ref_lflow_table); | |
141 | hmap_init(&lfrr->lflow_ref_table); | |
142 | } | |
143 | ||
144 | void | |
145 | lflow_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 | ||
169 | void | |
170 | lflow_resource_clear(struct lflow_resource_ref *lfrr) | |
171 | { | |
172 | lflow_resource_destroy(lfrr); | |
173 | lflow_resource_init(lfrr); | |
174 | } | |
175 | ||
176 | static struct ref_lflow_node* | |
177 | ref_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 | ||
191 | static struct lflow_ref_node* | |
192 | lflow_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 | ||
206 | static void | |
207 | lflow_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 | ||
239 | static void | |
240 | lflow_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 | 260 | static void |
0eb1e37c | 261 | add_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 |
320 | bool |
321 | lflow_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 |
407 | bool |
408 | lflow_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 |
512 | static bool |
513 | update_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 | ||
523 | static bool | |
d14e007c BP |
524 | consider_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 |
730 | static void |
731 | put_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 | 742 | static void |
d14e007c | 743 | consider_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 | 794 | static void |
d14e007c | 795 | add_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. */ | |
807 | void | |
8256e80b | 808 | lflow_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 | 838 | void |
48605550 | 839 | lflow_destroy(void) |
678729a2 BP |
840 | { |
841 | expr_symtab_destroy(&symtab); | |
c775160f | 842 | shash_destroy(&symtab); |
086470cd | 843 | ovn_destroy_ovnfields(); |
678729a2 | 844 | } |