]>
Commit | Line | Data |
---|---|---|
a9360f2a | 1 | /* Copyright (c) 2015, 2016 Nicira, Inc. |
e71ac5cd 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> | |
ab39371d | 17 | #include "binding.h" |
f4248336 BW |
18 | #include "byte-order.h" |
19 | #include "flow.h" | |
48605550 | 20 | #include "lflow.h" |
ab39371d | 21 | #include "lib/poll-loop.h" |
e71ac5cd | 22 | #include "ofctrl.h" |
b598f214 BW |
23 | #include "openvswitch/match.h" |
24 | #include "openvswitch/ofp-actions.h" | |
64c96779 | 25 | #include "openvswitch/ofpbuf.h" |
b598f214 | 26 | #include "openvswitch/vlog.h" |
e71ac5cd BP |
27 | #include "ovn-controller.h" |
28 | #include "ovn/lib/ovn-sb-idl.h" | |
3bd4ae23 | 29 | #include "ovn/lib/ovn-util.h" |
b598f214 | 30 | #include "physical.h" |
ee89ea7b | 31 | #include "openvswitch/shash.h" |
e71ac5cd | 32 | #include "simap.h" |
c0281929 | 33 | #include "smap.h" |
5868eb24 | 34 | #include "sset.h" |
ee89ea7b | 35 | #include "util.h" |
e71ac5cd BP |
36 | #include "vswitch-idl.h" |
37 | ||
5868eb24 BP |
38 | VLOG_DEFINE_THIS_MODULE(physical); |
39 | ||
e71ac5cd | 40 | void |
4a5a9e06 | 41 | physical_register_ovs_idl(struct ovsdb_idl *ovs_idl) |
e71ac5cd | 42 | { |
4a5a9e06 BP |
43 | ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge); |
44 | ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports); | |
e71ac5cd | 45 | |
4a5a9e06 BP |
46 | ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port); |
47 | ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name); | |
48 | ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces); | |
49 | ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_external_ids); | |
e71ac5cd | 50 | |
4a5a9e06 BP |
51 | ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface); |
52 | ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name); | |
53 | ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_ofport); | |
54 | ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids); | |
e71ac5cd BP |
55 | } |
56 | ||
40128e37 RM |
57 | static struct simap localvif_to_ofport = |
58 | SIMAP_INITIALIZER(&localvif_to_ofport); | |
59 | static struct hmap tunnels = HMAP_INITIALIZER(&tunnels); | |
60 | ||
fdbdb595 RM |
61 | /* UUID to identify OF flows not associated with ovsdb rows. */ |
62 | static struct uuid *hc_uuid = NULL; | |
70c7cfef RM |
63 | static bool full_binding_processing = false; |
64 | ||
65 | void | |
66 | physical_reset_processing(void) | |
67 | { | |
68 | full_binding_processing = true; | |
69 | } | |
fdbdb595 | 70 | |
5868eb24 BP |
71 | /* Maps from a chassis to the OpenFlow port number of the tunnel that can be |
72 | * used to reach that chassis. */ | |
73 | struct chassis_tunnel { | |
74 | struct hmap_node hmap_node; | |
75 | const char *chassis_id; | |
76 | ofp_port_t ofport; | |
bf388125 | 77 | enum chassis_tunnel_type type; |
5868eb24 BP |
78 | }; |
79 | ||
80 | static struct chassis_tunnel * | |
40128e37 | 81 | chassis_tunnel_find(const char *chassis_id) |
5868eb24 BP |
82 | { |
83 | struct chassis_tunnel *tun; | |
84 | HMAP_FOR_EACH_WITH_HASH (tun, hmap_node, hash_string(chassis_id, 0), | |
40128e37 | 85 | &tunnels) { |
5868eb24 BP |
86 | if (!strcmp(tun->chassis_id, chassis_id)) { |
87 | return tun; | |
88 | } | |
89 | } | |
90 | return NULL; | |
91 | } | |
92 | ||
93 | static void | |
94 | put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits, | |
95 | struct ofpbuf *ofpacts) | |
96 | { | |
97 | struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); | |
98 | sf->field = mf_from_id(dst); | |
99 | sf->flow_has_vlan = false; | |
100 | ||
101 | ovs_be64 n_value = htonll(value); | |
102 | bitwise_copy(&n_value, 8, 0, &sf->value, sf->field->n_bytes, ofs, n_bits); | |
103 | bitwise_one(&sf->mask, sf->field->n_bytes, ofs, n_bits); | |
104 | } | |
105 | ||
106 | static void | |
107 | put_move(enum mf_field_id src, int src_ofs, | |
108 | enum mf_field_id dst, int dst_ofs, | |
109 | int n_bits, | |
110 | struct ofpbuf *ofpacts) | |
111 | { | |
112 | struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts); | |
113 | move->src.field = mf_from_id(src); | |
114 | move->src.ofs = src_ofs; | |
115 | move->src.n_bits = n_bits; | |
116 | move->dst.field = mf_from_id(dst); | |
117 | move->dst.ofs = dst_ofs; | |
118 | move->dst.n_bits = n_bits; | |
119 | } | |
120 | ||
121 | static void | |
122 | put_resubmit(uint8_t table_id, struct ofpbuf *ofpacts) | |
123 | { | |
124 | struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ofpacts); | |
125 | resubmit->in_port = OFPP_IN_PORT; | |
126 | resubmit->table_id = table_id; | |
127 | } | |
128 | ||
129 | static void | |
130 | put_encapsulation(enum mf_field_id mff_ovn_geneve, | |
131 | const struct chassis_tunnel *tun, | |
132 | const struct sbrec_datapath_binding *datapath, | |
133 | uint16_t outport, struct ofpbuf *ofpacts) | |
134 | { | |
135 | if (tun->type == GENEVE) { | |
136 | put_load(datapath->tunnel_key, MFF_TUN_ID, 0, 24, ofpacts); | |
137 | put_load(outport, mff_ovn_geneve, 0, 32, ofpacts); | |
138 | put_move(MFF_LOG_INPORT, 0, mff_ovn_geneve, 16, 15, ofpacts); | |
139 | } else if (tun->type == STT) { | |
140 | put_load(datapath->tunnel_key | (outport << 24), MFF_TUN_ID, 0, 64, | |
141 | ofpacts); | |
142 | put_move(MFF_LOG_INPORT, 0, MFF_TUN_ID, 40, 15, ofpacts); | |
37910994 JP |
143 | } else if (tun->type == VXLAN) { |
144 | put_load(datapath->tunnel_key, MFF_TUN_ID, 0, 24, ofpacts); | |
5868eb24 BP |
145 | } else { |
146 | OVS_NOT_REACHED(); | |
147 | } | |
148 | } | |
149 | ||
150 | static void | |
151 | put_stack(enum mf_field_id field, struct ofpact_stack *stack) | |
152 | { | |
153 | stack->subfield.field = mf_from_id(field); | |
154 | stack->subfield.ofs = 0; | |
155 | stack->subfield.n_bits = stack->subfield.field->n_bits; | |
156 | } | |
157 | ||
6e6c3f91 HZ |
158 | static const struct sbrec_port_binding* |
159 | get_localnet_port(struct hmap *local_datapaths, int64_t tunnel_key) | |
160 | { | |
e4426e34 BP |
161 | struct local_datapath *ld = get_local_datapath(local_datapaths, |
162 | tunnel_key); | |
6e6c3f91 HZ |
163 | return ld ? ld->localnet_port : NULL; |
164 | } | |
165 | ||
40128e37 | 166 | static void |
fdbdb595 | 167 | consider_port_binding(enum mf_field_id mff_ovn_geneve, |
40128e37 RM |
168 | const struct simap *ct_zones, |
169 | struct hmap *local_datapaths, | |
170 | struct hmap *patched_datapaths, | |
171 | const struct sbrec_port_binding *binding, | |
172 | struct ofpbuf *ofpacts_p) | |
173 | { | |
174 | /* Skip the port binding if the port is on a datapath that is neither | |
175 | * local nor with any logical patch port connected, because local ports | |
176 | * would never need to talk to those ports. | |
177 | * | |
178 | * Even with this approach there could still be unnecessary port | |
179 | * bindings processed. A better approach would be a kind of "flood | |
180 | * fill" algorithm: | |
181 | * | |
182 | * 1. Initialize set S to the logical datapaths that have a port | |
183 | * located on the hypervisor. | |
184 | * | |
185 | * 2. For each patch port P in a logical datapath in S, add the | |
186 | * logical datapath of the remote end of P to S. Iterate | |
187 | * until S reaches a fixed point. | |
188 | * | |
189 | * This can be implemented in northd, which can generate the sets and | |
190 | * save it on each port-binding record in SB, and ovn-controller can | |
191 | * use the information directly. However, there can be update storms | |
192 | * when a pair of patch ports are added/removed to connect/disconnect | |
193 | * large lrouters and lswitches. This need to be studied further. | |
194 | */ | |
195 | uint32_t dp_key = binding->datapath->tunnel_key; | |
196 | uint32_t port_key = binding->tunnel_key; | |
197 | if (!get_local_datapath(local_datapaths, dp_key) | |
198 | && !get_patched_datapath(patched_datapaths, dp_key)) { | |
199 | return; | |
200 | } | |
201 | ||
202 | /* Find the OpenFlow port for the logical port, as 'ofport'. This is | |
203 | * one of: | |
204 | * | |
205 | * - If the port is a VIF on the chassis we're managing, the | |
206 | * OpenFlow port for the VIF. 'tun' will be NULL. | |
207 | * | |
208 | * The same logic handles logical patch ports, as well as | |
209 | * localnet patch ports. | |
210 | * | |
211 | * For a container nested inside a VM and accessible via a VLAN, | |
212 | * 'tag' is the VLAN ID; otherwise 'tag' is 0. | |
213 | * | |
214 | * For a localnet patch port, if a VLAN ID was configured, 'tag' | |
215 | * is set to that VLAN ID; otherwise 'tag' is 0. | |
216 | * | |
217 | * - If the port is on a remote chassis, the OpenFlow port for a | |
218 | * tunnel to the VIF's remote chassis. 'tun' identifies that | |
219 | * tunnel. | |
220 | */ | |
221 | ||
222 | int tag = 0; | |
223 | ofp_port_t ofport; | |
224 | bool is_remote = false; | |
225 | if (binding->parent_port && *binding->parent_port) { | |
226 | if (!binding->tag) { | |
227 | return; | |
228 | } | |
229 | ofport = u16_to_ofp(simap_get(&localvif_to_ofport, | |
230 | binding->parent_port)); | |
231 | if (ofport) { | |
232 | tag = *binding->tag; | |
233 | } | |
234 | } else { | |
235 | ofport = u16_to_ofp(simap_get(&localvif_to_ofport, | |
236 | binding->logical_port)); | |
237 | if ((!strcmp(binding->type, "localnet") | |
238 | || !strcmp(binding->type, "l2gateway")) | |
239 | && ofport && binding->tag) { | |
240 | tag = *binding->tag; | |
241 | } | |
242 | } | |
243 | ||
244 | const struct chassis_tunnel *tun = NULL; | |
245 | const struct sbrec_port_binding *localnet_port = | |
246 | get_localnet_port(local_datapaths, dp_key); | |
247 | if (!ofport) { | |
248 | /* It is remote port, may be reached by tunnel or localnet port */ | |
249 | is_remote = true; | |
250 | if (!binding->chassis) { | |
251 | return; | |
252 | } | |
253 | if (localnet_port) { | |
254 | ofport = u16_to_ofp(simap_get(&localvif_to_ofport, | |
255 | localnet_port->logical_port)); | |
256 | if (!ofport) { | |
257 | return; | |
258 | } | |
259 | } else { | |
260 | tun = chassis_tunnel_find(binding->chassis->name); | |
261 | if (!tun) { | |
262 | return; | |
263 | } | |
264 | ofport = tun->ofport; | |
265 | } | |
266 | } | |
267 | ||
268 | struct match match; | |
269 | if (!is_remote) { | |
270 | int zone_id = simap_get(ct_zones, binding->logical_port); | |
271 | /* Packets that arrive from a vif can belong to a VM or | |
272 | * to a container located inside that VM. Packets that | |
273 | * arrive from containers have a tag (vlan) associated with them. | |
274 | */ | |
275 | ||
276 | /* Table 0, Priority 150 and 100. | |
277 | * ============================== | |
278 | * | |
279 | * Priority 150 is for tagged traffic. This may be containers in a | |
280 | * VM or a VLAN on a local network. For such traffic, match on the | |
281 | * tags and then strip the tag. | |
282 | * | |
283 | * Priority 100 is for traffic belonging to VMs or untagged locally | |
284 | * connected networks. | |
285 | * | |
286 | * For both types of traffic: set MFF_LOG_INPORT to the logical | |
287 | * input port, MFF_LOG_DATAPATH to the logical datapath, and | |
288 | * resubmit into the logical ingress pipeline starting at table | |
289 | * 16. */ | |
290 | ofpbuf_clear(ofpacts_p); | |
291 | match_init_catchall(&match); | |
292 | match_set_in_port(&match, ofport); | |
293 | ||
294 | /* Match a VLAN tag and strip it, including stripping priority tags | |
295 | * (e.g. VLAN ID 0). In the latter case we'll add a second flow | |
296 | * for frames that lack any 802.1Q header later. */ | |
297 | if (tag || !strcmp(binding->type, "localnet") | |
298 | || !strcmp(binding->type, "l2gateway")) { | |
299 | match_set_dl_vlan(&match, htons(tag)); | |
300 | ofpact_put_STRIP_VLAN(ofpacts_p); | |
301 | } | |
302 | ||
303 | /* Remember the size with just strip vlan added so far, | |
304 | * as we're going to remove this with ofpbuf_pull() later. */ | |
305 | uint32_t ofpacts_orig_size = ofpacts_p->size; | |
306 | ||
307 | if (zone_id) { | |
308 | put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p); | |
309 | } | |
310 | ||
311 | int zone_id_dnat, zone_id_snat; | |
34114cf8 GS |
312 | char *key = xasprintf(UUID_FMT, |
313 | UUID_ARGS(&binding->datapath->header_.uuid)); | |
314 | char *dnat = alloc_nat_zone_key(key, "dnat"); | |
315 | char *snat = alloc_nat_zone_key(key, "snat"); | |
316 | free(key); | |
317 | ||
40128e37 RM |
318 | zone_id_dnat = simap_get(ct_zones, dnat); |
319 | if (zone_id_dnat) { | |
320 | put_load(zone_id_dnat, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p); | |
321 | } | |
322 | free(dnat); | |
323 | ||
324 | zone_id_snat = simap_get(ct_zones, snat); | |
325 | if (zone_id_snat) { | |
326 | put_load(zone_id_snat, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p); | |
327 | } | |
328 | free(snat); | |
329 | ||
330 | /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */ | |
331 | put_load(dp_key, MFF_LOG_DATAPATH, 0, 64, ofpacts_p); | |
332 | put_load(port_key, MFF_LOG_INPORT, 0, 32, ofpacts_p); | |
333 | ||
334 | /* Resubmit to first logical ingress pipeline table. */ | |
335 | put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p); | |
fdbdb595 RM |
336 | ofctrl_add_flow(OFTABLE_PHY_TO_LOG, |
337 | tag ? 150 : 100, &match, ofpacts_p, | |
338 | &binding->header_.uuid); | |
40128e37 RM |
339 | |
340 | if (!tag && (!strcmp(binding->type, "localnet") | |
341 | || !strcmp(binding->type, "l2gateway"))) { | |
342 | ||
343 | /* Add a second flow for frames that lack any 802.1Q | |
344 | * header. For these, drop the OFPACT_STRIP_VLAN | |
345 | * action. */ | |
346 | ofpbuf_pull(ofpacts_p, ofpacts_orig_size); | |
347 | match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI)); | |
fdbdb595 RM |
348 | ofctrl_add_flow(0, 100, &match, ofpacts_p, |
349 | &binding->header_.uuid); | |
40128e37 RM |
350 | } |
351 | ||
352 | /* Table 33, priority 100. | |
353 | * ======================= | |
354 | * | |
355 | * Implements output to local hypervisor. Each flow matches a | |
356 | * logical output port on the local hypervisor, and resubmits to | |
357 | * table 34. | |
358 | */ | |
359 | ||
360 | match_init_catchall(&match); | |
361 | ofpbuf_clear(ofpacts_p); | |
362 | ||
363 | /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ | |
364 | match_set_metadata(&match, htonll(dp_key)); | |
365 | match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); | |
366 | ||
367 | if (zone_id) { | |
368 | put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p); | |
369 | } | |
370 | if (zone_id_dnat) { | |
371 | put_load(zone_id_dnat, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p); | |
372 | } | |
373 | if (zone_id_snat) { | |
374 | put_load(zone_id_snat, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p); | |
375 | } | |
376 | ||
377 | /* Resubmit to table 34. */ | |
bf143492 | 378 | put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p); |
fdbdb595 RM |
379 | ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, |
380 | &match, ofpacts_p, &binding->header_.uuid); | |
40128e37 RM |
381 | |
382 | /* Table 34, Priority 100. | |
383 | * ======================= | |
384 | * | |
bf143492 JP |
385 | * Drop packets whose logical inport and outport are the same |
386 | * and the MLF_ALLOW_LOOPBACK flag is not set. */ | |
40128e37 RM |
387 | match_init_catchall(&match); |
388 | ofpbuf_clear(ofpacts_p); | |
389 | match_set_metadata(&match, htonll(dp_key)); | |
bf143492 JP |
390 | match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0, |
391 | 0, MLF_ALLOW_LOOPBACK); | |
40128e37 RM |
392 | match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, port_key); |
393 | match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); | |
bf143492 | 394 | ofctrl_add_flow(OFTABLE_CHECK_LOOPBACK, 100, |
fdbdb595 | 395 | &match, ofpacts_p, &binding->header_.uuid); |
40128e37 RM |
396 | |
397 | /* Table 64, Priority 100. | |
bf143492 JP |
398 | * ======================= |
399 | * | |
400 | * If the packet is supposed to hair-pin because the "loopback" | |
401 | * flag is set, temporarily set the in_port to zero, resubmit to | |
402 | * table 65 for logical-to-physical translation, then restore | |
403 | * the port number. */ | |
404 | match_init_catchall(&match); | |
405 | ofpbuf_clear(ofpacts_p); | |
406 | match_set_metadata(&match, htonll(dp_key)); | |
407 | match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0, | |
408 | MLF_ALLOW_LOOPBACK, MLF_ALLOW_LOOPBACK); | |
409 | match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); | |
410 | ||
411 | put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p)); | |
412 | put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p); | |
413 | put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p); | |
414 | put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p)); | |
415 | ofctrl_add_flow(OFTABLE_SAVE_INPORT, 100, | |
416 | &match, ofpacts_p, &binding->header_.uuid); | |
417 | ||
418 | /* Table 65, Priority 100. | |
40128e37 RM |
419 | * ======================= |
420 | * | |
421 | * Deliver the packet to the local vif. */ | |
422 | match_init_catchall(&match); | |
423 | ofpbuf_clear(ofpacts_p); | |
424 | match_set_metadata(&match, htonll(dp_key)); | |
425 | match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); | |
426 | if (tag) { | |
427 | /* For containers sitting behind a local vif, tag the packets | |
428 | * before delivering them. */ | |
429 | struct ofpact_vlan_vid *vlan_vid; | |
430 | vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts_p); | |
431 | vlan_vid->vlan_vid = tag; | |
432 | vlan_vid->push_vlan_if_needed = true; | |
433 | ||
434 | /* A packet might need to hair-pin back into its ingress | |
435 | * OpenFlow port (to a different logical port, which we already | |
436 | * checked back in table 34), so set the in_port to zero. */ | |
437 | put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p)); | |
438 | put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p); | |
439 | } | |
440 | ofpact_put_OUTPUT(ofpacts_p)->port = ofport; | |
441 | if (tag) { | |
442 | /* Revert the tag added to the packets headed to containers | |
443 | * in the previous step. If we don't do this, the packets | |
444 | * that are to be broadcasted to a VM in the same logical | |
445 | * switch will also contain the tag. Also revert the zero'd | |
446 | * in_port. */ | |
447 | ofpact_put_STRIP_VLAN(ofpacts_p); | |
448 | put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p)); | |
449 | } | |
fdbdb595 RM |
450 | ofctrl_add_flow(OFTABLE_LOG_TO_PHY, 100, |
451 | &match, ofpacts_p, &binding->header_.uuid); | |
40128e37 RM |
452 | } else if (!tun) { |
453 | /* Remote port connected by localnet port */ | |
454 | /* Table 33, priority 100. | |
455 | * ======================= | |
456 | * | |
457 | * Implements switching to localnet port. Each flow matches a | |
458 | * logical output port on remote hypervisor, switch the output port | |
459 | * to connected localnet port and resubmits to same table. | |
460 | */ | |
461 | ||
462 | match_init_catchall(&match); | |
463 | ofpbuf_clear(ofpacts_p); | |
464 | ||
465 | /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ | |
466 | match_set_metadata(&match, htonll(dp_key)); | |
467 | match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); | |
468 | ||
469 | put_load(localnet_port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p); | |
470 | ||
471 | /* Resubmit to table 33. */ | |
472 | put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p); | |
fdbdb595 RM |
473 | ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, |
474 | &match, ofpacts_p, &binding->header_.uuid); | |
40128e37 RM |
475 | } else { |
476 | /* Remote port connected by tunnel */ | |
477 | /* Table 32, priority 100. | |
478 | * ======================= | |
479 | * | |
480 | * Implements output to remote hypervisors. Each flow matches an | |
481 | * output port that includes a logical port on a remote hypervisor, | |
482 | * and tunnels the packet to that hypervisor. | |
483 | */ | |
484 | ||
485 | match_init_catchall(&match); | |
486 | ofpbuf_clear(ofpacts_p); | |
487 | ||
488 | /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ | |
489 | match_set_metadata(&match, htonll(dp_key)); | |
490 | match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); | |
491 | ||
492 | put_encapsulation(mff_ovn_geneve, tun, binding->datapath, | |
493 | port_key, ofpacts_p); | |
494 | ||
495 | /* Output to tunnel. */ | |
496 | ofpact_put_OUTPUT(ofpacts_p)->port = ofport; | |
fdbdb595 RM |
497 | ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 100, |
498 | &match, ofpacts_p, &binding->header_.uuid); | |
40128e37 RM |
499 | } |
500 | } | |
501 | ||
a2ba3806 | 502 | static void |
fdbdb595 | 503 | consider_mc_group(enum mf_field_id mff_ovn_geneve, |
a2ba3806 RM |
504 | const struct simap *ct_zones, |
505 | struct hmap *local_datapaths, | |
506 | const struct sbrec_multicast_group *mc, | |
507 | struct ofpbuf *ofpacts_p, | |
508 | struct ofpbuf *remote_ofpacts_p) | |
509 | { | |
510 | struct sset remote_chassis = SSET_INITIALIZER(&remote_chassis); | |
511 | struct match match; | |
512 | ||
513 | match_init_catchall(&match); | |
514 | match_set_metadata(&match, htonll(mc->datapath->tunnel_key)); | |
515 | match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, mc->tunnel_key); | |
516 | ||
517 | /* Go through all of the ports in the multicast group: | |
518 | * | |
519 | * - For remote ports, add the chassis to 'remote_chassis'. | |
520 | * | |
521 | * - For local ports (other than logical patch ports), add actions | |
522 | * to 'ofpacts_p' to set the output port and resubmit. | |
523 | * | |
524 | * - For logical patch ports, add actions to 'remote_ofpacts_p' | |
525 | * instead. (If we put them in 'ofpacts', then the output | |
526 | * would happen on every hypervisor in the multicast group, | |
527 | * effectively duplicating the packet.) | |
528 | */ | |
529 | ofpbuf_clear(ofpacts_p); | |
530 | ofpbuf_clear(remote_ofpacts_p); | |
531 | for (size_t i = 0; i < mc->n_ports; i++) { | |
532 | struct sbrec_port_binding *port = mc->ports[i]; | |
533 | ||
534 | if (port->datapath != mc->datapath) { | |
535 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); | |
536 | VLOG_WARN_RL(&rl, UUID_FMT": multicast group contains ports " | |
537 | "in wrong datapath", | |
538 | UUID_ARGS(&mc->header_.uuid)); | |
539 | continue; | |
540 | } | |
541 | ||
542 | int zone_id = simap_get(ct_zones, port->logical_port); | |
543 | if (zone_id) { | |
544 | put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p); | |
545 | } | |
546 | ||
547 | if (!strcmp(port->type, "patch")) { | |
548 | put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, | |
549 | remote_ofpacts_p); | |
bf143492 | 550 | put_resubmit(OFTABLE_CHECK_LOOPBACK, remote_ofpacts_p); |
a2ba3806 RM |
551 | } else if (simap_contains(&localvif_to_ofport, |
552 | (port->parent_port && *port->parent_port) | |
553 | ? port->parent_port : port->logical_port)) { | |
554 | put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p); | |
bf143492 | 555 | put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p); |
a2ba3806 RM |
556 | } else if (port->chassis && !get_localnet_port(local_datapaths, |
557 | mc->datapath->tunnel_key)) { | |
558 | /* Add remote chassis only when localnet port not exist, | |
559 | * otherwise multicast will reach remote ports through localnet | |
560 | * port. */ | |
561 | sset_add(&remote_chassis, port->chassis->name); | |
562 | } | |
563 | } | |
564 | ||
565 | /* Table 33, priority 100. | |
566 | * ======================= | |
567 | * | |
568 | * Handle output to the local logical ports in the multicast group, if | |
569 | * any. */ | |
570 | bool local_ports = ofpacts_p->size > 0; | |
571 | if (local_ports) { | |
572 | /* Following delivery to local logical ports, restore the multicast | |
573 | * group as the logical output port. */ | |
574 | put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p); | |
575 | ||
fdbdb595 RM |
576 | ofctrl_add_flow(OFTABLE_LOCAL_OUTPUT, 100, |
577 | &match, ofpacts_p, &mc->header_.uuid); | |
a2ba3806 RM |
578 | } |
579 | ||
580 | /* Table 32, priority 100. | |
581 | * ======================= | |
582 | * | |
583 | * Handle output to the remote chassis in the multicast group, if | |
584 | * any. */ | |
585 | if (!sset_is_empty(&remote_chassis) || remote_ofpacts_p->size > 0) { | |
586 | if (remote_ofpacts_p->size > 0) { | |
587 | /* Following delivery to logical patch ports, restore the | |
588 | * multicast group as the logical output port. */ | |
589 | put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, | |
590 | remote_ofpacts_p); | |
591 | } | |
592 | ||
593 | const char *chassis; | |
594 | const struct chassis_tunnel *prev = NULL; | |
595 | SSET_FOR_EACH (chassis, &remote_chassis) { | |
596 | const struct chassis_tunnel *tun | |
597 | = chassis_tunnel_find(chassis); | |
598 | if (!tun) { | |
599 | continue; | |
600 | } | |
601 | ||
602 | if (!prev || tun->type != prev->type) { | |
603 | put_encapsulation(mff_ovn_geneve, tun, mc->datapath, | |
604 | mc->tunnel_key, remote_ofpacts_p); | |
605 | prev = tun; | |
606 | } | |
607 | ofpact_put_OUTPUT(remote_ofpacts_p)->port = tun->ofport; | |
608 | } | |
609 | ||
610 | if (remote_ofpacts_p->size) { | |
611 | if (local_ports) { | |
612 | put_resubmit(OFTABLE_LOCAL_OUTPUT, remote_ofpacts_p); | |
613 | } | |
fdbdb595 RM |
614 | ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 100, |
615 | &match, remote_ofpacts_p, &mc->header_.uuid); | |
a2ba3806 RM |
616 | } |
617 | } | |
618 | sset_destroy(&remote_chassis); | |
619 | } | |
620 | ||
e71ac5cd | 621 | void |
5868eb24 BP |
622 | physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, |
623 | const struct ovsrec_bridge *br_int, const char *this_chassis_id, | |
fdbdb595 | 624 | const struct simap *ct_zones, |
c4f32696 | 625 | struct hmap *local_datapaths, struct hmap *patched_datapaths) |
e71ac5cd | 626 | { |
fdbdb595 RM |
627 | if (!hc_uuid) { |
628 | hc_uuid = xmalloc(sizeof(struct uuid)); | |
629 | uuid_generate(hc_uuid); | |
630 | } | |
631 | ||
ed48f0ed RM |
632 | /* This bool tracks physical mapping changes. */ |
633 | bool physical_map_changed = false; | |
634 | ||
ab39371d RM |
635 | struct simap new_localvif_to_ofport = |
636 | SIMAP_INITIALIZER(&new_localvif_to_ofport); | |
ed48f0ed RM |
637 | struct simap new_tunnel_to_ofport = |
638 | SIMAP_INITIALIZER(&new_tunnel_to_ofport); | |
422a9f73 BP |
639 | for (int i = 0; i < br_int->n_ports; i++) { |
640 | const struct ovsrec_port *port_rec = br_int->ports[i]; | |
641 | if (!strcmp(port_rec->name, br_int->name)) { | |
e71ac5cd BP |
642 | continue; |
643 | } | |
644 | ||
645 | const char *chassis_id = smap_get(&port_rec->external_ids, | |
646 | "ovn-chassis-id"); | |
4acc496e | 647 | if (chassis_id && !strcmp(chassis_id, this_chassis_id)) { |
e71ac5cd BP |
648 | continue; |
649 | } | |
650 | ||
c0281929 | 651 | const char *localnet = smap_get(&port_rec->external_ids, |
14f82efd | 652 | "ovn-localnet-port"); |
184bc3ca RB |
653 | const char *l2gateway = smap_get(&port_rec->external_ids, |
654 | "ovn-l2gateway-port"); | |
d387d24d BP |
655 | const char *logpatch = smap_get(&port_rec->external_ids, |
656 | "ovn-logical-patch-port"); | |
c0281929 | 657 | |
e71ac5cd BP |
658 | for (int j = 0; j < port_rec->n_interfaces; j++) { |
659 | const struct ovsrec_interface *iface_rec = port_rec->interfaces[j]; | |
660 | ||
661 | /* Get OpenFlow port number. */ | |
662 | if (!iface_rec->n_ofport) { | |
663 | continue; | |
664 | } | |
665 | int64_t ofport = iface_rec->ofport[0]; | |
666 | if (ofport < 1 || ofport > ofp_to_u16(OFPP_MAX)) { | |
667 | continue; | |
668 | } | |
669 | ||
d387d24d BP |
670 | /* Record as patch to local net, logical patch port, chassis, or |
671 | * local logical port. */ | |
672 | bool is_patch = !strcmp(iface_rec->type, "patch"); | |
673 | if (is_patch && localnet) { | |
e90aeb57 | 674 | /* localnet patch ports can be handled just like VIFs. */ |
ab39371d | 675 | simap_put(&new_localvif_to_ofport, localnet, ofport); |
c0281929 | 676 | break; |
184bc3ca RB |
677 | } else if (is_patch && l2gateway) { |
678 | /* L2 gateway patch ports can be handled just like VIFs. */ | |
ab39371d | 679 | simap_put(&new_localvif_to_ofport, l2gateway, ofport); |
184bc3ca | 680 | break; |
d387d24d BP |
681 | } else if (is_patch && logpatch) { |
682 | /* Logical patch ports can be handled just like VIFs. */ | |
ab39371d | 683 | simap_put(&new_localvif_to_ofport, logpatch, ofport); |
d387d24d | 684 | break; |
c0281929 | 685 | } else if (chassis_id) { |
5868eb24 BP |
686 | enum chassis_tunnel_type tunnel_type; |
687 | if (!strcmp(iface_rec->type, "geneve")) { | |
688 | tunnel_type = GENEVE; | |
689 | if (!mff_ovn_geneve) { | |
690 | continue; | |
691 | } | |
692 | } else if (!strcmp(iface_rec->type, "stt")) { | |
693 | tunnel_type = STT; | |
37910994 JP |
694 | } else if (!strcmp(iface_rec->type, "vxlan")) { |
695 | tunnel_type = VXLAN; | |
5868eb24 BP |
696 | } else { |
697 | continue; | |
698 | } | |
699 | ||
ed48f0ed RM |
700 | simap_put(&new_tunnel_to_ofport, chassis_id, ofport); |
701 | struct chassis_tunnel *tun = chassis_tunnel_find(chassis_id); | |
702 | if (tun) { | |
703 | /* If the tunnel's ofport has changed, update. */ | |
704 | if (tun->ofport != u16_to_ofp(ofport) || | |
705 | tun->type != tunnel_type) { | |
706 | tun->ofport = u16_to_ofp(ofport); | |
707 | tun->type = tunnel_type; | |
708 | physical_map_changed = true; | |
709 | } | |
710 | } else { | |
711 | tun = xmalloc(sizeof *tun); | |
712 | hmap_insert(&tunnels, &tun->hmap_node, | |
713 | hash_string(chassis_id, 0)); | |
714 | tun->chassis_id = chassis_id; | |
715 | tun->ofport = u16_to_ofp(ofport); | |
716 | tun->type = tunnel_type; | |
717 | physical_map_changed = true; | |
718 | } | |
e71ac5cd BP |
719 | break; |
720 | } else { | |
721 | const char *iface_id = smap_get(&iface_rec->external_ids, | |
722 | "iface-id"); | |
723 | if (iface_id) { | |
ab39371d | 724 | simap_put(&new_localvif_to_ofport, iface_id, ofport); |
e71ac5cd BP |
725 | } |
726 | } | |
727 | } | |
728 | } | |
729 | ||
ed48f0ed RM |
730 | /* Remove tunnels that are no longer here. */ |
731 | struct chassis_tunnel *tun, *tun_next; | |
732 | HMAP_FOR_EACH_SAFE (tun, tun_next, hmap_node, &tunnels) { | |
733 | if (!simap_find(&new_tunnel_to_ofport, tun->chassis_id)) { | |
734 | hmap_remove(&tunnels, &tun->hmap_node); | |
735 | physical_map_changed = true; | |
736 | free(tun); | |
737 | } | |
738 | } | |
739 | ||
ab39371d | 740 | /* Capture changed or removed openflow ports. */ |
ab39371d RM |
741 | struct simap_node *vif_name, *vif_name_next; |
742 | SIMAP_FOR_EACH_SAFE (vif_name, vif_name_next, &localvif_to_ofport) { | |
743 | int newport; | |
744 | if ((newport = simap_get(&new_localvif_to_ofport, vif_name->name))) { | |
745 | if (newport != simap_get(&localvif_to_ofport, vif_name->name)) { | |
746 | simap_put(&localvif_to_ofport, vif_name->name, newport); | |
ed48f0ed | 747 | physical_map_changed = true; |
ab39371d RM |
748 | } |
749 | } else { | |
750 | simap_find_and_delete(&localvif_to_ofport, vif_name->name); | |
ed48f0ed | 751 | physical_map_changed = true; |
ab39371d RM |
752 | } |
753 | } | |
754 | SIMAP_FOR_EACH (vif_name, &new_localvif_to_ofport) { | |
755 | if (!simap_get(&localvif_to_ofport, vif_name->name)) { | |
756 | simap_put(&localvif_to_ofport, vif_name->name, | |
757 | simap_get(&new_localvif_to_ofport, vif_name->name)); | |
ed48f0ed | 758 | physical_map_changed = true; |
ab39371d RM |
759 | } |
760 | } | |
ed48f0ed | 761 | if (physical_map_changed) { |
ab39371d RM |
762 | full_binding_processing = true; |
763 | ||
764 | /* Reprocess logical flow table immediately. */ | |
765 | lflow_reset_processing(); | |
766 | poll_immediate_wake(); | |
767 | } | |
768 | ||
e71ac5cd BP |
769 | struct ofpbuf ofpacts; |
770 | ofpbuf_init(&ofpacts, 0); | |
771 | ||
772 | /* Set up flows in table 0 for physical-to-logical translation and in table | |
773 | * 64 for logical-to-physical translation. */ | |
dcda6e0d | 774 | const struct sbrec_port_binding *binding; |
70c7cfef RM |
775 | if (full_binding_processing) { |
776 | SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { | |
1a58344b RM |
777 | /* Because it is possible in the above code to enter this |
778 | * for loop without having cleared the flow table first, we | |
779 | * should clear the old flows to avoid collisions. */ | |
780 | ofctrl_remove_flows(&binding->header_.uuid); | |
70c7cfef RM |
781 | consider_port_binding(mff_ovn_geneve, ct_zones, local_datapaths, |
782 | patched_datapaths, binding, &ofpacts); | |
783 | } | |
784 | full_binding_processing = false; | |
785 | } else { | |
786 | SBREC_PORT_BINDING_FOR_EACH_TRACKED (binding, ctx->ovnsb_idl) { | |
787 | if (sbrec_port_binding_is_deleted(binding)) { | |
788 | ofctrl_remove_flows(&binding->header_.uuid); | |
789 | } else { | |
790 | if (!sbrec_port_binding_is_new(binding)) { | |
791 | ofctrl_remove_flows(&binding->header_.uuid); | |
792 | } | |
793 | consider_port_binding(mff_ovn_geneve, ct_zones, local_datapaths, | |
794 | patched_datapaths, binding, &ofpacts); | |
795 | } | |
796 | } | |
5868eb24 BP |
797 | } |
798 | ||
799 | /* Handle output to multicast groups, in tables 32 and 33. */ | |
800 | const struct sbrec_multicast_group *mc; | |
0b7da177 BP |
801 | struct ofpbuf remote_ofpacts; |
802 | ofpbuf_init(&remote_ofpacts, 0); | |
5868eb24 | 803 | SBREC_MULTICAST_GROUP_FOR_EACH (mc, ctx->ovnsb_idl) { |
1a58344b RM |
804 | /* As multicast groups are always reprocessed each time, |
805 | * the first step is to clean the old flows for the group | |
806 | * so that we avoid warning messages on collisions. */ | |
807 | ofctrl_remove_flows(&mc->header_.uuid); | |
fdbdb595 | 808 | consider_mc_group(mff_ovn_geneve, ct_zones, |
a2ba3806 | 809 | local_datapaths, mc, &ofpacts, &remote_ofpacts); |
5868eb24 | 810 | } |
a2ba3806 | 811 | |
0b7da177 | 812 | ofpbuf_uninit(&remote_ofpacts); |
5868eb24 | 813 | |
1a58344b RM |
814 | /* Because flows using the hard-coded uuid are recalculated each |
815 | * cycle, let's first remove the old flows to avoid duplicate flow | |
816 | * warnings. */ | |
817 | ofctrl_remove_flows(hc_uuid); | |
818 | ||
5868eb24 BP |
819 | /* Table 0, priority 100. |
820 | * ====================== | |
821 | * | |
37910994 JP |
822 | * Process packets that arrive from a remote hypervisor (by matching |
823 | * on tunnel in_port). */ | |
824 | ||
825 | /* Add flows for Geneve and STT encapsulations. These | |
826 | * encapsulations have metadata about the ingress and egress logical | |
827 | * ports. We set MFF_LOG_DATAPATH, MFF_LOG_INPORT, and | |
828 | * MFF_LOG_OUTPORT from the tunnel key data, then resubmit to table | |
829 | * 33 to handle packets to the local hypervisor. */ | |
5868eb24 BP |
830 | HMAP_FOR_EACH (tun, hmap_node, &tunnels) { |
831 | struct match match = MATCH_CATCHALL_INITIALIZER; | |
832 | match_set_in_port(&match, tun->ofport); | |
833 | ||
834 | ofpbuf_clear(&ofpacts); | |
835 | if (tun->type == GENEVE) { | |
836 | put_move(MFF_TUN_ID, 0, MFF_LOG_DATAPATH, 0, 24, &ofpacts); | |
837 | put_move(mff_ovn_geneve, 16, MFF_LOG_INPORT, 0, 15, | |
838 | &ofpacts); | |
839 | put_move(mff_ovn_geneve, 0, MFF_LOG_OUTPORT, 0, 16, | |
840 | &ofpacts); | |
841 | } else if (tun->type == STT) { | |
842 | put_move(MFF_TUN_ID, 40, MFF_LOG_INPORT, 0, 15, &ofpacts); | |
843 | put_move(MFF_TUN_ID, 24, MFF_LOG_OUTPORT, 0, 16, &ofpacts); | |
844 | put_move(MFF_TUN_ID, 0, MFF_LOG_DATAPATH, 0, 24, &ofpacts); | |
37910994 JP |
845 | } else if (tun->type == VXLAN) { |
846 | /* We'll handle VXLAN later. */ | |
847 | continue; | |
5868eb24 BP |
848 | } else { |
849 | OVS_NOT_REACHED(); | |
26acc5a3 | 850 | } |
37910994 | 851 | |
5868eb24 BP |
852 | put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts); |
853 | ||
fdbdb595 RM |
854 | ofctrl_add_flow(OFTABLE_PHY_TO_LOG, 100, &match, &ofpacts, |
855 | hc_uuid); | |
e71ac5cd BP |
856 | } |
857 | ||
37910994 JP |
858 | /* Add flows for VXLAN encapsulations. Due to the limited amount of |
859 | * metadata, we only support VXLAN for connections to gateways. The | |
860 | * VNI is used to populate MFF_LOG_DATAPATH. The gateway's logical | |
861 | * port is set to MFF_LOG_INPORT. Then the packet is resubmitted to | |
862 | * table 16 to determine the logical egress port. | |
863 | * | |
864 | * xxx Due to resubmitting to table 16, broadcasts will be re-sent to | |
865 | * xxx all logical ports, including non-local ones which could cause | |
866 | * xxx duplicate packets to be received by multiply-connected gateways. */ | |
867 | HMAP_FOR_EACH (tun, hmap_node, &tunnels) { | |
868 | if (tun->type != VXLAN) { | |
869 | continue; | |
870 | } | |
871 | ||
872 | SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { | |
873 | struct match match = MATCH_CATCHALL_INITIALIZER; | |
874 | ||
875 | if (!binding->chassis || | |
876 | strcmp(tun->chassis_id, binding->chassis->name)) { | |
877 | continue; | |
878 | } | |
879 | ||
880 | match_set_in_port(&match, tun->ofport); | |
881 | match_set_tun_id(&match, htonll(binding->datapath->tunnel_key)); | |
882 | ||
883 | ofpbuf_clear(&ofpacts); | |
884 | put_move(MFF_TUN_ID, 0, MFF_LOG_DATAPATH, 0, 24, &ofpacts); | |
885 | put_load(binding->tunnel_key, MFF_LOG_INPORT, 0, 15, &ofpacts); | |
886 | put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts); | |
887 | ||
fdbdb595 | 888 | ofctrl_add_flow(OFTABLE_PHY_TO_LOG, 100, &match, &ofpacts, hc_uuid); |
37910994 JP |
889 | } |
890 | } | |
891 | ||
3e1c86d8 JP |
892 | /* Table 32, Priority 0. |
893 | * ======================= | |
894 | * | |
895 | * Resubmit packets that are not directed at tunnels or part of a | |
896 | * multicast group to the local output table. */ | |
897 | struct match match; | |
898 | match_init_catchall(&match); | |
899 | ofpbuf_clear(&ofpacts); | |
900 | put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts); | |
fdbdb595 | 901 | ofctrl_add_flow(OFTABLE_REMOTE_OUTPUT, 0, &match, &ofpacts, hc_uuid); |
3e1c86d8 | 902 | |
5868eb24 BP |
903 | /* Table 34, Priority 0. |
904 | * ======================= | |
905 | * | |
906 | * Resubmit packets that don't output to the ingress port (already checked | |
907 | * in table 33) to the logical egress pipeline, clearing the logical | |
908 | * registers (for consistent behavior with packets that get tunneled). */ | |
5868eb24 BP |
909 | match_init_catchall(&match); |
910 | ofpbuf_clear(&ofpacts); | |
98926186 BP |
911 | for (int i = 0; i < MFF_N_LOG_REGS; i++) { |
912 | put_load(0, MFF_REG0 + i, 0, 32, &ofpacts); | |
913 | } | |
5868eb24 | 914 | put_resubmit(OFTABLE_LOG_EGRESS_PIPELINE, &ofpacts); |
bf143492 JP |
915 | ofctrl_add_flow(OFTABLE_CHECK_LOOPBACK, 0, &match, &ofpacts, hc_uuid); |
916 | ||
917 | /* Table 64, Priority 0. | |
918 | * ======================= | |
919 | * | |
920 | * Resubmit packets that do not have the MLF_ALLOW_LOOPBACK flag set | |
921 | * to table 65 for logical-to-physical translation. */ | |
922 | match_init_catchall(&match); | |
923 | ofpbuf_clear(&ofpacts); | |
924 | put_resubmit(OFTABLE_LOG_TO_PHY, &ofpacts); | |
925 | ofctrl_add_flow(OFTABLE_SAVE_INPORT, 0, &match, &ofpacts, hc_uuid); | |
5868eb24 | 926 | |
e71ac5cd | 927 | ofpbuf_uninit(&ofpacts); |
ab39371d RM |
928 | |
929 | simap_destroy(&new_localvif_to_ofport); | |
ed48f0ed | 930 | simap_destroy(&new_tunnel_to_ofport); |
e71ac5cd | 931 | } |