]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_pbr.c
bgpd: display if FS entry is installed in PBR or not
[mirror_frr.git] / bgpd / bgp_pbr.c
CommitLineData
bbe6ffd6
PG
1/*
2 * BGP pbr
3 * Copyright (C) 6WIND
4 *
5 * FRR is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2, or (at your option) any
8 * later version.
9 *
10 * FRR is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include "zebra.h"
21#include "prefix.h"
22#include "zclient.h"
f3d32faa 23#include "jhash.h"
bbe6ffd6 24
f3d32faa 25#include "bgpd/bgpd.h"
bbe6ffd6 26#include "bgpd/bgp_pbr.h"
b46b6f1a 27#include "bgpd/bgp_debug.h"
45918cfb
PG
28#include "bgpd/bgp_flowspec_util.h"
29#include "bgpd/bgp_ecommunity.h"
30#include "bgpd/bgp_route.h"
31#include "bgpd/bgp_attr.h"
d114b0d7 32#include "bgpd/bgp_zebra.h"
529efa23 33#include "bgpd/bgp_mplsvpn.h"
d114b0d7
PG
34
35DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH_ENTRY, "PBR match entry")
36DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH, "PBR match")
37DEFINE_MTYPE_STATIC(BGPD, PBR_ACTION, "PBR action")
38
39static int bgp_pbr_match_counter_unique;
40static int bgp_pbr_match_entry_counter_unique;
41static int bgp_pbr_action_counter_unique;
42static int bgp_pbr_match_iptable_counter_unique;
b46b6f1a 43
1815c6fc
PG
44struct bgp_pbr_match_iptable_unique {
45 uint32_t unique;
46 struct bgp_pbr_match *bpm_found;
47};
48
c5d429e1
PG
49struct bgp_pbr_match_entry_unique {
50 uint32_t unique;
51 struct bgp_pbr_match_entry *bpme_found;
52};
53
70eabd12
PG
54struct bgp_pbr_action_unique {
55 uint32_t unique;
56 struct bgp_pbr_action *bpa_found;
57};
58
59static int bgp_pbr_action_walkcb(struct hash_backet *backet, void *arg)
60{
61 struct bgp_pbr_action *bpa = (struct bgp_pbr_action *)backet->data;
62 struct bgp_pbr_action_unique *bpau = (struct bgp_pbr_action_unique *)
63 arg;
64 uint32_t unique = bpau->unique;
65
66 if (bpa->unique == unique) {
67 bpau->bpa_found = bpa;
68 return HASHWALK_ABORT;
69 }
70 return HASHWALK_CONTINUE;
71}
72
c5d429e1
PG
73static int bgp_pbr_match_entry_walkcb(struct hash_backet *backet, void *arg)
74{
75 struct bgp_pbr_match_entry *bpme =
76 (struct bgp_pbr_match_entry *)backet->data;
77 struct bgp_pbr_match_entry_unique *bpmeu =
78 (struct bgp_pbr_match_entry_unique *)arg;
79 uint32_t unique = bpmeu->unique;
80
81 if (bpme->unique == unique) {
82 bpmeu->bpme_found = bpme;
83 return HASHWALK_ABORT;
84 }
85 return HASHWALK_CONTINUE;
86}
87
88struct bgp_pbr_match_ipsetname {
89 char *ipsetname;
90 struct bgp_pbr_match *bpm_found;
91};
92
93static int bgp_pbr_match_pername_walkcb(struct hash_backet *backet, void *arg)
94{
95 struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
96 struct bgp_pbr_match_ipsetname *bpmi =
97 (struct bgp_pbr_match_ipsetname *)arg;
98 char *ipset_name = bpmi->ipsetname;
99
100 if (!strncmp(ipset_name, bpm->ipset_name,
101 ZEBRA_IPSET_NAME_SIZE)) {
102 bpmi->bpm_found = bpm;
103 return HASHWALK_ABORT;
104 }
105 return HASHWALK_CONTINUE;
106}
107
1815c6fc
PG
108static int bgp_pbr_match_iptable_walkcb(struct hash_backet *backet, void *arg)
109{
110 struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
111 struct bgp_pbr_match_iptable_unique *bpmiu =
112 (struct bgp_pbr_match_iptable_unique *)arg;
113 uint32_t unique = bpmiu->unique;
114
115 if (bpm->unique2 == unique) {
116 bpmiu->bpm_found = bpm;
117 return HASHWALK_ABORT;
118 }
119 return HASHWALK_CONTINUE;
120}
121
c5d429e1
PG
122struct bgp_pbr_match_unique {
123 uint32_t unique;
124 struct bgp_pbr_match *bpm_found;
125};
126
127static int bgp_pbr_match_walkcb(struct hash_backet *backet, void *arg)
128{
129 struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
130 struct bgp_pbr_match_unique *bpmu = (struct bgp_pbr_match_unique *)
131 arg;
132 uint32_t unique = bpmu->unique;
133
134 if (bpm->unique == unique) {
135 bpmu->bpm_found = bpm;
136 return HASHWALK_ABORT;
137 }
138 return HASHWALK_CONTINUE;
139}
140
b46b6f1a
PG
141static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval,
142 const char *prepend)
143{
144 char *ptr = str;
145
146 if (prepend)
147 ptr += sprintf(ptr, "%s", prepend);
148 else {
149 if (mval->unary_operator & OPERATOR_UNARY_OR)
150 ptr += sprintf(ptr, ", or ");
151 if (mval->unary_operator & OPERATOR_UNARY_AND)
152 ptr += sprintf(ptr, ", and ");
153 }
154 if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN)
155 ptr += sprintf(ptr, "<");
156 if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN)
157 ptr += sprintf(ptr, ">");
158 if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO)
159 ptr += sprintf(ptr, "=");
160 if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH)
161 ptr += sprintf(ptr, "match");
162 ptr += sprintf(ptr, " %u", mval->value);
163 return (int)(ptr - str);
164}
165
166#define INCREMENT_DISPLAY(_ptr, _cnt) do { \
167 if (_cnt) \
168 (_ptr) += sprintf((_ptr), "; "); \
169 _cnt++; \
170 } while (0)
171
1de7dfff
PG
172struct bgp_pbr_range_port {
173 uint16_t min_port;
174 uint16_t max_port;
175};
176
177/* return true if extraction ok
178 */
179static bool bgp_pbr_extract(struct bgp_pbr_match_val list[],
180 int num,
181 struct bgp_pbr_range_port *range)
182{
183 int i = 0;
184 bool exact_match = false;
185
186 if (range)
187 memset(range, 0, sizeof(struct bgp_pbr_range_port));
188
189 if (num > 2)
190 return false;
191 for (i = 0; i < num; i++) {
192 if (i != 0 && (list[i].compare_operator ==
193 OPERATOR_COMPARE_EQUAL_TO))
194 return false;
195 if (i == 0 && (list[i].compare_operator ==
196 OPERATOR_COMPARE_EQUAL_TO)) {
197 if (range)
198 range->min_port = list[i].value;
199 exact_match = true;
200 }
201 if (exact_match == true && i > 0)
202 return false;
203 if (list[i].compare_operator ==
204 (OPERATOR_COMPARE_GREATER_THAN +
205 OPERATOR_COMPARE_EQUAL_TO)) {
206 if (range)
207 range->min_port = list[i].value;
208 } else if (list[i].compare_operator ==
209 (OPERATOR_COMPARE_LESS_THAN +
210 OPERATOR_COMPARE_EQUAL_TO)) {
211 if (range)
212 range->max_port = list[i].value;
213 } else if (list[i].compare_operator ==
214 OPERATOR_COMPARE_LESS_THAN) {
215 if (range)
216 range->max_port = list[i].value - 1;
217 } else if (list[i].compare_operator ==
218 OPERATOR_COMPARE_GREATER_THAN) {
219 if (range)
220 range->min_port = list[i].value + 1;
221 }
222 }
223 return true;
224}
225
b46b6f1a
PG
226static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
227{
228 /* because bgp pbr entry may contain unsupported
229 * combinations, a message will be displayed here if
230 * not supported.
231 * for now, only match/set supported is
232 * - combination src/dst => redirect nexthop [ + rate]
233 * - combination src/dst => redirect VRF [ + rate]
234 * - combination src/dst => drop
1de7dfff 235 * - combination srcport + @IP
b46b6f1a 236 */
1de7dfff 237 if (api->match_icmp_type_num || api->match_icmp_type_num
b46b6f1a
PG
238 || api->match_packet_length_num || api->match_dscp_num
239 || api->match_tcpflags_num) {
ac7c35f8 240 if (BGP_DEBUG(pbr, PBR)) {
b46b6f1a 241 bgp_pbr_print_policy_route(api);
ac7c35f8 242 zlog_debug("BGP: some SET actions not supported by Zebra. ignoring.");
1de7dfff 243 zlog_debug("BGP: case icmp or length or dscp or tcp flags");
ac7c35f8 244 }
b46b6f1a
PG
245 return 0;
246 }
1de7dfff
PG
247
248 if (api->match_protocol_num > 1) {
249 if (BGP_DEBUG(pbr, PBR))
250 zlog_debug("BGP: match protocol operations:"
251 "multiple protocols ( %d). ignoring.",
252 api->match_protocol_num);
253 return 0;
254 }
255 if (api->match_protocol_num == 1 &&
256 api->protocol[0].value != PROTOCOL_UDP &&
257 api->protocol[0].value != PROTOCOL_TCP) {
258 if (BGP_DEBUG(pbr, PBR))
259 zlog_debug("BGP: match protocol operations:"
260 "protocol (%d) not supported. ignoring",
261 api->match_protocol_num);
262 return 0;
263 }
264 if (!bgp_pbr_extract(api->src_port, api->match_src_port_num, NULL)) {
265 if (BGP_DEBUG(pbr, PBR))
266 zlog_debug("BGP: match src port operations:"
267 "too complex. ignoring.");
268 return 0;
269 }
270 if (!bgp_pbr_extract(api->dst_port, api->match_dst_port_num, NULL)) {
271 if (BGP_DEBUG(pbr, PBR))
272 zlog_debug("BGP: match dst port operations:"
273 "too complex. ignoring.");
274 return 0;
275 }
276 if (!bgp_pbr_extract(api->port, api->match_port_num, NULL)) {
277 if (BGP_DEBUG(pbr, PBR))
278 zlog_debug("BGP: match port operations:"
279 "too complex. ignoring.");
280 return 0;
281 }
282 /* no combinations with both src_port and dst_port
283 * or port with src_port and dst_port
284 */
285 if (api->match_src_port_num + api->match_dst_port_num +
286 api->match_port_num > 3) {
287 if (BGP_DEBUG(pbr, PBR))
288 zlog_debug("BGP: match multiple port operations:"
289 " too complex. ignoring.");
290 return 0;
291 }
b46b6f1a
PG
292 if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
293 !(api->match_bitmask & PREFIX_DST_PRESENT)) {
ac7c35f8 294 if (BGP_DEBUG(pbr, PBR)) {
b46b6f1a 295 bgp_pbr_print_policy_route(api);
ac7c35f8
PG
296 zlog_debug("BGP: match actions without src"
297 " or dst address can not operate."
298 " ignoring.");
299 }
b46b6f1a
PG
300 return 0;
301 }
302 return 1;
303}
bbe6ffd6 304
45918cfb
PG
305/* return -1 if build or validation failed */
306static int bgp_pbr_build_and_validate_entry(struct prefix *p,
307 struct bgp_info *info,
308 struct bgp_pbr_entry_main *api)
309{
310 int ret;
311 int i, action_count = 0;
312 struct ecommunity *ecom;
313 struct ecommunity_val *ecom_eval;
314 struct bgp_pbr_entry_action *api_action;
315 struct prefix *src = NULL, *dst = NULL;
316 int valid_prefix = 0;
317 afi_t afi = AFI_IP;
318
319 /* extract match from flowspec entries */
320 ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr,
321 p->u.prefix_flowspec.prefixlen, api);
322 if (ret < 0)
323 return -1;
324 /* extract actiosn from flowspec ecom list */
325 if (info && info->attr && info->attr->ecommunity) {
326 ecom = info->attr->ecommunity;
327 for (i = 0; i < ecom->size; i++) {
328 ecom_eval = (struct ecommunity_val *)
329 ecom->val + (i * ECOMMUNITY_SIZE);
330
331 if (action_count > ACTIONS_MAX_NUM) {
f146bb54
PG
332 if (BGP_DEBUG(pbr, PBR_ERROR))
333 zlog_err("%s: flowspec actions exceeds limit (max %u)",
334 __func__, action_count);
45918cfb
PG
335 break;
336 }
337 api_action = &api->actions[action_count];
338
339 if ((ecom_eval->val[1] ==
340 (char)ECOMMUNITY_REDIRECT_VRF) &&
341 (ecom_eval->val[0] ==
342 (char)ECOMMUNITY_ENCODE_TRANS_EXP ||
343 ecom_eval->val[0] ==
344 (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
345 ecom_eval->val[0] ==
346 (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_3)) {
347 struct ecommunity *eckey = ecommunity_new();
348 struct ecommunity_val ecom_copy;
349
350 memcpy(&ecom_copy, ecom_eval,
351 sizeof(struct ecommunity_val));
352 ecom_copy.val[0] &=
353 ~ECOMMUNITY_ENCODE_TRANS_EXP;
354 ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
355 ecommunity_add_val(eckey, &ecom_copy);
356
357 api_action->action = ACTION_REDIRECT;
358 api_action->u.redirect_vrf =
359 get_first_vrf_for_redirect_with_rt(
360 eckey);
361 ecommunity_free(&eckey);
362 } else if ((ecom_eval->val[0] ==
363 (char)ECOMMUNITY_ENCODE_REDIRECT_IP_NH) &&
364 (ecom_eval->val[1] ==
365 (char)ECOMMUNITY_REDIRECT_IP_NH)) {
366 api_action->action = ACTION_REDIRECT_IP;
367 api_action->u.zr.redirect_ip_v4.s_addr =
368 info->attr->nexthop.s_addr;
369 api_action->u.zr.duplicate = ecom_eval->val[7];
370 } else {
371 if (ecom_eval->val[0] !=
372 (char)ECOMMUNITY_ENCODE_TRANS_EXP)
373 continue;
374 ret = ecommunity_fill_pbr_action(ecom_eval,
375 api_action);
376 if (ret != 0)
377 continue;
378 }
379 api->action_num++;
380 }
381 }
382
383 /* validate if incoming matc/action is compatible
384 * with our policy routing engine
385 */
386 if (!bgp_pbr_validate_policy_route(api))
387 return -1;
388
389 /* check inconsistency in the match rule */
390 if (api->match_bitmask & PREFIX_SRC_PRESENT) {
391 src = &api->src_prefix;
392 afi = family2afi(src->family);
393 valid_prefix = 1;
394 }
395 if (api->match_bitmask & PREFIX_DST_PRESENT) {
396 dst = &api->dst_prefix;
397 if (valid_prefix && afi != family2afi(dst->family)) {
ac7c35f8 398 if (BGP_DEBUG(pbr, PBR)) {
45918cfb 399 bgp_pbr_print_policy_route(api);
ac7c35f8
PG
400 zlog_debug("%s: inconsistency:"
401 " no match for afi src and dst (%u/%u)",
402 __func__, afi, family2afi(dst->family));
403 }
45918cfb
PG
404 return -1;
405 }
406 }
407 return 0;
408}
409
a6b07429
PG
410static void bgp_pbr_match_entry_free(void *arg)
411{
412 struct bgp_pbr_match_entry *bpme;
413
414 bpme = (struct bgp_pbr_match_entry *)arg;
415
416 if (bpme->installed) {
417 bgp_send_pbr_ipset_entry_match(bpme, false);
418 bpme->installed = false;
419 bpme->backpointer = NULL;
420 }
421 XFREE(MTYPE_PBR_MATCH_ENTRY, bpme);
422}
423
424static void bgp_pbr_match_free(void *arg)
425{
426 struct bgp_pbr_match *bpm;
427
428 bpm = (struct bgp_pbr_match *)arg;
429
430 hash_clean(bpm->entry_hash, bgp_pbr_match_entry_free);
431
432 if (hashcount(bpm->entry_hash) == 0) {
433 /* delete iptable entry first */
434 /* then delete ipset match */
435 if (bpm->installed) {
436 if (bpm->installed_in_iptable) {
437 bgp_send_pbr_iptable(bpm->action,
438 bpm, false);
439 bpm->installed_in_iptable = false;
440 bpm->action->refcnt--;
441 }
442 bgp_send_pbr_ipset_match(bpm, false);
443 bpm->installed = false;
444 bpm->action = NULL;
445 }
446 }
447 hash_free(bpm->entry_hash);
448
449 XFREE(MTYPE_PBR_MATCH, bpm);
450}
451
d114b0d7
PG
452static void *bgp_pbr_match_alloc_intern(void *arg)
453{
454 struct bgp_pbr_match *bpm, *new;
455
456 bpm = (struct bgp_pbr_match *)arg;
457
458 new = XCALLOC(MTYPE_PBR_MATCH, sizeof(*new));
459 memcpy(new, bpm, sizeof(*bpm));
460
461 return new;
462}
463
a6b07429
PG
464static void bgp_pbr_action_free(void *arg)
465{
466 struct bgp_pbr_action *bpa;
467
468 bpa = (struct bgp_pbr_action *)arg;
469
470 if (bpa->refcnt == 0) {
471 if (bpa->installed && bpa->table_id != 0) {
472 bgp_send_pbr_rule_action(bpa, false);
473 bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
474 AFI_IP,
475 bpa->table_id,
476 false);
477 }
478 }
479 XFREE(MTYPE_PBR_ACTION, bpa);
480}
481
d114b0d7
PG
482static void *bgp_pbr_action_alloc_intern(void *arg)
483{
484 struct bgp_pbr_action *bpa, *new;
485
486 bpa = (struct bgp_pbr_action *)arg;
487
488 new = XCALLOC(MTYPE_PBR_ACTION, sizeof(*new));
489
490 memcpy(new, bpa, sizeof(*bpa));
491
492 return new;
493}
494
495static void *bgp_pbr_match_entry_alloc_intern(void *arg)
496{
497 struct bgp_pbr_match_entry *bpme, *new;
498
499 bpme = (struct bgp_pbr_match_entry *)arg;
500
501 new = XCALLOC(MTYPE_PBR_MATCH_ENTRY, sizeof(*new));
502
503 memcpy(new, bpme, sizeof(*bpme));
504
505 return new;
506}
507
f3d32faa
PG
508uint32_t bgp_pbr_match_hash_key(void *arg)
509{
510 struct bgp_pbr_match *pbm = (struct bgp_pbr_match *)arg;
511 uint32_t key;
512
513 key = jhash_1word(pbm->vrf_id, 0x4312abde);
514 key = jhash_1word(pbm->flags, key);
515 return jhash_1word(pbm->type, key);
516}
517
518int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2)
519{
520 const struct bgp_pbr_match *r1, *r2;
521
522 r1 = (const struct bgp_pbr_match *)arg1;
523 r2 = (const struct bgp_pbr_match *)arg2;
524
525 if (r1->vrf_id != r2->vrf_id)
526 return 0;
527
528 if (r1->type != r2->type)
529 return 0;
530
531 if (r1->flags != r2->flags)
532 return 0;
533
534 if (r1->action != r2->action)
535 return 0;
536
537 return 1;
538}
539
540uint32_t bgp_pbr_match_entry_hash_key(void *arg)
541{
542 struct bgp_pbr_match_entry *pbme;
543 uint32_t key;
544
545 pbme = (struct bgp_pbr_match_entry *)arg;
546 key = prefix_hash_key(&pbme->src);
547 key = jhash_1word(prefix_hash_key(&pbme->dst), key);
1de7dfff
PG
548 key = jhash(&pbme->dst_port_min, 2, key);
549 key = jhash(&pbme->src_port_min, 2, key);
550 key = jhash(&pbme->dst_port_max, 2, key);
551 key = jhash(&pbme->src_port_max, 2, key);
552 key = jhash(&pbme->proto, 1, key);
f3d32faa
PG
553
554 return key;
555}
556
557int bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2)
558{
559 const struct bgp_pbr_match_entry *r1, *r2;
560
561 r1 = (const struct bgp_pbr_match_entry *)arg1;
562 r2 = (const struct bgp_pbr_match_entry *)arg2;
563
564 /* on updates, comparing
565 * backpointer is not necessary
566 */
567
568 /* unique value is self calculated
569 */
570
571 /* rate is ignored for now
572 */
573
574 if (!prefix_same(&r1->src, &r2->src))
575 return 0;
576
577 if (!prefix_same(&r1->dst, &r2->dst))
578 return 0;
579
1de7dfff
PG
580 if (r1->src_port_min != r2->src_port_min)
581 return 0;
582
583 if (r1->dst_port_min != r2->dst_port_min)
584 return 0;
585
586 if (r1->src_port_max != r2->src_port_max)
587 return 0;
588
589 if (r1->dst_port_max != r2->dst_port_max)
590 return 0;
591
592 if (r1->proto != r2->proto)
593 return 0;
594
f3d32faa
PG
595 return 1;
596}
597
598uint32_t bgp_pbr_action_hash_key(void *arg)
599{
600 struct bgp_pbr_action *pbra;
601 uint32_t key;
602
603 pbra = (struct bgp_pbr_action *)arg;
604 key = jhash_1word(pbra->table_id, 0x4312abde);
605 key = jhash_1word(pbra->fwmark, key);
606 return key;
607}
608
609int bgp_pbr_action_hash_equal(const void *arg1, const void *arg2)
610{
611 const struct bgp_pbr_action *r1, *r2;
612
613 r1 = (const struct bgp_pbr_action *)arg1;
614 r2 = (const struct bgp_pbr_action *)arg2;
615
616 /* unique value is self calculated
617 * table and fwmark is self calculated
618 */
619 if (r1->rate != r2->rate)
620 return 0;
621
622 if (r1->vrf_id != r2->vrf_id)
623 return 0;
624
625 if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop)))
626 return 0;
627 return 1;
628}
bbe6ffd6 629
70eabd12
PG
630struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id,
631 uint32_t unique)
bbe6ffd6 632{
70eabd12
PG
633 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
634 struct bgp_pbr_action_unique bpau;
635
636 if (!bgp || unique == 0)
637 return NULL;
638 bpau.unique = unique;
639 bpau.bpa_found = NULL;
640 hash_walk(bgp->pbr_action_hash, bgp_pbr_action_walkcb, &bpau);
641 return bpau.bpa_found;
bbe6ffd6
PG
642}
643
644struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
645 uint32_t unique)
646{
c5d429e1
PG
647 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
648 struct bgp_pbr_match_unique bpmu;
649
650 if (!bgp || unique == 0)
651 return NULL;
652 bpmu.unique = unique;
653 bpmu.bpm_found = NULL;
654 hash_walk(bgp->pbr_match_hash, bgp_pbr_match_walkcb, &bpmu);
655 return bpmu.bpm_found;
bbe6ffd6
PG
656}
657
658struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(vrf_id_t vrf_id,
659 char *ipset_name,
660 uint32_t unique)
661{
c5d429e1
PG
662 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
663 struct bgp_pbr_match_entry_unique bpmeu;
664 struct bgp_pbr_match_ipsetname bpmi;
665
666 if (!bgp || unique == 0)
667 return NULL;
668 bpmi.ipsetname = XCALLOC(MTYPE_TMP, ZEBRA_IPSET_NAME_SIZE);
669 snprintf(bpmi.ipsetname, ZEBRA_IPSET_NAME_SIZE, "%s", ipset_name);
670 bpmi.bpm_found = NULL;
671 hash_walk(bgp->pbr_match_hash, bgp_pbr_match_pername_walkcb, &bpmi);
672 XFREE(MTYPE_TMP, bpmi.ipsetname);
673 if (!bpmi.bpm_found)
674 return NULL;
675 bpmeu.bpme_found = NULL;
676 bpmeu.unique = unique;
677 hash_walk(bpmi.bpm_found->entry_hash,
678 bgp_pbr_match_entry_walkcb, &bpmeu);
679 return bpmeu.bpme_found;
bbe6ffd6
PG
680}
681
1815c6fc
PG
682struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id,
683 uint32_t unique)
684{
685 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
686 struct bgp_pbr_match_iptable_unique bpmiu;
687
688 if (!bgp || unique == 0)
689 return NULL;
690 bpmiu.unique = unique;
691 bpmiu.bpm_found = NULL;
692 hash_walk(bgp->pbr_match_hash, bgp_pbr_match_iptable_walkcb, &bpmiu);
693 return bpmiu.bpm_found;
694}
695
a6b07429
PG
696void bgp_pbr_cleanup(struct bgp *bgp)
697{
698 if (bgp->pbr_match_hash) {
699 hash_clean(bgp->pbr_match_hash, bgp_pbr_match_free);
700 hash_free(bgp->pbr_match_hash);
701 bgp->pbr_match_hash = NULL;
702 }
703 if (bgp->pbr_action_hash) {
704 hash_clean(bgp->pbr_action_hash, bgp_pbr_action_free);
705 hash_free(bgp->pbr_action_hash);
706 bgp->pbr_action_hash = NULL;
707 }
708}
709
f3d32faa
PG
710void bgp_pbr_init(struct bgp *bgp)
711{
712 bgp->pbr_match_hash =
713 hash_create_size(8, bgp_pbr_match_hash_key,
714 bgp_pbr_match_hash_equal,
715 "Match Hash");
716 bgp->pbr_action_hash =
717 hash_create_size(8, bgp_pbr_action_hash_key,
718 bgp_pbr_action_hash_equal,
719 "Match Hash Entry");
720}
b46b6f1a
PG
721
722void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api)
723{
724 int i = 0;
725 char return_string[512];
726 char *ptr = return_string;
727 char buff[64];
728 int nb_items = 0;
729
730 ptr += sprintf(ptr, "MATCH : ");
731 if (api->match_bitmask & PREFIX_SRC_PRESENT) {
732 struct prefix *p = &(api->src_prefix);
733
734 ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64));
735 INCREMENT_DISPLAY(ptr, nb_items);
736 }
737 if (api->match_bitmask & PREFIX_DST_PRESENT) {
738 struct prefix *p = &(api->dst_prefix);
739
740 INCREMENT_DISPLAY(ptr, nb_items);
741 ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64));
742 }
743
744 if (api->match_protocol_num)
745 INCREMENT_DISPLAY(ptr, nb_items);
746 for (i = 0; i < api->match_protocol_num; i++)
747 ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i],
748 i > 0 ? NULL : "@proto ");
749
750 if (api->match_src_port_num)
751 INCREMENT_DISPLAY(ptr, nb_items);
752 for (i = 0; i < api->match_src_port_num; i++)
753 ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i],
754 i > 0 ? NULL : "@srcport ");
755
756 if (api->match_dst_port_num)
757 INCREMENT_DISPLAY(ptr, nb_items);
758 for (i = 0; i < api->match_dst_port_num; i++)
759 ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i],
760 i > 0 ? NULL : "@dstport ");
761
762 if (api->match_port_num)
763 INCREMENT_DISPLAY(ptr, nb_items);
764 for (i = 0; i < api->match_port_num; i++)
765 ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i],
766 i > 0 ? NULL : "@port ");
767
768 if (api->match_icmp_type_num)
769 INCREMENT_DISPLAY(ptr, nb_items);
770 for (i = 0; i < api->match_icmp_type_num; i++)
771 ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i],
772 i > 0 ? NULL : "@icmptype ");
773
774 if (api->match_icmp_code_num)
775 INCREMENT_DISPLAY(ptr, nb_items);
776 for (i = 0; i < api->match_icmp_code_num; i++)
777 ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i],
778 i > 0 ? NULL : "@icmpcode ");
779
780 if (api->match_packet_length_num)
781 INCREMENT_DISPLAY(ptr, nb_items);
782 for (i = 0; i < api->match_packet_length_num; i++)
783 ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i],
784 i > 0 ? NULL : "@plen ");
785
786 if (api->match_dscp_num)
787 INCREMENT_DISPLAY(ptr, nb_items);
788 for (i = 0; i < api->match_dscp_num; i++)
789 ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i],
790 i > 0 ? NULL : "@dscp ");
791
792 if (api->match_tcpflags_num)
793 INCREMENT_DISPLAY(ptr, nb_items);
794 for (i = 0; i < api->match_tcpflags_num; i++)
795 ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i],
796 i > 0 ? NULL : "@tcpflags ");
797
798 if (api->match_bitmask & FRAGMENT_PRESENT) {
799 INCREMENT_DISPLAY(ptr, nb_items);
800 ptr += sprintf(ptr, "@fragment %u", api->fragment.bitmask);
801 }
802 if (!nb_items)
803 ptr = return_string;
804 else
805 ptr += sprintf(ptr, "; ");
806 if (api->action_num)
807 ptr += sprintf(ptr, "SET : ");
808 nb_items = 0;
809 for (i = 0; i < api->action_num; i++) {
810 switch (api->actions[i].action) {
811 case ACTION_TRAFFICRATE:
812 INCREMENT_DISPLAY(ptr, nb_items);
813 ptr += sprintf(ptr, "@set rate %f",
814 api->actions[i].u.r.rate);
815 break;
816 case ACTION_TRAFFIC_ACTION:
817 INCREMENT_DISPLAY(ptr, nb_items);
818 ptr += sprintf(ptr, "@action ");
819 if (api->actions[i].u.za.filter
820 & TRAFFIC_ACTION_TERMINATE)
821 ptr += sprintf(ptr,
822 " terminate (apply filter(s))");
823 if (api->actions[i].u.za.filter
824 & TRAFFIC_ACTION_DISTRIBUTE)
825 ptr += sprintf(ptr, " distribute");
826 if (api->actions[i].u.za.filter
827 & TRAFFIC_ACTION_SAMPLE)
828 ptr += sprintf(ptr, " sample");
829 break;
830 case ACTION_REDIRECT_IP:
831 INCREMENT_DISPLAY(ptr, nb_items);
832 char local_buff[INET_ADDRSTRLEN];
833
834 if (inet_ntop(AF_INET,
835 &api->actions[i].u.zr.redirect_ip_v4,
836 local_buff, INET_ADDRSTRLEN) != NULL)
837 ptr += sprintf(ptr,
838 "@redirect ip nh %s", local_buff);
839 break;
840 case ACTION_REDIRECT:
841 INCREMENT_DISPLAY(ptr, nb_items);
842 ptr += sprintf(ptr, "@redirect vrf %u",
843 api->actions[i].u.redirect_vrf);
844 break;
845 case ACTION_MARKING:
846 INCREMENT_DISPLAY(ptr, nb_items);
847 ptr += sprintf(ptr, "@set dscp %u",
848 api->actions[i].u.marking_dscp);
849 break;
850 default:
851 break;
852 }
853 }
854 zlog_info("%s", return_string);
855}
45918cfb 856
d114b0d7
PG
857static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa,
858 struct bgp_pbr_match *bpm,
859 struct bgp_pbr_match_entry *bpme)
860{
861 /* if bpme is null, bpm is also null
862 */
863 if (bpme == NULL)
864 return;
865 /* ipset del entry */
866 if (bpme->installed) {
867 bgp_send_pbr_ipset_entry_match(bpme, false);
868 bpme->installed = false;
869 bpme->backpointer = NULL;
b588b642
PG
870 if (bpme->bgp_info) {
871 struct bgp_info *bgp_info;
872 struct bgp_info_extra *extra;
873
874 /* unlink bgp_info to bpme */
875 bgp_info = (struct bgp_info *)bpme->bgp_info;
876 extra = bgp_info_extra_get(bgp_info);
877 extra->bgp_fs_pbr = NULL;
878 bpme->bgp_info = NULL;
879 }
d114b0d7
PG
880 }
881 hash_release(bpm->entry_hash, bpme);
882 if (hashcount(bpm->entry_hash) == 0) {
883 /* delete iptable entry first */
884 /* then delete ipset match */
885 if (bpm->installed) {
886 if (bpm->installed_in_iptable) {
887 bgp_send_pbr_iptable(bpm->action,
888 bpm, false);
889 bpm->installed_in_iptable = false;
a6b07429 890 bpm->action->refcnt--;
d114b0d7
PG
891 }
892 bgp_send_pbr_ipset_match(bpm, false);
893 bpm->installed = false;
894 bpm->action = NULL;
895 }
896 hash_release(bgp->pbr_match_hash, bpm);
897 /* XXX release pbr_match_action if not used
898 * note that drop does not need to call send_pbr_action
899 */
900 }
a6b07429
PG
901 if (bpa->refcnt == 0) {
902 if (bpa->installed && bpa->table_id != 0) {
903 bgp_send_pbr_rule_action(bpa, false);
904 bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
905 AFI_IP,
906 bpa->table_id,
907 false);
908 }
909 }
d114b0d7
PG
910}
911
912struct bgp_pbr_match_entry_remain {
913 struct bgp_pbr_match_entry *bpme_to_match;
914 struct bgp_pbr_match_entry *bpme_found;
915};
916
917static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg)
918{
919 struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
920 struct bgp_pbr_match_entry_remain *bpmer =
921 (struct bgp_pbr_match_entry_remain *)arg;
922 struct bgp_pbr_match *bpm_temp;
923 struct bgp_pbr_match_entry *bpme = bpmer->bpme_to_match;
924
925 if (!bpme->backpointer ||
926 bpm == bpme->backpointer ||
927 bpme->backpointer->action == bpm->action)
928 return HASHWALK_CONTINUE;
929 /* ensure bpm other characteristics are equal */
930 bpm_temp = bpme->backpointer;
931 if (bpm_temp->vrf_id != bpm->vrf_id ||
932 bpm_temp->type != bpm->type ||
933 bpm_temp->flags != bpm->flags)
934 return HASHWALK_CONTINUE;
935
936 /* look for remaining bpme */
937 bpmer->bpme_found = hash_lookup(bpm->entry_hash, bpme);
938 if (!bpmer->bpme_found)
939 return HASHWALK_CONTINUE;
940 return HASHWALK_ABORT;
941}
942
943static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
1de7dfff
PG
944 struct bgp_info *binfo,
945 vrf_id_t vrf_id,
946 struct prefix *src,
947 struct prefix *dst,
948 uint8_t protocol,
949 struct bgp_pbr_range_port *src_port,
950 struct bgp_pbr_range_port *dst_port)
d114b0d7
PG
951{
952 struct bgp_pbr_match temp;
953 struct bgp_pbr_match_entry temp2;
954 struct bgp_pbr_match *bpm;
955 struct bgp_pbr_match_entry *bpme;
956 struct bgp_pbr_match_entry_remain bpmer;
957
958 /* as we don't know information from EC
959 * look for bpm that have the bpm
960 * with vrf_id characteristics
961 */
962 memset(&temp2, 0, sizeof(temp2));
963 memset(&temp, 0, sizeof(temp));
964 if (src) {
965 temp.flags |= MATCH_IP_SRC_SET;
966 prefix_copy(&temp2.src, src);
967 } else
968 temp2.src.family = AF_INET;
969 if (dst) {
970 temp.flags |= MATCH_IP_DST_SET;
971 prefix_copy(&temp2.dst, dst);
972 } else
973 temp2.dst.family = AF_INET;
1de7dfff
PG
974 if (src_port) {
975 temp.flags |= MATCH_PORT_SRC_SET;
976 temp2.src_port_min = src_port->min_port;
977 if (src_port->max_port) {
978 temp.flags |= MATCH_PORT_SRC_RANGE_SET;
979 temp2.src_port_max = src_port->max_port;
980 }
981 }
982 if (dst_port) {
983 temp.flags |= MATCH_PORT_DST_SET;
984 temp2.dst_port_min = dst_port->min_port;
985 if (dst_port->max_port) {
986 temp.flags |= MATCH_PORT_DST_RANGE_SET;
987 temp2.dst_port_max = dst_port->max_port;
988 }
989 }
990 temp2.proto = protocol;
991
992 if (src == NULL || dst == NULL) {
993 if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
994 temp.type = IPSET_NET_PORT;
995 else
996 temp.type = IPSET_NET;
997 } else {
998 if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
999 temp.type = IPSET_NET_PORT_NET;
1000 else
1001 temp.type = IPSET_NET_NET;
1002 }
d114b0d7
PG
1003 if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
1004 temp.vrf_id = 0;
1005 else
1006 temp.vrf_id = vrf_id;
1007 bpme = &temp2;
1008 bpm = &temp;
1009 bpme->backpointer = bpm;
1010 /* right now, a previous entry may already exist
1011 * flush previous entry if necessary
1012 */
1013 bpmer.bpme_to_match = bpme;
1014 bpmer.bpme_found = NULL;
1015 hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
1016 if (bpmer.bpme_found) {
1017 static struct bgp_pbr_match *local_bpm;
1018 static struct bgp_pbr_action *local_bpa;
1019
1020 local_bpm = bpmer.bpme_found->backpointer;
1021 local_bpa = local_bpm->action;
1022 bgp_pbr_flush_entry(bgp, local_bpa,
1023 local_bpm, bpmer.bpme_found);
1024 }
1025}
1026
1027static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
1de7dfff
PG
1028 struct bgp_info *binfo,
1029 vrf_id_t vrf_id,
1030 struct prefix *src,
1031 struct prefix *dst,
1032 struct nexthop *nh,
1033 float *rate,
1034 uint8_t protocol,
1035 struct bgp_pbr_range_port *src_port,
1036 struct bgp_pbr_range_port *dst_port)
d114b0d7
PG
1037{
1038 struct bgp_pbr_match temp;
1039 struct bgp_pbr_match_entry temp2;
1040 struct bgp_pbr_match *bpm;
1041 struct bgp_pbr_match_entry *bpme = NULL;
1042 struct bgp_pbr_action temp3;
1043 struct bgp_pbr_action *bpa = NULL;
1044 struct bgp_pbr_match_entry_remain bpmer;
1045
1046 /* look for bpa first */
1047 memset(&temp3, 0, sizeof(temp3));
1048 if (rate)
1049 temp3.rate = *rate;
1050 if (nh)
1051 memcpy(&temp3.nh, nh, sizeof(struct nexthop));
1052 temp3.vrf_id = vrf_id;
1053 bpa = hash_get(bgp->pbr_action_hash, &temp3,
1054 bgp_pbr_action_alloc_intern);
1055
1056 if (bpa->fwmark == 0) {
d114b0d7
PG
1057 /* drop is handled by iptable */
1058 if (nh && nh->type == NEXTHOP_TYPE_BLACKHOLE) {
1059 bpa->table_id = 0;
1060 bpa->installed = true;
1061 } else {
31c28cd7
PG
1062 bpa->fwmark = bgp_zebra_tm_get_id();
1063 bpa->table_id = bpa->fwmark;
d114b0d7
PG
1064 bpa->installed = false;
1065 }
a6b07429 1066 bpa->bgp = bgp;
d114b0d7
PG
1067 bpa->unique = ++bgp_pbr_action_counter_unique;
1068 /* 0 value is forbidden */
1069 bpa->install_in_progress = false;
1070 }
1071
1072 /* then look for bpm */
1073 memset(&temp, 0, sizeof(temp));
1de7dfff
PG
1074 if (src == NULL || dst == NULL) {
1075 if ((src_port && src_port->min_port) ||
1076 (dst_port && dst_port->min_port))
1077 temp.type = IPSET_NET_PORT;
1078 else
1079 temp.type = IPSET_NET;
1080 } else {
1081 if ((src_port && src_port->min_port) ||
1082 (dst_port && dst_port->min_port))
1083 temp.type = IPSET_NET_PORT_NET;
1084 else
1085 temp.type = IPSET_NET_NET;
1086 }
d114b0d7
PG
1087 temp.vrf_id = vrf_id;
1088 if (src)
1089 temp.flags |= MATCH_IP_SRC_SET;
1090 if (dst)
1091 temp.flags |= MATCH_IP_DST_SET;
1de7dfff
PG
1092
1093 if (src_port && src_port->min_port)
1094 temp.flags |= MATCH_PORT_SRC_SET;
1095 if (dst_port && dst_port->min_port)
1096 temp.flags |= MATCH_PORT_DST_SET;
1097 if (src_port && src_port->max_port)
1098 temp.flags |= MATCH_PORT_SRC_RANGE_SET;
1099 if (dst_port && dst_port->max_port)
1100 temp.flags |= MATCH_PORT_DST_RANGE_SET;
d114b0d7
PG
1101 temp.action = bpa;
1102 bpm = hash_get(bgp->pbr_match_hash, &temp,
1103 bgp_pbr_match_alloc_intern);
1104
1105 /* new, then self allocate ipset_name and unique */
1106 if (bpm && bpm->unique == 0) {
1107 bpm->unique = ++bgp_pbr_match_counter_unique;
1108 /* 0 value is forbidden */
1109 sprintf(bpm->ipset_name, "match%p", bpm);
1110 bpm->entry_hash = hash_create_size(8,
1111 bgp_pbr_match_entry_hash_key,
1112 bgp_pbr_match_entry_hash_equal,
1113 "Match Entry Hash");
1114 bpm->installed = false;
1115
1116 /* unique2 should be updated too */
1117 bpm->unique2 = ++bgp_pbr_match_iptable_counter_unique;
1118 bpm->installed_in_iptable = false;
1119 bpm->install_in_progress = false;
1120 bpm->install_iptable_in_progress = false;
1121 }
1122
1123 memset(&temp2, 0, sizeof(temp2));
1124 if (src)
1125 prefix_copy(&temp2.src, src);
1126 else
1127 temp2.src.family = AF_INET;
1128 if (dst)
1129 prefix_copy(&temp2.dst, dst);
1130 else
1131 temp2.dst.family = AF_INET;
1de7dfff
PG
1132 temp2.src_port_min = src_port ? src_port->min_port : 0;
1133 temp2.dst_port_min = dst_port ? dst_port->min_port : 0;
1134 temp2.src_port_max = src_port ? src_port->max_port : 0;
1135 temp2.dst_port_max = dst_port ? dst_port->max_port : 0;
1136 temp2.proto = protocol;
d114b0d7
PG
1137 if (bpm)
1138 bpme = hash_get(bpm->entry_hash, &temp2,
1de7dfff 1139 bgp_pbr_match_entry_alloc_intern);
d114b0d7
PG
1140 if (bpme && bpme->unique == 0) {
1141 bpme->unique = ++bgp_pbr_match_entry_counter_unique;
1142 /* 0 value is forbidden */
1143 bpme->backpointer = bpm;
1144 bpme->installed = false;
1145 bpme->install_in_progress = false;
b588b642
PG
1146 /* link bgp info to bpme */
1147 bpme->bgp_info = (void *)binfo;
d114b0d7
PG
1148 }
1149
1150 /* BGP FS: append entry to zebra
1151 * - policies are not routing entries and as such
1152 * route replace semantics don't necessarily follow
1153 * through to policy entries
1154 * - because of that, not all policing information will be stored
1155 * into zebra. and non selected policies will be suppressed from zebra
1156 * - as consequence, in order to bring consistency
1157 * a policy will be added, then ifan ecmp policy exists,
1158 * it will be suppressed subsequently
1159 */
1160 /* ip rule add */
f7df1907 1161 if (!bpa->installed) {
d114b0d7 1162 bgp_send_pbr_rule_action(bpa, true);
f7df1907
PG
1163 bgp_zebra_announce_default(bgp, nh,
1164 AFI_IP, bpa->table_id, true);
1165 }
d114b0d7
PG
1166
1167 /* ipset create */
1168 if (bpm && !bpm->installed)
1169 bgp_send_pbr_ipset_match(bpm, true);
1170 /* ipset add */
1171 if (bpme && !bpme->installed)
1172 bgp_send_pbr_ipset_entry_match(bpme, true);
1173
1174 /* iptables */
1175 if (bpm && !bpm->installed_in_iptable)
1176 bgp_send_pbr_iptable(bpa, bpm, true);
1177
1178 /* A previous entry may already exist
1179 * flush previous entry if necessary
1180 */
1181 bpmer.bpme_to_match = bpme;
1182 bpmer.bpme_found = NULL;
1183 hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
1184 if (bpmer.bpme_found) {
1185 static struct bgp_pbr_match *local_bpm;
1186 static struct bgp_pbr_action *local_bpa;
1187
1188 local_bpm = bpmer.bpme_found->backpointer;
1189 local_bpa = local_bpm->action;
1190 bgp_pbr_flush_entry(bgp, local_bpa,
1191 local_bpm, bpmer.bpme_found);
1192 }
1193
1194
1195}
1196
1197static void bgp_pbr_handle_entry(struct bgp *bgp,
1198 struct bgp_info *binfo,
1199 struct bgp_pbr_entry_main *api,
1200 bool add)
1201{
1202 struct nexthop nh;
1203 int i = 0;
1204 int continue_loop = 1;
1205 float rate = 0;
1206 struct prefix *src = NULL, *dst = NULL;
1de7dfff
PG
1207 uint8_t proto = 0;
1208 struct bgp_pbr_range_port *srcp = NULL, *dstp = NULL;
1209 struct bgp_pbr_range_port range;
d114b0d7
PG
1210
1211 if (api->match_bitmask & PREFIX_SRC_PRESENT)
1212 src = &api->src_prefix;
1213 if (api->match_bitmask & PREFIX_DST_PRESENT)
1214 dst = &api->dst_prefix;
1215 memset(&nh, 0, sizeof(struct nexthop));
1216 nh.vrf_id = VRF_UNKNOWN;
1de7dfff
PG
1217 if (api->match_protocol_num)
1218 proto = (uint8_t)api->protocol[0].value;
1219 /* if match_port is selected, then either src or dst port will be parsed
1220 * but not both at the same time
1221 */
1222 if (api->match_port_num >= 1) {
1223 bgp_pbr_extract(api->port,
1224 api->match_port_num,
1225 &range);
1226 srcp = dstp = &range;
1227 } else if (api->match_src_port_num >= 1) {
1228 bgp_pbr_extract(api->src_port,
1229 api->match_src_port_num,
1230 &range);
1231 srcp = &range;
1232 dstp = NULL;
1233 } else if (api->match_dst_port_num >= 1) {
1234 bgp_pbr_extract(api->dst_port,
1235 api->match_dst_port_num,
1236 &range);
1237 dstp = &range;
1238 srcp = NULL;
1239 }
d114b0d7
PG
1240 if (!add)
1241 return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo,
1de7dfff
PG
1242 api->vrf_id, src, dst,
1243 proto, srcp, dstp);
d114b0d7
PG
1244 /* no action for add = true */
1245 for (i = 0; i < api->action_num; i++) {
1246 switch (api->actions[i].action) {
1247 case ACTION_TRAFFICRATE:
1248 /* drop packet */
1249 if (api->actions[i].u.r.rate == 0) {
1250 nh.vrf_id = api->vrf_id;
1251 nh.type = NEXTHOP_TYPE_BLACKHOLE;
1252 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
1253 api->vrf_id, src, dst,
1de7dfff
PG
1254 &nh, &rate, proto,
1255 srcp, dstp);
d114b0d7
PG
1256 } else {
1257 /* update rate. can be reentrant */
1258 rate = api->actions[i].u.r.rate;
ac7c35f8 1259 if (BGP_DEBUG(pbr, PBR)) {
d114b0d7 1260 bgp_pbr_print_policy_route(api);
ac7c35f8
PG
1261 zlog_warn("PBR: ignoring Set action rate %f",
1262 api->actions[i].u.r.rate);
1263 }
d114b0d7
PG
1264 }
1265 break;
1266 case ACTION_TRAFFIC_ACTION:
1267 if (api->actions[i].u.za.filter
1268 & TRAFFIC_ACTION_SAMPLE) {
ac7c35f8 1269 if (BGP_DEBUG(pbr, PBR)) {
d114b0d7 1270 bgp_pbr_print_policy_route(api);
ac7c35f8
PG
1271 zlog_warn("PBR: Sample action Ignored");
1272 }
d114b0d7
PG
1273 }
1274#if 0
1275 if (api->actions[i].u.za.filter
1276 & TRAFFIC_ACTION_DISTRIBUTE) {
ac7c35f8 1277 if (BGP_DEBUG(pbr, PBR)) {
d114b0d7 1278 bgp_pbr_print_policy_route(api);
ac7c35f8
PG
1279 zlog_warn("PBR: Distribute action Applies");
1280 }
d114b0d7
PG
1281 continue_loop = 0;
1282 /* continue forwarding entry as before
1283 * no action
1284 */
1285 }
1286#endif /* XXX to confirm behaviour of traffic action. for now , ignore */
1287 /* terminate action: run other filters
1288 */
1289 break;
1290 case ACTION_REDIRECT_IP:
1291 nh.type = NEXTHOP_TYPE_IPV4;
1292 nh.gate.ipv4.s_addr =
1293 api->actions[i].u.zr.redirect_ip_v4.s_addr;
1294 nh.vrf_id = api->vrf_id;
1295 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
1296 api->vrf_id,
1297 src, dst,
1de7dfff
PG
1298 &nh, &rate, proto,
1299 srcp, dstp);
d114b0d7
PG
1300 /* XXX combination with REDIRECT_VRF
1301 * + REDIRECT_NH_IP not done
1302 */
1303 continue_loop = 0;
1304 break;
1305 case ACTION_REDIRECT:
1306 nh.vrf_id = api->actions[i].u.redirect_vrf;
1307 nh.type = NEXTHOP_TYPE_IPV4;
1308 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
1309 api->vrf_id,
1310 src, dst,
1de7dfff
PG
1311 &nh, &rate, proto,
1312 srcp, dstp);
d114b0d7
PG
1313 continue_loop = 0;
1314 break;
1315 case ACTION_MARKING:
ac7c35f8 1316 if (BGP_DEBUG(pbr, PBR)) {
d114b0d7 1317 bgp_pbr_print_policy_route(api);
ac7c35f8
PG
1318 zlog_warn("PBR: Set DSCP %u Ignored",
1319 api->actions[i].u.marking_dscp);
1320 }
d114b0d7
PG
1321 break;
1322 default:
1323 break;
1324 }
1325 if (continue_loop == 0)
1326 break;
1327 }
1328}
1329
45918cfb
PG
1330void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p,
1331 struct bgp_info *info, afi_t afi, safi_t safi,
1332 bool nlri_update)
1333{
1334 struct bgp_pbr_entry_main api;
1335
1336 if (afi == AFI_IP6)
1337 return; /* IPv6 not supported */
1338 if (safi != SAFI_FLOWSPEC)
1339 return; /* not supported */
1340 /* Make Zebra API structure. */
1341 memset(&api, 0, sizeof(api));
1342 api.vrf_id = bgp->vrf_id;
1343 api.afi = afi;
1344
1345 if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) {
f146bb54
PG
1346 if (BGP_DEBUG(pbr, PBR_ERROR))
1347 zlog_err("%s: cancel updating entry in bgp pbr",
1348 __func__);
45918cfb
PG
1349 return;
1350 }
d114b0d7 1351 bgp_pbr_handle_entry(bgp, info, &api, nlri_update);
45918cfb 1352}