Branch data Line data Source code
1 : : /*
2 : : * Licensed under the Apache License, Version 2.0 (the "License");
3 : : * you may not use this file except in compliance with the License.
4 : : * You may obtain a copy of the License at:
5 : : *
6 : : * http://www.apache.org/licenses/LICENSE-2.0
7 : : *
8 : : * Unless required by applicable law or agreed to in writing, software
9 : : * distributed under the License is distributed on an "AS IS" BASIS,
10 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 : : * See the License for the specific language governing permissions and
12 : : * limitations under the License.
13 : : */
14 : :
15 : : #include <config.h>
16 : :
17 : : #include <getopt.h>
18 : : #include <stdlib.h>
19 : : #include <stdio.h>
20 : :
21 : : #include "bitmap.h"
22 : : #include "command-line.h"
23 : : #include "daemon.h"
24 : : #include "dirs.h"
25 : : #include "openvswitch/dynamic-string.h"
26 : : #include "fatal-signal.h"
27 : : #include "hash.h"
28 : : #include "openvswitch/hmap.h"
29 : : #include "openvswitch/json.h"
30 : : #include "ovn/lex.h"
31 : : #include "ovn/lib/ovn-dhcp.h"
32 : : #include "ovn/lib/ovn-nb-idl.h"
33 : : #include "ovn/lib/ovn-sb-idl.h"
34 : : #include "ovn/lib/ovn-util.h"
35 : : #include "packets.h"
36 : : #include "poll-loop.h"
37 : : #include "smap.h"
38 : : #include "sset.h"
39 : : #include "stream.h"
40 : : #include "stream-ssl.h"
41 : : #include "unixctl.h"
42 : : #include "util.h"
43 : : #include "uuid.h"
44 : : #include "openvswitch/vlog.h"
45 : :
46 : 84 : VLOG_DEFINE_THIS_MODULE(ovn_northd);
47 : :
48 : : static unixctl_cb_func ovn_northd_exit;
49 : :
50 : : struct northd_context {
51 : : struct ovsdb_idl *ovnnb_idl;
52 : : struct ovsdb_idl *ovnsb_idl;
53 : : struct ovsdb_idl_txn *ovnnb_txn;
54 : : struct ovsdb_idl_txn *ovnsb_txn;
55 : : };
56 : :
57 : : static const char *ovnnb_db;
58 : : static const char *ovnsb_db;
59 : :
60 : : #define MAC_ADDR_PREFIX 0x0A0000000000ULL
61 : : #define MAC_ADDR_SPACE 0xffffff
62 : :
63 : : /* MAC address management (macam) table of "struct eth_addr"s, that holds the
64 : : * MAC addresses allocated by the OVN ipam module. */
65 : : static struct hmap macam = HMAP_INITIALIZER(&macam);
66 : :
67 : : #define MAX_OVN_TAGS 4096
68 : :
69 : : /* Pipeline stages. */
70 : :
71 : : /* The two pipelines in an OVN logical flow table. */
72 : : enum ovn_pipeline {
73 : : P_IN, /* Ingress pipeline. */
74 : : P_OUT /* Egress pipeline. */
75 : : };
76 : :
77 : : /* The two purposes for which ovn-northd uses OVN logical datapaths. */
78 : : enum ovn_datapath_type {
79 : : DP_SWITCH, /* OVN logical switch. */
80 : : DP_ROUTER /* OVN logical router. */
81 : : };
82 : :
83 : : /* Returns an "enum ovn_stage" built from the arguments.
84 : : *
85 : : * (It's better to use ovn_stage_build() for type-safety reasons, but inline
86 : : * functions can't be used in enums or switch cases.) */
87 : : #define OVN_STAGE_BUILD(DP_TYPE, PIPELINE, TABLE) \
88 : : (((DP_TYPE) << 9) | ((PIPELINE) << 8) | (TABLE))
89 : :
90 : : /* A stage within an OVN logical switch or router.
91 : : *
92 : : * An "enum ovn_stage" indicates whether the stage is part of a logical switch
93 : : * or router, whether the stage is part of the ingress or egress pipeline, and
94 : : * the table within that pipeline. The first three components are combined to
95 : : * form the stage's full name, e.g. S_SWITCH_IN_PORT_SEC_L2,
96 : : * S_ROUTER_OUT_DELIVERY. */
97 : : enum ovn_stage {
98 : : #define PIPELINE_STAGES \
99 : : /* Logical switch ingress stages. */ \
100 : : PIPELINE_STAGE(SWITCH, IN, PORT_SEC_L2, 0, "ls_in_port_sec_l2") \
101 : : PIPELINE_STAGE(SWITCH, IN, PORT_SEC_IP, 1, "ls_in_port_sec_ip") \
102 : : PIPELINE_STAGE(SWITCH, IN, PORT_SEC_ND, 2, "ls_in_port_sec_nd") \
103 : : PIPELINE_STAGE(SWITCH, IN, PRE_ACL, 3, "ls_in_pre_acl") \
104 : : PIPELINE_STAGE(SWITCH, IN, PRE_LB, 4, "ls_in_pre_lb") \
105 : : PIPELINE_STAGE(SWITCH, IN, PRE_STATEFUL, 5, "ls_in_pre_stateful") \
106 : : PIPELINE_STAGE(SWITCH, IN, ACL, 6, "ls_in_acl") \
107 : : PIPELINE_STAGE(SWITCH, IN, LB, 7, "ls_in_lb") \
108 : : PIPELINE_STAGE(SWITCH, IN, STATEFUL, 8, "ls_in_stateful") \
109 : : PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 9, "ls_in_arp_rsp") \
110 : : PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 10, "ls_in_dhcp_options") \
111 : : PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 11, "ls_in_dhcp_response") \
112 : : PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 12, "ls_in_l2_lkup") \
113 : : \
114 : : /* Logical switch egress stages. */ \
115 : : PIPELINE_STAGE(SWITCH, OUT, PRE_LB, 0, "ls_out_pre_lb") \
116 : : PIPELINE_STAGE(SWITCH, OUT, PRE_ACL, 1, "ls_out_pre_acl") \
117 : : PIPELINE_STAGE(SWITCH, OUT, PRE_STATEFUL, 2, "ls_out_pre_stateful") \
118 : : PIPELINE_STAGE(SWITCH, OUT, LB, 3, "ls_out_lb") \
119 : : PIPELINE_STAGE(SWITCH, OUT, ACL, 4, "ls_out_acl") \
120 : : PIPELINE_STAGE(SWITCH, OUT, STATEFUL, 5, "ls_out_stateful") \
121 : : PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_IP, 6, "ls_out_port_sec_ip") \
122 : : PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2, 7, "ls_out_port_sec_l2") \
123 : : \
124 : : /* Logical router ingress stages. */ \
125 : : PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \
126 : : PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 1, "lr_in_ip_input") \
127 : : PIPELINE_STAGE(ROUTER, IN, DEFRAG, 2, "lr_in_defrag") \
128 : : PIPELINE_STAGE(ROUTER, IN, UNSNAT, 3, "lr_in_unsnat") \
129 : : PIPELINE_STAGE(ROUTER, IN, DNAT, 4, "lr_in_dnat") \
130 : : PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 5, "lr_in_ip_routing") \
131 : : PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 6, "lr_in_arp_resolve") \
132 : : PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 7, "lr_in_arp_request") \
133 : : \
134 : : /* Logical router egress stages. */ \
135 : : PIPELINE_STAGE(ROUTER, OUT, SNAT, 0, "lr_out_snat") \
136 : : PIPELINE_STAGE(ROUTER, OUT, DELIVERY, 1, "lr_out_delivery")
137 : :
138 : : #define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
139 : : S_##DP_TYPE##_##PIPELINE##_##STAGE \
140 : : = OVN_STAGE_BUILD(DP_##DP_TYPE, P_##PIPELINE, TABLE),
141 : : PIPELINE_STAGES
142 : : #undef PIPELINE_STAGE
143 : : };
144 : :
145 : : /* Due to various hard-coded priorities need to implement ACLs, the
146 : : * northbound database supports a smaller range of ACL priorities than
147 : : * are available to logical flows. This value is added to an ACL
148 : : * priority to determine the ACL's logical flow priority. */
149 : : #define OVN_ACL_PRI_OFFSET 1000
150 : :
151 : : #define REGBIT_CONNTRACK_DEFRAG "reg0[0]"
152 : : #define REGBIT_CONNTRACK_COMMIT "reg0[1]"
153 : : #define REGBIT_CONNTRACK_NAT "reg0[2]"
154 : : #define REGBIT_DHCP_OPTS_RESULT "reg0[3]"
155 : :
156 : : /* Returns an "enum ovn_stage" built from the arguments. */
157 : : static enum ovn_stage
158 : 316514 : ovn_stage_build(enum ovn_datapath_type dp_type, enum ovn_pipeline pipeline,
159 : : uint8_t table)
160 : : {
161 : 316514 : return OVN_STAGE_BUILD(dp_type, pipeline, table);
162 : : }
163 : :
164 : : /* Returns the pipeline to which 'stage' belongs. */
165 : : static enum ovn_pipeline
166 : 4650 : ovn_stage_get_pipeline(enum ovn_stage stage)
167 : : {
168 : 4650 : return (stage >> 8) & 1;
169 : : }
170 : :
171 : : /* Returns the table to which 'stage' belongs. */
172 : : static uint8_t
173 : 4650 : ovn_stage_get_table(enum ovn_stage stage)
174 : : {
175 : 4650 : return stage & 0xff;
176 : : }
177 : :
178 : : /* Returns a string name for 'stage'. */
179 : : static const char *
180 : 4650 : ovn_stage_to_str(enum ovn_stage stage)
181 : : {
182 [ + + + + : 4650 : switch (stage) {
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + - ]
183 : : #define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
184 : : case S_##DP_TYPE##_##PIPELINE##_##STAGE: return NAME;
185 : 4650 : PIPELINE_STAGES
186 : : #undef PIPELINE_STAGE
187 : 0 : default: return "<unknown>";
188 : : }
189 : : }
190 : :
191 : : /* Returns the type of the datapath to which a flow with the given 'stage' may
192 : : * be added. */
193 : : static enum ovn_datapath_type
194 : 320840 : ovn_stage_to_datapath_type(enum ovn_stage stage)
195 : : {
196 [ + + + + : 320840 : switch (stage) {
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + - ]
197 : : #define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
198 : : case S_##DP_TYPE##_##PIPELINE##_##STAGE: return DP_##DP_TYPE;
199 : 320840 : PIPELINE_STAGES
200 : : #undef PIPELINE_STAGE
201 : 0 : default: OVS_NOT_REACHED();
202 : : }
203 : : }
204 : :
205 : : static void
206 : 0 : usage(void)
207 : : {
208 : 0 : printf("\
209 : : %s: OVN northbound management daemon\n\
210 : : usage: %s [OPTIONS]\n\
211 : : \n\
212 : : Options:\n\
213 : : --ovnnb-db=DATABASE connect to ovn-nb database at DATABASE\n\
214 : : (default: %s)\n\
215 : : --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\n\
216 : : (default: %s)\n\
217 : : -h, --help display this help message\n\
218 : : -o, --options list available options\n\
219 : : -V, --version display version information\n\
220 : : ", program_name, program_name, default_nb_db(), default_sb_db());
221 : 0 : daemon_usage();
222 : 0 : vlog_usage();
223 : 0 : stream_usage("database", true, true, false);
224 : 0 : }
225 : :
226 : : struct tnlid_node {
227 : : struct hmap_node hmap_node;
228 : : uint32_t tnlid;
229 : : };
230 : :
231 : : static void
232 : 6428 : destroy_tnlids(struct hmap *tnlids)
233 : : {
234 : : struct tnlid_node *node;
235 [ + + ][ - + ]: 32280 : HMAP_FOR_EACH_POP (node, hmap_node, tnlids) {
[ + + ]
236 : 25852 : free(node);
237 : : }
238 : 6428 : hmap_destroy(tnlids);
239 : 6428 : }
240 : :
241 : : static void
242 : 25852 : add_tnlid(struct hmap *set, uint32_t tnlid)
243 : : {
244 : 25852 : struct tnlid_node *node = xmalloc(sizeof *node);
245 : 25852 : hmap_insert(set, &node->hmap_node, hash_int(tnlid, 0));
246 : 25852 : node->tnlid = tnlid;
247 : 25852 : }
248 : :
249 : : static bool
250 : 391 : tnlid_in_use(const struct hmap *set, uint32_t tnlid)
251 : : {
252 : : const struct tnlid_node *node;
253 [ + + ][ - + ]: 656 : HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash_int(tnlid, 0), set) {
254 [ - + ]: 265 : if (node->tnlid == tnlid) {
255 : 0 : return true;
256 : : }
257 : : }
258 : 391 : return false;
259 : : }
260 : :
261 : : static uint32_t
262 : 391 : allocate_tnlid(struct hmap *set, const char *name, uint32_t max,
263 : : uint32_t *hint)
264 : : {
265 [ + - ]: 391 : for (uint32_t tnlid = *hint + 1; tnlid != *hint;
266 [ # # ]: 0 : tnlid = tnlid + 1 <= max ? tnlid + 1 : 1) {
267 [ + - ]: 391 : if (!tnlid_in_use(set, tnlid)) {
268 : 391 : add_tnlid(set, tnlid);
269 : 391 : *hint = tnlid;
270 : 391 : return tnlid;
271 : : }
272 : : }
273 : :
274 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
275 [ # # ]: 0 : VLOG_WARN_RL(&rl, "all %s tunnel ids exhausted", name);
276 : 0 : return 0;
277 : : }
278 : :
279 : : /* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or
280 : : * sb->external_ids:logical-switch. */
281 : : struct ovn_datapath {
282 : : struct hmap_node key_node; /* Index on 'key'. */
283 : : struct uuid key; /* (nbs/nbr)->header_.uuid. */
284 : :
285 : : const struct nbrec_logical_switch *nbs; /* May be NULL. */
286 : : const struct nbrec_logical_router *nbr; /* May be NULL. */
287 : : const struct sbrec_datapath_binding *sb; /* May be NULL. */
288 : :
289 : : struct ovs_list list; /* In list of similar records. */
290 : :
291 : : /* Logical switch data. */
292 : : struct ovn_port **router_ports;
293 : : size_t n_router_ports;
294 : :
295 : : struct hmap port_tnlids;
296 : : uint32_t port_key_hint;
297 : :
298 : : bool has_unknown;
299 : :
300 : : /* IPAM data. */
301 : : struct hmap ipam;
302 : : };
303 : :
304 : : struct macam_node {
305 : : struct hmap_node hmap_node;
306 : : struct eth_addr mac_addr; /* Allocated MAC address. */
307 : : };
308 : :
309 : : static void
310 : 2244 : cleanup_macam(struct hmap *macam)
311 : : {
312 : : struct macam_node *node;
313 [ + + ][ - + ]: 4192 : HMAP_FOR_EACH_POP (node, hmap_node, macam) {
[ + + ]
314 : 1948 : free(node);
315 : : }
316 : 2244 : }
317 : :
318 : : struct ipam_node {
319 : : struct hmap_node hmap_node;
320 : : uint32_t ip_addr; /* Allocated IP address. */
321 : : };
322 : :
323 : : static void
324 : 6328 : destroy_ipam(struct hmap *ipam)
325 : : {
326 : : struct ipam_node *node;
327 [ + + ][ - + ]: 8224 : HMAP_FOR_EACH_POP (node, hmap_node, ipam) {
[ + + ]
328 : 1896 : free(node);
329 : : }
330 : 6328 : hmap_destroy(ipam);
331 : 6328 : }
332 : :
333 : : static struct ovn_datapath *
334 : 6328 : ovn_datapath_create(struct hmap *datapaths, const struct uuid *key,
335 : : const struct nbrec_logical_switch *nbs,
336 : : const struct nbrec_logical_router *nbr,
337 : : const struct sbrec_datapath_binding *sb)
338 : : {
339 : 6328 : struct ovn_datapath *od = xzalloc(sizeof *od);
340 : 6328 : od->key = *key;
341 : 6328 : od->sb = sb;
342 : 6328 : od->nbs = nbs;
343 : 6328 : od->nbr = nbr;
344 : 6328 : hmap_init(&od->port_tnlids);
345 : 6328 : hmap_init(&od->ipam);
346 : 6328 : od->port_key_hint = 0;
347 : 6328 : hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key));
348 : 6328 : return od;
349 : : }
350 : :
351 : : static void
352 : 6328 : ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od)
353 : : {
354 [ + - ]: 6328 : if (od) {
355 : : /* Don't remove od->list. It is used within build_datapaths() as a
356 : : * private list and once we've exited that function it is not safe to
357 : : * use it. */
358 : 6328 : hmap_remove(datapaths, &od->key_node);
359 : 6328 : destroy_tnlids(&od->port_tnlids);
360 : 6328 : destroy_ipam(&od->ipam);
361 : 6328 : free(od->router_ports);
362 : 6328 : free(od);
363 : : }
364 : 6328 : }
365 : :
366 : : /* Returns 'od''s datapath type. */
367 : : static enum ovn_datapath_type
368 : 320840 : ovn_datapath_get_type(const struct ovn_datapath *od)
369 : : {
370 : 320840 : return od->nbs ? DP_SWITCH : DP_ROUTER;
371 : : }
372 : :
373 : : static struct ovn_datapath *
374 : 334981 : ovn_datapath_find(struct hmap *datapaths, const struct uuid *uuid)
375 : : {
376 : : struct ovn_datapath *od;
377 : :
378 [ + + ][ - + ]: 334981 : HMAP_FOR_EACH_WITH_HASH (od, key_node, uuid_hash(uuid), datapaths) {
379 [ + - ]: 328468 : if (uuid_equals(uuid, &od->key)) {
380 : 328468 : return od;
381 : : }
382 : : }
383 : 6513 : return NULL;
384 : : }
385 : :
386 : : static struct ovn_datapath *
387 : 322430 : ovn_datapath_from_sbrec(struct hmap *datapaths,
388 : : const struct sbrec_datapath_binding *sb)
389 : : {
390 : : struct uuid key;
391 : :
392 [ + + - + ]: 433915 : if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) &&
393 : 111485 : !smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
394 : 0 : return NULL;
395 : : }
396 : 322430 : return ovn_datapath_find(datapaths, &key);
397 : : }
398 : :
399 : : static bool
400 : 1763 : lrouter_is_enabled(const struct nbrec_logical_router *lrouter)
401 : : {
402 [ + + ][ - + ]: 1763 : return !lrouter->enabled || *lrouter->enabled;
403 : : }
404 : :
405 : : static void
406 : 2244 : join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
407 : : struct ovs_list *sb_only, struct ovs_list *nb_only,
408 : : struct ovs_list *both)
409 : : {
410 : 2244 : hmap_init(datapaths);
411 : 2244 : ovs_list_init(sb_only);
412 : 2244 : ovs_list_init(nb_only);
413 : 2244 : ovs_list_init(both);
414 : :
415 : : const struct sbrec_datapath_binding *sb, *sb_next;
416 [ + + ][ + + ]: 8472 : SBREC_DATAPATH_BINDING_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) {
417 : : struct uuid key;
418 [ + + - + ]: 7944 : if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) &&
419 : 1716 : !smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
420 : 0 : ovsdb_idl_txn_add_comment(
421 : : ctx->ovnsb_txn,
422 : : "deleting Datapath_Binding "UUID_FMT" that lacks "
423 : : "external-ids:logical-switch and "
424 : : "external-ids:logical-router",
425 : 0 : UUID_ARGS(&sb->header_.uuid));
426 : 0 : sbrec_datapath_binding_delete(sb);
427 : 0 : continue;
428 : : }
429 : :
430 [ - + ]: 6228 : if (ovn_datapath_find(datapaths, &key)) {
431 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
432 [ # # ]: 0 : VLOG_INFO_RL(
433 : : &rl, "deleting Datapath_Binding "UUID_FMT" with "
434 : : "duplicate external-ids:logical-switch/router "UUID_FMT,
435 : : UUID_ARGS(&sb->header_.uuid), UUID_ARGS(&key));
436 : 0 : sbrec_datapath_binding_delete(sb);
437 : 0 : continue;
438 : : }
439 : :
440 : 6228 : struct ovn_datapath *od = ovn_datapath_create(datapaths, &key,
441 : : NULL, NULL, sb);
442 : 6228 : ovs_list_push_back(sb_only, &od->list);
443 : : }
444 : :
445 : : const struct nbrec_logical_switch *nbs;
446 [ + + ]: 6826 : NBREC_LOGICAL_SWITCH_FOR_EACH (nbs, ctx->ovnnb_idl) {
447 : 4582 : struct ovn_datapath *od = ovn_datapath_find(datapaths,
448 : : &nbs->header_.uuid);
449 [ + + ]: 4582 : if (od) {
450 : 4510 : od->nbs = nbs;
451 : 4510 : ovs_list_remove(&od->list);
452 : 4510 : ovs_list_push_back(both, &od->list);
453 : : } else {
454 : 72 : od = ovn_datapath_create(datapaths, &nbs->header_.uuid,
455 : : nbs, NULL, NULL);
456 : 72 : ovs_list_push_back(nb_only, &od->list);
457 : : }
458 : : }
459 : :
460 : : const struct nbrec_logical_router *nbr;
461 [ + + ]: 4007 : NBREC_LOGICAL_ROUTER_FOR_EACH (nbr, ctx->ovnnb_idl) {
462 [ + + ]: 1763 : if (!lrouter_is_enabled(nbr)) {
463 : 22 : continue;
464 : : }
465 : :
466 : 1741 : struct ovn_datapath *od = ovn_datapath_find(datapaths,
467 : : &nbr->header_.uuid);
468 [ + + ]: 1741 : if (od) {
469 [ + - ]: 1713 : if (!od->nbs) {
470 : 1713 : od->nbr = nbr;
471 : 1713 : ovs_list_remove(&od->list);
472 : 1713 : ovs_list_push_back(both, &od->list);
473 : : } else {
474 : : /* Can't happen! */
475 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
476 [ # # ]: 0 : VLOG_WARN_RL(&rl,
477 : : "duplicate UUID "UUID_FMT" in OVN_Northbound",
478 : : UUID_ARGS(&nbr->header_.uuid));
479 : 0 : continue;
480 : : }
481 : : } else {
482 : 28 : od = ovn_datapath_create(datapaths, &nbr->header_.uuid,
483 : : NULL, nbr, NULL);
484 : 28 : ovs_list_push_back(nb_only, &od->list);
485 : : }
486 : : }
487 : 2244 : }
488 : :
489 : : static uint32_t
490 : 100 : ovn_datapath_allocate_key(struct hmap *dp_tnlids)
491 : : {
492 : : static uint32_t hint;
493 : 100 : return allocate_tnlid(dp_tnlids, "datapath", (1u << 24) - 1, &hint);
494 : : }
495 : :
496 : : /* Updates the southbound Datapath_Binding table so that it contains the
497 : : * logical switches and routers specified by the northbound database.
498 : : *
499 : : * Initializes 'datapaths' to contain a "struct ovn_datapath" for every logical
500 : : * switch and router. */
501 : : static void
502 : 2244 : build_datapaths(struct northd_context *ctx, struct hmap *datapaths)
503 : : {
504 : : struct ovs_list sb_only, nb_only, both;
505 : :
506 : 2244 : join_datapaths(ctx, datapaths, &sb_only, &nb_only, &both);
507 : :
508 [ + + ]: 2244 : if (!ovs_list_is_empty(&nb_only)) {
509 : : /* First index the in-use datapath tunnel IDs. */
510 : 100 : struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids);
511 : : struct ovn_datapath *od;
512 [ + + ]: 242 : LIST_FOR_EACH (od, list, &both) {
513 : 142 : add_tnlid(&dp_tnlids, od->sb->tunnel_key);
514 : : }
515 : :
516 : : /* Add southbound record for each unmatched northbound record. */
517 [ + + ]: 200 : LIST_FOR_EACH (od, list, &nb_only) {
518 : 100 : uint16_t tunnel_key = ovn_datapath_allocate_key(&dp_tnlids);
519 [ - + ]: 100 : if (!tunnel_key) {
520 : 0 : break;
521 : : }
522 : :
523 : 100 : od->sb = sbrec_datapath_binding_insert(ctx->ovnsb_txn);
524 : :
525 : : /* Get the logical-switch or logical-router UUID to set in
526 : : * external-ids. */
527 : : char uuid_s[UUID_LEN + 1];
528 : 100 : sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->key));
529 [ + + ]: 100 : const char *key = od->nbs ? "logical-switch" : "logical-router";
530 : :
531 : : /* Get name to set in external-ids. */
532 [ + + ]: 100 : const char *name = od->nbs ? od->nbs->name : od->nbr->name;
533 : :
534 : : /* Set external-ids. */
535 : 100 : struct smap ids = SMAP_INITIALIZER(&ids);
536 : 100 : smap_add(&ids, key, uuid_s);
537 [ + - ]: 100 : if (*name) {
538 : 100 : smap_add(&ids, "name", name);
539 : : }
540 : 100 : sbrec_datapath_binding_set_external_ids(od->sb, &ids);
541 : 100 : smap_destroy(&ids);
542 : :
543 : 100 : sbrec_datapath_binding_set_tunnel_key(od->sb, tunnel_key);
544 : : }
545 : 100 : destroy_tnlids(&dp_tnlids);
546 : : }
547 : :
548 : : /* Delete southbound records without northbound matches. */
549 : : struct ovn_datapath *od, *next;
550 [ + + ][ + + ]: 2249 : LIST_FOR_EACH_SAFE (od, next, list, &sb_only) {
551 : 5 : ovs_list_remove(&od->list);
552 : 5 : sbrec_datapath_binding_delete(od->sb);
553 : 5 : ovn_datapath_destroy(datapaths, od);
554 : : }
555 : 2244 : }
556 : :
557 : : struct ovn_port {
558 : : struct hmap_node key_node; /* Index on 'key'. */
559 : : char *key; /* nbs->name, nbr->name, sb->logical_port. */
560 : : char *json_key; /* 'key', quoted for use in JSON. */
561 : :
562 : : const struct sbrec_port_binding *sb; /* May be NULL. */
563 : :
564 : : /* Logical switch port data. */
565 : : const struct nbrec_logical_switch_port *nbsp; /* May be NULL. */
566 : :
567 : : struct lport_addresses *lsp_addrs; /* Logical switch port addresses. */
568 : : unsigned int n_lsp_addrs;
569 : :
570 : : struct lport_addresses *ps_addrs; /* Port security addresses. */
571 : : unsigned int n_ps_addrs;
572 : :
573 : : /* Logical router port data. */
574 : : const struct nbrec_logical_router_port *nbrp; /* May be NULL. */
575 : :
576 : : struct lport_addresses lrp_networks;
577 : :
578 : : /* The port's peer:
579 : : *
580 : : * - A switch port S of type "router" has a router port R as a peer,
581 : : * and R in turn has S has its peer.
582 : : *
583 : : * - Two connected logical router ports have each other as peer. */
584 : : struct ovn_port *peer;
585 : :
586 : : struct ovn_datapath *od;
587 : :
588 : : struct ovs_list list; /* In list of similar records. */
589 : : };
590 : :
591 : : static struct ovn_port *
592 : 25623 : ovn_port_create(struct hmap *ports, const char *key,
593 : : const struct nbrec_logical_switch_port *nbsp,
594 : : const struct nbrec_logical_router_port *nbrp,
595 : : const struct sbrec_port_binding *sb)
596 : : {
597 : 25623 : struct ovn_port *op = xzalloc(sizeof *op);
598 : :
599 : 25623 : struct ds json_key = DS_EMPTY_INITIALIZER;
600 : 25623 : json_string_escape(key, &json_key);
601 : 25623 : op->json_key = ds_steal_cstr(&json_key);
602 : :
603 : 25623 : op->key = xstrdup(key);
604 : 25623 : op->sb = sb;
605 : 25623 : op->nbsp = nbsp;
606 : 25623 : op->nbrp = nbrp;
607 : 25623 : hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
608 : 25623 : return op;
609 : : }
610 : :
611 : : static void
612 : 25623 : ovn_port_destroy(struct hmap *ports, struct ovn_port *port)
613 : : {
614 [ + - ]: 25623 : if (port) {
615 : : /* Don't remove port->list. It is used within build_ports() as a
616 : : * private list and once we've exited that function it is not safe to
617 : : * use it. */
618 : 25623 : hmap_remove(ports, &port->key_node);
619 : :
620 [ + + ]: 45435 : for (int i = 0; i < port->n_lsp_addrs; i++) {
621 : 19812 : destroy_lport_addresses(&port->lsp_addrs[i]);
622 : : }
623 : 25623 : free(port->lsp_addrs);
624 : :
625 [ + + ]: 27176 : for (int i = 0; i < port->n_ps_addrs; i++) {
626 : 1553 : destroy_lport_addresses(&port->ps_addrs[i]);
627 : : }
628 : 25623 : free(port->ps_addrs);
629 : :
630 : 25623 : destroy_lport_addresses(&port->lrp_networks);
631 : 25623 : free(port->json_key);
632 : 25623 : free(port->key);
633 : 25623 : free(port);
634 : : }
635 : 25623 : }
636 : :
637 : : static struct ovn_port *
638 : 75403 : ovn_port_find(struct hmap *ports, const char *name)
639 : : {
640 : : struct ovn_port *op;
641 : :
642 [ + + ][ - + ]: 75403 : HMAP_FOR_EACH_WITH_HASH (op, key_node, hash_string(name, 0), ports) {
643 [ + - ]: 75013 : if (!strcmp(op->key, name)) {
644 : 75013 : return op;
645 : : }
646 : : }
647 : 390 : return NULL;
648 : : }
649 : :
650 : : static uint32_t
651 : 291 : ovn_port_allocate_key(struct ovn_datapath *od)
652 : : {
653 : 291 : return allocate_tnlid(&od->port_tnlids, "port",
654 : : (1u << 15) - 1, &od->port_key_hint);
655 : : }
656 : :
657 : : static bool
658 : 1954 : ipam_is_duplicate_mac(struct eth_addr *ea, uint64_t mac64, bool warn)
659 : : {
660 : : struct macam_node *macam_node;
661 [ + + ][ - + ]: 1954 : HMAP_FOR_EACH_WITH_HASH (macam_node, hmap_node, hash_uint64(mac64),
662 : : &macam) {
663 [ + - ]: 6 : if (eth_addr_equals(*ea, macam_node->mac_addr)) {
664 [ - + ]: 6 : if (warn) {
665 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
666 [ # # ]: 0 : VLOG_WARN_RL(&rl, "Duplicate MAC set: "ETH_ADDR_FMT,
667 : : ETH_ADDR_ARGS(macam_node->mac_addr));
668 : : }
669 : 6 : return true;
670 : : }
671 : : }
672 : 1948 : return false;
673 : : }
674 : :
675 : : static bool
676 : 2059 : ipam_is_duplicate_ip(struct ovn_datapath *od, uint32_t ip, bool warn)
677 : : {
678 : : struct ipam_node *ipam_node;
679 [ + + ][ - + ]: 2059 : HMAP_FOR_EACH_WITH_HASH (ipam_node, hmap_node, hash_int(ip, 0),
680 : : &od->ipam) {
681 [ + - ]: 163 : if (ipam_node->ip_addr == ip) {
682 [ - + ]: 163 : if (warn) {
683 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
684 [ # # ]: 0 : VLOG_WARN_RL(&rl, "Duplicate IP set: "IP_FMT,
685 : : IP_ARGS(htonl(ip)));
686 : : }
687 : 163 : return true;
688 : : }
689 : : }
690 : 1896 : return false;
691 : : }
692 : :
693 : : static void
694 : 24796 : ipam_insert_mac(struct eth_addr *ea, bool check)
695 : : {
696 [ - + ]: 24796 : if (!ea) {
697 : 0 : return;
698 : : }
699 : :
700 : 24796 : uint64_t mac64 = eth_addr_to_uint64(*ea);
701 : : /* If the new MAC was not assigned by this address management system or
702 : : * check is true and the new MAC is a duplicate, do not insert it into the
703 : : * macam hmap. */
704 [ + + ]: 24796 : if (((mac64 ^ MAC_ADDR_PREFIX) >> 24)
705 [ + + ][ - + ]: 1948 : || (check && ipam_is_duplicate_mac(ea, mac64, true))) {
706 : 22848 : return;
707 : : }
708 : :
709 : 1948 : struct macam_node *new_macam_node = xmalloc(sizeof *new_macam_node);
710 : 1948 : new_macam_node->mac_addr = *ea;
711 : 1948 : hmap_insert(&macam, &new_macam_node->hmap_node, hash_uint64(mac64));
712 : : }
713 : :
714 : : static void
715 : 1896 : ipam_insert_ip(struct ovn_datapath *od, uint32_t ip, bool check)
716 : : {
717 [ - + ]: 1896 : if (!od) {
718 : 0 : return;
719 : : }
720 : :
721 [ + + ][ - + ]: 1896 : if (check && ipam_is_duplicate_ip(od, ip, true)) {
722 : 0 : return;
723 : : }
724 : :
725 : 1896 : struct ipam_node *new_ipam_node = xmalloc(sizeof *new_ipam_node);
726 : 1896 : new_ipam_node->ip_addr = ip;
727 : 1896 : hmap_insert(&od->ipam, &new_ipam_node->hmap_node, hash_int(ip, 0));
728 : : }
729 : :
730 : : static void
731 : 23527 : ipam_insert_lsp_addresses(struct ovn_datapath *od, struct ovn_port *op,
732 : : char *address)
733 : : {
734 [ + - ][ + - ]: 23527 : if (!od || !op || !address || !strcmp(address, "unknown")
[ + - ][ + + ]
735 [ + + ]: 21618 : || !strcmp(address, "dynamic")) {
736 : 21608 : return;
737 : : }
738 : :
739 : : struct lport_addresses laddrs;
740 [ - + ]: 19795 : if (!extract_lsp_addresses(address, &laddrs)) {
741 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
742 [ # # ]: 0 : VLOG_WARN_RL(&rl, "Extract addresses failed.");
743 : 0 : return;
744 : : }
745 : 19795 : ipam_insert_mac(&laddrs.ea, true);
746 : :
747 : : /* IP is only added to IPAM if the switch's subnet option
748 : : * is set, whereas MAC is always added to MACAM. */
749 [ + + ]: 19795 : if (!smap_get(&od->nbs->other_config, "subnet")) {
750 : 17876 : destroy_lport_addresses(&laddrs);
751 : 17876 : return;
752 : : }
753 : :
754 [ + + ]: 3786 : for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) {
755 : 1867 : uint32_t ip = ntohl(laddrs.ipv4_addrs[j].addr);
756 : 1867 : ipam_insert_ip(od, ip, true);
757 : : }
758 : :
759 : 1919 : destroy_lport_addresses(&laddrs);
760 : : }
761 : :
762 : : static void
763 : 25610 : ipam_add_port_addresses(struct ovn_datapath *od, struct ovn_port *op)
764 : : {
765 [ + - ][ - + ]: 25610 : if (!od || !op) {
766 : 0 : return;
767 : : }
768 : :
769 [ + + ]: 25610 : if (op->nbsp) {
770 : : /* Add all the port's addresses to address data structures. */
771 [ + + ]: 42382 : for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
772 : 21744 : ipam_insert_lsp_addresses(od, op, op->nbsp->addresses[i]);
773 : : }
774 [ + + ]: 20638 : if (op->nbsp->dynamic_addresses) {
775 : 20638 : ipam_insert_lsp_addresses(od, op, op->nbsp->dynamic_addresses);
776 : : }
777 [ + - ]: 4972 : } else if (op->nbrp) {
778 : : struct lport_addresses lrp_networks;
779 [ - + ]: 4972 : if (!extract_lrp_networks(op->nbrp, &lrp_networks)) {
780 : : static struct vlog_rate_limit rl
781 : : = VLOG_RATE_LIMIT_INIT(1, 1);
782 [ # # ]: 0 : VLOG_WARN_RL(&rl, "Extract addresses failed.");
783 : 4972 : return;
784 : : }
785 : 4972 : ipam_insert_mac(&lrp_networks.ea, true);
786 : :
787 [ - + ][ # # ]: 4972 : if (!op->peer || !op->peer->nbsp || !op->peer->od || !op->peer->od->nbs
[ # # ][ # # ]
788 [ # # ]: 0 : || !smap_get(&op->peer->od->nbs->other_config, "subnet")) {
789 : 4972 : destroy_lport_addresses(&lrp_networks);
790 : 4972 : return;
791 : : }
792 : :
793 [ # # ]: 0 : for (size_t i = 0; i < lrp_networks.n_ipv4_addrs; i++) {
794 : 0 : uint32_t ip = ntohl(lrp_networks.ipv4_addrs[i].addr);
795 : 0 : ipam_insert_ip(op->peer->od, ip, true);
796 : : }
797 : :
798 : 0 : destroy_lport_addresses(&lrp_networks);
799 : : }
800 : : }
801 : :
802 : : static uint64_t
803 : 29 : ipam_get_unused_mac(void)
804 : : {
805 : : /* Stores the suffix of the most recently ipam-allocated MAC address. */
806 : : static uint32_t last_mac;
807 : :
808 : : uint64_t mac64;
809 : : struct eth_addr mac;
810 : : uint32_t mac_addr_suffix, i;
811 [ + - ]: 35 : for (i = 0; i < MAC_ADDR_SPACE - 1; i++) {
812 : : /* The tentative MAC's suffix will be in the interval (1, 0xfffffe). */
813 : 35 : mac_addr_suffix = ((last_mac + i) % (MAC_ADDR_SPACE - 1)) + 1;
814 : 35 : mac64 = MAC_ADDR_PREFIX | mac_addr_suffix;
815 : 35 : eth_addr_from_uint64(mac64, &mac);
816 [ + + ]: 35 : if (!ipam_is_duplicate_mac(&mac, mac64, false)) {
817 : 29 : last_mac = mac_addr_suffix;
818 : 29 : break;
819 : : }
820 : : }
821 : :
822 [ - + ]: 29 : if (i == MAC_ADDR_SPACE) {
823 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
824 [ # # ]: 0 : VLOG_WARN_RL(&rl, "MAC address space exhausted.");
825 : 0 : mac64 = 0;
826 : : }
827 : :
828 : 29 : return mac64;
829 : : }
830 : :
831 : : static uint32_t
832 : 48 : ipam_get_unused_ip(struct ovn_datapath *od, uint32_t subnet, uint32_t mask)
833 : : {
834 [ - + ]: 48 : if (!od) {
835 : 0 : return 0;
836 : : }
837 : :
838 : 48 : uint32_t ip = 0;
839 : :
840 : : /* Find an unused IP address in subnet. x.x.x.1 is reserved for a
841 : : * logical router port. */
842 [ + + ]: 211 : for (uint32_t i = 2; i < ~mask; i++) {
843 : 192 : uint32_t tentative_ip = subnet + i;
844 [ + + ]: 192 : if (!ipam_is_duplicate_ip(od, tentative_ip, false)) {
845 : 29 : ip = tentative_ip;
846 : 29 : break;
847 : : }
848 : : }
849 : :
850 [ + + ]: 48 : if (!ip) {
851 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
852 [ + - ]: 19 : VLOG_WARN_RL( &rl, "Subnet address space has been exhausted.");
853 : : }
854 : :
855 : 48 : return ip;
856 : : }
857 : :
858 : : static bool
859 : 48 : ipam_allocate_addresses(struct ovn_datapath *od, struct ovn_port *op,
860 : : ovs_be32 subnet, ovs_be32 mask)
861 : : {
862 [ + - ][ + - ]: 48 : if (!od || !op || !op->nbsp) {
[ - + ]
863 : 0 : return false;
864 : : }
865 : :
866 : 48 : uint32_t ip = ipam_get_unused_ip(od, ntohl(subnet), ntohl(mask));
867 [ + + ]: 48 : if (!ip) {
868 : 19 : return false;
869 : : }
870 : :
871 : : struct eth_addr mac;
872 : 29 : uint64_t mac64 = ipam_get_unused_mac();
873 [ - + ]: 29 : if (!mac64) {
874 : 0 : return false;
875 : : }
876 : 29 : eth_addr_from_uint64(mac64, &mac);
877 : :
878 : : /* Add MAC/IP to MACAM/IPAM hmaps if both addresses were allocated
879 : : * successfully. */
880 : 29 : ipam_insert_ip(od, ip, false);
881 : 29 : ipam_insert_mac(&mac, false);
882 : :
883 : 29 : char *new_addr = xasprintf(ETH_ADDR_FMT" "IP_FMT,
884 : 29 : ETH_ADDR_ARGS(mac), IP_ARGS(htonl(ip)));
885 : 29 : nbrec_logical_switch_port_set_dynamic_addresses(op->nbsp, new_addr);
886 : 29 : free(new_addr);
887 : :
888 : 48 : return true;
889 : : }
890 : :
891 : : static void
892 : 2244 : build_ipam(struct hmap *datapaths, struct hmap *ports)
893 : : {
894 : : /* IPAM generally stands for IP address management. In non-virtualized
895 : : * world, MAC addresses come with the hardware. But, with virtualized
896 : : * workloads, they need to be assigned and managed. This function
897 : : * does both IP address management (ipam) and MAC address management
898 : : * (macam). */
899 : :
900 : : /* If the switch's other_config:subnet is set, allocate new addresses for
901 : : * ports that have the "dynamic" keyword in their addresses column. */
902 : : struct ovn_datapath *od;
903 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
904 [ + + ]: 6323 : if (od->nbs) {
905 : 4582 : const char *subnet_str = smap_get(&od->nbs->other_config,
906 : : "subnet");
907 [ + + ]: 4582 : if (!subnet_str) {
908 : 4295 : continue;
909 : : }
910 : :
911 : : ovs_be32 subnet, mask;
912 : 287 : char *error = ip_parse_masked(subnet_str, &subnet, &mask);
913 [ + - ][ + - ]: 287 : if (error || mask == OVS_BE32_MAX || !ip_is_cidr(mask)) {
[ - + ]
914 : : static struct vlog_rate_limit rl
915 : : = VLOG_RATE_LIMIT_INIT(5, 1);
916 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad 'subnet' %s", subnet_str);
917 : 0 : free(error);
918 : 0 : continue;
919 : : }
920 : :
921 : : struct ovn_port *op;
922 [ + + ]: 2252 : for (size_t i = 0; i < od->nbs->n_ports; i++) {
923 : 1965 : const struct nbrec_logical_switch_port *nbsp =
924 : 1965 : od->nbs->ports[i];
925 : :
926 [ - + ]: 1965 : if (!nbsp) {
927 : 0 : continue;
928 : : }
929 : :
930 : 1965 : op = ovn_port_find(ports, nbsp->name);
931 [ + - ][ + - ]: 1965 : if (!op || (op->nbsp && op->peer)) {
[ + + ]
932 : : /* Do not allocate addresses for logical switch ports that
933 : : * have a peer. */
934 : 104 : continue;
935 : : }
936 : :
937 [ + + ]: 3704 : for (size_t j = 0; j < nbsp->n_addresses; j++) {
938 [ + + ]: 1891 : if (!strcmp(nbsp->addresses[j], "dynamic")
939 [ + + ]: 1819 : && !nbsp->dynamic_addresses) {
940 [ + + ]: 48 : if (!ipam_allocate_addresses(od, op, subnet, mask)
941 [ - + ]: 29 : || !extract_lsp_addresses(nbsp->dynamic_addresses,
942 : 19 : &op->lsp_addrs[op->n_lsp_addrs])) {
943 : : static struct vlog_rate_limit rl
944 : : = VLOG_RATE_LIMIT_INIT(1, 1);
945 [ + - ]: 19 : VLOG_INFO_RL(&rl, "Failed to allocate address.");
946 : : } else {
947 : 29 : op->n_lsp_addrs++;
948 : : }
949 : 48 : break;
950 : : }
951 : : }
952 : : }
953 : : }
954 : : }
955 : 2244 : }
956 : :
957 : : /* Tag allocation for nested containers.
958 : : *
959 : : * For a logical switch port with 'parent_name' and a request to allocate tags,
960 : : * keeps a track of all allocated tags. */
961 : : struct tag_alloc_node {
962 : : struct hmap_node hmap_node;
963 : : char *parent_name;
964 : : unsigned long *allocated_tags; /* A bitmap to track allocated tags. */
965 : : };
966 : :
967 : : static void
968 : 2244 : tag_alloc_destroy(struct hmap *tag_alloc_table)
969 : : {
970 : : struct tag_alloc_node *node;
971 [ + + ][ - + ]: 2281 : HMAP_FOR_EACH_POP (node, hmap_node, tag_alloc_table) {
[ + + ]
972 : 37 : bitmap_free(node->allocated_tags);
973 : 37 : free(node->parent_name);
974 : 37 : free(node);
975 : : }
976 : 2244 : hmap_destroy(tag_alloc_table);
977 : 2244 : }
978 : :
979 : : static struct tag_alloc_node *
980 : 100 : tag_alloc_get_node(struct hmap *tag_alloc_table, const char *parent_name)
981 : : {
982 : : /* If a node for the 'parent_name' exists, return it. */
983 : : struct tag_alloc_node *tag_alloc_node;
984 [ + + ][ - + ]: 100 : HMAP_FOR_EACH_WITH_HASH (tag_alloc_node, hmap_node,
985 : : hash_string(parent_name, 0),
986 : : tag_alloc_table) {
987 [ + - ]: 63 : if (!strcmp(tag_alloc_node->parent_name, parent_name)) {
988 : 63 : return tag_alloc_node;
989 : : }
990 : : }
991 : :
992 : : /* Create a new node. */
993 : 37 : tag_alloc_node = xmalloc(sizeof *tag_alloc_node);
994 : 37 : tag_alloc_node->parent_name = xstrdup(parent_name);
995 : 37 : tag_alloc_node->allocated_tags = bitmap_allocate(MAX_OVN_TAGS);
996 : : /* Tag 0 is invalid for nested containers. */
997 : 37 : bitmap_set1(tag_alloc_node->allocated_tags, 0);
998 : 37 : hmap_insert(tag_alloc_table, &tag_alloc_node->hmap_node,
999 : : hash_string(parent_name, 0));
1000 : :
1001 : 37 : return tag_alloc_node;
1002 : : }
1003 : :
1004 : : static void
1005 : 20638 : tag_alloc_add_existing_tags(struct hmap *tag_alloc_table,
1006 : : const struct nbrec_logical_switch_port *nbsp)
1007 : : {
1008 : : /* Add the tags of already existing nested containers. If there is no
1009 : : * 'nbsp->parent_name' or no 'nbsp->tag' set, there is nothing to do. */
1010 [ + + ][ + + ]: 20638 : if (!nbsp->parent_name || !nbsp->parent_name[0] || !nbsp->tag) {
[ + + ]
1011 : 20544 : return;
1012 : : }
1013 : :
1014 : : struct tag_alloc_node *tag_alloc_node;
1015 : 94 : tag_alloc_node = tag_alloc_get_node(tag_alloc_table, nbsp->parent_name);
1016 : 94 : bitmap_set1(tag_alloc_node->allocated_tags, *nbsp->tag);
1017 : : }
1018 : :
1019 : : static void
1020 : 20408 : tag_alloc_create_new_tag(struct hmap *tag_alloc_table,
1021 : : const struct nbrec_logical_switch_port *nbsp)
1022 : : {
1023 [ + + ]: 20408 : if (!nbsp->tag_request) {
1024 : 20197 : return;
1025 : : }
1026 : :
1027 [ + - ][ + + ]: 211 : if (nbsp->parent_name && nbsp->parent_name[0]
1028 [ + + ]: 107 : && *nbsp->tag_request == 0) {
1029 : : /* For nested containers that need allocation, do the allocation. */
1030 : :
1031 [ + + ]: 76 : if (nbsp->tag) {
1032 : : /* This has already been allocated. */
1033 : 70 : return;
1034 : : }
1035 : :
1036 : : struct tag_alloc_node *tag_alloc_node;
1037 : : int64_t tag;
1038 : 6 : tag_alloc_node = tag_alloc_get_node(tag_alloc_table,
1039 : 6 : nbsp->parent_name);
1040 : 6 : tag = bitmap_scan(tag_alloc_node->allocated_tags, 0, 1, MAX_OVN_TAGS);
1041 [ - + ]: 6 : if (tag == MAX_OVN_TAGS) {
1042 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
1043 [ # # ]: 0 : VLOG_ERR_RL(&rl, "out of vlans for logical switch ports with "
1044 : : "parent %s", nbsp->parent_name);
1045 : 0 : return;
1046 : : }
1047 : 6 : bitmap_set1(tag_alloc_node->allocated_tags, tag);
1048 : 6 : nbrec_logical_switch_port_set_tag(nbsp, &tag, 1);
1049 [ + - ]: 135 : } else if (*nbsp->tag_request != 0) {
1050 : : /* For everything else, copy the contents of 'tag_request' to 'tag'. */
1051 : 135 : nbrec_logical_switch_port_set_tag(nbsp, nbsp->tag_request, 1);
1052 : : }
1053 : : }
1054 : :
1055 : :
1056 : : static void
1057 : 2244 : join_logical_ports(struct northd_context *ctx,
1058 : : struct hmap *datapaths, struct hmap *ports,
1059 : : struct hmap *tag_alloc_table, struct ovs_list *sb_only,
1060 : : struct ovs_list *nb_only, struct ovs_list *both)
1061 : : {
1062 : 2244 : hmap_init(ports);
1063 : 2244 : ovs_list_init(sb_only);
1064 : 2244 : ovs_list_init(nb_only);
1065 : 2244 : ovs_list_init(both);
1066 : :
1067 : : const struct sbrec_port_binding *sb;
1068 [ + + ]: 27576 : SBREC_PORT_BINDING_FOR_EACH (sb, ctx->ovnsb_idl) {
1069 : 25332 : struct ovn_port *op = ovn_port_create(ports, sb->logical_port,
1070 : : NULL, NULL, sb);
1071 : 25332 : ovs_list_push_back(sb_only, &op->list);
1072 : : }
1073 : :
1074 : : struct ovn_datapath *od;
1075 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
1076 [ + + ]: 6323 : if (od->nbs) {
1077 [ + + ]: 25220 : for (size_t i = 0; i < od->nbs->n_ports; i++) {
1078 : 20638 : const struct nbrec_logical_switch_port *nbsp
1079 : 20638 : = od->nbs->ports[i];
1080 : 20638 : struct ovn_port *op = ovn_port_find(ports, nbsp->name);
1081 [ + + ]: 20638 : if (op) {
1082 [ + - ][ - + ]: 20408 : if (op->nbsp || op->nbrp) {
1083 : : static struct vlog_rate_limit rl
1084 : : = VLOG_RATE_LIMIT_INIT(5, 1);
1085 [ # # ]: 0 : VLOG_WARN_RL(&rl, "duplicate logical port %s",
1086 : : nbsp->name);
1087 : 0 : continue;
1088 : : }
1089 : 20408 : op->nbsp = nbsp;
1090 : 20408 : ovs_list_remove(&op->list);
1091 : 20408 : ovs_list_push_back(both, &op->list);
1092 : :
1093 : : /* This port exists due to a SB binding, but should
1094 : : * not have been initialized fully. */
1095 [ + - ][ - + ]: 20408 : ovs_assert(!op->n_lsp_addrs && !op->n_ps_addrs);
1096 : : } else {
1097 : 230 : op = ovn_port_create(ports, nbsp->name, nbsp, NULL, NULL);
1098 : 230 : ovs_list_push_back(nb_only, &op->list);
1099 : : }
1100 : :
1101 : : op->lsp_addrs
1102 : 20638 : = xmalloc(sizeof *op->lsp_addrs * nbsp->n_addresses);
1103 [ + + ]: 42382 : for (size_t j = 0; j < nbsp->n_addresses; j++) {
1104 [ + + ]: 21744 : if (!strcmp(nbsp->addresses[j], "unknown")) {
1105 : 1909 : continue;
1106 : : }
1107 [ + + ]: 19835 : if (!strcmp(nbsp->addresses[j], "dynamic")) {
1108 [ + + ]: 1823 : if (nbsp->dynamic_addresses) {
1109 [ - + ]: 1771 : if (!extract_lsp_addresses(nbsp->dynamic_addresses,
1110 : 3542 : &op->lsp_addrs[op->n_lsp_addrs])) {
1111 : : static struct vlog_rate_limit rl
1112 : : = VLOG_RATE_LIMIT_INIT(1, 1);
1113 [ # # ]: 0 : VLOG_INFO_RL(&rl, "invalid syntax '%s' in "
1114 : : "logical switch port "
1115 : : "dynamic_addresses. No "
1116 : : "MAC address found",
1117 : : op->nbsp->dynamic_addresses);
1118 : 0 : continue;
1119 : : }
1120 : : } else {
1121 : 52 : continue;
1122 : : }
1123 [ - + ]: 18012 : } else if (!extract_lsp_addresses(nbsp->addresses[j],
1124 : 36024 : &op->lsp_addrs[op->n_lsp_addrs])) {
1125 : : static struct vlog_rate_limit rl
1126 : : = VLOG_RATE_LIMIT_INIT(1, 1);
1127 [ # # ]: 0 : VLOG_INFO_RL(&rl, "invalid syntax '%s' in logical "
1128 : : "switch port addresses. No MAC "
1129 : : "address found",
1130 : : op->nbsp->addresses[j]);
1131 : 0 : continue;
1132 : : }
1133 : 19783 : op->n_lsp_addrs++;
1134 : : }
1135 : :
1136 : : op->ps_addrs
1137 : 20638 : = xmalloc(sizeof *op->ps_addrs * nbsp->n_port_security);
1138 [ + + ]: 22191 : for (size_t j = 0; j < nbsp->n_port_security; j++) {
1139 [ - + ]: 1553 : if (!extract_lsp_addresses(nbsp->port_security[j],
1140 : 3106 : &op->ps_addrs[op->n_ps_addrs])) {
1141 : : static struct vlog_rate_limit rl
1142 : : = VLOG_RATE_LIMIT_INIT(1, 1);
1143 [ # # ]: 0 : VLOG_INFO_RL(&rl, "invalid syntax '%s' in port "
1144 : : "security. No MAC address found",
1145 : : op->nbsp->port_security[j]);
1146 : 0 : continue;
1147 : : }
1148 : 1553 : op->n_ps_addrs++;
1149 : : }
1150 : :
1151 : 20638 : op->od = od;
1152 : 20638 : ipam_add_port_addresses(od, op);
1153 : 20638 : tag_alloc_add_existing_tags(tag_alloc_table, nbsp);
1154 : : }
1155 : : } else {
1156 [ + + ]: 6713 : for (size_t i = 0; i < od->nbr->n_ports; i++) {
1157 : 4972 : const struct nbrec_logical_router_port *nbrp
1158 : 4972 : = od->nbr->ports[i];
1159 : :
1160 : : struct lport_addresses lrp_networks;
1161 [ - + ]: 4972 : if (!extract_lrp_networks(nbrp, &lrp_networks)) {
1162 : : static struct vlog_rate_limit rl
1163 : : = VLOG_RATE_LIMIT_INIT(5, 1);
1164 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad 'mac' %s", nbrp->mac);
1165 : 0 : continue;
1166 : : }
1167 : :
1168 [ - + ][ # # ]: 4972 : if (!lrp_networks.n_ipv4_addrs && !lrp_networks.n_ipv6_addrs) {
1169 : 0 : continue;
1170 : : }
1171 : :
1172 : 4972 : struct ovn_port *op = ovn_port_find(ports, nbrp->name);
1173 [ + + ]: 4972 : if (op) {
1174 [ + - ][ - + ]: 4911 : if (op->nbsp || op->nbrp) {
1175 : : static struct vlog_rate_limit rl
1176 : : = VLOG_RATE_LIMIT_INIT(5, 1);
1177 [ # # ]: 0 : VLOG_WARN_RL(&rl, "duplicate logical router port %s",
1178 : : nbrp->name);
1179 : 0 : continue;
1180 : : }
1181 : 4911 : op->nbrp = nbrp;
1182 : 4911 : ovs_list_remove(&op->list);
1183 : 4911 : ovs_list_push_back(both, &op->list);
1184 : :
1185 : : /* This port exists but should not have been
1186 : : * initialized fully. */
1187 [ + - ][ - + ]: 4911 : ovs_assert(!op->lrp_networks.n_ipv4_addrs
1188 : : && !op->lrp_networks.n_ipv6_addrs);
1189 : : } else {
1190 : 61 : op = ovn_port_create(ports, nbrp->name, NULL, nbrp, NULL);
1191 : 61 : ovs_list_push_back(nb_only, &op->list);
1192 : : }
1193 : :
1194 : 4972 : op->lrp_networks = lrp_networks;
1195 : 4972 : op->od = od;
1196 : 4972 : ipam_add_port_addresses(op->od, op);
1197 : : }
1198 : : }
1199 : : }
1200 : :
1201 : : /* Connect logical router ports, and logical switch ports of type "router",
1202 : : * to their peers. */
1203 : : struct ovn_port *op;
1204 [ + + ][ - + ]: 27867 : HMAP_FOR_EACH (op, key_node, ports) {
1205 [ + + ][ + + ]: 30305 : if (op->nbsp && !strcmp(op->nbsp->type, "router")) {
1206 : 4733 : const char *peer_name = smap_get(&op->nbsp->options, "router-port");
1207 [ - + ]: 4733 : if (!peer_name) {
1208 : 0 : continue;
1209 : : }
1210 : :
1211 : 4733 : struct ovn_port *peer = ovn_port_find(ports, peer_name);
1212 [ + + ][ + + ]: 4733 : if (!peer || !peer->nbrp) {
1213 : 51 : continue;
1214 : : }
1215 : :
1216 : 4682 : peer->peer = op;
1217 : 4682 : op->peer = peer;
1218 : 4682 : op->od->router_ports = xrealloc(
1219 : 4682 : op->od->router_ports,
1220 : 4682 : sizeof *op->od->router_ports * (op->od->n_router_ports + 1));
1221 : 4682 : op->od->router_ports[op->od->n_router_ports++] = op;
1222 [ + + ][ + + ]: 20890 : } else if (op->nbrp && op->nbrp->peer) {
1223 : 144 : struct ovn_port *peer = ovn_port_find(ports, op->nbrp->peer);
1224 [ + + ]: 144 : if (peer) {
1225 [ + - ]: 140 : if (peer->nbrp) {
1226 : 140 : op->peer = peer;
1227 [ # # ]: 0 : } else if (peer->nbsp) {
1228 : : /* An ovn_port for a switch port of type "router" does have
1229 : : * a router port as its peer (see the case above for
1230 : : * "router" ports), but this is set via options:router-port
1231 : : * in Logical_Switch_Port and does not involve the
1232 : : * Logical_Router_Port's 'peer' column. */
1233 : : static struct vlog_rate_limit rl =
1234 : : VLOG_RATE_LIMIT_INIT(5, 1);
1235 [ # # ]: 0 : VLOG_WARN_RL(&rl, "Bad configuration: The peer of router "
1236 : : "port %s is a switch port", op->key);
1237 : : }
1238 : : }
1239 : : }
1240 : : }
1241 : 2244 : }
1242 : :
1243 : : static void
1244 : 25610 : ovn_port_update_sbrec(const struct ovn_port *op)
1245 : : {
1246 : 25610 : sbrec_port_binding_set_datapath(op->sb, op->od->sb);
1247 [ + + ]: 25610 : if (op->nbrp) {
1248 : : /* If the router is for l3 gateway, it resides on a chassis
1249 : : * and its port type is "l3gateway". */
1250 : 4972 : const char *chassis = smap_get(&op->od->nbr->options, "chassis");
1251 [ + + ]: 4972 : if (chassis) {
1252 : 417 : sbrec_port_binding_set_type(op->sb, "l3gateway");
1253 : : } else {
1254 : 4555 : sbrec_port_binding_set_type(op->sb, "patch");
1255 : : }
1256 : :
1257 [ + + ]: 4972 : const char *peer = op->peer ? op->peer->key : "<error>";
1258 : : struct smap new;
1259 : 4972 : smap_init(&new);
1260 : 4972 : smap_add(&new, "peer", peer);
1261 [ + + ]: 4972 : if (chassis) {
1262 : 417 : smap_add(&new, "l3gateway-chassis", chassis);
1263 : : }
1264 : 4972 : sbrec_port_binding_set_options(op->sb, &new);
1265 : 4972 : smap_destroy(&new);
1266 : :
1267 : 4972 : sbrec_port_binding_set_parent_port(op->sb, NULL);
1268 : 4972 : sbrec_port_binding_set_tag(op->sb, NULL, 0);
1269 : 4972 : sbrec_port_binding_set_mac(op->sb, NULL, 0);
1270 : : } else {
1271 [ + + ]: 20638 : if (strcmp(op->nbsp->type, "router")) {
1272 : 15905 : sbrec_port_binding_set_type(op->sb, op->nbsp->type);
1273 : 15905 : sbrec_port_binding_set_options(op->sb, &op->nbsp->options);
1274 : : } else {
1275 : 4733 : const char *chassis = NULL;
1276 [ + + ][ + - ]: 4733 : if (op->peer && op->peer->od && op->peer->od->nbr) {
[ + - ]
1277 : 4682 : chassis = smap_get(&op->peer->od->nbr->options, "chassis");
1278 : : }
1279 : :
1280 : : /* A switch port connected to a gateway router is also of
1281 : : * type "l3gateway". */
1282 [ + + ]: 4733 : if (chassis) {
1283 : 375 : sbrec_port_binding_set_type(op->sb, "l3gateway");
1284 : : } else {
1285 : 4358 : sbrec_port_binding_set_type(op->sb, "patch");
1286 : : }
1287 : :
1288 : 4733 : const char *router_port = smap_get_def(&op->nbsp->options,
1289 : : "router-port", "<error>");
1290 : : struct smap new;
1291 : 4733 : smap_init(&new);
1292 : 4733 : smap_add(&new, "peer", router_port);
1293 [ + + ]: 4733 : if (chassis) {
1294 : 375 : smap_add(&new, "l3gateway-chassis", chassis);
1295 : : }
1296 : :
1297 : 4733 : const char *nat_addresses = smap_get(&op->nbsp->options,
1298 : : "nat-addresses");
1299 [ + + ]: 4733 : if (nat_addresses) {
1300 : : struct lport_addresses laddrs;
1301 [ - + ]: 19 : if (!extract_lsp_addresses(nat_addresses, &laddrs)) {
1302 : : static struct vlog_rate_limit rl =
1303 : : VLOG_RATE_LIMIT_INIT(1, 1);
1304 [ # # ]: 0 : VLOG_WARN_RL(&rl, "Error extracting nat-addresses.");
1305 : : } else {
1306 : 19 : smap_add(&new, "nat-addresses", nat_addresses);
1307 : 19 : destroy_lport_addresses(&laddrs);
1308 : : }
1309 : : }
1310 : 4733 : sbrec_port_binding_set_options(op->sb, &new);
1311 : 4733 : smap_destroy(&new);
1312 : : }
1313 : 20638 : sbrec_port_binding_set_parent_port(op->sb, op->nbsp->parent_name);
1314 : 20638 : sbrec_port_binding_set_tag(op->sb, op->nbsp->tag, op->nbsp->n_tag);
1315 : 20638 : sbrec_port_binding_set_mac(op->sb, (const char **) op->nbsp->addresses,
1316 : 20638 : op->nbsp->n_addresses);
1317 : : }
1318 : 25610 : }
1319 : :
1320 : : /* Remove mac_binding entries that refer to logical_ports which are
1321 : : * deleted. */
1322 : : static void
1323 : 11 : cleanup_mac_bindings(struct northd_context *ctx, struct hmap *ports)
1324 : : {
1325 : : const struct sbrec_mac_binding *b, *n;
1326 [ + + ][ + + ]: 14 : SBREC_MAC_BINDING_FOR_EACH_SAFE (b, n, ctx->ovnsb_idl) {
1327 [ + + ]: 3 : if (!ovn_port_find(ports, b->logical_port)) {
1328 : 2 : sbrec_mac_binding_delete(b);
1329 : : }
1330 : : }
1331 : 11 : }
1332 : :
1333 : : /* Updates the southbound Port_Binding table so that it contains the logical
1334 : : * switch ports specified by the northbound database.
1335 : : *
1336 : : * Initializes 'ports' to contain a "struct ovn_port" for every logical port,
1337 : : * using the "struct ovn_datapath"s in 'datapaths' to look up logical
1338 : : * datapaths. */
1339 : : static void
1340 : 2244 : build_ports(struct northd_context *ctx, struct hmap *datapaths,
1341 : : struct hmap *ports)
1342 : : {
1343 : : struct ovs_list sb_only, nb_only, both;
1344 : : struct hmap tag_alloc_table;
1345 : 2244 : hmap_init(&tag_alloc_table);
1346 : :
1347 : 2244 : join_logical_ports(ctx, datapaths, ports, &tag_alloc_table, &sb_only,
1348 : : &nb_only, &both);
1349 : :
1350 : : struct ovn_port *op, *next;
1351 : : /* For logical ports that are in both databases, update the southbound
1352 : : * record based on northbound data. Also index the in-use tunnel_keys.
1353 : : * For logical ports that are in NB database, do any tag allocation
1354 : : * needed. */
1355 [ + + ][ + + ]: 27563 : LIST_FOR_EACH_SAFE (op, next, list, &both) {
1356 [ + + ]: 25319 : if (op->nbsp) {
1357 : 20408 : tag_alloc_create_new_tag(&tag_alloc_table, op->nbsp);
1358 : : }
1359 : 25319 : ovn_port_update_sbrec(op);
1360 : :
1361 : 25319 : add_tnlid(&op->od->port_tnlids, op->sb->tunnel_key);
1362 [ + + ]: 25319 : if (op->sb->tunnel_key > op->od->port_key_hint) {
1363 : 12505 : op->od->port_key_hint = op->sb->tunnel_key;
1364 : : }
1365 : : }
1366 : :
1367 : : /* Add southbound record for each unmatched northbound record. */
1368 [ + + ][ + + ]: 2535 : LIST_FOR_EACH_SAFE (op, next, list, &nb_only) {
1369 : 291 : uint16_t tunnel_key = ovn_port_allocate_key(op->od);
1370 [ - + ]: 291 : if (!tunnel_key) {
1371 : 0 : continue;
1372 : : }
1373 : :
1374 : 291 : op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn);
1375 : 291 : ovn_port_update_sbrec(op);
1376 : :
1377 : 291 : sbrec_port_binding_set_logical_port(op->sb, op->key);
1378 : 291 : sbrec_port_binding_set_tunnel_key(op->sb, tunnel_key);
1379 : : }
1380 : :
1381 : 2244 : bool remove_mac_bindings = false;
1382 [ + + ]: 2244 : if (!ovs_list_is_empty(&sb_only)) {
1383 : 11 : remove_mac_bindings = true;
1384 : : }
1385 : :
1386 : : /* Delete southbound records without northbound matches. */
1387 [ + + ][ + + ]: 2257 : LIST_FOR_EACH_SAFE(op, next, list, &sb_only) {
1388 : 13 : ovs_list_remove(&op->list);
1389 : 13 : sbrec_port_binding_delete(op->sb);
1390 : 13 : ovn_port_destroy(ports, op);
1391 : : }
1392 [ + + ]: 2244 : if (remove_mac_bindings) {
1393 : 11 : cleanup_mac_bindings(ctx, ports);
1394 : : }
1395 : :
1396 : 2244 : tag_alloc_destroy(&tag_alloc_table);
1397 : 2244 : }
1398 : :
1399 : : #define OVN_MIN_MULTICAST 32768
1400 : : #define OVN_MAX_MULTICAST 65535
1401 : :
1402 : : struct multicast_group {
1403 : : const char *name;
1404 : : uint16_t key; /* OVN_MIN_MULTICAST...OVN_MAX_MULTICAST. */
1405 : : };
1406 : :
1407 : : #define MC_FLOOD "_MC_flood"
1408 : : static const struct multicast_group mc_flood = { MC_FLOOD, 65535 };
1409 : :
1410 : : #define MC_UNKNOWN "_MC_unknown"
1411 : : static const struct multicast_group mc_unknown = { MC_UNKNOWN, 65534 };
1412 : :
1413 : : static bool
1414 : 22464 : multicast_group_equal(const struct multicast_group *a,
1415 : : const struct multicast_group *b)
1416 : : {
1417 [ + - ][ + - ]: 22464 : return !strcmp(a->name, b->name) && a->key == b->key;
1418 : : }
1419 : :
1420 : : /* Multicast group entry. */
1421 : : struct ovn_multicast {
1422 : : struct hmap_node hmap_node; /* Index on 'datapath' and 'key'. */
1423 : : struct ovn_datapath *datapath;
1424 : : const struct multicast_group *group;
1425 : :
1426 : : struct ovn_port **ports;
1427 : : size_t n_ports, allocated_ports;
1428 : : };
1429 : :
1430 : : static uint32_t
1431 : 34091 : ovn_multicast_hash(const struct ovn_datapath *datapath,
1432 : : const struct multicast_group *group)
1433 : : {
1434 : 34091 : return hash_pointer(datapath, group->key);
1435 : : }
1436 : :
1437 : : static struct ovn_multicast *
1438 : 28278 : ovn_multicast_find(struct hmap *mcgroups, struct ovn_datapath *datapath,
1439 : : const struct multicast_group *group)
1440 : : {
1441 : : struct ovn_multicast *mc;
1442 : :
1443 [ + + ][ - + ]: 28278 : HMAP_FOR_EACH_WITH_HASH (mc, hmap_node,
1444 : : ovn_multicast_hash(datapath, group), mcgroups) {
1445 [ + - ]: 22464 : if (mc->datapath == datapath
1446 [ + - ]: 22464 : && multicast_group_equal(mc->group, group)) {
1447 : 22464 : return mc;
1448 : : }
1449 : : }
1450 : 5814 : return NULL;
1451 : : }
1452 : :
1453 : : static void
1454 : 22547 : ovn_multicast_add(struct hmap *mcgroups, const struct multicast_group *group,
1455 : : struct ovn_port *port)
1456 : : {
1457 : 22547 : struct ovn_datapath *od = port->od;
1458 : 22547 : struct ovn_multicast *mc = ovn_multicast_find(mcgroups, od, group);
1459 [ + + ]: 22547 : if (!mc) {
1460 : 5813 : mc = xmalloc(sizeof *mc);
1461 : 5813 : hmap_insert(mcgroups, &mc->hmap_node, ovn_multicast_hash(od, group));
1462 : 5813 : mc->datapath = od;
1463 : 5813 : mc->group = group;
1464 : 5813 : mc->n_ports = 0;
1465 : 5813 : mc->allocated_ports = 4;
1466 : 5813 : mc->ports = xmalloc(mc->allocated_ports * sizeof *mc->ports);
1467 : : }
1468 [ + + ]: 22547 : if (mc->n_ports >= mc->allocated_ports) {
1469 : 2768 : mc->ports = x2nrealloc(mc->ports, &mc->allocated_ports,
1470 : : sizeof *mc->ports);
1471 : : }
1472 : 22547 : mc->ports[mc->n_ports++] = port;
1473 : 22547 : }
1474 : :
1475 : : static void
1476 : 5813 : ovn_multicast_destroy(struct hmap *mcgroups, struct ovn_multicast *mc)
1477 : : {
1478 [ + - ]: 5813 : if (mc) {
1479 : 5813 : hmap_remove(mcgroups, &mc->hmap_node);
1480 : 5813 : free(mc->ports);
1481 : 5813 : free(mc);
1482 : : }
1483 : 5813 : }
1484 : :
1485 : : static void
1486 : 5813 : ovn_multicast_update_sbrec(const struct ovn_multicast *mc,
1487 : : const struct sbrec_multicast_group *sb)
1488 : : {
1489 : 5813 : struct sbrec_port_binding **ports = xmalloc(mc->n_ports * sizeof *ports);
1490 [ + + ]: 28360 : for (size_t i = 0; i < mc->n_ports; i++) {
1491 : 22547 : ports[i] = CONST_CAST(struct sbrec_port_binding *, mc->ports[i]->sb);
1492 : : }
1493 : 5813 : sbrec_multicast_group_set_ports(sb, ports, mc->n_ports);
1494 : 5813 : free(ports);
1495 : 5813 : }
1496 : :
1497 : : /* Logical flow generation.
1498 : : *
1499 : : * This code generates the Logical_Flow table in the southbound database, as a
1500 : : * function of most of the northbound database.
1501 : : */
1502 : :
1503 : : struct ovn_lflow {
1504 : : struct hmap_node hmap_node;
1505 : :
1506 : : struct ovn_datapath *od;
1507 : : enum ovn_stage stage;
1508 : : uint16_t priority;
1509 : : char *match;
1510 : : char *actions;
1511 : : };
1512 : :
1513 : : static size_t
1514 : 637354 : ovn_lflow_hash(const struct ovn_lflow *lflow)
1515 : : {
1516 : 637354 : size_t hash = uuid_hash(&lflow->od->key);
1517 : 637354 : hash = hash_2words((lflow->stage << 16) | lflow->priority, hash);
1518 : 637354 : hash = hash_string(lflow->match, hash);
1519 : 637354 : return hash_string(lflow->actions, hash);
1520 : : }
1521 : :
1522 : : static bool
1523 : 316190 : ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b)
1524 : : {
1525 : 316190 : return (a->od == b->od
1526 [ + - ]: 316190 : && a->stage == b->stage
1527 [ + - ]: 316190 : && a->priority == b->priority
1528 [ + - ]: 316190 : && !strcmp(a->match, b->match)
1529 [ + - ][ + - ]: 632380 : && !strcmp(a->actions, b->actions));
1530 : : }
1531 : :
1532 : : static void
1533 : 637354 : ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
1534 : : enum ovn_stage stage, uint16_t priority,
1535 : : char *match, char *actions)
1536 : : {
1537 : 637354 : lflow->od = od;
1538 : 637354 : lflow->stage = stage;
1539 : 637354 : lflow->priority = priority;
1540 : 637354 : lflow->match = match;
1541 : 637354 : lflow->actions = actions;
1542 : 637354 : }
1543 : :
1544 : : /* Adds a row with the specified contents to the Logical_Flow table. */
1545 : : static void
1546 : 320840 : ovn_lflow_add(struct hmap *lflow_map, struct ovn_datapath *od,
1547 : : enum ovn_stage stage, uint16_t priority,
1548 : : const char *match, const char *actions)
1549 : : {
1550 [ - + ]: 320840 : ovs_assert(ovn_stage_to_datapath_type(stage) == ovn_datapath_get_type(od));
1551 : :
1552 : 320840 : struct ovn_lflow *lflow = xmalloc(sizeof *lflow);
1553 : 320840 : ovn_lflow_init(lflow, od, stage, priority,
1554 : : xstrdup(match), xstrdup(actions));
1555 : 320840 : hmap_insert(lflow_map, &lflow->hmap_node, ovn_lflow_hash(lflow));
1556 : 320840 : }
1557 : :
1558 : : static struct ovn_lflow *
1559 : 316514 : ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
1560 : : enum ovn_stage stage, uint16_t priority,
1561 : : const char *match, const char *actions)
1562 : : {
1563 : : struct ovn_lflow target;
1564 : 316514 : ovn_lflow_init(&target, od, stage, priority,
1565 : : CONST_CAST(char *, match), CONST_CAST(char *, actions));
1566 : :
1567 : : struct ovn_lflow *lflow;
1568 [ + + ][ - + ]: 316514 : HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, ovn_lflow_hash(&target),
1569 : : lflows) {
1570 [ + - ]: 316190 : if (ovn_lflow_equal(lflow, &target)) {
1571 : 316190 : return lflow;
1572 : : }
1573 : : }
1574 : 316514 : return NULL;
1575 : : }
1576 : :
1577 : : static void
1578 : 320840 : ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow)
1579 : : {
1580 [ + - ]: 320840 : if (lflow) {
1581 : 320840 : hmap_remove(lflows, &lflow->hmap_node);
1582 : 320840 : free(lflow->match);
1583 : 320840 : free(lflow->actions);
1584 : 320840 : free(lflow);
1585 : : }
1586 : 320840 : }
1587 : :
1588 : : /* Appends port security constraints on L2 address field 'eth_addr_field'
1589 : : * (e.g. "eth.src" or "eth.dst") to 'match'. 'ps_addrs', with 'n_ps_addrs'
1590 : : * elements, is the collection of port_security constraints from an
1591 : : * OVN_NB Logical_Switch_Port row generated by extract_lsp_addresses(). */
1592 : : static void
1593 : 41276 : build_port_security_l2(const char *eth_addr_field,
1594 : : struct lport_addresses *ps_addrs,
1595 : : unsigned int n_ps_addrs,
1596 : : struct ds *match)
1597 : : {
1598 [ + + ]: 41276 : if (!n_ps_addrs) {
1599 : 38392 : return;
1600 : : }
1601 : :
1602 : 2884 : ds_put_format(match, " && %s == {", eth_addr_field);
1603 : :
1604 [ + + ]: 5990 : for (size_t i = 0; i < n_ps_addrs; i++) {
1605 : 3106 : ds_put_format(match, "%s ", ps_addrs[i].ea_s);
1606 : : }
1607 : 2884 : ds_chomp(match, ' ');
1608 : 2884 : ds_put_cstr(match, "}");
1609 : : }
1610 : :
1611 : : static void
1612 : 1272 : build_port_security_ipv6_nd_flow(
1613 : : struct ds *match, struct eth_addr ea, struct ipv6_netaddr *ipv6_addrs,
1614 : : int n_ipv6_addrs)
1615 : : {
1616 : 1272 : ds_put_format(match, " && ip6 && nd && ((nd.sll == "ETH_ADDR_FMT" || "
1617 : : "nd.sll == "ETH_ADDR_FMT") || ((nd.tll == "ETH_ADDR_FMT" || "
1618 : 7632 : "nd.tll == "ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth_addr_zero),
1619 : 15264 : ETH_ADDR_ARGS(ea), ETH_ADDR_ARGS(eth_addr_zero),
1620 : 7632 : ETH_ADDR_ARGS(ea));
1621 [ + + ]: 1272 : if (!n_ipv6_addrs) {
1622 : 991 : ds_put_cstr(match, "))");
1623 : 991 : return;
1624 : : }
1625 : :
1626 : : char ip6_str[INET6_ADDRSTRLEN + 1];
1627 : : struct in6_addr lla;
1628 : 281 : in6_generate_lla(ea, &lla);
1629 : 281 : memset(ip6_str, 0, sizeof(ip6_str));
1630 : 281 : ipv6_string_mapped(ip6_str, &lla);
1631 : 281 : ds_put_format(match, " && (nd.target == %s", ip6_str);
1632 : :
1633 [ + + ]: 562 : for(int i = 0; i < n_ipv6_addrs; i++) {
1634 : 281 : memset(ip6_str, 0, sizeof(ip6_str));
1635 : 281 : ipv6_string_mapped(ip6_str, &ipv6_addrs[i].addr);
1636 : 281 : ds_put_format(match, " || nd.target == %s", ip6_str);
1637 : : }
1638 : :
1639 : 281 : ds_put_format(match, ")))");
1640 : : }
1641 : :
1642 : : static void
1643 : 562 : build_port_security_ipv6_flow(
1644 : : enum ovn_pipeline pipeline, struct ds *match, struct eth_addr ea,
1645 : : struct ipv6_netaddr *ipv6_addrs, int n_ipv6_addrs)
1646 : : {
1647 : : char ip6_str[INET6_ADDRSTRLEN + 1];
1648 : :
1649 [ + + ]: 562 : ds_put_format(match, " && %s == {",
1650 : : pipeline == P_IN ? "ip6.src" : "ip6.dst");
1651 : :
1652 : : /* Allow link-local address. */
1653 : : struct in6_addr lla;
1654 : 562 : in6_generate_lla(ea, &lla);
1655 : 562 : ipv6_string_mapped(ip6_str, &lla);
1656 : 562 : ds_put_format(match, "%s, ", ip6_str);
1657 : :
1658 : : /* Allow ip6.dst=ff00::/8 for multicast packets */
1659 [ + + ]: 562 : if (pipeline == P_OUT) {
1660 : 281 : ds_put_cstr(match, "ff00::/8, ");
1661 : : }
1662 [ + + ]: 1124 : for(int i = 0; i < n_ipv6_addrs; i++) {
1663 : 562 : ipv6_string_mapped(ip6_str, &ipv6_addrs[i].addr);
1664 : 562 : ds_put_format(match, "%s, ", ip6_str);
1665 : : }
1666 : : /* Replace ", " by "}". */
1667 : 562 : ds_chomp(match, ' ');
1668 : 562 : ds_chomp(match, ',');
1669 : 562 : ds_put_cstr(match, "}");
1670 : 562 : }
1671 : :
1672 : : /**
1673 : : * Build port security constraints on ARP and IPv6 ND fields
1674 : : * and add logical flows to S_SWITCH_IN_PORT_SEC_ND stage.
1675 : : *
1676 : : * For each port security of the logical port, following
1677 : : * logical flows are added
1678 : : * - If the port security has no IP (both IPv4 and IPv6) or
1679 : : * if it has IPv4 address(es)
1680 : : * - Priority 90 flow to allow ARP packets for known MAC addresses
1681 : : * in the eth.src and arp.spa fields. If the port security
1682 : : * has IPv4 addresses, allow known IPv4 addresses in the arp.tpa field.
1683 : : *
1684 : : * - If the port security has no IP (both IPv4 and IPv6) or
1685 : : * if it has IPv6 address(es)
1686 : : * - Priority 90 flow to allow IPv6 ND packets for known MAC addresses
1687 : : * in the eth.src and nd.sll/nd.tll fields. If the port security
1688 : : * has IPv6 addresses, allow known IPv6 addresses in the nd.target field
1689 : : * for IPv6 Neighbor Advertisement packet.
1690 : : *
1691 : : * - Priority 80 flow to drop ARP and IPv6 ND packets.
1692 : : */
1693 : : static void
1694 : 1442 : build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
1695 : : {
1696 : 1442 : struct ds match = DS_EMPTY_INITIALIZER;
1697 : :
1698 [ + + ]: 2995 : for (size_t i = 0; i < op->n_ps_addrs; i++) {
1699 : 1553 : struct lport_addresses *ps = &op->ps_addrs[i];
1700 : :
1701 [ + + ][ + + ]: 1553 : bool no_ip = !(ps->n_ipv4_addrs || ps->n_ipv6_addrs);
1702 : :
1703 : 1553 : ds_clear(&match);
1704 [ + + ][ + + ]: 1553 : if (ps->n_ipv4_addrs || no_ip) {
1705 : 1354 : ds_put_format(&match,
1706 : : "inport == %s && eth.src == %s && arp.sha == %s",
1707 : 1354 : op->json_key, ps->ea_s, ps->ea_s);
1708 : :
1709 [ + + ]: 1354 : if (ps->n_ipv4_addrs) {
1710 : 363 : ds_put_cstr(&match, " && arp.spa == {");
1711 [ + + ]: 829 : for (size_t j = 0; j < ps->n_ipv4_addrs; j++) {
1712 : : /* When the netmask is applied, if the host portion is
1713 : : * non-zero, the host can only use the specified
1714 : : * address in the arp.spa. If zero, the host is allowed
1715 : : * to use any address in the subnet. */
1716 [ + + ]: 466 : if (ps->ipv4_addrs[j].plen == 32
1717 [ + + ]: 34 : || ps->ipv4_addrs[j].addr & ~ps->ipv4_addrs[j].mask) {
1718 : 449 : ds_put_cstr(&match, ps->ipv4_addrs[j].addr_s);
1719 : : } else {
1720 : 17 : ds_put_format(&match, "%s/%d",
1721 : 17 : ps->ipv4_addrs[j].network_s,
1722 : 17 : ps->ipv4_addrs[j].plen);
1723 : : }
1724 : 466 : ds_put_cstr(&match, ", ");
1725 : : }
1726 : 363 : ds_chomp(&match, ' ');
1727 : 363 : ds_chomp(&match, ',');
1728 : 363 : ds_put_cstr(&match, "}");
1729 : : }
1730 : 1354 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
1731 : 1354 : ds_cstr(&match), "next;");
1732 : : }
1733 : :
1734 [ + + ][ + + ]: 1553 : if (ps->n_ipv6_addrs || no_ip) {
1735 : 1272 : ds_clear(&match);
1736 : 1272 : ds_put_format(&match, "inport == %s && eth.src == %s",
1737 : 1272 : op->json_key, ps->ea_s);
1738 : 1272 : build_port_security_ipv6_nd_flow(&match, ps->ea, ps->ipv6_addrs,
1739 : 1272 : ps->n_ipv6_addrs);
1740 : 1272 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
1741 : 1272 : ds_cstr(&match), "next;");
1742 : : }
1743 : : }
1744 : :
1745 : 1442 : ds_clear(&match);
1746 : 1442 : ds_put_format(&match, "inport == %s && (arp || nd)", op->json_key);
1747 : 1442 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80,
1748 : 1442 : ds_cstr(&match), "drop;");
1749 : 1442 : ds_destroy(&match);
1750 : 1442 : }
1751 : :
1752 : : /**
1753 : : * Build port security constraints on IPv4 and IPv6 src and dst fields
1754 : : * and add logical flows to S_SWITCH_(IN/OUT)_PORT_SEC_IP stage.
1755 : : *
1756 : : * For each port security of the logical port, following
1757 : : * logical flows are added
1758 : : * - If the port security has IPv4 addresses,
1759 : : * - Priority 90 flow to allow IPv4 packets for known IPv4 addresses
1760 : : *
1761 : : * - If the port security has IPv6 addresses,
1762 : : * - Priority 90 flow to allow IPv6 packets for known IPv6 addresses
1763 : : *
1764 : : * - If the port security has IPv4 addresses or IPv6 addresses or both
1765 : : * - Priority 80 flow to drop all IPv4 and IPv6 traffic
1766 : : */
1767 : : static void
1768 : 2884 : build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
1769 : : struct hmap *lflows)
1770 : : {
1771 : : char *port_direction;
1772 : : enum ovn_stage stage;
1773 [ + + ]: 2884 : if (pipeline == P_IN) {
1774 : 1442 : port_direction = "inport";
1775 : 1442 : stage = S_SWITCH_IN_PORT_SEC_IP;
1776 : : } else {
1777 : 1442 : port_direction = "outport";
1778 : 1442 : stage = S_SWITCH_OUT_PORT_SEC_IP;
1779 : : }
1780 : :
1781 [ + + ]: 5990 : for (size_t i = 0; i < op->n_ps_addrs; i++) {
1782 : 3106 : struct lport_addresses *ps = &op->ps_addrs[i];
1783 : :
1784 [ + + ][ + + ]: 3106 : if (!(ps->n_ipv4_addrs || ps->n_ipv6_addrs)) {
1785 : 1982 : continue;
1786 : : }
1787 : :
1788 [ + + ]: 1124 : if (ps->n_ipv4_addrs) {
1789 : 726 : struct ds match = DS_EMPTY_INITIALIZER;
1790 [ + + ]: 726 : if (pipeline == P_IN) {
1791 : : /* Permit use of the unspecified address for DHCP discovery */
1792 : 363 : struct ds dhcp_match = DS_EMPTY_INITIALIZER;
1793 : 363 : ds_put_format(&dhcp_match, "inport == %s"
1794 : : " && eth.src == %s"
1795 : : " && ip4.src == 0.0.0.0"
1796 : : " && ip4.dst == 255.255.255.255"
1797 : : " && udp.src == 68 && udp.dst == 67",
1798 : 363 : op->json_key, ps->ea_s);
1799 : 363 : ovn_lflow_add(lflows, op->od, stage, 90,
1800 : 363 : ds_cstr(&dhcp_match), "next;");
1801 : 363 : ds_destroy(&dhcp_match);
1802 : 363 : ds_put_format(&match, "inport == %s && eth.src == %s"
1803 : : " && ip4.src == {", op->json_key,
1804 : 363 : ps->ea_s);
1805 : : } else {
1806 : 363 : ds_put_format(&match, "outport == %s && eth.dst == %s"
1807 : : " && ip4.dst == {255.255.255.255, 224.0.0.0/4, ",
1808 : 363 : op->json_key, ps->ea_s);
1809 : : }
1810 : :
1811 [ + + ]: 1658 : for (int j = 0; j < ps->n_ipv4_addrs; j++) {
1812 : 932 : ovs_be32 mask = ps->ipv4_addrs[j].mask;
1813 : : /* When the netmask is applied, if the host portion is
1814 : : * non-zero, the host can only use the specified
1815 : : * address. If zero, the host is allowed to use any
1816 : : * address in the subnet.
1817 : : */
1818 [ + + ]: 932 : if (ps->ipv4_addrs[j].plen == 32
1819 [ + + ]: 68 : || ps->ipv4_addrs[j].addr & ~mask) {
1820 : 898 : ds_put_format(&match, "%s", ps->ipv4_addrs[j].addr_s);
1821 [ + + ][ + + ]: 915 : if (pipeline == P_OUT && ps->ipv4_addrs[j].plen != 32) {
1822 : : /* Host is also allowed to receive packets to the
1823 : : * broadcast address in the specified subnet. */
1824 : 17 : ds_put_format(&match, ", %s",
1825 : 17 : ps->ipv4_addrs[j].bcast_s);
1826 : : }
1827 : : } else {
1828 : : /* host portion is zero */
1829 : 34 : ds_put_format(&match, "%s/%d", ps->ipv4_addrs[j].network_s,
1830 : 34 : ps->ipv4_addrs[j].plen);
1831 : : }
1832 : 932 : ds_put_cstr(&match, ", ");
1833 : : }
1834 : :
1835 : : /* Replace ", " by "}". */
1836 : 726 : ds_chomp(&match, ' ');
1837 : 726 : ds_chomp(&match, ',');
1838 : 726 : ds_put_cstr(&match, "}");
1839 : 726 : ovn_lflow_add(lflows, op->od, stage, 90, ds_cstr(&match), "next;");
1840 : 726 : ds_destroy(&match);
1841 : : }
1842 : :
1843 [ + + ]: 1124 : if (ps->n_ipv6_addrs) {
1844 : 562 : struct ds match = DS_EMPTY_INITIALIZER;
1845 [ + + ]: 562 : if (pipeline == P_IN) {
1846 : : /* Permit use of unspecified address for duplicate address
1847 : : * detection */
1848 : 281 : struct ds dad_match = DS_EMPTY_INITIALIZER;
1849 : 281 : ds_put_format(&dad_match, "inport == %s"
1850 : : " && eth.src == %s"
1851 : : " && ip6.src == ::"
1852 : : " && ip6.dst == ff02::/16"
1853 : : " && icmp6.type == {131, 135, 143}", op->json_key,
1854 : 281 : ps->ea_s);
1855 : 281 : ovn_lflow_add(lflows, op->od, stage, 90,
1856 : 281 : ds_cstr(&dad_match), "next;");
1857 : 281 : ds_destroy(&dad_match);
1858 : : }
1859 [ + + ]: 562 : ds_put_format(&match, "%s == %s && %s == %s",
1860 : : port_direction, op->json_key,
1861 : 562 : pipeline == P_IN ? "eth.src" : "eth.dst", ps->ea_s);
1862 : 562 : build_port_security_ipv6_flow(pipeline, &match, ps->ea,
1863 : 562 : ps->ipv6_addrs, ps->n_ipv6_addrs);
1864 : 562 : ovn_lflow_add(lflows, op->od, stage, 90,
1865 : 562 : ds_cstr(&match), "next;");
1866 : 562 : ds_destroy(&match);
1867 : : }
1868 : :
1869 [ + + ]: 1124 : char *match = xasprintf("%s == %s && %s == %s && ip",
1870 : : port_direction, op->json_key,
1871 : : pipeline == P_IN ? "eth.src" : "eth.dst",
1872 : 1124 : ps->ea_s);
1873 : 1124 : ovn_lflow_add(lflows, op->od, stage, 80, match, "drop;");
1874 : 1124 : free(match);
1875 : : }
1876 : :
1877 : 2884 : }
1878 : :
1879 : : static bool
1880 : 84461 : lsp_is_enabled(const struct nbrec_logical_switch_port *lsp)
1881 : : {
1882 [ - + ][ # # ]: 84461 : return !lsp->enabled || *lsp->enabled;
1883 : : }
1884 : :
1885 : : static bool
1886 : 20638 : lsp_is_up(const struct nbrec_logical_switch_port *lsp)
1887 : : {
1888 [ + + ][ + + ]: 20638 : return !lsp->up || *lsp->up;
1889 : : }
1890 : :
1891 : : static bool
1892 : 105 : build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
1893 : : struct ds *options_action, struct ds *response_action)
1894 : : {
1895 [ + + ]: 105 : if (!op->nbsp->dhcpv4_options) {
1896 : : /* CMS has disabled native DHCPv4 for this lport. */
1897 : 35 : return false;
1898 : : }
1899 : :
1900 : : ovs_be32 host_ip, mask;
1901 : 70 : char *error = ip_parse_masked(op->nbsp->dhcpv4_options->cidr, &host_ip,
1902 : : &mask);
1903 [ + - ][ - + ]: 70 : if (error || ((offer_ip ^ host_ip) & mask)) {
1904 : : /* Either
1905 : : * - cidr defined is invalid or
1906 : : * - the offer ip of the logical port doesn't belong to the cidr
1907 : : * defined in the DHCPv4 options.
1908 : : * */
1909 : 0 : free(error);
1910 : 0 : return false;
1911 : : }
1912 : :
1913 : 70 : const char *server_ip = smap_get(
1914 : 70 : &op->nbsp->dhcpv4_options->options, "server_id");
1915 : 70 : const char *server_mac = smap_get(
1916 : 70 : &op->nbsp->dhcpv4_options->options, "server_mac");
1917 : 70 : const char *lease_time = smap_get(
1918 : 70 : &op->nbsp->dhcpv4_options->options, "lease_time");
1919 : 70 : const char *router = smap_get(
1920 : 70 : &op->nbsp->dhcpv4_options->options, "router");
1921 : :
1922 [ + - ][ + - ]: 70 : if (!(server_ip && server_mac && lease_time && router)) {
[ + - ][ + + ]
1923 : : /* "server_id", "server_mac", "lease_time" and "router" should be
1924 : : * present in the dhcp_options. */
1925 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
1926 [ + - ]: 22 : VLOG_WARN_RL(&rl, "Required DHCPv4 options not defined for lport - %s",
1927 : : op->json_key);
1928 : 22 : return false;
1929 : : }
1930 : :
1931 : 48 : struct smap dhcpv4_options = SMAP_INITIALIZER(&dhcpv4_options);
1932 : 48 : smap_clone(&dhcpv4_options, &op->nbsp->dhcpv4_options->options);
1933 : :
1934 : : /* server_mac is not DHCPv4 option, delete it from the smap. */
1935 : 48 : smap_remove(&dhcpv4_options, "server_mac");
1936 : 48 : char *netmask = xasprintf(IP_FMT, IP_ARGS(mask));
1937 : 48 : smap_add(&dhcpv4_options, "netmask", netmask);
1938 : 48 : free(netmask);
1939 : :
1940 : 48 : ds_put_format(options_action,
1941 : : REGBIT_DHCP_OPTS_RESULT" = put_dhcp_opts(offerip = "
1942 : 48 : IP_FMT", ", IP_ARGS(offer_ip));
1943 : : struct smap_node *node;
1944 [ + + ][ - + ]: 240 : SMAP_FOR_EACH(node, &dhcpv4_options) {
1945 : 192 : ds_put_format(options_action, "%s = %s, ", node->key, node->value);
1946 : : }
1947 : :
1948 : 48 : ds_chomp(options_action, ' ');
1949 : 48 : ds_chomp(options_action, ',');
1950 : 48 : ds_put_cstr(options_action, "); next;");
1951 : :
1952 : 48 : ds_put_format(response_action, "eth.dst = eth.src; eth.src = %s; "
1953 : : "ip4.dst = "IP_FMT"; ip4.src = %s; udp.src = 67; "
1954 : : "udp.dst = 68; outport = inport; flags.loopback = 1; "
1955 : : "output;",
1956 : 48 : server_mac, IP_ARGS(offer_ip), server_ip);
1957 : :
1958 : 48 : smap_destroy(&dhcpv4_options);
1959 : 105 : return true;
1960 : : }
1961 : :
1962 : : static bool
1963 : 70 : build_dhcpv6_action(struct ovn_port *op, struct in6_addr *offer_ip,
1964 : : struct ds *options_action, struct ds *response_action)
1965 : : {
1966 [ - + ]: 70 : if (!op->nbsp->dhcpv6_options) {
1967 : : /* CMS has disabled native DHCPv6 for this lport. */
1968 : 0 : return false;
1969 : : }
1970 : :
1971 : : struct in6_addr host_ip, mask;
1972 : :
1973 : 70 : char *error = ipv6_parse_masked(op->nbsp->dhcpv6_options->cidr, &host_ip,
1974 : : &mask);
1975 [ - + ]: 70 : if (error) {
1976 : 0 : free(error);
1977 : 0 : return false;
1978 : : }
1979 : 70 : struct in6_addr ip6_mask = ipv6_addr_bitxor(offer_ip, &host_ip);
1980 : 70 : ip6_mask = ipv6_addr_bitand(&ip6_mask, &mask);
1981 [ - + ]: 70 : if (!ipv6_mask_is_any(&ip6_mask)) {
1982 : : /* offer_ip doesn't belongs to the cidr defined in lport's DHCPv6
1983 : : * options.*/
1984 : 0 : return false;
1985 : : }
1986 : :
1987 : : /* "server_id" should be the MAC address. */
1988 : 70 : const char *server_mac = smap_get(&op->nbsp->dhcpv6_options->options,
1989 : : "server_id");
1990 : : struct eth_addr ea;
1991 [ + - ][ - + ]: 70 : if (!server_mac || !eth_addr_from_string(server_mac, &ea)) {
1992 : : /* "server_id" should be present in the dhcpv6_options. */
1993 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
1994 [ # # ]: 0 : VLOG_WARN_RL(&rl, "server_id not present in the DHCPv6 options"
1995 : : " for lport %s", op->json_key);
1996 : 0 : return false;
1997 : : }
1998 : :
1999 : : /* Get the link local IP of the DHCPv6 server from the server MAC. */
2000 : : struct in6_addr lla;
2001 : 70 : in6_generate_lla(ea, &lla);
2002 : :
2003 : : char server_ip[INET6_ADDRSTRLEN + 1];
2004 : 70 : ipv6_string_mapped(server_ip, &lla);
2005 : :
2006 : : char ia_addr[INET6_ADDRSTRLEN + 1];
2007 : 70 : ipv6_string_mapped(ia_addr, offer_ip);
2008 : :
2009 : 70 : ds_put_format(options_action,
2010 : : REGBIT_DHCP_OPTS_RESULT" = put_dhcpv6_opts(ia_addr = %s, ",
2011 : : ia_addr);
2012 : : struct smap_node *node;
2013 [ + + ][ - + ]: 140 : SMAP_FOR_EACH (node, &op->nbsp->dhcpv6_options->options) {
2014 : 70 : ds_put_format(options_action, "%s = %s, ", node->key, node->value);
2015 : : }
2016 : 70 : ds_chomp(options_action, ' ');
2017 : 70 : ds_chomp(options_action, ',');
2018 : 70 : ds_put_cstr(options_action, "); next;");
2019 : :
2020 : 70 : ds_put_format(response_action, "eth.dst = eth.src; eth.src = %s; "
2021 : : "ip6.dst = ip6.src; ip6.src = %s; udp.src = 547; "
2022 : : "udp.dst = 546; outport = inport; flags.loopback = 1; "
2023 : : "output;",
2024 : : server_mac, server_ip);
2025 : 70 : return true;
2026 : : }
2027 : :
2028 : : static bool
2029 : 9164 : has_stateful_acl(struct ovn_datapath *od)
2030 : : {
2031 [ + + ]: 9344 : for (size_t i = 0; i < od->nbs->n_acls; i++) {
2032 : 208 : struct nbrec_acl *acl = od->nbs->acls[i];
2033 [ + + ]: 208 : if (!strcmp(acl->action, "allow-related")) {
2034 : 28 : return true;
2035 : : }
2036 : : }
2037 : :
2038 : 9136 : return false;
2039 : : }
2040 : :
2041 : : static void
2042 : 4582 : build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
2043 : : {
2044 : 4582 : bool has_stateful = has_stateful_acl(od);
2045 : :
2046 : : /* Ingress and Egress Pre-ACL Table (Priority 0): Packets are
2047 : : * allowed by default. */
2048 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 0, "1", "next;");
2049 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 0, "1", "next;");
2050 : :
2051 : : /* If there are any stateful ACL rules in this datapath, we must
2052 : : * send all IP packets through the conntrack action, which handles
2053 : : * defragmentation, in order to match L4 headers. */
2054 [ + + ]: 4582 : if (has_stateful) {
2055 [ - + ]: 14 : for (size_t i = 0; i < od->n_router_ports; i++) {
2056 : 0 : struct ovn_port *op = od->router_ports[i];
2057 : : /* Can't use ct() for router ports. Consider the
2058 : : * following configuration: lp1(10.0.0.2) on
2059 : : * hostA--ls1--lr0--ls2--lp2(10.0.1.2) on hostB, For a
2060 : : * ping from lp1 to lp2, First, the response will go
2061 : : * through ct() with a zone for lp2 in the ls2 ingress
2062 : : * pipeline on hostB. That ct zone knows about this
2063 : : * connection. Next, it goes through ct() with the zone
2064 : : * for the router port in the egress pipeline of ls2 on
2065 : : * hostB. This zone does not know about the connection,
2066 : : * as the icmp request went through the logical router
2067 : : * on hostA, not hostB. This would only work with
2068 : : * distributed conntrack state across all chassis. */
2069 : 0 : struct ds match_in = DS_EMPTY_INITIALIZER;
2070 : 0 : struct ds match_out = DS_EMPTY_INITIALIZER;
2071 : :
2072 : 0 : ds_put_format(&match_in, "ip && inport == %s", op->json_key);
2073 : 0 : ds_put_format(&match_out, "ip && outport == %s", op->json_key);
2074 : 0 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
2075 : 0 : ds_cstr(&match_in), "next;");
2076 : 0 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
2077 : 0 : ds_cstr(&match_out), "next;");
2078 : :
2079 : 0 : ds_destroy(&match_in);
2080 : 0 : ds_destroy(&match_out);
2081 : : }
2082 : : /* Ingress and Egress Pre-ACL Table (Priority 110).
2083 : : *
2084 : : * Not to do conntrack on ND packets. */
2085 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110, "nd", "next;");
2086 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110, "nd", "next;");
2087 : :
2088 : : /* Ingress and Egress Pre-ACL Table (Priority 100).
2089 : : *
2090 : : * Regardless of whether the ACL is "from-lport" or "to-lport",
2091 : : * we need rules in both the ingress and egress table, because
2092 : : * the return traffic needs to be followed.
2093 : : *
2094 : : * 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send
2095 : : * it to conntrack for tracking and defragmentation. */
2096 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 100, "ip",
2097 : : REGBIT_CONNTRACK_DEFRAG" = 1; next;");
2098 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 100, "ip",
2099 : : REGBIT_CONNTRACK_DEFRAG" = 1; next;");
2100 : : }
2101 : 4582 : }
2102 : :
2103 : : /* For a 'key' of the form "IP:port" or just "IP", sets 'port' and
2104 : : * 'ip_address'. The caller must free() the memory allocated for
2105 : : * 'ip_address'. */
2106 : : static void
2107 : 202 : ip_address_and_port_from_lb_key(const char *key, char **ip_address,
2108 : : uint16_t *port)
2109 : : {
2110 : : char *ip_str, *start, *next;
2111 : 202 : *ip_address = NULL;
2112 : 202 : *port = 0;
2113 : :
2114 : 202 : next = start = xstrdup(key);
2115 : 202 : ip_str = strsep(&next, ":");
2116 [ + - ][ - + ]: 202 : if (!ip_str || !ip_str[0]) {
2117 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
2118 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad ip address for load balancer key %s", key);
2119 : 0 : free(start);
2120 : 0 : return;
2121 : : }
2122 : :
2123 : : ovs_be32 ip, mask;
2124 : 202 : char *error = ip_parse_masked(ip_str, &ip, &mask);
2125 [ + - ][ - + ]: 202 : if (error || mask != OVS_BE32_MAX) {
2126 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
2127 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad ip address for load balancer key %s", key);
2128 : 0 : free(start);
2129 : 0 : free(error);
2130 : 0 : return;
2131 : : }
2132 : :
2133 : 202 : int l4_port = 0;
2134 [ + + ][ + - ]: 202 : if (next && next[0]) {
2135 [ + - ][ + - ]: 76 : if (!str_to_int(next, 0, &l4_port) || l4_port < 0 || l4_port > 65535) {
[ - + ]
2136 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
2137 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad ip port for load balancer key %s", key);
2138 : 0 : free(start);
2139 : 0 : return;
2140 : : }
2141 : : }
2142 : :
2143 : 202 : *port = l4_port;
2144 : 202 : *ip_address = strdup(ip_str);
2145 : 202 : free(start);
2146 : : }
2147 : :
2148 : : static void
2149 : 4582 : build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
2150 : : {
2151 : : /* Allow all packets to go to next tables by default. */
2152 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 0, "1", "next;");
2153 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 0, "1", "next;");
2154 : :
2155 : 4582 : struct sset all_ips = SSET_INITIALIZER(&all_ips);
2156 : 4582 : bool vip_configured = false;
2157 [ + + ]: 4624 : for (int i = 0; i < od->nbs->n_load_balancer; i++) {
2158 : 42 : struct nbrec_load_balancer *lb = od->nbs->load_balancer[i];
2159 : 42 : struct smap *vips = &lb->vips;
2160 : : struct smap_node *node;
2161 : :
2162 [ + + ][ - + ]: 104 : SMAP_FOR_EACH (node, vips) {
2163 : 62 : vip_configured = true;
2164 : :
2165 : : /* node->key contains IP:port or just IP. */
2166 : 62 : char *ip_address = NULL;
2167 : : uint16_t port;
2168 : 62 : ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
2169 [ - + ]: 62 : if (!ip_address) {
2170 : 0 : continue;
2171 : : }
2172 : :
2173 [ + - ]: 62 : if (!sset_contains(&all_ips, ip_address)) {
2174 : 62 : sset_add(&all_ips, ip_address);
2175 : : }
2176 : :
2177 : 62 : free(ip_address);
2178 : :
2179 : : /* Ignore L4 port information in the key because fragmented packets
2180 : : * may not have L4 information. The pre-stateful table will send
2181 : : * the packet through ct() action to de-fragment. In stateful
2182 : : * table, we will eventually look at L4 information. */
2183 : : }
2184 : : }
2185 : :
2186 : : /* 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send
2187 : : * packet to conntrack for defragmentation. */
2188 : : const char *ip_address;
2189 [ + + ][ + + ]: 4644 : SSET_FOR_EACH(ip_address, &all_ips) {
[ + + ]
2190 : 62 : char *match = xasprintf("ip && ip4.dst == %s", ip_address);
2191 : 62 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB,
2192 : : 100, match, REGBIT_CONNTRACK_DEFRAG" = 1; next;");
2193 : 62 : free(match);
2194 : : }
2195 : :
2196 : 4582 : sset_destroy(&all_ips);
2197 : :
2198 [ + + ]: 4582 : if (vip_configured) {
2199 : 30 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB,
2200 : : 100, "ip", REGBIT_CONNTRACK_DEFRAG" = 1; next;");
2201 : : }
2202 : 4582 : }
2203 : :
2204 : : static void
2205 : 4582 : build_pre_stateful(struct ovn_datapath *od, struct hmap *lflows)
2206 : : {
2207 : : /* Ingress and Egress pre-stateful Table (Priority 0): Packets are
2208 : : * allowed by default. */
2209 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 0, "1", "next;");
2210 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_STATEFUL, 0, "1", "next;");
2211 : :
2212 : : /* If REGBIT_CONNTRACK_DEFRAG is set as 1, then the packets should be
2213 : : * sent to conntrack for tracking and defragmentation. */
2214 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 100,
2215 : : REGBIT_CONNTRACK_DEFRAG" == 1", "ct_next;");
2216 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_STATEFUL, 100,
2217 : : REGBIT_CONNTRACK_DEFRAG" == 1", "ct_next;");
2218 : 4582 : }
2219 : :
2220 : : static void
2221 : 4582 : build_acls(struct ovn_datapath *od, struct hmap *lflows)
2222 : : {
2223 : 4582 : bool has_stateful = has_stateful_acl(od);
2224 : :
2225 : : /* Ingress and Egress ACL Table (Priority 0): Packets are allowed by
2226 : : * default. A related rule at priority 1 is added below if there
2227 : : * are any stateful ACLs in this datapath. */
2228 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 0, "1", "next;");
2229 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 0, "1", "next;");
2230 : :
2231 [ + + ]: 4582 : if (has_stateful) {
2232 : : /* Ingress and Egress ACL Table (Priority 1).
2233 : : *
2234 : : * By default, traffic is allowed. This is partially handled by
2235 : : * the Priority 0 ACL flows added earlier, but we also need to
2236 : : * commit IP flows. This is because, while the initiater's
2237 : : * direction may not have any stateful rules, the server's may
2238 : : * and then its return traffic would not have an associated
2239 : : * conntrack entry and would return "+invalid".
2240 : : *
2241 : : * We use "ct_commit" for a connection that is not already known
2242 : : * by the connection tracker. Once a connection is committed,
2243 : : * subsequent packets will hit the flow at priority 0 that just
2244 : : * uses "next;"
2245 : : *
2246 : : * We also check for established connections that have ct_label[0]
2247 : : * set on them. That's a connection that was disallowed, but is
2248 : : * now allowed by policy again since it hit this default-allow flow.
2249 : : * We need to set ct_label[0]=0 to let the connection continue,
2250 : : * which will be done by ct_commit() in the "stateful" stage.
2251 : : * Subsequent packets will hit the flow at priority 0 that just
2252 : : * uses "next;". */
2253 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 1,
2254 : : "ip && (!ct.est || (ct.est && ct_label[0] == 1))",
2255 : : REGBIT_CONNTRACK_COMMIT" = 1; next;");
2256 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 1,
2257 : : "ip && (!ct.est || (ct.est && ct_label[0] == 1))",
2258 : : REGBIT_CONNTRACK_COMMIT" = 1; next;");
2259 : :
2260 : : /* Ingress and Egress ACL Table (Priority 65535).
2261 : : *
2262 : : * Always drop traffic that's in an invalid state. Also drop
2263 : : * reply direction packets for connections that have been marked
2264 : : * for deletion (bit 0 of ct_label is set).
2265 : : *
2266 : : * This is enforced at a higher priority than ACLs can be defined. */
2267 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
2268 : : "ct.inv || (ct.est && ct.rpl && ct_label[0] == 1)",
2269 : : "drop;");
2270 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
2271 : : "ct.inv || (ct.est && ct.rpl && ct_label[0] == 1)",
2272 : : "drop;");
2273 : :
2274 : : /* Ingress and Egress ACL Table (Priority 65535).
2275 : : *
2276 : : * Allow reply traffic that is part of an established
2277 : : * conntrack entry that has not been marked for deletion
2278 : : * (bit 0 of ct_label). We only match traffic in the
2279 : : * reply direction because we want traffic in the request
2280 : : * direction to hit the currently defined policy from ACLs.
2281 : : *
2282 : : * This is enforced at a higher priority than ACLs can be defined. */
2283 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
2284 : : "ct.est && !ct.rel && !ct.new && !ct.inv "
2285 : : "&& ct.rpl && ct_label[0] == 0",
2286 : : "next;");
2287 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
2288 : : "ct.est && !ct.rel && !ct.new && !ct.inv "
2289 : : "&& ct.rpl && ct_label[0] == 0",
2290 : : "next;");
2291 : :
2292 : : /* Ingress and Egress ACL Table (Priority 65535).
2293 : : *
2294 : : * Allow traffic that is related to an existing conntrack entry that
2295 : : * has not been marked for deletion (bit 0 of ct_label).
2296 : : *
2297 : : * This is enforced at a higher priority than ACLs can be defined.
2298 : : *
2299 : : * NOTE: This does not support related data sessions (eg,
2300 : : * a dynamically negotiated FTP data channel), but will allow
2301 : : * related traffic such as an ICMP Port Unreachable through
2302 : : * that's generated from a non-listening UDP port. */
2303 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
2304 : : "!ct.est && ct.rel && !ct.new && !ct.inv "
2305 : : "&& ct_label[0] == 0",
2306 : : "next;");
2307 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
2308 : : "!ct.est && ct.rel && !ct.new && !ct.inv "
2309 : : "&& ct_label[0] == 0",
2310 : : "next;");
2311 : :
2312 : : /* Ingress and Egress ACL Table (Priority 65535).
2313 : : *
2314 : : * Not to do conntrack on ND packets. */
2315 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX, "nd", "next;");
2316 : 14 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX, "nd", "next;");
2317 : : }
2318 : :
2319 : : /* Ingress or Egress ACL Table (Various priorities). */
2320 [ + + ]: 4708 : for (size_t i = 0; i < od->nbs->n_acls; i++) {
2321 : 126 : struct nbrec_acl *acl = od->nbs->acls[i];
2322 : 126 : bool ingress = !strcmp(acl->direction, "from-lport") ? true :false;
2323 [ + + ]: 126 : enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL : S_SWITCH_OUT_ACL;
2324 : :
2325 [ + - ]: 126 : if (!strcmp(acl->action, "allow")
2326 [ + + ]: 126 : || !strcmp(acl->action, "allow-related")) {
2327 : : /* If there are any stateful flows, we must even commit "allow"
2328 : : * actions. This is because, while the initiater's
2329 : : * direction may not have any stateful rules, the server's
2330 : : * may and then its return traffic would not have an
2331 : : * associated conntrack entry and would return "+invalid". */
2332 [ - + ]: 72 : if (!has_stateful) {
2333 : 0 : ovn_lflow_add(lflows, od, stage,
2334 : 0 : acl->priority + OVN_ACL_PRI_OFFSET,
2335 : 0 : acl->match, "next;");
2336 : : } else {
2337 : 36 : struct ds match = DS_EMPTY_INITIALIZER;
2338 : :
2339 : : /* Commit the connection tracking entry if it's a new
2340 : : * connection that matches this ACL. After this commit,
2341 : : * the reply traffic is allowed by a flow we create at
2342 : : * priority 65535, defined earlier.
2343 : : *
2344 : : * It's also possible that a known connection was marked for
2345 : : * deletion after a policy was deleted, but the policy was
2346 : : * re-added while that connection is still known. We catch
2347 : : * that case here and un-set ct_label[0] (which will be done
2348 : : * by ct_commit in the "stateful" stage) to indicate that the
2349 : : * connection should be allowed to resume.
2350 : : */
2351 : 36 : ds_put_format(&match, "((ct.new && !ct.est)"
2352 : : " || (!ct.new && ct.est && !ct.rpl "
2353 : : "&& ct_label[0] == 1)) "
2354 : : "&& (%s)", acl->match);
2355 : 36 : ovn_lflow_add(lflows, od, stage,
2356 : 36 : acl->priority + OVN_ACL_PRI_OFFSET,
2357 : 36 : ds_cstr(&match),
2358 : : REGBIT_CONNTRACK_COMMIT" = 1; next;");
2359 : :
2360 : : /* Match on traffic in the request direction for an established
2361 : : * connection tracking entry that has not been marked for
2362 : : * deletion. There is no need to commit here, so we can just
2363 : : * proceed to the next table. We use this to ensure that this
2364 : : * connection is still allowed by the currently defined
2365 : : * policy. */
2366 : 36 : ds_clear(&match);
2367 : 36 : ds_put_format(&match,
2368 : : "!ct.new && ct.est && !ct.rpl"
2369 : : " && ct_label[0] == 0 && (%s)",
2370 : : acl->match);
2371 : 36 : ovn_lflow_add(lflows, od, stage,
2372 : 36 : acl->priority + OVN_ACL_PRI_OFFSET,
2373 : 36 : ds_cstr(&match), "next;");
2374 : :
2375 : 36 : ds_destroy(&match);
2376 : : }
2377 [ - + ]: 90 : } else if (!strcmp(acl->action, "drop")
2378 [ # # ]: 0 : || !strcmp(acl->action, "reject")) {
2379 : 90 : struct ds match = DS_EMPTY_INITIALIZER;
2380 : :
2381 : : /* XXX Need to support "reject", treat it as "drop;" for now. */
2382 [ - + ]: 90 : if (!strcmp(acl->action, "reject")) {
2383 [ # # ]: 0 : VLOG_INFO("reject is not a supported action");
2384 : : }
2385 : :
2386 : : /* The implementation of "drop" differs if stateful ACLs are in
2387 : : * use for this datapath. In that case, the actions differ
2388 : : * depending on whether the connection was previously committed
2389 : : * to the connection tracker with ct_commit. */
2390 [ - + ]: 90 : if (has_stateful) {
2391 : : /* If the packet is not part of an established connection, then
2392 : : * we can simply drop it. */
2393 : 0 : ds_put_format(&match,
2394 : : "(!ct.est || (ct.est && ct_label[0] == 1)) "
2395 : : "&& (%s)",
2396 : : acl->match);
2397 : 0 : ovn_lflow_add(lflows, od, stage, acl->priority +
2398 : 0 : OVN_ACL_PRI_OFFSET, ds_cstr(&match), "drop;");
2399 : :
2400 : : /* For an existing connection without ct_label set, we've
2401 : : * encountered a policy change. ACLs previously allowed
2402 : : * this connection and we committed the connection tracking
2403 : : * entry. Current policy says that we should drop this
2404 : : * connection. First, we set bit 0 of ct_label to indicate
2405 : : * that this connection is set for deletion. By not
2406 : : * specifying "next;", we implicitly drop the packet after
2407 : : * updating conntrack state. We would normally defer
2408 : : * ct_commit() to the "stateful" stage, but since we're
2409 : : * dropping the packet, we go ahead and do it here. */
2410 : 0 : ds_clear(&match);
2411 : 0 : ds_put_format(&match,
2412 : : "ct.est && ct_label[0] == 0 && (%s)",
2413 : : acl->match);
2414 : 0 : ovn_lflow_add(lflows, od, stage,
2415 : 0 : acl->priority + OVN_ACL_PRI_OFFSET,
2416 : 0 : ds_cstr(&match), "ct_commit(ct_label=1/1);");
2417 : :
2418 : 0 : ds_destroy(&match);
2419 : : } else {
2420 : : /* There are no stateful ACLs in use on this datapath,
2421 : : * so a "drop" ACL is simply the "drop" logical flow action
2422 : : * in all cases. */
2423 : 90 : ovn_lflow_add(lflows, od, stage,
2424 : 90 : acl->priority + OVN_ACL_PRI_OFFSET,
2425 : 90 : acl->match, "drop;");
2426 : : }
2427 : : }
2428 : : }
2429 : :
2430 : : /* Add 34000 priority flow to allow DHCP reply from ovn-controller to all
2431 : : * logical ports of the datapath if the CMS has configured DHCPv4 options*/
2432 [ + + ]: 25220 : for (size_t i = 0; i < od->nbs->n_ports; i++) {
2433 [ + + ]: 20638 : if (od->nbs->ports[i]->dhcpv4_options) {
2434 : 70 : const char *server_id = smap_get(
2435 : 70 : &od->nbs->ports[i]->dhcpv4_options->options, "server_id");
2436 : 70 : const char *server_mac = smap_get(
2437 : 70 : &od->nbs->ports[i]->dhcpv4_options->options, "server_mac");
2438 : 70 : const char *lease_time = smap_get(
2439 : 70 : &od->nbs->ports[i]->dhcpv4_options->options, "lease_time");
2440 : 70 : const char *router = smap_get(
2441 : 70 : &od->nbs->ports[i]->dhcpv4_options->options, "router");
2442 [ + - ][ + - ]: 70 : if (server_id && server_mac && lease_time && router) {
[ + - ][ + + ]
2443 : 48 : struct ds match = DS_EMPTY_INITIALIZER;
2444 : 48 : const char *actions =
2445 [ - + ]: 48 : has_stateful ? "ct_commit; next;" : "next;";
2446 : 48 : ds_put_format(&match, "outport == \"%s\" && eth.src == %s "
2447 : : "&& ip4.src == %s && udp && udp.src == 67 "
2448 : 48 : "&& udp.dst == 68", od->nbs->ports[i]->name,
2449 : : server_mac, server_id);
2450 : 48 : ovn_lflow_add(
2451 : 48 : lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
2452 : : actions);
2453 : 48 : ds_destroy(&match);
2454 : : }
2455 : : }
2456 : :
2457 [ + + ]: 20638 : if (od->nbs->ports[i]->dhcpv6_options) {
2458 : 70 : const char *server_mac = smap_get(
2459 : 70 : &od->nbs->ports[i]->dhcpv6_options->options, "server_id");
2460 : : struct eth_addr ea;
2461 [ + - ][ + - ]: 70 : if (server_mac && eth_addr_from_string(server_mac, &ea)) {
2462 : : /* Get the link local IP of the DHCPv6 server from the
2463 : : * server MAC. */
2464 : : struct in6_addr lla;
2465 : 70 : in6_generate_lla(ea, &lla);
2466 : :
2467 : : char server_ip[INET6_ADDRSTRLEN + 1];
2468 : 70 : ipv6_string_mapped(server_ip, &lla);
2469 : :
2470 : 70 : struct ds match = DS_EMPTY_INITIALIZER;
2471 [ - + ]: 70 : const char *actions = has_stateful ? "ct_commit; next;" :
2472 : : "next;";
2473 : 70 : ds_put_format(&match, "outport == \"%s\" && eth.src == %s "
2474 : : "&& ip6.src == %s && udp && udp.src == 547 "
2475 : 70 : "&& udp.dst == 546", od->nbs->ports[i]->name,
2476 : : server_mac, server_ip);
2477 : 70 : ovn_lflow_add(
2478 : 70 : lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
2479 : : actions);
2480 : 70 : ds_destroy(&match);
2481 : : }
2482 : : }
2483 : : }
2484 : 4582 : }
2485 : :
2486 : : static void
2487 : 4582 : build_lb(struct ovn_datapath *od, struct hmap *lflows)
2488 : : {
2489 : : /* Ingress and Egress LB Table (Priority 0): Packets are allowed by
2490 : : * default. */
2491 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 0, "1", "next;");
2492 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, 0, "1", "next;");
2493 : :
2494 [ + + ]: 4582 : if (od->nbs->load_balancer) {
2495 : : /* Ingress and Egress LB Table (Priority 65535).
2496 : : *
2497 : : * Send established traffic through conntrack for just NAT. */
2498 : 30 : ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, UINT16_MAX,
2499 : : "ct.est && !ct.rel && !ct.new && !ct.inv",
2500 : : REGBIT_CONNTRACK_NAT" = 1; next;");
2501 : 30 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, UINT16_MAX,
2502 : : "ct.est && !ct.rel && !ct.new && !ct.inv",
2503 : : REGBIT_CONNTRACK_NAT" = 1; next;");
2504 : : }
2505 : 4582 : }
2506 : :
2507 : : static void
2508 : 4582 : build_stateful(struct ovn_datapath *od, struct hmap *lflows)
2509 : : {
2510 : : /* Ingress and Egress stateful Table (Priority 0): Packets are
2511 : : * allowed by default. */
2512 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 0, "1", "next;");
2513 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 0, "1", "next;");
2514 : :
2515 : : /* If REGBIT_CONNTRACK_COMMIT is set as 1, then the packets should be
2516 : : * committed to conntrack. We always set ct_label[0] to 0 here as
2517 : : * any packet that makes it this far is part of a connection we
2518 : : * want to allow to continue. */
2519 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
2520 : : REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit(ct_label=0/1); next;");
2521 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
2522 : : REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit(ct_label=0/1); next;");
2523 : :
2524 : : /* If REGBIT_CONNTRACK_NAT is set as 1, then packets should just be sent
2525 : : * through nat (without committing).
2526 : : *
2527 : : * REGBIT_CONNTRACK_COMMIT is set for new connections and
2528 : : * REGBIT_CONNTRACK_NAT is set for established connections. So they
2529 : : * don't overlap.
2530 : : */
2531 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
2532 : : REGBIT_CONNTRACK_NAT" == 1", "ct_lb;");
2533 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
2534 : : REGBIT_CONNTRACK_NAT" == 1", "ct_lb;");
2535 : :
2536 : : /* Load balancing rules for new connections get committed to conntrack
2537 : : * table. So even if REGBIT_CONNTRACK_COMMIT is set in a previous table
2538 : : * a higher priority rule for load balancing below also commits the
2539 : : * connection, so it is okay if we do not hit the above match on
2540 : : * REGBIT_CONNTRACK_COMMIT. */
2541 [ + + ]: 4624 : for (int i = 0; i < od->nbs->n_load_balancer; i++) {
2542 : 42 : struct nbrec_load_balancer *lb = od->nbs->load_balancer[i];
2543 : 42 : struct smap *vips = &lb->vips;
2544 : : struct smap_node *node;
2545 : :
2546 [ + + ][ - + ]: 104 : SMAP_FOR_EACH (node, vips) {
2547 : 62 : uint16_t port = 0;
2548 : :
2549 : : /* node->key contains IP:port or just IP. */
2550 : 62 : char *ip_address = NULL;
2551 : 62 : ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
2552 [ - + ]: 62 : if (!ip_address) {
2553 : 0 : continue;
2554 : : }
2555 : :
2556 : : /* New connections in Ingress table. */
2557 : 62 : char *action = xasprintf("ct_lb(%s);", node->value);
2558 : 62 : struct ds match = DS_EMPTY_INITIALIZER;
2559 : 62 : ds_put_format(&match, "ct.new && ip && ip4.dst == %s", ip_address);
2560 [ + + ]: 62 : if (port) {
2561 [ - + ][ # # ]: 20 : if (lb->protocol && !strcmp(lb->protocol, "udp")) {
2562 : 0 : ds_put_format(&match, "&& udp && udp.dst == %d", port);
2563 : : } else {
2564 : 20 : ds_put_format(&match, "&& tcp && tcp.dst == %d", port);
2565 : : }
2566 : 20 : ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
2567 : 20 : 120, ds_cstr(&match), action);
2568 : : } else {
2569 : 42 : ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
2570 : 42 : 110, ds_cstr(&match), action);
2571 : : }
2572 : :
2573 : 62 : ds_destroy(&match);
2574 : 62 : free(action);
2575 : : }
2576 : : }
2577 : 4582 : }
2578 : :
2579 : : static void
2580 : 2244 : build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
2581 : : struct hmap *lflows, struct hmap *mcgroups)
2582 : : {
2583 : : /* This flow table structure is documented in ovn-northd(8), so please
2584 : : * update ovn-northd.8.xml if you change anything. */
2585 : :
2586 : 2244 : struct ds match = DS_EMPTY_INITIALIZER;
2587 : 2244 : struct ds actions = DS_EMPTY_INITIALIZER;
2588 : :
2589 : : /* Build pre-ACL and ACL tables for both ingress and egress.
2590 : : * Ingress tables 3 and 4. Egress tables 0 and 1. */
2591 : : struct ovn_datapath *od;
2592 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
2593 [ + + ]: 6323 : if (!od->nbs) {
2594 : 1741 : continue;
2595 : : }
2596 : :
2597 : 4582 : build_pre_acls(od, lflows);
2598 : 4582 : build_pre_lb(od, lflows);
2599 : 4582 : build_pre_stateful(od, lflows);
2600 : 4582 : build_acls(od, lflows);
2601 : 4582 : build_lb(od, lflows);
2602 : 4582 : build_stateful(od, lflows);
2603 : : }
2604 : :
2605 : : /* Logical switch ingress table 0: Admission control framework (priority
2606 : : * 100). */
2607 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
2608 [ + + ]: 6323 : if (!od->nbs) {
2609 : 1741 : continue;
2610 : : }
2611 : :
2612 : : /* Logical VLANs not supported. */
2613 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_L2, 100, "vlan.present",
2614 : : "drop;");
2615 : :
2616 : : /* Broadcast/multicast source address is invalid. */
2617 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_L2, 100, "eth.src[40]",
2618 : : "drop;");
2619 : :
2620 : : /* Port security flows have priority 50 (see below) and will continue
2621 : : * to the next table if packet source is acceptable. */
2622 : : }
2623 : :
2624 : : /* Logical switch ingress table 0: Ingress port security - L2
2625 : : * (priority 50).
2626 : : * Ingress table 1: Ingress port security - IP (priority 90 and 80)
2627 : : * Ingress table 2: Ingress port security - ND (priority 90 and 80)
2628 : : */
2629 : : struct ovn_port *op;
2630 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
2631 [ + + ]: 25610 : if (!op->nbsp) {
2632 : 4972 : continue;
2633 : : }
2634 : :
2635 [ - + ]: 20638 : if (!lsp_is_enabled(op->nbsp)) {
2636 : : /* Drop packets from disabled logical ports (since logical flow
2637 : : * tables are default-drop). */
2638 : 0 : continue;
2639 : : }
2640 : :
2641 : 20638 : ds_clear(&match);
2642 : 20638 : ds_put_format(&match, "inport == %s", op->json_key);
2643 : 20638 : build_port_security_l2("eth.src", op->ps_addrs, op->n_ps_addrs,
2644 : : &match);
2645 : 20638 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50,
2646 : 20638 : ds_cstr(&match), "next;");
2647 : :
2648 [ + + ]: 20638 : if (op->nbsp->n_port_security) {
2649 : 1442 : build_port_security_ip(P_IN, op, lflows);
2650 : 1442 : build_port_security_nd(op, lflows);
2651 : : }
2652 : : }
2653 : :
2654 : : /* Ingress table 1 and 2: Port security - IP and ND, by default goto next.
2655 : : * (priority 0)*/
2656 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
2657 [ + + ]: 6323 : if (!od->nbs) {
2658 : 1741 : continue;
2659 : : }
2660 : :
2661 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_ND, 0, "1", "next;");
2662 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 0, "1", "next;");
2663 : : }
2664 : :
2665 : : /* Ingress table 9: ARP/ND responder, skip requests coming from localnet
2666 : : * ports. (priority 100). */
2667 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
2668 [ + + ]: 25610 : if (!op->nbsp) {
2669 : 4972 : continue;
2670 : : }
2671 : :
2672 [ + + ]: 20638 : if (!strcmp(op->nbsp->type, "localnet")) {
2673 : 340 : ds_clear(&match);
2674 : 340 : ds_put_format(&match, "inport == %s", op->json_key);
2675 : 340 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
2676 : 340 : ds_cstr(&match), "next;");
2677 : : }
2678 : : }
2679 : :
2680 : : /* Ingress table 9: ARP/ND responder, reply for known IPs.
2681 : : * (priority 50). */
2682 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
2683 [ + + ]: 25610 : if (!op->nbsp) {
2684 : 4972 : continue;
2685 : : }
2686 : :
2687 : : /*
2688 : : * Add ARP/ND reply flows if either the
2689 : : * - port is up or
2690 : : * - port type is router
2691 : : */
2692 [ + + ][ + + ]: 20638 : if (!lsp_is_up(op->nbsp) && strcmp(op->nbsp->type, "router")) {
2693 : 7309 : continue;
2694 : : }
2695 : :
2696 [ + + ]: 26492 : for (size_t i = 0; i < op->n_lsp_addrs; i++) {
2697 [ + + ]: 21461 : for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
2698 : 8298 : ds_clear(&match);
2699 : 8298 : ds_put_format(&match, "arp.tpa == %s && arp.op == 1",
2700 : 8298 : op->lsp_addrs[i].ipv4_addrs[j].addr_s);
2701 : 8298 : ds_clear(&actions);
2702 : 8298 : ds_put_format(&actions,
2703 : : "eth.dst = eth.src; "
2704 : : "eth.src = %s; "
2705 : : "arp.op = 2; /* ARP reply */ "
2706 : : "arp.tha = arp.sha; "
2707 : : "arp.sha = %s; "
2708 : : "arp.tpa = arp.spa; "
2709 : : "arp.spa = %s; "
2710 : : "outport = inport; "
2711 : : "flags.loopback = 1; "
2712 : : "output;",
2713 : 8298 : op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s,
2714 : 8298 : op->lsp_addrs[i].ipv4_addrs[j].addr_s);
2715 : 8298 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 50,
2716 : 8298 : ds_cstr(&match), ds_cstr(&actions));
2717 : : }
2718 : :
2719 : : /* For ND solicitations, we need to listen for both the
2720 : : * unicast IPv6 address and its all-nodes multicast address,
2721 : : * but always respond with the unicast IPv6 address. */
2722 [ + + ]: 13454 : for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
2723 : 291 : ds_clear(&match);
2724 : 291 : ds_put_format(&match,
2725 : : "nd_ns && ip6.dst == {%s, %s} && nd.target == %s",
2726 : 291 : op->lsp_addrs[i].ipv6_addrs[j].addr_s,
2727 : 291 : op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s,
2728 : 291 : op->lsp_addrs[i].ipv6_addrs[j].addr_s);
2729 : :
2730 : 291 : ds_clear(&actions);
2731 : 291 : ds_put_format(&actions,
2732 : : "nd_na { "
2733 : : "eth.src = %s; "
2734 : : "ip6.src = %s; "
2735 : : "nd.target = %s; "
2736 : : "nd.tll = %s; "
2737 : : "outport = inport; "
2738 : : "flags.loopback = 1; "
2739 : : "output; "
2740 : : "};",
2741 : 291 : op->lsp_addrs[i].ea_s,
2742 : 291 : op->lsp_addrs[i].ipv6_addrs[j].addr_s,
2743 : 291 : op->lsp_addrs[i].ipv6_addrs[j].addr_s,
2744 : 291 : op->lsp_addrs[i].ea_s);
2745 : 291 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 50,
2746 : 291 : ds_cstr(&match), ds_cstr(&actions));
2747 : : }
2748 : : }
2749 : : }
2750 : :
2751 : : /* Ingress table 9: ARP/ND responder, by default goto next.
2752 : : * (priority 0)*/
2753 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
2754 [ + + ]: 6323 : if (!od->nbs) {
2755 : 1741 : continue;
2756 : : }
2757 : :
2758 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;");
2759 : : }
2760 : :
2761 : : /* Logical switch ingress table 10 and 11: DHCP options and response
2762 : : * priority 100 flows. */
2763 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
2764 [ + + ]: 25610 : if (!op->nbsp) {
2765 : 4972 : continue;
2766 : : }
2767 : :
2768 [ + - ][ + + ]: 20638 : if (!lsp_is_enabled(op->nbsp) || !strcmp(op->nbsp->type, "router")) {
2769 : : /* Don't add the DHCP flows if the port is not enabled or if the
2770 : : * port is a router port. */
2771 : 4733 : continue;
2772 : : }
2773 : :
2774 [ + + ][ + + ]: 15905 : if (!op->nbsp->dhcpv4_options && !op->nbsp->dhcpv6_options) {
2775 : : /* CMS has disabled both native DHCPv4 and DHCPv6 for this lport.
2776 : : */
2777 : 15765 : continue;
2778 : : }
2779 : :
2780 [ + + ]: 280 : for (size_t i = 0; i < op->n_lsp_addrs; i++) {
2781 [ + + ]: 197 : for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
2782 : 105 : struct ds options_action = DS_EMPTY_INITIALIZER;
2783 : 105 : struct ds response_action = DS_EMPTY_INITIALIZER;
2784 [ + + ]: 105 : if (build_dhcpv4_action(
2785 : 105 : op, op->lsp_addrs[i].ipv4_addrs[j].addr,
2786 : : &options_action, &response_action)) {
2787 : 48 : struct ds match = DS_EMPTY_INITIALIZER;
2788 : 48 : ds_put_format(
2789 : : &match, "inport == %s && eth.src == %s && "
2790 : : "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
2791 : : "udp.src == 68 && udp.dst == 67", op->json_key,
2792 : 48 : op->lsp_addrs[i].ea_s);
2793 : :
2794 : 48 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
2795 : 48 : 100, ds_cstr(&match),
2796 : 48 : ds_cstr(&options_action));
2797 : : /* If REGBIT_DHCP_OPTS_RESULT is set, it means the
2798 : : * put_dhcp_opts action is successful */
2799 : 48 : ds_put_cstr(&match, " && "REGBIT_DHCP_OPTS_RESULT);
2800 : 48 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE,
2801 : 48 : 100, ds_cstr(&match),
2802 : 48 : ds_cstr(&response_action));
2803 : 48 : ds_destroy(&match);
2804 : 48 : ds_destroy(&options_action);
2805 : 48 : ds_destroy(&response_action);
2806 : 48 : break;
2807 : : }
2808 : : }
2809 : :
2810 [ + + ]: 140 : for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
2811 : 70 : struct ds options_action = DS_EMPTY_INITIALIZER;
2812 : 70 : struct ds response_action = DS_EMPTY_INITIALIZER;
2813 [ + - ]: 70 : if (build_dhcpv6_action(
2814 : 70 : op, &op->lsp_addrs[i].ipv6_addrs[j].addr,
2815 : : &options_action, &response_action)) {
2816 : 70 : struct ds match = DS_EMPTY_INITIALIZER;
2817 : 70 : ds_put_format(
2818 : : &match, "inport == %s && eth.src == %s"
2819 : : " && ip6.dst == ff02::1:2 && udp.src == 546 &&"
2820 : : " udp.dst == 547", op->json_key,
2821 : 70 : op->lsp_addrs[i].ea_s);
2822 : :
2823 : 70 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100,
2824 : 70 : ds_cstr(&match), ds_cstr(&options_action));
2825 : :
2826 : : /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it means the
2827 : : * put_dhcpv6_opts action is successful */
2828 : 70 : ds_put_cstr(&match, " && "REGBIT_DHCP_OPTS_RESULT);
2829 : 70 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE, 100,
2830 : 70 : ds_cstr(&match), ds_cstr(&response_action));
2831 : 70 : ds_destroy(&match);
2832 : 70 : ds_destroy(&options_action);
2833 : 70 : ds_destroy(&response_action);
2834 : 70 : break;
2835 : : }
2836 : : }
2837 : : }
2838 : : }
2839 : :
2840 : : /* Ingress table 10 and 11: DHCP options and response, by default goto next.
2841 : : * (priority 0). */
2842 : :
2843 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
2844 [ + + ]: 6323 : if (!od->nbs) {
2845 : 1741 : continue;
2846 : : }
2847 : :
2848 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_OPTIONS, 0, "1", "next;");
2849 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_RESPONSE, 0, "1", "next;");
2850 : : }
2851 : :
2852 : : /* Ingress table 12: Destination lookup, broadcast and multicast handling
2853 : : * (priority 100). */
2854 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
2855 [ + + ]: 25610 : if (!op->nbsp) {
2856 : 4972 : continue;
2857 : : }
2858 : :
2859 [ + - ]: 20638 : if (lsp_is_enabled(op->nbsp)) {
2860 : 20638 : ovn_multicast_add(mcgroups, &mc_flood, op);
2861 : : }
2862 : : }
2863 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
2864 [ + + ]: 6323 : if (!od->nbs) {
2865 : 1741 : continue;
2866 : : }
2867 : :
2868 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 100, "eth.mcast",
2869 : : "outport = \""MC_FLOOD"\"; output;");
2870 : : }
2871 : :
2872 : : /* Ingress table 12: Destination lookup, unicast handling (priority 50), */
2873 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
2874 [ + + ]: 25610 : if (!op->nbsp) {
2875 : 4972 : continue;
2876 : : }
2877 : :
2878 [ + + ]: 42382 : for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
2879 : : struct eth_addr mac;
2880 : :
2881 [ + + ]: 21744 : if (eth_addr_from_string(op->nbsp->addresses[i], &mac)) {
2882 : 18012 : ds_clear(&match);
2883 : 18012 : ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT,
2884 : 108072 : ETH_ADDR_ARGS(mac));
2885 : :
2886 : 18012 : ds_clear(&actions);
2887 : 18012 : ds_put_format(&actions, "outport = %s; output;", op->json_key);
2888 : 18012 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
2889 : 18012 : ds_cstr(&match), ds_cstr(&actions));
2890 [ + + ]: 3732 : } else if (!strcmp(op->nbsp->addresses[i], "unknown")) {
2891 [ + - ]: 1909 : if (lsp_is_enabled(op->nbsp)) {
2892 : 1909 : ovn_multicast_add(mcgroups, &mc_unknown, op);
2893 : 1909 : op->od->has_unknown = true;
2894 : : }
2895 [ + - ]: 1823 : } else if (!strcmp(op->nbsp->addresses[i], "dynamic")) {
2896 [ + + ]: 1823 : if (!op->nbsp->dynamic_addresses
2897 [ - + ]: 1800 : || !eth_addr_from_string(op->nbsp->dynamic_addresses,
2898 : : &mac)) {
2899 : 23 : continue;
2900 : : }
2901 : 1800 : ds_clear(&match);
2902 : 1800 : ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT,
2903 : 10800 : ETH_ADDR_ARGS(mac));
2904 : :
2905 : 1800 : ds_clear(&actions);
2906 : 1800 : ds_put_format(&actions, "outport = %s; output;", op->json_key);
2907 : 1800 : ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
2908 : 1800 : ds_cstr(&match), ds_cstr(&actions));
2909 : : } else {
2910 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
2911 : :
2912 [ # # ]: 21721 : VLOG_INFO_RL(&rl,
2913 : : "%s: invalid syntax '%s' in addresses column",
2914 : : op->nbsp->name, op->nbsp->addresses[i]);
2915 : : }
2916 : : }
2917 : : }
2918 : :
2919 : : /* Ingress table 12: Destination lookup for unknown MACs (priority 0). */
2920 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
2921 [ + + ]: 6323 : if (!od->nbs) {
2922 : 1741 : continue;
2923 : : }
2924 : :
2925 [ + + ]: 4582 : if (od->has_unknown) {
2926 : 1703 : ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 0, "1",
2927 : : "outport = \""MC_UNKNOWN"\"; output;");
2928 : : }
2929 : : }
2930 : :
2931 : : /* Egress tables 6: Egress port security - IP (priority 0)
2932 : : * Egress table 7: Egress port security L2 - multicast/broadcast
2933 : : * (priority 100). */
2934 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
2935 [ + + ]: 6323 : if (!od->nbs) {
2936 : 1741 : continue;
2937 : : }
2938 : :
2939 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_IP, 0, "1", "next;");
2940 : 4582 : ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_L2, 100, "eth.mcast",
2941 : : "output;");
2942 : : }
2943 : :
2944 : : /* Egress table 6: Egress port security - IP (priorities 90 and 80)
2945 : : * if port security enabled.
2946 : : *
2947 : : * Egress table 7: Egress port security - L2 (priorities 50 and 150).
2948 : : *
2949 : : * Priority 50 rules implement port security for enabled logical port.
2950 : : *
2951 : : * Priority 150 rules drop packets to disabled logical ports, so that they
2952 : : * don't even receive multicast or broadcast packets. */
2953 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
2954 [ + + ]: 25610 : if (!op->nbsp) {
2955 : 4972 : continue;
2956 : : }
2957 : :
2958 : 20638 : ds_clear(&match);
2959 : 20638 : ds_put_format(&match, "outport == %s", op->json_key);
2960 [ + - ]: 20638 : if (lsp_is_enabled(op->nbsp)) {
2961 : 20638 : build_port_security_l2("eth.dst", op->ps_addrs, op->n_ps_addrs,
2962 : : &match);
2963 : 20638 : ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 50,
2964 : 20638 : ds_cstr(&match), "output;");
2965 : : } else {
2966 : 0 : ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 150,
2967 : 0 : ds_cstr(&match), "drop;");
2968 : : }
2969 : :
2970 [ + + ]: 20638 : if (op->nbsp->n_port_security) {
2971 : 1442 : build_port_security_ip(P_OUT, op, lflows);
2972 : : }
2973 : : }
2974 : :
2975 : 2244 : ds_destroy(&match);
2976 : 2244 : ds_destroy(&actions);
2977 : 2244 : }
2978 : :
2979 : : static bool
2980 : 9944 : lrport_is_enabled(const struct nbrec_logical_router_port *lrport)
2981 : : {
2982 [ - + ][ # # ]: 9944 : return !lrport->enabled || *lrport->enabled;
2983 : : }
2984 : :
2985 : : /* Returns a string of the IP address of the router port 'op' that
2986 : : * overlaps with 'ip_s". If one is not found, returns NULL.
2987 : : *
2988 : : * The caller must not free the returned string. */
2989 : : static const char *
2990 : 27481 : find_lrp_member_ip(const struct ovn_port *op, const char *ip_s)
2991 : : {
2992 : 27481 : bool is_ipv4 = strchr(ip_s, '.') ? true : false;
2993 : :
2994 [ + - ]: 27481 : if (is_ipv4) {
2995 : : ovs_be32 ip;
2996 : :
2997 [ - + ]: 27481 : if (!ip_parse(ip_s, &ip)) {
2998 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
2999 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad ip address %s", ip_s);
3000 : 10583 : return NULL;
3001 : : }
3002 : :
3003 [ + + ]: 44392 : for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
3004 : 27494 : const struct ipv4_netaddr *na = &op->lrp_networks.ipv4_addrs[i];
3005 : :
3006 [ + + ]: 27494 : if (!((na->network ^ ip) & na->mask)) {
3007 : : /* There should be only 1 interface that matches the
3008 : : * supplied IP. Otherwise, it's a configuration error,
3009 : : * because subnets of a router's interfaces should NOT
3010 : : * overlap. */
3011 : 10583 : return na->addr_s;
3012 : : }
3013 : : }
3014 : : } else {
3015 : : struct in6_addr ip6;
3016 : :
3017 [ # # ]: 0 : if (!ipv6_parse(ip_s, &ip6)) {
3018 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
3019 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad ipv6 address %s", ip_s);
3020 : 0 : return NULL;
3021 : : }
3022 : :
3023 [ # # ]: 0 : for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
3024 : 0 : const struct ipv6_netaddr *na = &op->lrp_networks.ipv6_addrs[i];
3025 : 0 : struct in6_addr xor_addr = ipv6_addr_bitxor(&na->network, &ip6);
3026 : 0 : struct in6_addr and_addr = ipv6_addr_bitand(&xor_addr, &na->mask);
3027 : :
3028 [ # # ]: 0 : if (ipv6_is_zero(&and_addr)) {
3029 : : /* There should be only 1 interface that matches the
3030 : : * supplied IP. Otherwise, it's a configuration error,
3031 : : * because subnets of a router's interfaces should NOT
3032 : : * overlap. */
3033 : 0 : return na->addr_s;
3034 : : }
3035 : : }
3036 : : }
3037 : :
3038 : 16898 : return NULL;
3039 : : }
3040 : :
3041 : : static void
3042 : 10666 : add_route(struct hmap *lflows, const struct ovn_port *op,
3043 : : const char *lrp_addr_s, const char *network_s, int plen,
3044 : : const char *gateway)
3045 : : {
3046 : 10666 : bool is_ipv4 = strchr(network_s, '.') ? true : false;
3047 : 10666 : struct ds match = DS_EMPTY_INITIALIZER;
3048 : :
3049 : : /* IPv6 link-local addresses must be scoped to the local router port. */
3050 [ + + ]: 10666 : if (!is_ipv4) {
3051 : : struct in6_addr network;
3052 [ - + ]: 4972 : ovs_assert(ipv6_parse(network_s, &network));
3053 [ + - ]: 4972 : if (in6_is_lla(&network)) {
3054 : 4972 : ds_put_format(&match, "inport == %s && ", op->json_key);
3055 : : }
3056 : : }
3057 [ + + ]: 10666 : ds_put_format(&match, "ip%s.dst == %s/%d", is_ipv4 ? "4" : "6",
3058 : : network_s, plen);
3059 : :
3060 : 10666 : struct ds actions = DS_EMPTY_INITIALIZER;
3061 [ + + ]: 10666 : ds_put_format(&actions, "ip.ttl--; %sreg0 = ", is_ipv4 ? "" : "xx");
3062 : :
3063 [ + + ]: 10666 : if (gateway) {
3064 : 705 : ds_put_cstr(&actions, gateway);
3065 : : } else {
3066 [ + + ]: 9961 : ds_put_format(&actions, "ip%s.dst", is_ipv4 ? "4" : "6");
3067 : : }
3068 [ + + ]: 10666 : ds_put_format(&actions, "; "
3069 : : "%sreg1 = %s; "
3070 : : "eth.src = %s; "
3071 : : "outport = %s; "
3072 : : "flags.loopback = 1; "
3073 : : "next;",
3074 : : is_ipv4 ? "" : "xx",
3075 : : lrp_addr_s,
3076 : 10666 : op->lrp_networks.ea_s,
3077 : : op->json_key);
3078 : :
3079 : : /* The priority here is calculated to implement longest-prefix-match
3080 : : * routing. */
3081 : 10666 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, plen,
3082 : 10666 : ds_cstr(&match), ds_cstr(&actions));
3083 : 10666 : ds_destroy(&match);
3084 : 10666 : ds_destroy(&actions);
3085 : 10666 : }
3086 : :
3087 : : static void
3088 : 705 : build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
3089 : : struct hmap *ports,
3090 : : const struct nbrec_logical_router_static_route *route)
3091 : : {
3092 : : ovs_be32 nexthop;
3093 : : const char *lrp_addr_s;
3094 : : unsigned int plen;
3095 : : bool is_ipv4;
3096 : :
3097 : : /* Verify that the next hop is an IP address with an all-ones mask. */
3098 : 705 : char *error = ip_parse_cidr(route->nexthop, &nexthop, &plen);
3099 [ + - ]: 705 : if (!error) {
3100 [ - + ]: 705 : if (plen != 32) {
3101 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
3102 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad next hop mask %s", route->nexthop);
3103 : 0 : return;
3104 : : }
3105 : 705 : is_ipv4 = true;
3106 : : } else {
3107 : 0 : free(error);
3108 : :
3109 : : struct in6_addr ip6;
3110 : 0 : char *error = ipv6_parse_cidr(route->nexthop, &ip6, &plen);
3111 [ # # ]: 0 : if (!error) {
3112 [ # # ]: 0 : if (plen != 128) {
3113 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
3114 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad next hop mask %s", route->nexthop);
3115 : 0 : return;
3116 : : }
3117 : 0 : is_ipv4 = false;
3118 : : } else {
3119 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
3120 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad next hop ip address %s", route->nexthop);
3121 : 0 : free(error);
3122 : 0 : return;
3123 : : }
3124 : : }
3125 : :
3126 : : char *prefix_s;
3127 [ + - ]: 705 : if (is_ipv4) {
3128 : : ovs_be32 prefix;
3129 : : /* Verify that ip prefix is a valid IPv4 address. */
3130 : 705 : error = ip_parse_cidr(route->ip_prefix, &prefix, &plen);
3131 [ - + ]: 705 : if (error) {
3132 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
3133 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad 'ip_prefix' in static routes %s",
3134 : : route->ip_prefix);
3135 : 0 : free(error);
3136 : 0 : return;
3137 : : }
3138 : 705 : prefix_s = xasprintf(IP_FMT, IP_ARGS(prefix & be32_prefix_mask(plen)));
3139 : : } else {
3140 : : /* Verify that ip prefix is a valid IPv6 address. */
3141 : : struct in6_addr prefix;
3142 : 0 : error = ipv6_parse_cidr(route->ip_prefix, &prefix, &plen);
3143 [ # # ]: 0 : if (error) {
3144 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
3145 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad 'ip_prefix' in static routes %s",
3146 : : route->ip_prefix);
3147 : 0 : free(error);
3148 : 0 : return;
3149 : : }
3150 : 0 : struct in6_addr mask = ipv6_create_mask(plen);
3151 : 0 : struct in6_addr network = ipv6_addr_bitand(&prefix, &mask);
3152 : 0 : prefix_s = xmalloc(INET6_ADDRSTRLEN);
3153 : 0 : inet_ntop(AF_INET6, &network, prefix_s, INET6_ADDRSTRLEN);
3154 : : }
3155 : :
3156 : : /* Find the outgoing port. */
3157 : 705 : struct ovn_port *out_port = NULL;
3158 [ + + ]: 705 : if (route->output_port) {
3159 : 58 : out_port = ovn_port_find(ports, route->output_port);
3160 [ - + ]: 58 : if (!out_port) {
3161 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
3162 [ # # ]: 0 : VLOG_WARN_RL(&rl, "Bad out port %s for static route %s",
3163 : : route->output_port, route->ip_prefix);
3164 : 0 : goto free_prefix_s;
3165 : : }
3166 : 58 : lrp_addr_s = find_lrp_member_ip(out_port, route->nexthop);
3167 : : } else {
3168 : : /* output_port is not specified, find the
3169 : : * router port matching the next hop. */
3170 : : int i;
3171 [ + - ]: 1021 : for (i = 0; i < od->nbr->n_ports; i++) {
3172 : 1021 : struct nbrec_logical_router_port *lrp = od->nbr->ports[i];
3173 : 1021 : out_port = ovn_port_find(ports, lrp->name);
3174 [ - + ]: 1021 : if (!out_port) {
3175 : : /* This should not happen. */
3176 : 0 : continue;
3177 : : }
3178 : :
3179 : 1021 : lrp_addr_s = find_lrp_member_ip(out_port, route->nexthop);
3180 [ + + ]: 1021 : if (lrp_addr_s) {
3181 : 647 : break;
3182 : : }
3183 : : }
3184 : : }
3185 : :
3186 [ - + ]: 705 : if (!lrp_addr_s) {
3187 : : /* There is no matched out port. */
3188 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
3189 [ # # ]: 0 : VLOG_WARN_RL(&rl, "No path for static route %s; next hop %s",
3190 : : route->ip_prefix, route->nexthop);
3191 : 0 : goto free_prefix_s;
3192 : : }
3193 : :
3194 : 705 : add_route(lflows, out_port, lrp_addr_s, prefix_s, plen, route->nexthop);
3195 : :
3196 : : free_prefix_s:
3197 : 705 : free(prefix_s);
3198 : : }
3199 : :
3200 : : static void
3201 : 16136 : op_put_v4_networks(struct ds *ds, const struct ovn_port *op, bool add_bcast)
3202 : : {
3203 [ + + ][ + + ]: 16136 : if (!add_bcast && op->lrp_networks.n_ipv4_addrs == 1) {
3204 : 11147 : ds_put_format(ds, "%s", op->lrp_networks.ipv4_addrs[0].addr_s);
3205 : 11147 : return;
3206 : : }
3207 : :
3208 : 4989 : ds_put_cstr(ds, "{");
3209 [ + + ]: 10012 : for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
3210 : 5023 : ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].addr_s);
3211 [ + + ]: 5023 : if (add_bcast) {
3212 : 4989 : ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].bcast_s);
3213 : : }
3214 : : }
3215 : 4989 : ds_chomp(ds, ' ');
3216 : 4989 : ds_chomp(ds, ',');
3217 : 4989 : ds_put_cstr(ds, "}");
3218 : : }
3219 : :
3220 : : static void
3221 : 21108 : op_put_v6_networks(struct ds *ds, const struct ovn_port *op)
3222 : : {
3223 [ + - ]: 21108 : if (op->lrp_networks.n_ipv6_addrs == 1) {
3224 : 21108 : ds_put_format(ds, "%s", op->lrp_networks.ipv6_addrs[0].addr_s);
3225 : 21108 : return;
3226 : : }
3227 : :
3228 : 0 : ds_put_cstr(ds, "{");
3229 [ # # ]: 0 : for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
3230 : 0 : ds_put_format(ds, "%s, ", op->lrp_networks.ipv6_addrs[i].addr_s);
3231 : : }
3232 : 0 : ds_chomp(ds, ' ');
3233 : 0 : ds_chomp(ds, ',');
3234 : 0 : ds_put_cstr(ds, "}");
3235 : : }
3236 : :
3237 : : static void
3238 : 2244 : build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
3239 : : struct hmap *lflows)
3240 : : {
3241 : : /* This flow table structure is documented in ovn-northd(8), so please
3242 : : * update ovn-northd.8.xml if you change anything. */
3243 : :
3244 : 2244 : struct ds match = DS_EMPTY_INITIALIZER;
3245 : 2244 : struct ds actions = DS_EMPTY_INITIALIZER;
3246 : :
3247 : : /* Logical router ingress table 0: Admission control framework. */
3248 : : struct ovn_datapath *od;
3249 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
3250 [ + + ]: 6323 : if (!od->nbr) {
3251 : 4582 : continue;
3252 : : }
3253 : :
3254 : : /* Logical VLANs not supported.
3255 : : * Broadcast/multicast source address is invalid. */
3256 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_ADMISSION, 100,
3257 : : "vlan.present || eth.src[40]", "drop;");
3258 : : }
3259 : :
3260 : : /* Logical router ingress table 0: match (priority 50). */
3261 : : struct ovn_port *op;
3262 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
3263 [ + + ]: 25610 : if (!op->nbrp) {
3264 : 20638 : continue;
3265 : : }
3266 : :
3267 [ - + ]: 4972 : if (!lrport_is_enabled(op->nbrp)) {
3268 : : /* Drop packets from disabled logical ports (since logical flow
3269 : : * tables are default-drop). */
3270 : 0 : continue;
3271 : : }
3272 : :
3273 : 4972 : ds_clear(&match);
3274 : 4972 : ds_put_format(&match, "(eth.mcast || eth.dst == %s) && inport == %s",
3275 : 4972 : op->lrp_networks.ea_s, op->json_key);
3276 : 4972 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
3277 : 4972 : ds_cstr(&match), "next;");
3278 : : }
3279 : :
3280 : : /* Logical router ingress table 1: IP Input. */
3281 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
3282 [ + + ]: 6323 : if (!od->nbr) {
3283 : 4582 : continue;
3284 : : }
3285 : :
3286 : : /* L3 admission control: drop multicast and broadcast source, localhost
3287 : : * source or destination, and zero network source or destination
3288 : : * (priority 100). */
3289 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 100,
3290 : : "ip4.mcast || "
3291 : : "ip4.src == 255.255.255.255 || "
3292 : : "ip4.src == 127.0.0.0/8 || "
3293 : : "ip4.dst == 127.0.0.0/8 || "
3294 : : "ip4.src == 0.0.0.0/8 || "
3295 : : "ip4.dst == 0.0.0.0/8",
3296 : : "drop;");
3297 : :
3298 : : /* ARP reply handling. Use ARP replies to populate the logical
3299 : : * router's ARP table. */
3300 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "arp.op == 2",
3301 : : "put_arp(inport, arp.spa, arp.sha);");
3302 : :
3303 : : /* Drop Ethernet local broadcast. By definition this traffic should
3304 : : * not be forwarded.*/
3305 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 50,
3306 : : "eth.bcast", "drop;");
3307 : :
3308 : : /* TTL discard.
3309 : : *
3310 : : * XXX Need to send ICMP time exceeded if !ip.later_frag. */
3311 : 1741 : ds_clear(&match);
3312 : 1741 : ds_put_cstr(&match, "ip4 && ip.ttl == {0, 1}");
3313 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30,
3314 : 1741 : ds_cstr(&match), "drop;");
3315 : :
3316 : : /* ND advertisement handling. Use advertisements to populate
3317 : : * the logical router's ARP/ND table. */
3318 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "nd_na",
3319 : : "put_nd(inport, nd.target, nd.tll);");
3320 : :
3321 : : /* Lean from neighbor solicitations that were not directed at
3322 : : * us. (A priority-90 flow will respond to requests to us and
3323 : : * learn the sender's mac address. */
3324 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 80, "nd_ns",
3325 : : "put_nd(inport, ip6.src, nd.sll);");
3326 : :
3327 : : /* Pass other traffic not already handled to the next table for
3328 : : * routing. */
3329 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 0, "1", "next;");
3330 : : }
3331 : :
3332 : : /* Logical router ingress table 1: IP Input for IPv4. */
3333 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
3334 [ + + ]: 25610 : if (!op->nbrp) {
3335 : 20638 : continue;
3336 : : }
3337 : :
3338 : :
3339 [ + - ]: 4972 : if (op->lrp_networks.n_ipv4_addrs) {
3340 : : /* L3 admission control: drop packets that originate from an
3341 : : * IPv4 address owned by the router or a broadcast address
3342 : : * known to the router (priority 100). */
3343 : 4972 : ds_clear(&match);
3344 : 4972 : ds_put_cstr(&match, "ip4.src == ");
3345 : 4972 : op_put_v4_networks(&match, op, true);
3346 : 4972 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
3347 : 4972 : ds_cstr(&match), "drop;");
3348 : :
3349 : : /* ICMP echo reply. These flows reply to ICMP echo requests
3350 : : * received for the router's IP address. Since packets only
3351 : : * get here as part of the logical router datapath, the inport
3352 : : * (i.e. the incoming locally attached net) does not matter.
3353 : : * The ip.ttl also does not matter (RFC1812 section 4.2.2.9) */
3354 : 4972 : ds_clear(&match);
3355 : 4972 : ds_put_cstr(&match, "ip4.dst == ");
3356 : 4972 : op_put_v4_networks(&match, op, false);
3357 : 4972 : ds_put_cstr(&match, " && icmp4.type == 8 && icmp4.code == 0");
3358 : :
3359 : 4972 : ds_clear(&actions);
3360 : 4972 : ds_put_format(&actions,
3361 : : "ip4.dst <-> ip4.src; "
3362 : : "ip.ttl = 255; "
3363 : : "icmp4.type = 0; "
3364 : : "flags.loopback = 1; "
3365 : : "next; ");
3366 : 4972 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
3367 : 4972 : ds_cstr(&match), ds_cstr(&actions));
3368 : : }
3369 : :
3370 : : /* ARP reply. These flows reply to ARP requests for the router's own
3371 : : * IP address. */
3372 [ + + ]: 9961 : for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
3373 : 4989 : ds_clear(&match);
3374 : 4989 : ds_put_format(&match,
3375 : : "inport == %s && arp.tpa == %s && arp.op == 1",
3376 : 4989 : op->json_key, op->lrp_networks.ipv4_addrs[i].addr_s);
3377 : :
3378 : 4989 : ds_clear(&actions);
3379 : 4989 : ds_put_format(&actions,
3380 : : "eth.dst = eth.src; "
3381 : : "eth.src = %s; "
3382 : : "arp.op = 2; /* ARP reply */ "
3383 : : "arp.tha = arp.sha; "
3384 : : "arp.sha = %s; "
3385 : : "arp.tpa = arp.spa; "
3386 : : "arp.spa = %s; "
3387 : : "outport = %s; "
3388 : : "flags.loopback = 1; "
3389 : : "output;",
3390 : 4989 : op->lrp_networks.ea_s,
3391 : 4989 : op->lrp_networks.ea_s,
3392 : 4989 : op->lrp_networks.ipv4_addrs[i].addr_s,
3393 : : op->json_key);
3394 : 4989 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
3395 : 4989 : ds_cstr(&match), ds_cstr(&actions));
3396 : : }
3397 : :
3398 : : /* A set to hold all load-balancer vips that need ARP responses. */
3399 : 4972 : struct sset all_ips = SSET_INITIALIZER(&all_ips);
3400 : :
3401 [ + + ]: 5000 : for (int i = 0; i < op->od->nbr->n_load_balancer; i++) {
3402 : 28 : struct nbrec_load_balancer *lb = op->od->nbr->load_balancer[i];
3403 : 28 : struct smap *vips = &lb->vips;
3404 : : struct smap_node *node;
3405 : :
3406 [ + + ][ - + ]: 80 : SMAP_FOR_EACH (node, vips) {
3407 : : /* node->key contains IP:port or just IP. */
3408 : 52 : char *ip_address = NULL;
3409 : : uint16_t port;
3410 : :
3411 : 52 : ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
3412 [ - + ]: 52 : if (!ip_address) {
3413 : 0 : continue;
3414 : : }
3415 : :
3416 [ + - ]: 52 : if (!sset_contains(&all_ips, ip_address)) {
3417 : 52 : sset_add(&all_ips, ip_address);
3418 : : }
3419 : :
3420 : 52 : free(ip_address);
3421 : : }
3422 : : }
3423 : :
3424 : : const char *ip_address;
3425 [ + + ][ + + ]: 5024 : SSET_FOR_EACH(ip_address, &all_ips) {
[ + + ]
3426 : : ovs_be32 ip;
3427 [ + - ][ - + ]: 52 : if (!ip_parse(ip_address, &ip) || !ip) {
3428 : 0 : continue;
3429 : : }
3430 : :
3431 : 52 : ds_clear(&match);
3432 : 52 : ds_put_format(&match,
3433 : : "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
3434 : 52 : op->json_key, IP_ARGS(ip));
3435 : :
3436 : 52 : ds_clear(&actions);
3437 : 52 : ds_put_format(&actions,
3438 : : "eth.dst = eth.src; "
3439 : : "eth.src = %s; "
3440 : : "arp.op = 2; /* ARP reply */ "
3441 : : "arp.tha = arp.sha; "
3442 : : "arp.sha = %s; "
3443 : : "arp.tpa = arp.spa; "
3444 : : "arp.spa = "IP_FMT"; "
3445 : : "outport = %s; "
3446 : : "flags.loopback = 1; "
3447 : : "output;",
3448 : 52 : op->lrp_networks.ea_s,
3449 : 52 : op->lrp_networks.ea_s,
3450 : 52 : IP_ARGS(ip),
3451 : : op->json_key);
3452 : 52 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
3453 : 52 : ds_cstr(&match), ds_cstr(&actions));
3454 : : }
3455 : :
3456 : 4972 : sset_destroy(&all_ips);
3457 : :
3458 : 4972 : ovs_be32 *snat_ips = xmalloc(sizeof *snat_ips * op->od->nbr->n_nat);
3459 : 4972 : size_t n_snat_ips = 0;
3460 [ + + ]: 5074 : for (int i = 0; i < op->od->nbr->n_nat; i++) {
3461 : : const struct nbrec_nat *nat;
3462 : :
3463 : 102 : nat = op->od->nbr->nat[i];
3464 : :
3465 : : ovs_be32 ip;
3466 [ + - ][ - + ]: 102 : if (!ip_parse(nat->external_ip, &ip) || !ip) {
3467 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
3468 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
3469 : : "for router %s", nat->external_ip, op->key);
3470 : 62 : continue;
3471 : : }
3472 : :
3473 [ + + ]: 102 : if (!strcmp(nat->type, "snat")) {
3474 : 62 : snat_ips[n_snat_ips++] = ip;
3475 : 62 : continue;
3476 : : }
3477 : :
3478 : : /* ARP handling for external IP addresses.
3479 : : *
3480 : : * DNAT IP addresses are external IP addresses that need ARP
3481 : : * handling. */
3482 : 40 : ds_clear(&match);
3483 : 40 : ds_put_format(&match,
3484 : : "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
3485 : 40 : op->json_key, IP_ARGS(ip));
3486 : :
3487 : 40 : ds_clear(&actions);
3488 : 40 : ds_put_format(&actions,
3489 : : "eth.dst = eth.src; "
3490 : : "eth.src = %s; "
3491 : : "arp.op = 2; /* ARP reply */ "
3492 : : "arp.tha = arp.sha; "
3493 : : "arp.sha = %s; "
3494 : : "arp.tpa = arp.spa; "
3495 : : "arp.spa = "IP_FMT"; "
3496 : : "outport = %s; "
3497 : : "flags.loopback = 1; "
3498 : : "output;",
3499 : 40 : op->lrp_networks.ea_s,
3500 : 40 : op->lrp_networks.ea_s,
3501 : 40 : IP_ARGS(ip),
3502 : : op->json_key);
3503 : 40 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
3504 : 40 : ds_cstr(&match), ds_cstr(&actions));
3505 : : }
3506 : :
3507 : 4972 : ds_clear(&match);
3508 : 4972 : ds_put_cstr(&match, "ip4.dst == {");
3509 : 4972 : bool has_drop_ips = false;
3510 [ + + ]: 9961 : for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
3511 : 4989 : bool snat_ip_is_router_ip = false;
3512 [ + + ]: 5035 : for (int j = 0; j < n_snat_ips; j++) {
3513 : : /* Packets to SNAT IPs should not be dropped. */
3514 [ + + ]: 62 : if (op->lrp_networks.ipv4_addrs[i].addr == snat_ips[j]) {
3515 : 16 : snat_ip_is_router_ip = true;
3516 : 16 : break;
3517 : : }
3518 : : }
3519 [ + + ]: 4989 : if (snat_ip_is_router_ip) {
3520 : 16 : continue;
3521 : : }
3522 : 4973 : ds_put_format(&match, "%s, ",
3523 : 4973 : op->lrp_networks.ipv4_addrs[i].addr_s);
3524 : 4973 : has_drop_ips = true;
3525 : : }
3526 : 4972 : ds_chomp(&match, ' ');
3527 : 4972 : ds_chomp(&match, ',');
3528 : 4972 : ds_put_cstr(&match, "}");
3529 : :
3530 [ + + ]: 4972 : if (has_drop_ips) {
3531 : : /* Drop IP traffic to this router. */
3532 : 4956 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
3533 : 4956 : ds_cstr(&match), "drop;");
3534 : : }
3535 : :
3536 : 4972 : free(snat_ips);
3537 : : }
3538 : :
3539 : : /* Logical router ingress table 1: IP Input for IPv6. */
3540 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
3541 [ + + ]: 25610 : if (!op->nbrp) {
3542 : 20638 : continue;
3543 : : }
3544 : :
3545 [ + - ]: 4972 : if (op->lrp_networks.n_ipv6_addrs) {
3546 : : /* L3 admission control: drop packets that originate from an
3547 : : * IPv6 address owned by the router (priority 100). */
3548 : 4972 : ds_clear(&match);
3549 : 4972 : ds_put_cstr(&match, "ip6.src == ");
3550 : 4972 : op_put_v6_networks(&match, op);
3551 : 4972 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
3552 : 4972 : ds_cstr(&match), "drop;");
3553 : :
3554 : : /* ICMPv6 echo reply. These flows reply to echo requests
3555 : : * received for the router's IP address. */
3556 : 4972 : ds_clear(&match);
3557 : 4972 : ds_put_cstr(&match, "ip6.dst == ");
3558 : 4972 : op_put_v6_networks(&match, op);
3559 : 4972 : ds_put_cstr(&match, " && icmp6.type == 128 && icmp6.code == 0");
3560 : :
3561 : 4972 : ds_clear(&actions);
3562 : 4972 : ds_put_cstr(&actions,
3563 : : "ip6.dst <-> ip6.src; "
3564 : : "ip.ttl = 255; "
3565 : : "icmp6.type = 129; "
3566 : : "flags.loopback = 1; "
3567 : : "next; ");
3568 : 4972 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
3569 : 4972 : ds_cstr(&match), ds_cstr(&actions));
3570 : :
3571 : : /* Drop IPv6 traffic to this router. */
3572 : 4972 : ds_clear(&match);
3573 : 4972 : ds_put_cstr(&match, "ip6.dst == ");
3574 : 4972 : op_put_v6_networks(&match, op);
3575 : 4972 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
3576 : 4972 : ds_cstr(&match), "drop;");
3577 : : }
3578 : :
3579 : : /* ND reply. These flows reply to ND solicitations for the
3580 : : * router's own IP address. */
3581 [ + + ]: 9944 : for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
3582 : 4972 : ds_clear(&match);
3583 : 4972 : ds_put_format(&match,
3584 : : "inport == %s && nd_ns && ip6.dst == {%s, %s} "
3585 : : "&& nd.target == %s",
3586 : : op->json_key,
3587 : 4972 : op->lrp_networks.ipv6_addrs[i].addr_s,
3588 : 4972 : op->lrp_networks.ipv6_addrs[i].sn_addr_s,
3589 : 4972 : op->lrp_networks.ipv6_addrs[i].addr_s);
3590 : :
3591 : 4972 : ds_clear(&actions);
3592 : 4972 : ds_put_format(&actions,
3593 : : "put_nd(inport, ip6.src, nd.sll); "
3594 : : "nd_na { "
3595 : : "eth.src = %s; "
3596 : : "ip6.src = %s; "
3597 : : "nd.target = %s; "
3598 : : "nd.tll = %s; "
3599 : : "outport = inport; "
3600 : : "flags.loopback = 1; "
3601 : : "output; "
3602 : : "};",
3603 : 4972 : op->lrp_networks.ea_s,
3604 : 4972 : op->lrp_networks.ipv6_addrs[i].addr_s,
3605 : 4972 : op->lrp_networks.ipv6_addrs[i].addr_s,
3606 : 4972 : op->lrp_networks.ea_s);
3607 : 4972 : ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
3608 : 4972 : ds_cstr(&match), ds_cstr(&actions));
3609 : : }
3610 : : }
3611 : :
3612 : : /* NAT, Defrag and load balancing in Gateway routers. */
3613 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
3614 [ + + ]: 6323 : if (!od->nbr) {
3615 : 6021 : continue;
3616 : : }
3617 : :
3618 : : /* Packets are allowed by default. */
3619 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 0, "1", "next;");
3620 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 0, "1", "next;");
3621 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 0, "1", "next;");
3622 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 0, "1", "next;");
3623 : :
3624 : : /* NAT rules, packet defrag and load balancing are only valid on
3625 : : * Gateway routers. */
3626 [ + + ]: 1741 : if (!smap_get(&od->nbr->options, "chassis")) {
3627 : 1439 : continue;
3628 : : }
3629 : :
3630 : : /* A set to hold all ips that need defragmentation and tracking. */
3631 : 302 : struct sset all_ips = SSET_INITIALIZER(&all_ips);
3632 : :
3633 [ + + ]: 316 : for (int i = 0; i < od->nbr->n_load_balancer; i++) {
3634 : 14 : struct nbrec_load_balancer *lb = od->nbr->load_balancer[i];
3635 : 14 : struct smap *vips = &lb->vips;
3636 : : struct smap_node *node;
3637 : :
3638 [ + + ][ - + ]: 40 : SMAP_FOR_EACH (node, vips) {
3639 : 26 : uint16_t port = 0;
3640 : :
3641 : : /* node->key contains IP:port or just IP. */
3642 : 26 : char *ip_address = NULL;
3643 : 26 : ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
3644 [ - + ]: 26 : if (!ip_address) {
3645 : 0 : continue;
3646 : : }
3647 : :
3648 [ + - ]: 26 : if (!sset_contains(&all_ips, ip_address)) {
3649 : 26 : sset_add(&all_ips, ip_address);
3650 : : }
3651 : :
3652 : : /* Higher priority rules are added in DNAT table to match on
3653 : : * ct.new which in-turn have group id as an action for load
3654 : : * balancing. */
3655 : 26 : ds_clear(&actions);
3656 : 26 : ds_put_format(&actions, "ct_lb(%s);", node->value);
3657 : :
3658 : 26 : ds_clear(&match);
3659 : 26 : ds_put_format(&match, "ct.new && ip && ip4.dst == %s",
3660 : : ip_address);
3661 : 26 : free(ip_address);
3662 : :
3663 [ + + ]: 26 : if (port) {
3664 [ - + ][ # # ]: 12 : if (lb->protocol && !strcmp(lb->protocol, "udp")) {
3665 : 0 : ds_put_format(&match, "&& udp && udp.dst == %d", port);
3666 : : } else {
3667 : 12 : ds_put_format(&match, "&& tcp && tcp.dst == %d", port);
3668 : : }
3669 : 12 : ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT,
3670 : 12 : 120, ds_cstr(&match), ds_cstr(&actions));
3671 : : } else {
3672 : 26 : ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT,
3673 : 14 : 110, ds_cstr(&match), ds_cstr(&actions));
3674 : : }
3675 : : }
3676 : : }
3677 : :
3678 : : /* If there are any load balancing rules, we should send the
3679 : : * packet to conntrack for defragmentation and tracking. This helps
3680 : : * with two things.
3681 : : *
3682 : : * 1. With tracking, we can send only new connections to pick a
3683 : : * DNAT ip address from a group.
3684 : : * 2. If there are L4 ports in load balancing rules, we need the
3685 : : * defragmentation to match on L4 ports. */
3686 : : const char *ip_address;
3687 [ + + ][ + + ]: 328 : SSET_FOR_EACH(ip_address, &all_ips) {
[ + + ]
3688 : 26 : ds_clear(&match);
3689 : 26 : ds_put_format(&match, "ip && ip4.dst == %s", ip_address);
3690 : 26 : ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
3691 : 26 : 100, ds_cstr(&match), "ct_next;");
3692 : : }
3693 : :
3694 : 302 : sset_destroy(&all_ips);
3695 : :
3696 [ + + ]: 353 : for (int i = 0; i < od->nbr->n_nat; i++) {
3697 : : const struct nbrec_nat *nat;
3698 : :
3699 : 51 : nat = od->nbr->nat[i];
3700 : :
3701 : : ovs_be32 ip, mask;
3702 : :
3703 : 51 : char *error = ip_parse_masked(nat->external_ip, &ip, &mask);
3704 [ + - ][ - + ]: 51 : if (error || mask != OVS_BE32_MAX) {
3705 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
3706 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad external ip %s for nat",
3707 : : nat->external_ip);
3708 : 0 : free(error);
3709 : 0 : continue;
3710 : : }
3711 : :
3712 : : /* Check the validity of nat->logical_ip. 'logical_ip' can
3713 : : * be a subnet when the type is "snat". */
3714 : 51 : error = ip_parse_masked(nat->logical_ip, &ip, &mask);
3715 [ + + ]: 51 : if (!strcmp(nat->type, "snat")) {
3716 [ - + ]: 31 : if (error) {
3717 : : static struct vlog_rate_limit rl =
3718 : : VLOG_RATE_LIMIT_INIT(5, 1);
3719 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad ip network or ip %s for snat "
3720 : : "in router "UUID_FMT"",
3721 : : nat->logical_ip, UUID_ARGS(&od->key));
3722 : 0 : free(error);
3723 : 0 : continue;
3724 : : }
3725 : : } else {
3726 [ + - ][ - + ]: 20 : if (error || mask != OVS_BE32_MAX) {
3727 : : static struct vlog_rate_limit rl =
3728 : : VLOG_RATE_LIMIT_INIT(5, 1);
3729 [ # # ]: 0 : VLOG_WARN_RL(&rl, "bad ip %s for dnat in router "
3730 : : ""UUID_FMT"", nat->logical_ip, UUID_ARGS(&od->key));
3731 : 0 : free(error);
3732 : 0 : continue;
3733 : : }
3734 : : }
3735 : :
3736 : : /* Ingress UNSNAT table: It is for already established connections'
3737 : : * reverse traffic. i.e., SNAT has already been done in egress
3738 : : * pipeline and now the packet has entered the ingress pipeline as
3739 : : * part of a reply. We undo the SNAT here.
3740 : : *
3741 : : * Undoing SNAT has to happen before DNAT processing. This is
3742 : : * because when the packet was DNATed in ingress pipeline, it did
3743 : : * not know about the possibility of eventual additional SNAT in
3744 : : * egress pipeline. */
3745 [ + + ]: 51 : if (!strcmp(nat->type, "snat")
3746 [ - + ]: 20 : || !strcmp(nat->type, "dnat_and_snat")) {
3747 : 31 : ds_clear(&match);
3748 : 31 : ds_put_format(&match, "ip && ip4.dst == %s", nat->external_ip);
3749 : 31 : ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
3750 : 31 : ds_cstr(&match), "ct_snat; next;");
3751 : : }
3752 : :
3753 : : /* Ingress DNAT table: Packets enter the pipeline with destination
3754 : : * IP address that needs to be DNATted from a external IP address
3755 : : * to a logical IP address. */
3756 [ + + ]: 51 : if (!strcmp(nat->type, "dnat")
3757 [ - + ]: 31 : || !strcmp(nat->type, "dnat_and_snat")) {
3758 : : /* Packet when it goes from the initiator to destination.
3759 : : * We need to zero the inport because the router can
3760 : : * send the packet back through the same interface. */
3761 : 20 : ds_clear(&match);
3762 : 20 : ds_put_format(&match, "ip && ip4.dst == %s", nat->external_ip);
3763 : 20 : ds_clear(&actions);
3764 : 20 : ds_put_format(&actions,"flags.loopback = 1; ct_dnat(%s);",
3765 : : nat->logical_ip);
3766 : 20 : ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
3767 : 20 : ds_cstr(&match), ds_cstr(&actions));
3768 : : }
3769 : :
3770 : : /* Egress SNAT table: Packets enter the egress pipeline with
3771 : : * source ip address that needs to be SNATted to a external ip
3772 : : * address. */
3773 [ + + ]: 51 : if (!strcmp(nat->type, "snat")
3774 [ - + ]: 20 : || !strcmp(nat->type, "dnat_and_snat")) {
3775 : 31 : ds_clear(&match);
3776 : 31 : ds_put_format(&match, "ip && ip4.src == %s", nat->logical_ip);
3777 : 31 : ds_clear(&actions);
3778 : 31 : ds_put_format(&actions, "ct_snat(%s);", nat->external_ip);
3779 : :
3780 : : /* The priority here is calculated such that the
3781 : : * nat->logical_ip with the longest mask gets a higher
3782 : : * priority. */
3783 : 51 : ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT,
3784 : 31 : count_1bits(ntohl(mask)) + 1,
3785 : 31 : ds_cstr(&match), ds_cstr(&actions));
3786 : : }
3787 : : }
3788 : :
3789 : : /* Re-circulate every packet through the DNAT zone.
3790 : : * This helps with three things.
3791 : : *
3792 : : * 1. Any packet that needs to be unDNATed in the reverse
3793 : : * direction gets unDNATed. Ideally this could be done in
3794 : : * the egress pipeline. But since the gateway router
3795 : : * does not have any feature that depends on the source
3796 : : * ip address being external IP address for IP routing,
3797 : : * we can do it here, saving a future re-circulation.
3798 : : *
3799 : : * 2. Established load-balanced connections automatically get
3800 : : * DNATed.
3801 : : *
3802 : : * 3. Any packet that was sent through SNAT zone in the
3803 : : * previous table automatically gets re-circulated to get
3804 : : * back the new destination IP address that is needed for
3805 : : * routing in the openflow pipeline. */
3806 : 302 : ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
3807 : : "ip", "flags.loopback = 1; ct_dnat;");
3808 : : }
3809 : :
3810 : : /* Logical router ingress table 4: IP Routing.
3811 : : *
3812 : : * A packet that arrives at this table is an IP packet that should be
3813 : : * routed to the address in 'ip[46].dst'. This table sets outport to
3814 : : * the correct output port, eth.src to the output port's MAC
3815 : : * address, and '[xx]reg0' to the next-hop IP address (leaving
3816 : : * 'ip[46].dst', the packet’s final destination, unchanged), and
3817 : : * advances to the next table for ARP/ND resolution. */
3818 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
3819 [ + + ]: 25610 : if (!op->nbrp) {
3820 : 20638 : continue;
3821 : : }
3822 : :
3823 [ + + ]: 9961 : for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
3824 : 4989 : add_route(lflows, op, op->lrp_networks.ipv4_addrs[i].addr_s,
3825 : 4989 : op->lrp_networks.ipv4_addrs[i].network_s,
3826 : 4989 : op->lrp_networks.ipv4_addrs[i].plen, NULL);
3827 : : }
3828 : :
3829 [ + + ]: 9944 : for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
3830 : 4972 : add_route(lflows, op, op->lrp_networks.ipv6_addrs[i].addr_s,
3831 : 4972 : op->lrp_networks.ipv6_addrs[i].network_s,
3832 : 4972 : op->lrp_networks.ipv6_addrs[i].plen, NULL);
3833 : : }
3834 : : }
3835 : :
3836 : : /* Convert the static routes to flows. */
3837 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
3838 [ + + ]: 6323 : if (!od->nbr) {
3839 : 4582 : continue;
3840 : : }
3841 : :
3842 [ + + ]: 2446 : for (int i = 0; i < od->nbr->n_static_routes; i++) {
3843 : : const struct nbrec_logical_router_static_route *route;
3844 : :
3845 : 705 : route = od->nbr->static_routes[i];
3846 : 705 : build_static_route_flow(lflows, od, ports, route);
3847 : : }
3848 : : }
3849 : :
3850 : : /* XXX destination unreachable */
3851 : :
3852 : : /* Local router ingress table 5: ARP Resolution.
3853 : : *
3854 : : * Any packet that reaches this table is an IP packet whose next-hop IP
3855 : : * address is in reg0. (ip4.dst is the final destination.) This table
3856 : : * resolves the IP address in reg0 into an output port in outport and an
3857 : : * Ethernet address in eth.dst. */
3858 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
3859 [ + + ]: 25610 : if (op->nbrp) {
3860 : : /* This is a logical router port. If next-hop IP address in
3861 : : * '[xx]reg0' matches IP address of this router port, then
3862 : : * the packet is intended to eventually be sent to this
3863 : : * logical port. Set the destination mac address using this
3864 : : * port's mac address.
3865 : : *
3866 : : * The packet is still in peer's logical pipeline. So the match
3867 : : * should be on peer's outport. */
3868 [ + + ][ + + ]: 4972 : if (op->peer && op->nbrp->peer) {
3869 [ + - ]: 140 : if (op->lrp_networks.n_ipv4_addrs) {
3870 : 140 : ds_clear(&match);
3871 : 140 : ds_put_format(&match, "outport == %s && reg0 == ",
3872 : 140 : op->peer->json_key);
3873 : 140 : op_put_v4_networks(&match, op, false);
3874 : :
3875 : 140 : ds_clear(&actions);
3876 : 140 : ds_put_format(&actions, "eth.dst = %s; next;",
3877 : 140 : op->lrp_networks.ea_s);
3878 : 140 : ovn_lflow_add(lflows, op->peer->od, S_ROUTER_IN_ARP_RESOLVE,
3879 : 140 : 100, ds_cstr(&match), ds_cstr(&actions));
3880 : : }
3881 : :
3882 [ + - ]: 140 : if (op->lrp_networks.n_ipv6_addrs) {
3883 : 140 : ds_clear(&match);
3884 : 140 : ds_put_format(&match, "outport == %s && xxreg0 == ",
3885 : 140 : op->peer->json_key);
3886 : 140 : op_put_v6_networks(&match, op);
3887 : :
3888 : 140 : ds_clear(&actions);
3889 : 140 : ds_put_format(&actions, "eth.dst = %s; next;",
3890 : 140 : op->lrp_networks.ea_s);
3891 : 4972 : ovn_lflow_add(lflows, op->peer->od, S_ROUTER_IN_ARP_RESOLVE,
3892 : 140 : 100, ds_cstr(&match), ds_cstr(&actions));
3893 : : }
3894 : : }
3895 [ + + ][ + + ]: 20638 : } else if (op->od->n_router_ports && strcmp(op->nbsp->type, "router")) {
3896 : : /* This is a logical switch port that backs a VM or a container.
3897 : : * Extract its addresses. For each of the address, go through all
3898 : : * the router ports attached to the switch (to which this port
3899 : : * connects) and if the address in question is reachable from the
3900 : : * router port, add an ARP/ND entry in that router's pipeline. */
3901 : :
3902 [ + + ]: 19969 : for (size_t i = 0; i < op->n_lsp_addrs; i++) {
3903 : 9992 : const char *ea_s = op->lsp_addrs[i].ea_s;
3904 [ + + ]: 19978 : for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
3905 : 9986 : const char *ip_s = op->lsp_addrs[i].ipv4_addrs[j].addr_s;
3906 [ + + ]: 36388 : for (size_t k = 0; k < op->od->n_router_ports; k++) {
3907 : : /* Get the Logical_Router_Port that the
3908 : : * Logical_Switch_Port is connected to, as
3909 : : * 'peer'. */
3910 : 26402 : const char *peer_name = smap_get(
3911 : 26402 : &op->od->router_ports[k]->nbsp->options,
3912 : : "router-port");
3913 [ - + ]: 26402 : if (!peer_name) {
3914 : 0 : continue;
3915 : : }
3916 : :
3917 : 26402 : struct ovn_port *peer = ovn_port_find(ports, peer_name);
3918 [ + - ][ - + ]: 26402 : if (!peer || !peer->nbrp) {
3919 : 0 : continue;
3920 : : }
3921 : :
3922 [ + + ]: 26402 : if (!find_lrp_member_ip(peer, ip_s)) {
3923 : 16524 : continue;
3924 : : }
3925 : :
3926 : 9878 : ds_clear(&match);
3927 : 9878 : ds_put_format(&match, "outport == %s && reg0 == %s",
3928 : : peer->json_key, ip_s);
3929 : :
3930 : 9878 : ds_clear(&actions);
3931 : 9878 : ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
3932 : 9878 : ovn_lflow_add(lflows, peer->od,
3933 : : S_ROUTER_IN_ARP_RESOLVE, 100,
3934 : 9878 : ds_cstr(&match), ds_cstr(&actions));
3935 : : }
3936 : : }
3937 : :
3938 [ - + ]: 9992 : for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
3939 : 0 : const char *ip_s = op->lsp_addrs[i].ipv6_addrs[j].addr_s;
3940 [ # # ]: 0 : for (size_t k = 0; k < op->od->n_router_ports; k++) {
3941 : : /* Get the Logical_Router_Port that the
3942 : : * Logical_Switch_Port is connected to, as
3943 : : * 'peer'. */
3944 : 0 : const char *peer_name = smap_get(
3945 : 0 : &op->od->router_ports[k]->nbsp->options,
3946 : : "router-port");
3947 [ # # ]: 0 : if (!peer_name) {
3948 : 0 : continue;
3949 : : }
3950 : :
3951 : 0 : struct ovn_port *peer = ovn_port_find(ports, peer_name);
3952 [ # # ][ # # ]: 0 : if (!peer || !peer->nbrp) {
3953 : 0 : continue;
3954 : : }
3955 : :
3956 [ # # ]: 0 : if (!find_lrp_member_ip(peer, ip_s)) {
3957 : 0 : continue;
3958 : : }
3959 : :
3960 : 0 : ds_clear(&match);
3961 : 0 : ds_put_format(&match, "outport == %s && xxreg0 == %s",
3962 : : peer->json_key, ip_s);
3963 : :
3964 : 0 : ds_clear(&actions);
3965 : 0 : ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
3966 : 0 : ovn_lflow_add(lflows, peer->od,
3967 : : S_ROUTER_IN_ARP_RESOLVE, 100,
3968 : 0 : ds_cstr(&match), ds_cstr(&actions));
3969 : : }
3970 : : }
3971 : : }
3972 [ + + ]: 10661 : } else if (!strcmp(op->nbsp->type, "router")) {
3973 : : /* This is a logical switch port that connects to a router. */
3974 : :
3975 : : /* The peer of this switch port is the router port for which
3976 : : * we need to add logical flows such that it can resolve
3977 : : * ARP entries for all the other router ports connected to
3978 : : * the switch in question. */
3979 : :
3980 : 4733 : const char *peer_name = smap_get(&op->nbsp->options,
3981 : : "router-port");
3982 [ - + ]: 4733 : if (!peer_name) {
3983 : 0 : continue;
3984 : : }
3985 : :
3986 : 4733 : struct ovn_port *peer = ovn_port_find(ports, peer_name);
3987 [ + + ][ + + ]: 4733 : if (!peer || !peer->nbrp) {
3988 : 51 : continue;
3989 : : }
3990 : :
3991 [ + + ]: 15416 : for (size_t i = 0; i < op->od->n_router_ports; i++) {
3992 : 10734 : const char *router_port_name = smap_get(
3993 : 10734 : &op->od->router_ports[i]->nbsp->options,
3994 : : "router-port");
3995 : 10734 : struct ovn_port *router_port = ovn_port_find(ports,
3996 : : router_port_name);
3997 [ + - ][ - + ]: 10734 : if (!router_port || !router_port->nbrp) {
3998 : 0 : continue;
3999 : : }
4000 : :
4001 : : /* Skip the router port under consideration. */
4002 [ + + ]: 10734 : if (router_port == peer) {
4003 : 4682 : continue;
4004 : : }
4005 : :
4006 [ + - ]: 6052 : if (router_port->lrp_networks.n_ipv4_addrs) {
4007 : 6052 : ds_clear(&match);
4008 : 6052 : ds_put_format(&match, "outport == %s && reg0 == ",
4009 : : peer->json_key);
4010 : 6052 : op_put_v4_networks(&match, router_port, false);
4011 : :
4012 : 6052 : ds_clear(&actions);
4013 : 6052 : ds_put_format(&actions, "eth.dst = %s; next;",
4014 : 6052 : router_port->lrp_networks.ea_s);
4015 : 6052 : ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE,
4016 : 6052 : 100, ds_cstr(&match), ds_cstr(&actions));
4017 : : }
4018 : :
4019 [ + - ]: 6052 : if (router_port->lrp_networks.n_ipv6_addrs) {
4020 : 6052 : ds_clear(&match);
4021 : 6052 : ds_put_format(&match, "outport == %s && xxreg0 == ",
4022 : : peer->json_key);
4023 : 6052 : op_put_v6_networks(&match, router_port);
4024 : :
4025 : 6052 : ds_clear(&actions);
4026 : 6052 : ds_put_format(&actions, "eth.dst = %s; next;",
4027 : 6052 : router_port->lrp_networks.ea_s);
4028 : 6052 : ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE,
4029 : 6052 : 100, ds_cstr(&match), ds_cstr(&actions));
4030 : : }
4031 : : }
4032 : : }
4033 : : }
4034 : :
4035 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
4036 [ + + ]: 6323 : if (!od->nbr) {
4037 : 4582 : continue;
4038 : : }
4039 : :
4040 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 0, "1",
4041 : : "get_arp(outport, reg0); next;");
4042 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 0, "ip4",
4043 : : "get_arp(outport, reg0); next;");
4044 : :
4045 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 0, "ip6",
4046 : : "get_nd(outport, xxreg0); next;");
4047 : : }
4048 : :
4049 : : /* Local router ingress table 6: ARP request.
4050 : : *
4051 : : * In the common case where the Ethernet destination has been resolved,
4052 : : * this table outputs the packet (priority 0). Otherwise, it composes
4053 : : * and sends an ARP request (priority 100). */
4054 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH (od, key_node, datapaths) {
4055 [ + + ]: 6323 : if (!od->nbr) {
4056 : 4582 : continue;
4057 : : }
4058 : :
4059 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
4060 : : "eth.dst == 00:00:00:00:00:00",
4061 : : "arp { "
4062 : : "eth.dst = ff:ff:ff:ff:ff:ff; "
4063 : : "arp.spa = reg1; "
4064 : : "arp.tpa = reg0; "
4065 : : "arp.op = 1; " /* ARP request */
4066 : : "output; "
4067 : : "};");
4068 : 1741 : ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, "1", "output;");
4069 : : }
4070 : :
4071 : : /* Logical router egress table 1: Delivery (priority 100).
4072 : : *
4073 : : * Priority 100 rules deliver packets to enabled logical ports. */
4074 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH (op, key_node, ports) {
4075 [ + + ]: 25610 : if (!op->nbrp) {
4076 : 20638 : continue;
4077 : : }
4078 : :
4079 [ - + ]: 4972 : if (!lrport_is_enabled(op->nbrp)) {
4080 : : /* Drop packets to disabled logical ports (since logical flow
4081 : : * tables are default-drop). */
4082 : 0 : continue;
4083 : : }
4084 : :
4085 : 4972 : ds_clear(&match);
4086 : 4972 : ds_put_format(&match, "outport == %s", op->json_key);
4087 : 4972 : ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 100,
4088 : 4972 : ds_cstr(&match), "output;");
4089 : : }
4090 : :
4091 : 2244 : ds_destroy(&match);
4092 : 2244 : ds_destroy(&actions);
4093 : 2244 : }
4094 : :
4095 : : /* Updates the Logical_Flow and Multicast_Group tables in the OVN_SB database,
4096 : : * constructing their contents based on the OVN_NB database. */
4097 : : static void
4098 : 2244 : build_lflows(struct northd_context *ctx, struct hmap *datapaths,
4099 : : struct hmap *ports)
4100 : : {
4101 : 2244 : struct hmap lflows = HMAP_INITIALIZER(&lflows);
4102 : 2244 : struct hmap mcgroups = HMAP_INITIALIZER(&mcgroups);
4103 : :
4104 : 2244 : build_lswitch_flows(datapaths, ports, &lflows, &mcgroups);
4105 : 2244 : build_lrouter_flows(datapaths, ports, &lflows);
4106 : :
4107 : : /* Push changes to the Logical_Flow table to database. */
4108 : : const struct sbrec_logical_flow *sbflow, *next_sbflow;
4109 [ + + ][ + + ]: 318941 : SBREC_LOGICAL_FLOW_FOR_EACH_SAFE (sbflow, next_sbflow, ctx->ovnsb_idl) {
4110 : 316697 : struct ovn_datapath *od
4111 : 316697 : = ovn_datapath_from_sbrec(datapaths, sbflow->logical_datapath);
4112 [ + + ]: 316697 : if (!od) {
4113 : 183 : sbrec_logical_flow_delete(sbflow);
4114 : 183 : continue;
4115 : : }
4116 : :
4117 : 316514 : enum ovn_datapath_type dp_type = od->nbs ? DP_SWITCH : DP_ROUTER;
4118 : 316514 : enum ovn_pipeline pipeline
4119 : 316514 : = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT;
4120 : 316514 : struct ovn_lflow *lflow = ovn_lflow_find(
4121 : 316514 : &lflows, od, ovn_stage_build(dp_type, pipeline, sbflow->table_id),
4122 : 316514 : sbflow->priority, sbflow->match, sbflow->actions);
4123 [ + + ]: 316514 : if (lflow) {
4124 : 316190 : ovn_lflow_destroy(&lflows, lflow);
4125 : : } else {
4126 : 324 : sbrec_logical_flow_delete(sbflow);
4127 : : }
4128 : : }
4129 : : struct ovn_lflow *lflow, *next_lflow;
4130 [ + + ][ - + ]: 6894 : HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) {
[ + + ]
4131 : 4650 : enum ovn_pipeline pipeline = ovn_stage_get_pipeline(lflow->stage);
4132 : 4650 : uint8_t table = ovn_stage_get_table(lflow->stage);
4133 : :
4134 : 4650 : sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
4135 : 4650 : sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
4136 [ + + ]: 4650 : sbrec_logical_flow_set_pipeline(
4137 : : sbflow, pipeline == P_IN ? "ingress" : "egress");
4138 : 4650 : sbrec_logical_flow_set_table_id(sbflow, table);
4139 : 4650 : sbrec_logical_flow_set_priority(sbflow, lflow->priority);
4140 : 4650 : sbrec_logical_flow_set_match(sbflow, lflow->match);
4141 : 4650 : sbrec_logical_flow_set_actions(sbflow, lflow->actions);
4142 : :
4143 : 4650 : const struct smap ids = SMAP_CONST1(&ids, "stage-name",
4144 : : ovn_stage_to_str(lflow->stage));
4145 : 4650 : sbrec_logical_flow_set_external_ids(sbflow, &ids);
4146 : :
4147 : 4650 : ovn_lflow_destroy(&lflows, lflow);
4148 : : }
4149 : 2244 : hmap_destroy(&lflows);
4150 : :
4151 : : /* Push changes to the Multicast_Group table to database. */
4152 : : const struct sbrec_multicast_group *sbmc, *next_sbmc;
4153 [ + + ][ + + ]: 7977 : SBREC_MULTICAST_GROUP_FOR_EACH_SAFE (sbmc, next_sbmc, ctx->ovnsb_idl) {
4154 : 5733 : struct ovn_datapath *od = ovn_datapath_from_sbrec(datapaths,
4155 : 5733 : sbmc->datapath);
4156 [ + + ]: 5733 : if (!od) {
4157 : 2 : sbrec_multicast_group_delete(sbmc);
4158 : 2 : continue;
4159 : : }
4160 : :
4161 : 11462 : struct multicast_group group = { .name = sbmc->name,
4162 : 5731 : .key = sbmc->tunnel_key };
4163 : 5731 : struct ovn_multicast *mc = ovn_multicast_find(&mcgroups, od, &group);
4164 [ + + ]: 5731 : if (mc) {
4165 : 5730 : ovn_multicast_update_sbrec(mc, sbmc);
4166 : 5730 : ovn_multicast_destroy(&mcgroups, mc);
4167 : : } else {
4168 : 5731 : sbrec_multicast_group_delete(sbmc);
4169 : : }
4170 : : }
4171 : : struct ovn_multicast *mc, *next_mc;
4172 [ + + ][ - + ]: 2327 : HMAP_FOR_EACH_SAFE (mc, next_mc, hmap_node, &mcgroups) {
[ + + ]
4173 : 83 : sbmc = sbrec_multicast_group_insert(ctx->ovnsb_txn);
4174 : 83 : sbrec_multicast_group_set_datapath(sbmc, mc->datapath->sb);
4175 : 83 : sbrec_multicast_group_set_name(sbmc, mc->group->name);
4176 : 83 : sbrec_multicast_group_set_tunnel_key(sbmc, mc->group->key);
4177 : 83 : ovn_multicast_update_sbrec(mc, sbmc);
4178 : 83 : ovn_multicast_destroy(&mcgroups, mc);
4179 : : }
4180 : 2244 : hmap_destroy(&mcgroups);
4181 : 2244 : }
4182 : :
4183 : : /* OVN_Northbound and OVN_Southbound have an identical Address_Set table.
4184 : : * We always update OVN_Southbound to match the current data in
4185 : : * OVN_Northbound, so that the address sets used in Logical_Flows in
4186 : : * OVN_Southbound is checked against the proper set.*/
4187 : : static void
4188 : 2244 : sync_address_sets(struct northd_context *ctx)
4189 : : {
4190 : 2244 : struct shash sb_address_sets = SHASH_INITIALIZER(&sb_address_sets);
4191 : :
4192 : : const struct sbrec_address_set *sb_address_set;
4193 [ + + ]: 2267 : SBREC_ADDRESS_SET_FOR_EACH (sb_address_set, ctx->ovnsb_idl) {
4194 : 23 : shash_add(&sb_address_sets, sb_address_set->name, sb_address_set);
4195 : : }
4196 : :
4197 : : const struct nbrec_address_set *nb_address_set;
4198 [ + + ]: 2268 : NBREC_ADDRESS_SET_FOR_EACH (nb_address_set, ctx->ovnnb_idl) {
4199 : 24 : sb_address_set = shash_find_and_delete(&sb_address_sets,
4200 : 24 : nb_address_set->name);
4201 [ + + ]: 24 : if (!sb_address_set) {
4202 : 2 : sb_address_set = sbrec_address_set_insert(ctx->ovnsb_txn);
4203 : 2 : sbrec_address_set_set_name(sb_address_set, nb_address_set->name);
4204 : : }
4205 : :
4206 : 24 : sbrec_address_set_set_addresses(sb_address_set,
4207 : : /* "char **" is not compatible with "const char **" */
4208 : 24 : (const char **) nb_address_set->addresses,
4209 : : nb_address_set->n_addresses);
4210 : : }
4211 : :
4212 : : struct shash_node *node, *next;
4213 [ + + ][ - + ]: 2245 : SHASH_FOR_EACH_SAFE (node, next, &sb_address_sets) {
[ + + ]
4214 : 1 : sbrec_address_set_delete(node->data);
4215 : 1 : shash_delete(&sb_address_sets, node);
4216 : : }
4217 : 2244 : shash_destroy(&sb_address_sets);
4218 : 2244 : }
4219 : :
4220 : : static void
4221 : 3554 : ovnnb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop)
4222 : : {
4223 [ + + ][ + + ]: 3554 : if (!ctx->ovnsb_txn || !ctx->ovnnb_txn) {
4224 : 1310 : return;
4225 : : }
4226 : : struct hmap datapaths, ports;
4227 : 2244 : build_datapaths(ctx, &datapaths);
4228 : 2244 : build_ports(ctx, &datapaths, &ports);
4229 : 2244 : build_ipam(&datapaths, &ports);
4230 : 2244 : build_lflows(ctx, &datapaths, &ports);
4231 : :
4232 : 2244 : sync_address_sets(ctx);
4233 : :
4234 : : struct ovn_datapath *dp, *next_dp;
4235 [ + + ][ - + ]: 8567 : HMAP_FOR_EACH_SAFE (dp, next_dp, key_node, &datapaths) {
[ + + ]
4236 : 6323 : ovn_datapath_destroy(&datapaths, dp);
4237 : : }
4238 : 2244 : hmap_destroy(&datapaths);
4239 : :
4240 : : struct ovn_port *port, *next_port;
4241 [ + + ][ - + ]: 27854 : HMAP_FOR_EACH_SAFE (port, next_port, key_node, &ports) {
[ + + ]
4242 : 25610 : ovn_port_destroy(&ports, port);
4243 : : }
4244 : 2244 : hmap_destroy(&ports);
4245 : :
4246 : : /* Copy nb_cfg from northbound to southbound database.
4247 : : *
4248 : : * Also set up to update sb_cfg once our southbound transaction commits. */
4249 : 2244 : const struct nbrec_nb_global *nb = nbrec_nb_global_first(ctx->ovnnb_idl);
4250 : 2244 : const struct sbrec_sb_global *sb = sbrec_sb_global_first(ctx->ovnsb_idl);
4251 [ + + ][ + + ]: 2244 : if (nb && sb) {
4252 : 2185 : sbrec_sb_global_set_nb_cfg(sb, nb->nb_cfg);
4253 : 2185 : sb_loop->next_cfg = nb->nb_cfg;
4254 : : }
4255 : :
4256 : 2244 : cleanup_macam(&macam);
4257 : : }
4258 : :
4259 : : /* Handle changes to the 'chassis' column of the 'Port_Binding' table. When
4260 : : * this column is not empty, it means we need to set the corresponding logical
4261 : : * port as 'up' in the northbound DB. */
4262 : : static void
4263 : 2984 : update_logical_port_status(struct northd_context *ctx)
4264 : : {
4265 : : struct hmap lports_hmap;
4266 : : const struct sbrec_port_binding *sb;
4267 : : const struct nbrec_logical_switch_port *nbsp;
4268 : :
4269 : : struct lport_hash_node {
4270 : : struct hmap_node node;
4271 : : const struct nbrec_logical_switch_port *nbsp;
4272 : : } *hash_node;
4273 : :
4274 : 2984 : hmap_init(&lports_hmap);
4275 : :
4276 [ + + ]: 28815 : NBREC_LOGICAL_SWITCH_PORT_FOR_EACH(nbsp, ctx->ovnnb_idl) {
4277 : 25831 : hash_node = xzalloc(sizeof *hash_node);
4278 : 25831 : hash_node->nbsp = nbsp;
4279 : 25831 : hmap_insert(&lports_hmap, &hash_node->node, hash_string(nbsp->name, 0));
4280 : : }
4281 : :
4282 [ + + ]: 34824 : SBREC_PORT_BINDING_FOR_EACH(sb, ctx->ovnsb_idl) {
4283 : 31840 : nbsp = NULL;
4284 [ + + ][ - + ]: 31840 : HMAP_FOR_EACH_WITH_HASH(hash_node, node,
4285 : : hash_string(sb->logical_port, 0),
4286 : : &lports_hmap) {
4287 [ + - ]: 25769 : if (!strcmp(sb->logical_port, hash_node->nbsp->name)) {
4288 : 25769 : nbsp = hash_node->nbsp;
4289 : 25769 : break;
4290 : : }
4291 : : }
4292 : :
4293 [ + + ]: 31840 : if (!nbsp) {
4294 : : /* The logical port doesn't exist for this port binding. This can
4295 : : * happen under normal circumstances when ovn-northd hasn't gotten
4296 : : * around to pruning the Port_Binding yet. */
4297 : 6071 : continue;
4298 : : }
4299 : :
4300 [ + + ][ + - ]: 25908 : if (sb->chassis && (!nbsp->up || !*nbsp->up)) {
[ + + ]
4301 : 139 : bool up = true;
4302 : 139 : nbrec_logical_switch_port_set_up(nbsp, &up, 1);
4303 [ + + ][ + + ]: 25630 : } else if (!sb->chassis && (!nbsp->up || *nbsp->up)) {
[ + + ]
4304 : 358 : bool up = false;
4305 : 358 : nbrec_logical_switch_port_set_up(nbsp, &up, 1);
4306 : : }
4307 : : }
4308 : :
4309 [ + + ][ - + ]: 28815 : HMAP_FOR_EACH_POP(hash_node, node, &lports_hmap) {
[ + + ]
4310 : 25831 : free(hash_node);
4311 : : }
4312 : 2984 : hmap_destroy(&lports_hmap);
4313 : 2984 : }
4314 : :
4315 : : static struct dhcp_opts_map supported_dhcp_opts[] = {
4316 : : OFFERIP,
4317 : : DHCP_OPT_NETMASK,
4318 : : DHCP_OPT_ROUTER,
4319 : : DHCP_OPT_DNS_SERVER,
4320 : : DHCP_OPT_LOG_SERVER,
4321 : : DHCP_OPT_LPR_SERVER,
4322 : : DHCP_OPT_SWAP_SERVER,
4323 : : DHCP_OPT_POLICY_FILTER,
4324 : : DHCP_OPT_ROUTER_SOLICITATION,
4325 : : DHCP_OPT_NIS_SERVER,
4326 : : DHCP_OPT_NTP_SERVER,
4327 : : DHCP_OPT_SERVER_ID,
4328 : : DHCP_OPT_TFTP_SERVER,
4329 : : DHCP_OPT_CLASSLESS_STATIC_ROUTE,
4330 : : DHCP_OPT_MS_CLASSLESS_STATIC_ROUTE,
4331 : : DHCP_OPT_IP_FORWARD_ENABLE,
4332 : : DHCP_OPT_ROUTER_DISCOVERY,
4333 : : DHCP_OPT_ETHERNET_ENCAP,
4334 : : DHCP_OPT_DEFAULT_TTL,
4335 : : DHCP_OPT_TCP_TTL,
4336 : : DHCP_OPT_MTU,
4337 : : DHCP_OPT_LEASE_TIME,
4338 : : DHCP_OPT_T1,
4339 : : DHCP_OPT_T2
4340 : : };
4341 : :
4342 : : static struct dhcp_opts_map supported_dhcpv6_opts[] = {
4343 : : DHCPV6_OPT_IA_ADDR,
4344 : : DHCPV6_OPT_SERVER_ID,
4345 : : DHCPV6_OPT_DOMAIN_SEARCH,
4346 : : DHCPV6_OPT_DNS_SERVER
4347 : : };
4348 : :
4349 : : static void
4350 : 2471 : check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
4351 : : {
4352 : 2471 : struct hmap dhcp_opts_to_add = HMAP_INITIALIZER(&dhcp_opts_to_add);
4353 [ + + ]: 61775 : for (size_t i = 0; (i < sizeof(supported_dhcp_opts) /
4354 : 59304 : sizeof(supported_dhcp_opts[0])); i++) {
4355 : 59304 : hmap_insert(&dhcp_opts_to_add, &supported_dhcp_opts[i].hmap_node,
4356 : : dhcp_opt_hash(supported_dhcp_opts[i].name));
4357 : : }
4358 : :
4359 : : const struct sbrec_dhcp_options *opt_row, *opt_row_next;
4360 [ + + ][ + + ]: 60839 : SBREC_DHCP_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) {
4361 : 58368 : struct dhcp_opts_map *dhcp_opt =
4362 : 58368 : dhcp_opts_find(&dhcp_opts_to_add, opt_row->name);
4363 [ + - ]: 58368 : if (dhcp_opt) {
4364 : 58368 : hmap_remove(&dhcp_opts_to_add, &dhcp_opt->hmap_node);
4365 : : } else {
4366 : 0 : sbrec_dhcp_options_delete(opt_row);
4367 : : }
4368 : : }
4369 : :
4370 : : struct dhcp_opts_map *opt;
4371 [ + + ][ - + ]: 3407 : HMAP_FOR_EACH (opt, hmap_node, &dhcp_opts_to_add) {
4372 : 936 : struct sbrec_dhcp_options *sbrec_dhcp_option =
4373 : 936 : sbrec_dhcp_options_insert(ctx->ovnsb_txn);
4374 : 936 : sbrec_dhcp_options_set_name(sbrec_dhcp_option, opt->name);
4375 : 936 : sbrec_dhcp_options_set_code(sbrec_dhcp_option, opt->code);
4376 : 936 : sbrec_dhcp_options_set_type(sbrec_dhcp_option, opt->type);
4377 : : }
4378 : :
4379 : 2471 : hmap_destroy(&dhcp_opts_to_add);
4380 : 2471 : }
4381 : :
4382 : : static void
4383 : 2471 : check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)
4384 : : {
4385 : 2471 : struct hmap dhcpv6_opts_to_add = HMAP_INITIALIZER(&dhcpv6_opts_to_add);
4386 [ + + ]: 12355 : for (size_t i = 0; (i < sizeof(supported_dhcpv6_opts) /
4387 : 9884 : sizeof(supported_dhcpv6_opts[0])); i++) {
4388 : 9884 : hmap_insert(&dhcpv6_opts_to_add, &supported_dhcpv6_opts[i].hmap_node,
4389 : : dhcp_opt_hash(supported_dhcpv6_opts[i].name));
4390 : : }
4391 : :
4392 : : const struct sbrec_dhcpv6_options *opt_row, *opt_row_next;
4393 [ + + ][ + + ]: 12199 : SBREC_DHCPV6_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) {
4394 : 9728 : struct dhcp_opts_map *dhcp_opt =
4395 : 9728 : dhcp_opts_find(&dhcpv6_opts_to_add, opt_row->name);
4396 [ + - ]: 9728 : if (dhcp_opt) {
4397 : 9728 : hmap_remove(&dhcpv6_opts_to_add, &dhcp_opt->hmap_node);
4398 : : } else {
4399 : 0 : sbrec_dhcpv6_options_delete(opt_row);
4400 : : }
4401 : : }
4402 : :
4403 : : struct dhcp_opts_map *opt;
4404 [ + + ][ - + ]: 2627 : HMAP_FOR_EACH(opt, hmap_node, &dhcpv6_opts_to_add) {
4405 : 156 : struct sbrec_dhcpv6_options *sbrec_dhcpv6_option =
4406 : 156 : sbrec_dhcpv6_options_insert(ctx->ovnsb_txn);
4407 : 156 : sbrec_dhcpv6_options_set_name(sbrec_dhcpv6_option, opt->name);
4408 : 156 : sbrec_dhcpv6_options_set_code(sbrec_dhcpv6_option, opt->code);
4409 : 156 : sbrec_dhcpv6_options_set_type(sbrec_dhcpv6_option, opt->type);
4410 : : }
4411 : :
4412 : 2471 : hmap_destroy(&dhcpv6_opts_to_add);
4413 : 2471 : }
4414 : :
4415 : : /* Updates the sb_cfg and hv_cfg columns in the northbound NB_Global table. */
4416 : : static void
4417 : 2984 : update_northbound_cfg(struct northd_context *ctx,
4418 : : struct ovsdb_idl_loop *sb_loop)
4419 : : {
4420 : : /* Update northbound sb_cfg if appropriate. */
4421 : 2984 : const struct nbrec_nb_global *nbg = nbrec_nb_global_first(ctx->ovnnb_idl);
4422 : 2984 : int64_t sb_cfg = sb_loop->cur_cfg;
4423 [ + + ][ + + ]: 2984 : if (nbg && sb_cfg && nbg->sb_cfg != sb_cfg) {
[ + + ]
4424 : 8 : nbrec_nb_global_set_sb_cfg(nbg, sb_cfg);
4425 : : }
4426 : :
4427 : : /* Update northbound hv_cfg if appropriate. */
4428 [ + + ]: 2984 : if (nbg) {
4429 : : /* Find minimum nb_cfg among all chassis. */
4430 : : const struct sbrec_chassis *chassis;
4431 : 2978 : int64_t hv_cfg = nbg->nb_cfg;
4432 [ + + ]: 5764 : SBREC_CHASSIS_FOR_EACH (chassis, ctx->ovnsb_idl) {
4433 [ - + ]: 2786 : if (chassis->nb_cfg < hv_cfg) {
4434 : 0 : hv_cfg = chassis->nb_cfg;
4435 : : }
4436 : : }
4437 : :
4438 : : /* Update hv_cfg. */
4439 [ + + ]: 2978 : if (nbg->hv_cfg != hv_cfg) {
4440 : 8 : nbrec_nb_global_set_hv_cfg(nbg, hv_cfg);
4441 : : }
4442 : : }
4443 : 2984 : }
4444 : :
4445 : : /* Handle a fairly small set of changes in the southbound database. */
4446 : : static void
4447 : 3554 : ovnsb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop)
4448 : : {
4449 [ + + ][ + + ]: 3554 : if (!ctx->ovnnb_txn || !ovsdb_idl_has_ever_connected(ctx->ovnsb_idl)) {
4450 : 570 : return;
4451 : : }
4452 : :
4453 : 2984 : update_logical_port_status(ctx);
4454 : 2984 : update_northbound_cfg(ctx, sb_loop);
4455 : : }
4456 : :
4457 : : static void
4458 : 42 : parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
4459 : : {
4460 : : enum {
4461 : : DAEMON_OPTION_ENUMS,
4462 : : VLOG_OPTION_ENUMS,
4463 : : };
4464 : : static const struct option long_options[] = {
4465 : : {"ovnsb-db", required_argument, NULL, 'd'},
4466 : : {"ovnnb-db", required_argument, NULL, 'D'},
4467 : : {"help", no_argument, NULL, 'h'},
4468 : : {"options", no_argument, NULL, 'o'},
4469 : : {"version", no_argument, NULL, 'V'},
4470 : : DAEMON_LONG_OPTIONS,
4471 : : VLOG_LONG_OPTIONS,
4472 : : STREAM_SSL_LONG_OPTIONS,
4473 : : {NULL, 0, NULL, 0},
4474 : : };
4475 : 42 : char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
4476 : :
4477 : : for (;;) {
4478 : : int c;
4479 : :
4480 : 308 : c = getopt_long(argc, argv, short_options, long_options, NULL);
4481 [ + + ]: 308 : if (c == -1) {
4482 : 42 : break;
4483 : : }
4484 : :
4485 [ + - + + : 266 : switch (c) {
- - - + +
- - - - -
+ + - - -
- ]
4486 : 118 : DAEMON_OPTION_HANDLERS;
4487 : 76 : VLOG_OPTION_HANDLERS;
4488 : 0 : STREAM_SSL_OPTION_HANDLERS;
4489 : :
4490 : : case 'd':
4491 : 36 : ovnsb_db = optarg;
4492 : 36 : break;
4493 : :
4494 : : case 'D':
4495 : 36 : ovnnb_db = optarg;
4496 : 36 : break;
4497 : :
4498 : : case 'h':
4499 : 0 : usage();
4500 : 0 : exit(EXIT_SUCCESS);
4501 : :
4502 : : case 'o':
4503 : 0 : ovs_cmdl_print_options(long_options);
4504 : 0 : exit(EXIT_SUCCESS);
4505 : :
4506 : : case 'V':
4507 : 0 : ovs_print_version(0, 0);
4508 : 0 : exit(EXIT_SUCCESS);
4509 : :
4510 : : default:
4511 : 0 : break;
4512 : : }
4513 : 266 : }
4514 : :
4515 [ + + ]: 42 : if (!ovnsb_db) {
4516 : 6 : ovnsb_db = default_sb_db();
4517 : : }
4518 : :
4519 [ + + ]: 42 : if (!ovnnb_db) {
4520 : 6 : ovnnb_db = default_nb_db();
4521 : : }
4522 : :
4523 : 42 : free(short_options);
4524 : 42 : }
4525 : :
4526 : : static void
4527 : 1287 : add_column_noalert(struct ovsdb_idl *idl,
4528 : : const struct ovsdb_idl_column *column)
4529 : : {
4530 : 1287 : ovsdb_idl_add_column(idl, column);
4531 : 1287 : ovsdb_idl_omit_alert(idl, column);
4532 : 1287 : }
4533 : :
4534 : : int
4535 : 42 : main(int argc, char *argv[])
4536 : : {
4537 : 42 : int res = EXIT_SUCCESS;
4538 : : struct unixctl_server *unixctl;
4539 : : int retval;
4540 : : bool exiting;
4541 : :
4542 : 42 : fatal_ignore_sigpipe();
4543 : 42 : set_program_name(argv[0]);
4544 : 42 : service_start(&argc, &argv);
4545 : 42 : parse_options(argc, argv);
4546 : :
4547 : 42 : daemonize_start(false);
4548 : :
4549 : 39 : retval = unixctl_server_create(NULL, &unixctl);
4550 [ - + ]: 39 : if (retval) {
4551 : 0 : exit(EXIT_FAILURE);
4552 : : }
4553 : 39 : unixctl_command_register("exit", "", 0, 0, ovn_northd_exit, &exiting);
4554 : :
4555 : 39 : daemonize_complete();
4556 : :
4557 : 39 : nbrec_init();
4558 : 39 : sbrec_init();
4559 : :
4560 : : /* We want to detect (almost) all changes to the ovn-nb db. */
4561 : 39 : struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
4562 : : ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, true, true));
4563 : 39 : ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_sb_cfg);
4564 : 39 : ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_hv_cfg);
4565 : :
4566 : : /* We want to detect only selected changes to the ovn-sb db. */
4567 : 39 : struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
4568 : : ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, false, true));
4569 : :
4570 : 39 : ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global);
4571 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_nb_cfg);
4572 : :
4573 : 39 : ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow);
4574 : 39 : add_column_noalert(ovnsb_idl_loop.idl,
4575 : : &sbrec_logical_flow_col_logical_datapath);
4576 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_pipeline);
4577 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_table_id);
4578 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_priority);
4579 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_match);
4580 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_actions);
4581 : :
4582 : 39 : ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_multicast_group);
4583 : 39 : add_column_noalert(ovnsb_idl_loop.idl,
4584 : : &sbrec_multicast_group_col_datapath);
4585 : 39 : add_column_noalert(ovnsb_idl_loop.idl,
4586 : : &sbrec_multicast_group_col_tunnel_key);
4587 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_name);
4588 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_ports);
4589 : :
4590 : 39 : ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_datapath_binding);
4591 : 39 : add_column_noalert(ovnsb_idl_loop.idl,
4592 : : &sbrec_datapath_binding_col_tunnel_key);
4593 : 39 : add_column_noalert(ovnsb_idl_loop.idl,
4594 : : &sbrec_datapath_binding_col_external_ids);
4595 : :
4596 : 39 : ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_binding);
4597 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_datapath);
4598 : 39 : add_column_noalert(ovnsb_idl_loop.idl,
4599 : : &sbrec_port_binding_col_logical_port);
4600 : 39 : add_column_noalert(ovnsb_idl_loop.idl,
4601 : : &sbrec_port_binding_col_tunnel_key);
4602 : 39 : add_column_noalert(ovnsb_idl_loop.idl,
4603 : : &sbrec_port_binding_col_parent_port);
4604 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_tag);
4605 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_type);
4606 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_options);
4607 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_mac);
4608 : 39 : ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis);
4609 : 39 : ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_mac_binding);
4610 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_datapath);
4611 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_ip);
4612 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_mac);
4613 : 39 : add_column_noalert(ovnsb_idl_loop.idl,
4614 : : &sbrec_mac_binding_col_logical_port);
4615 : 39 : ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcp_options);
4616 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_code);
4617 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_type);
4618 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_name);
4619 : 39 : ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcpv6_options);
4620 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_code);
4621 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_type);
4622 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_name);
4623 : 39 : ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_address_set);
4624 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_name);
4625 : 39 : add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_addresses);
4626 : :
4627 : 39 : ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);
4628 : 39 : ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
4629 : :
4630 : : /* Main loop. */
4631 : 39 : exiting = false;
4632 [ + + ]: 3593 : while (!exiting) {
4633 : 14216 : struct northd_context ctx = {
4634 : 3554 : .ovnnb_idl = ovnnb_idl_loop.idl,
4635 : 3554 : .ovnnb_txn = ovsdb_idl_loop_run(&ovnnb_idl_loop),
4636 : 3554 : .ovnsb_idl = ovnsb_idl_loop.idl,
4637 : 3554 : .ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
4638 : : };
4639 : :
4640 : 3554 : ovnnb_db_run(&ctx, &ovnsb_idl_loop);
4641 : 3554 : ovnsb_db_run(&ctx, &ovnsb_idl_loop);
4642 [ + + ]: 3554 : if (ctx.ovnsb_txn) {
4643 : 2471 : check_and_add_supported_dhcp_opts_to_sb_db(&ctx);
4644 : 2471 : check_and_add_supported_dhcpv6_opts_to_sb_db(&ctx);
4645 : : }
4646 : :
4647 : 3554 : unixctl_server_run(unixctl);
4648 : 3554 : unixctl_server_wait(unixctl);
4649 [ + + ]: 3554 : if (exiting) {
4650 : 39 : poll_immediate_wake();
4651 : : }
4652 : 3554 : ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop);
4653 : 3554 : ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
4654 : :
4655 : 3554 : poll_block();
4656 [ - + ]: 3554 : if (should_service_stop()) {
4657 : 3554 : exiting = true;
4658 : : }
4659 : : }
4660 : :
4661 : 39 : unixctl_server_destroy(unixctl);
4662 : 39 : ovsdb_idl_loop_destroy(&ovnnb_idl_loop);
4663 : 39 : ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
4664 : 39 : service_stop();
4665 : :
4666 : 39 : exit(res);
4667 : : }
4668 : :
4669 : : static void
4670 : 39 : ovn_northd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
4671 : : const char *argv[] OVS_UNUSED, void *exiting_)
4672 : : {
4673 : 39 : bool *exiting = exiting_;
4674 : 39 : *exiting = true;
4675 : :
4676 : 39 : unixctl_command_reply(conn, NULL);
4677 : 39 : }
|