Branch data Line data Source code
1 : : /* Copyright (c) 2015, 2016 Nicira, Inc.
2 : : *
3 : : * Licensed under the Apache License, Version 2.0 (the "License");
4 : : * you may not use this file except in compliance with the License.
5 : : * You may obtain a copy of the License at:
6 : : *
7 : : * http://www.apache.org/licenses/LICENSE-2.0
8 : : *
9 : : * Unless required by applicable law or agreed to in writing, software
10 : : * distributed under the License is distributed on an "AS IS" BASIS,
11 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : * See the License for the specific language governing permissions and
13 : : * limitations under the License.
14 : : */
15 : :
16 : : #include <config.h>
17 : : #include "lflow.h"
18 : : #include "lport.h"
19 : : #include "ofctrl.h"
20 : : #include "openvswitch/dynamic-string.h"
21 : : #include "openvswitch/ofp-actions.h"
22 : : #include "openvswitch/ofpbuf.h"
23 : : #include "openvswitch/vlog.h"
24 : : #include "ovn-controller.h"
25 : : #include "ovn/actions.h"
26 : : #include "ovn/expr.h"
27 : : #include "ovn/lib/ovn-dhcp.h"
28 : : #include "ovn/lib/ovn-sb-idl.h"
29 : : #include "packets.h"
30 : : #include "physical.h"
31 : : #include "simap.h"
32 : : #include "sset.h"
33 : :
34 : 94 : VLOG_DEFINE_THIS_MODULE(lflow);
35 : :
36 : : /* Symbol table. */
37 : :
38 : : /* Contains "struct expr_symbol"s for fields supported by OVN lflows. */
39 : : static struct shash symtab;
40 : :
41 : : void
42 : 44 : lflow_init(void)
43 : : {
44 : 44 : ovn_init_symtab(&symtab);
45 : 44 : }
46 : :
47 : : /* Iterate address sets in the southbound database. Create and update the
48 : : * corresponding symtab entries as necessary. */
49 : : static void
50 : 3167 : update_address_sets(struct controller_ctx *ctx,
51 : : struct shash *expr_address_sets_p)
52 : :
53 : : {
54 : : const struct sbrec_address_set *as;
55 [ + + ]: 3278 : SBREC_ADDRESS_SET_FOR_EACH (as, ctx->ovnsb_idl) {
56 : 111 : expr_macros_add(expr_address_sets_p, as->name,
57 : 111 : (const char *const *) as->addresses, as->n_addresses);
58 : : }
59 : 3167 : }
60 : :
61 : : struct lookup_port_aux {
62 : : const struct lport_index *lports;
63 : : const struct mcgroup_index *mcgroups;
64 : : const struct sbrec_datapath_binding *dp;
65 : : };
66 : :
67 : : static void consider_logical_flow(const struct lport_index *lports,
68 : : const struct mcgroup_index *mcgroups,
69 : : const struct sbrec_logical_flow *lflow,
70 : : const struct hmap *local_datapaths,
71 : : const struct hmap *patched_datapaths,
72 : : struct group_table *group_table,
73 : : const struct simap *ct_zones,
74 : : struct hmap *dhcp_opts_p,
75 : : struct hmap *dhcpv6_opts_p,
76 : : uint32_t *conj_id_ofs_p,
77 : : struct hmap *flow_table,
78 : : struct shash *expr_address_sets_p);
79 : :
80 : : static bool
81 : 378793 : lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
82 : : {
83 : 378793 : const struct lookup_port_aux *aux = aux_;
84 : :
85 : 378793 : const struct sbrec_port_binding *pb
86 : 378793 : = lport_lookup_by_name(aux->lports, port_name);
87 [ + + ][ + - ]: 378793 : if (pb && pb->datapath == aux->dp) {
88 : 368549 : *portp = pb->tunnel_key;
89 : 368549 : return true;
90 : : }
91 : :
92 : 10244 : const struct sbrec_multicast_group *mg
93 : 10244 : = mcgroup_lookup_by_dp_name(aux->mcgroups, aux->dp, port_name);
94 [ + - ]: 10244 : if (mg) {
95 : 10244 : *portp = mg->tunnel_key;
96 : 10244 : return true;
97 : : }
98 : :
99 : 0 : return false;
100 : : }
101 : :
102 : : static bool
103 : 1293245 : is_switch(const struct sbrec_datapath_binding *ldp)
104 : : {
105 : 1293245 : return smap_get(&ldp->external_ids, "logical-switch") != NULL;
106 : :
107 : : }
108 : :
109 : : /* Adds the logical flows from the Logical_Flow table to flow tables. */
110 : : static void
111 : 3167 : add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
112 : : const struct mcgroup_index *mcgroups,
113 : : const struct hmap *local_datapaths,
114 : : const struct hmap *patched_datapaths,
115 : : struct group_table *group_table,
116 : : const struct simap *ct_zones,
117 : : struct hmap *flow_table,
118 : : struct shash *expr_address_sets_p)
119 : : {
120 : 3167 : uint32_t conj_id_ofs = 1;
121 : : const struct sbrec_logical_flow *lflow;
122 : :
123 : 3167 : struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
124 : 3167 : struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
125 : : const struct sbrec_dhcp_options *dhcp_opt_row;
126 [ + + ]: 75671 : SBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opt_row, ctx->ovnsb_idl) {
127 : 72504 : dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
128 : : dhcp_opt_row->type);
129 : : }
130 : :
131 : :
132 : : const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
133 [ + + ]: 15251 : SBREC_DHCPV6_OPTIONS_FOR_EACH(dhcpv6_opt_row, ctx->ovnsb_idl) {
134 : 12084 : dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name, dhcpv6_opt_row->code,
135 : : dhcpv6_opt_row->type);
136 : : }
137 : :
138 [ + + ]: 665440 : SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) {
139 : 662273 : consider_logical_flow(lports, mcgroups, lflow, local_datapaths,
140 : : patched_datapaths, group_table, ct_zones,
141 : : &dhcp_opts, &dhcpv6_opts, &conj_id_ofs,
142 : : flow_table, expr_address_sets_p);
143 : : }
144 : :
145 : 3167 : dhcp_opts_destroy(&dhcp_opts);
146 : 3167 : dhcp_opts_destroy(&dhcpv6_opts);
147 : 3167 : }
148 : :
149 : : static void
150 : 662273 : consider_logical_flow(const struct lport_index *lports,
151 : : const struct mcgroup_index *mcgroups,
152 : : const struct sbrec_logical_flow *lflow,
153 : : const struct hmap *local_datapaths,
154 : : const struct hmap *patched_datapaths,
155 : : struct group_table *group_table,
156 : : const struct simap *ct_zones,
157 : : struct hmap *dhcp_opts_p,
158 : : struct hmap *dhcpv6_opts_p,
159 : : uint32_t *conj_id_ofs_p,
160 : : struct hmap *flow_table,
161 : : struct shash *expr_address_sets_p)
162 : : {
163 : : /* Determine translation of logical table IDs to physical table IDs. */
164 : 662273 : bool ingress = !strcmp(lflow->pipeline, "ingress");
165 : :
166 : 662273 : const struct sbrec_datapath_binding *ldp = lflow->logical_datapath;
167 [ - + ]: 662273 : if (!ldp) {
168 : 31301 : return;
169 : : }
170 [ + + ]: 662273 : if (is_switch(ldp)) {
171 : : /* For a logical switch datapath, local_datapaths tells us if there
172 : : * are any local ports for this datapath. If not, we can skip
173 : : * processing logical flows if that logical switch datapath is not
174 : : * patched to any logical router.
175 : : *
176 : : * Otherwise, we still need both ingress and egress pipeline
177 : : * because even if there are no local ports, we still may need to
178 : : * execute the ingress pipeline after a packet leaves a logical
179 : : * router and we need to do egress pipeline for a switch that
180 : : * is connected to only routers. Further optimization is possible,
181 : : * but not based on what we know with local_datapaths right now.
182 : : *
183 : : * A better approach would be a kind of "flood fill" algorithm:
184 : : *
185 : : * 1. Initialize set S to the logical datapaths that have a port
186 : : * located on the hypervisor.
187 : : *
188 : : * 2. For each patch port P in a logical datapath in S, add the
189 : : * logical datapath of the remote end of P to S. Iterate
190 : : * until S reaches a fixed point.
191 : : *
192 : : * This can be implemented in northd, which can generate the sets and
193 : : * save it on each port-binding record in SB, and ovn-controller can
194 : : * use the information directly. However, there can be update storms
195 : : * when a pair of patch ports are added/removed to connect/disconnect
196 : : * large lrouters and lswitches. This need to be studied further.
197 : : */
198 : :
199 [ + + ]: 395847 : if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) {
200 [ + + ]: 77089 : if (!get_patched_datapath(patched_datapaths,
201 : 77089 : ldp->tunnel_key)) {
202 : 31301 : return;
203 : : }
204 : : }
205 : : }
206 : :
207 : : /* Determine translation of logical table IDs to physical table IDs. */
208 [ + + ]: 630972 : uint8_t first_ptable = (ingress
209 : : ? OFTABLE_LOG_INGRESS_PIPELINE
210 : : : OFTABLE_LOG_EGRESS_PIPELINE);
211 : 630972 : uint8_t ptable = first_ptable + lflow->table_id;
212 [ + + ]: 630972 : uint8_t output_ptable = (ingress
213 : : ? OFTABLE_REMOTE_OUTPUT
214 : : : OFTABLE_SAVE_INPORT);
215 : :
216 : : /* Parse OVN logical actions.
217 : : *
218 : : * XXX Deny changes to 'outport' in egress pipeline. */
219 : : uint64_t ovnacts_stub[1024 / 8];
220 : 630972 : struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(ovnacts_stub);
221 : 1261944 : struct ovnact_parse_params pp = {
222 : : .symtab = &symtab,
223 : : .dhcp_opts = dhcp_opts_p,
224 : : .dhcpv6_opts = dhcpv6_opts_p,
225 : :
226 : : .n_tables = LOG_PIPELINE_LEN,
227 : 630972 : .cur_ltable = lflow->table_id,
228 : : };
229 : : struct expr *prereqs;
230 : : char *error;
231 : :
232 : 630972 : error = ovnacts_parse_string(lflow->actions, &pp, &ovnacts, &prereqs);
233 [ - + ]: 630972 : if (error) {
234 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
235 [ # # ]: 0 : VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s",
236 : : lflow->actions, error);
237 : 0 : free(error);
238 : 0 : ovnacts_free(ovnacts.data, ovnacts.size);
239 : 0 : ofpbuf_uninit(&ovnacts);
240 : 0 : return;
241 : : }
242 : :
243 : : /* Encode OVN logical actions into OpenFlow. */
244 : : uint64_t ofpacts_stub[1024 / 8];
245 : 630972 : struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
246 : 1261944 : struct lookup_port_aux aux = {
247 : : .lports = lports,
248 : : .mcgroups = mcgroups,
249 : 630972 : .dp = lflow->logical_datapath
250 : : };
251 : 1261944 : struct ovnact_encode_params ep = {
252 : : .lookup_port = lookup_port_cb,
253 : : .aux = &aux,
254 : 630972 : .is_switch = is_switch(ldp),
255 : : .ct_zones = ct_zones,
256 : : .group_table = group_table,
257 : :
258 : : .first_ptable = first_ptable,
259 : : .output_ptable = output_ptable,
260 : : .mac_bind_ptable = OFTABLE_MAC_BINDING,
261 : : };
262 : 630972 : ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
263 : 630972 : ovnacts_free(ovnacts.data, ovnacts.size);
264 : 630972 : ofpbuf_uninit(&ovnacts);
265 : :
266 : : /* Translate OVN match into table of OpenFlow matches. */
267 : : struct hmap matches;
268 : : struct expr *expr;
269 : :
270 : 630972 : expr = expr_parse_string(lflow->match, &symtab,
271 : : expr_address_sets_p, &error);
272 [ + - ]: 630972 : if (!error) {
273 [ + + ]: 630972 : if (prereqs) {
274 : 154913 : expr = expr_combine(EXPR_T_AND, expr, prereqs);
275 : 154913 : prereqs = NULL;
276 : : }
277 : 630972 : expr = expr_annotate(expr, &symtab, &error);
278 : : }
279 [ - + ]: 630972 : if (error) {
280 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
281 [ # # ]: 0 : VLOG_WARN_RL(&rl, "error parsing match \"%s\": %s",
282 : : lflow->match, error);
283 : 0 : expr_destroy(prereqs);
284 : 0 : ofpbuf_uninit(&ofpacts);
285 : 0 : free(error);
286 : 0 : return;
287 : : }
288 : :
289 : 630972 : expr = expr_simplify(expr);
290 : 630972 : expr = expr_normalize(expr);
291 : 630972 : uint32_t n_conjs = expr_to_matches(expr, lookup_port_cb, &aux,
292 : : &matches);
293 : 630972 : expr_destroy(expr);
294 : :
295 : : /* Prepare the OpenFlow matches for adding to the flow table. */
296 : : struct expr_match *m;
297 [ + + ][ - + ]: 1374662 : HMAP_FOR_EACH (m, hmap_node, &matches) {
298 : 743690 : match_set_metadata(&m->match,
299 : 743690 : htonll(lflow->logical_datapath->tunnel_key));
300 [ - + ]: 743690 : if (m->match.wc.masks.conj_id) {
301 : 0 : m->match.flow.conj_id += *conj_id_ofs_p;
302 : : }
303 [ + - ]: 743690 : if (!m->n) {
304 : 743690 : ofctrl_add_flow(flow_table, ptable, lflow->priority, &m->match,
305 : : &ofpacts);
306 : : } else {
307 : : uint64_t conj_stubs[64 / 8];
308 : : struct ofpbuf conj;
309 : :
310 : 0 : ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs);
311 [ # # ]: 0 : for (int i = 0; i < m->n; i++) {
312 : 0 : const struct cls_conjunction *src = &m->conjunctions[i];
313 : : struct ofpact_conjunction *dst;
314 : :
315 : 0 : dst = ofpact_put_CONJUNCTION(&conj);
316 : 0 : dst->id = src->id + *conj_id_ofs_p;
317 : 0 : dst->clause = src->clause;
318 : 0 : dst->n_clauses = src->n_clauses;
319 : : }
320 : 0 : ofctrl_add_flow(flow_table, ptable, lflow->priority, &m->match,
321 : : &conj);
322 : 0 : ofpbuf_uninit(&conj);
323 : : }
324 : : }
325 : :
326 : : /* Clean up. */
327 : 630972 : expr_matches_destroy(&matches);
328 : 630972 : ofpbuf_uninit(&ofpacts);
329 : 630972 : *conj_id_ofs_p += n_conjs;
330 : : }
331 : :
332 : : static void
333 : 60796 : put_load(const uint8_t *data, size_t len,
334 : : enum mf_field_id dst, int ofs, int n_bits,
335 : : struct ofpbuf *ofpacts)
336 : : {
337 : 60796 : struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
338 : : mf_from_id(dst), NULL,
339 : : NULL);
340 : 60796 : bitwise_copy(data, len, 0, sf->value, sf->field->n_bytes, ofs, n_bits);
341 : 60796 : bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits);
342 : 60796 : }
343 : :
344 : : static void
345 : 60800 : consider_neighbor_flow(const struct lport_index *lports,
346 : : const struct sbrec_mac_binding *b,
347 : : struct hmap *flow_table)
348 : : {
349 : 60800 : const struct sbrec_port_binding *pb
350 : 60800 : = lport_lookup_by_name(lports, b->logical_port);
351 [ - + ]: 60800 : if (!pb) {
352 : 4 : return;
353 : : }
354 : :
355 : : struct eth_addr mac;
356 [ + + ]: 60800 : if (!eth_addr_from_string(b->mac, &mac)) {
357 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
358 [ + - ]: 4 : VLOG_WARN_RL(&rl, "bad 'mac' %s", b->mac);
359 : 4 : return;
360 : : }
361 : :
362 : 60796 : struct match match = MATCH_CATCHALL_INITIALIZER;
363 [ + + ]: 60796 : if (strchr(b->ip, '.')) {
364 : : ovs_be32 ip;
365 [ - + ]: 60689 : if (!ip_parse(b->ip, &ip)) {
366 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
367 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
368 : 0 : return;
369 : : }
370 : 60689 : match_set_reg(&match, 0, ntohl(ip));
371 : : } else {
372 : : struct in6_addr ip6;
373 [ - + ]: 107 : if (!ipv6_parse(b->ip, &ip6)) {
374 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
375 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
376 : 0 : return;
377 : : }
378 : : ovs_be128 value;
379 : 107 : memcpy(&value, &ip6, sizeof(value));
380 : 107 : match_set_xxreg(&match, 0, ntoh128(value));
381 : : }
382 : :
383 : 60796 : match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
384 : 60796 : match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
385 : :
386 : : uint64_t stub[1024 / 8];
387 : 60796 : struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
388 : 60796 : put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
389 : 60796 : ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, &match, &ofpacts);
390 : 60796 : ofpbuf_uninit(&ofpacts);
391 : : }
392 : :
393 : : /* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN
394 : : * southbound database, using 'lports' to resolve logical port names to
395 : : * numbers. */
396 : : static void
397 : 3167 : add_neighbor_flows(struct controller_ctx *ctx,
398 : : const struct lport_index *lports,
399 : : struct hmap *flow_table)
400 : : {
401 : : const struct sbrec_mac_binding *b;
402 [ + + ]: 63967 : SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) {
403 : 60800 : consider_neighbor_flow(lports, b, flow_table);
404 : : }
405 : 3167 : }
406 : :
407 : : /* Translates logical flows in the Logical_Flow table in the OVN_SB database
408 : : * into OpenFlow flows. See ovn-architecture(7) for more information. */
409 : : void
410 : 3167 : lflow_run(struct controller_ctx *ctx, const struct lport_index *lports,
411 : : const struct mcgroup_index *mcgroups,
412 : : const struct hmap *local_datapaths,
413 : : const struct hmap *patched_datapaths,
414 : : struct group_table *group_table,
415 : : const struct simap *ct_zones,
416 : : struct hmap *flow_table)
417 : : {
418 : 3167 : struct shash expr_address_sets = SHASH_INITIALIZER(&expr_address_sets);
419 : :
420 : 3167 : update_address_sets(ctx, &expr_address_sets);
421 : 3167 : add_logical_flows(ctx, lports, mcgroups, local_datapaths,
422 : : patched_datapaths, group_table, ct_zones, flow_table,
423 : : &expr_address_sets);
424 : 3167 : add_neighbor_flows(ctx, lports, flow_table);
425 : :
426 : 3167 : expr_macros_destroy(&expr_address_sets);
427 : 3167 : shash_destroy(&expr_address_sets);
428 : 3167 : }
429 : :
430 : : void
431 : 44 : lflow_destroy(void)
432 : : {
433 : 44 : expr_symtab_destroy(&symtab);
434 : 44 : shash_destroy(&symtab);
435 : 44 : }
|