]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_pbr.c
bgpd: pbr support for port redirecting
[mirror_frr.git] / bgpd / bgp_pbr.c
CommitLineData
8046aadf
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"
34d44f18 23#include "jhash.h"
8046aadf 24
34d44f18 25#include "bgpd/bgpd.h"
8046aadf 26#include "bgpd/bgp_pbr.h"
16239153 27#include "bgpd/bgp_debug.h"
2749b788
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"
ed132710 32#include "bgpd/bgp_zebra.h"
0a3eeab8 33#include "bgpd/bgp_mplsvpn.h"
ed132710
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;
16239153 43
4c793038
PG
44struct bgp_pbr_match_iptable_unique {
45 uint32_t unique;
46 struct bgp_pbr_match *bpm_found;
47};
48
5bbc648d
PG
49struct bgp_pbr_match_entry_unique {
50 uint32_t unique;
51 struct bgp_pbr_match_entry *bpme_found;
52};
53
2055c776
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
5bbc648d
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
4c793038
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
5bbc648d
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
16239153
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
9353fe4e
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
16239153
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
9353fe4e 235 * - combination srcport + @IP
16239153 236 */
9353fe4e 237 if (api->match_icmp_type_num || api->match_icmp_type_num
16239153
PG
238 || api->match_packet_length_num || api->match_dscp_num
239 || api->match_tcpflags_num) {
aeb8f086 240 if (BGP_DEBUG(pbr, PBR)) {
16239153 241 bgp_pbr_print_policy_route(api);
aeb8f086 242 zlog_debug("BGP: some SET actions not supported by Zebra. ignoring.");
9353fe4e 243 zlog_debug("BGP: case icmp or length or dscp or tcp flags");
aeb8f086 244 }
16239153
PG
245 return 0;
246 }
9353fe4e
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 }
16239153
PG
292 if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
293 !(api->match_bitmask & PREFIX_DST_PRESENT)) {
aeb8f086 294 if (BGP_DEBUG(pbr, PBR)) {
16239153 295 bgp_pbr_print_policy_route(api);
aeb8f086
PG
296 zlog_debug("BGP: match actions without src"
297 " or dst address can not operate."
298 " ignoring.");
299 }
16239153
PG
300 return 0;
301 }
302 return 1;
303}
8046aadf 304
2749b788
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) {
060eadea
PG
332 if (BGP_DEBUG(pbr, PBR_ERROR))
333 zlog_err("%s: flowspec actions exceeds limit (max %u)",
334 __func__, action_count);
2749b788
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)) {
aeb8f086 398 if (BGP_DEBUG(pbr, PBR)) {
2749b788 399 bgp_pbr_print_policy_route(api);
aeb8f086
PG
400 zlog_debug("%s: inconsistency:"
401 " no match for afi src and dst (%u/%u)",
402 __func__, afi, family2afi(dst->family));
403 }
2749b788
PG
404 return -1;
405 }
406 }
407 return 0;
408}
409
317a239f
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
ed132710
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
317a239f
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
ed132710
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
34d44f18
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);
9353fe4e
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);
34d44f18
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
9353fe4e
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
34d44f18
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}
8046aadf 629
2055c776
PG
630struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id,
631 uint32_t unique)
8046aadf 632{
2055c776
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;
8046aadf
PG
642}
643
644struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
645 uint32_t unique)
646{
5bbc648d
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;
8046aadf
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{
5bbc648d
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;
8046aadf
PG
680}
681
4c793038
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
317a239f
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
34d44f18
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}
16239153
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}
2749b788 856
ed132710
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;
870 }
871 hash_release(bpm->entry_hash, bpme);
872 if (hashcount(bpm->entry_hash) == 0) {
873 /* delete iptable entry first */
874 /* then delete ipset match */
875 if (bpm->installed) {
876 if (bpm->installed_in_iptable) {
877 bgp_send_pbr_iptable(bpm->action,
878 bpm, false);
879 bpm->installed_in_iptable = false;
317a239f 880 bpm->action->refcnt--;
ed132710
PG
881 }
882 bgp_send_pbr_ipset_match(bpm, false);
883 bpm->installed = false;
884 bpm->action = NULL;
885 }
886 hash_release(bgp->pbr_match_hash, bpm);
887 /* XXX release pbr_match_action if not used
888 * note that drop does not need to call send_pbr_action
889 */
890 }
317a239f
PG
891 if (bpa->refcnt == 0) {
892 if (bpa->installed && bpa->table_id != 0) {
893 bgp_send_pbr_rule_action(bpa, false);
894 bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
895 AFI_IP,
896 bpa->table_id,
897 false);
898 }
899 }
ed132710
PG
900}
901
902struct bgp_pbr_match_entry_remain {
903 struct bgp_pbr_match_entry *bpme_to_match;
904 struct bgp_pbr_match_entry *bpme_found;
905};
906
907static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg)
908{
909 struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
910 struct bgp_pbr_match_entry_remain *bpmer =
911 (struct bgp_pbr_match_entry_remain *)arg;
912 struct bgp_pbr_match *bpm_temp;
913 struct bgp_pbr_match_entry *bpme = bpmer->bpme_to_match;
914
915 if (!bpme->backpointer ||
916 bpm == bpme->backpointer ||
917 bpme->backpointer->action == bpm->action)
918 return HASHWALK_CONTINUE;
919 /* ensure bpm other characteristics are equal */
920 bpm_temp = bpme->backpointer;
921 if (bpm_temp->vrf_id != bpm->vrf_id ||
922 bpm_temp->type != bpm->type ||
923 bpm_temp->flags != bpm->flags)
924 return HASHWALK_CONTINUE;
925
926 /* look for remaining bpme */
927 bpmer->bpme_found = hash_lookup(bpm->entry_hash, bpme);
928 if (!bpmer->bpme_found)
929 return HASHWALK_CONTINUE;
930 return HASHWALK_ABORT;
931}
932
933static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
9353fe4e
PG
934 struct bgp_info *binfo,
935 vrf_id_t vrf_id,
936 struct prefix *src,
937 struct prefix *dst,
938 uint8_t protocol,
939 struct bgp_pbr_range_port *src_port,
940 struct bgp_pbr_range_port *dst_port)
ed132710
PG
941{
942 struct bgp_pbr_match temp;
943 struct bgp_pbr_match_entry temp2;
944 struct bgp_pbr_match *bpm;
945 struct bgp_pbr_match_entry *bpme;
946 struct bgp_pbr_match_entry_remain bpmer;
947
948 /* as we don't know information from EC
949 * look for bpm that have the bpm
950 * with vrf_id characteristics
951 */
952 memset(&temp2, 0, sizeof(temp2));
953 memset(&temp, 0, sizeof(temp));
954 if (src) {
955 temp.flags |= MATCH_IP_SRC_SET;
956 prefix_copy(&temp2.src, src);
957 } else
958 temp2.src.family = AF_INET;
959 if (dst) {
960 temp.flags |= MATCH_IP_DST_SET;
961 prefix_copy(&temp2.dst, dst);
962 } else
963 temp2.dst.family = AF_INET;
9353fe4e
PG
964 if (src_port) {
965 temp.flags |= MATCH_PORT_SRC_SET;
966 temp2.src_port_min = src_port->min_port;
967 if (src_port->max_port) {
968 temp.flags |= MATCH_PORT_SRC_RANGE_SET;
969 temp2.src_port_max = src_port->max_port;
970 }
971 }
972 if (dst_port) {
973 temp.flags |= MATCH_PORT_DST_SET;
974 temp2.dst_port_min = dst_port->min_port;
975 if (dst_port->max_port) {
976 temp.flags |= MATCH_PORT_DST_RANGE_SET;
977 temp2.dst_port_max = dst_port->max_port;
978 }
979 }
980 temp2.proto = protocol;
981
982 if (src == NULL || dst == NULL) {
983 if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
984 temp.type = IPSET_NET_PORT;
985 else
986 temp.type = IPSET_NET;
987 } else {
988 if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
989 temp.type = IPSET_NET_PORT_NET;
990 else
991 temp.type = IPSET_NET_NET;
992 }
ed132710
PG
993 if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
994 temp.vrf_id = 0;
995 else
996 temp.vrf_id = vrf_id;
997 bpme = &temp2;
998 bpm = &temp;
999 bpme->backpointer = bpm;
1000 /* right now, a previous entry may already exist
1001 * flush previous entry if necessary
1002 */
1003 bpmer.bpme_to_match = bpme;
1004 bpmer.bpme_found = NULL;
1005 hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
1006 if (bpmer.bpme_found) {
1007 static struct bgp_pbr_match *local_bpm;
1008 static struct bgp_pbr_action *local_bpa;
1009
1010 local_bpm = bpmer.bpme_found->backpointer;
1011 local_bpa = local_bpm->action;
1012 bgp_pbr_flush_entry(bgp, local_bpa,
1013 local_bpm, bpmer.bpme_found);
1014 }
1015}
1016
1017static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
9353fe4e
PG
1018 struct bgp_info *binfo,
1019 vrf_id_t vrf_id,
1020 struct prefix *src,
1021 struct prefix *dst,
1022 struct nexthop *nh,
1023 float *rate,
1024 uint8_t protocol,
1025 struct bgp_pbr_range_port *src_port,
1026 struct bgp_pbr_range_port *dst_port)
ed132710
PG
1027{
1028 struct bgp_pbr_match temp;
1029 struct bgp_pbr_match_entry temp2;
1030 struct bgp_pbr_match *bpm;
1031 struct bgp_pbr_match_entry *bpme = NULL;
1032 struct bgp_pbr_action temp3;
1033 struct bgp_pbr_action *bpa = NULL;
1034 struct bgp_pbr_match_entry_remain bpmer;
1035
1036 /* look for bpa first */
1037 memset(&temp3, 0, sizeof(temp3));
1038 if (rate)
1039 temp3.rate = *rate;
1040 if (nh)
1041 memcpy(&temp3.nh, nh, sizeof(struct nexthop));
1042 temp3.vrf_id = vrf_id;
1043 bpa = hash_get(bgp->pbr_action_hash, &temp3,
1044 bgp_pbr_action_alloc_intern);
1045
1046 if (bpa->fwmark == 0) {
ed132710
PG
1047 /* drop is handled by iptable */
1048 if (nh && nh->type == NEXTHOP_TYPE_BLACKHOLE) {
1049 bpa->table_id = 0;
1050 bpa->installed = true;
1051 } else {
d53bfbb3
PG
1052 bpa->fwmark = bgp_zebra_tm_get_id();
1053 bpa->table_id = bpa->fwmark;
ed132710
PG
1054 bpa->installed = false;
1055 }
317a239f 1056 bpa->bgp = bgp;
ed132710
PG
1057 bpa->unique = ++bgp_pbr_action_counter_unique;
1058 /* 0 value is forbidden */
1059 bpa->install_in_progress = false;
1060 }
1061
1062 /* then look for bpm */
1063 memset(&temp, 0, sizeof(temp));
9353fe4e
PG
1064 if (src == NULL || dst == NULL) {
1065 if ((src_port && src_port->min_port) ||
1066 (dst_port && dst_port->min_port))
1067 temp.type = IPSET_NET_PORT;
1068 else
1069 temp.type = IPSET_NET;
1070 } else {
1071 if ((src_port && src_port->min_port) ||
1072 (dst_port && dst_port->min_port))
1073 temp.type = IPSET_NET_PORT_NET;
1074 else
1075 temp.type = IPSET_NET_NET;
1076 }
ed132710
PG
1077 temp.vrf_id = vrf_id;
1078 if (src)
1079 temp.flags |= MATCH_IP_SRC_SET;
1080 if (dst)
1081 temp.flags |= MATCH_IP_DST_SET;
9353fe4e
PG
1082
1083 if (src_port && src_port->min_port)
1084 temp.flags |= MATCH_PORT_SRC_SET;
1085 if (dst_port && dst_port->min_port)
1086 temp.flags |= MATCH_PORT_DST_SET;
1087 if (src_port && src_port->max_port)
1088 temp.flags |= MATCH_PORT_SRC_RANGE_SET;
1089 if (dst_port && dst_port->max_port)
1090 temp.flags |= MATCH_PORT_DST_RANGE_SET;
ed132710
PG
1091 temp.action = bpa;
1092 bpm = hash_get(bgp->pbr_match_hash, &temp,
1093 bgp_pbr_match_alloc_intern);
1094
1095 /* new, then self allocate ipset_name and unique */
1096 if (bpm && bpm->unique == 0) {
1097 bpm->unique = ++bgp_pbr_match_counter_unique;
1098 /* 0 value is forbidden */
1099 sprintf(bpm->ipset_name, "match%p", bpm);
1100 bpm->entry_hash = hash_create_size(8,
1101 bgp_pbr_match_entry_hash_key,
1102 bgp_pbr_match_entry_hash_equal,
1103 "Match Entry Hash");
1104 bpm->installed = false;
1105
1106 /* unique2 should be updated too */
1107 bpm->unique2 = ++bgp_pbr_match_iptable_counter_unique;
1108 bpm->installed_in_iptable = false;
1109 bpm->install_in_progress = false;
1110 bpm->install_iptable_in_progress = false;
1111 }
1112
1113 memset(&temp2, 0, sizeof(temp2));
1114 if (src)
1115 prefix_copy(&temp2.src, src);
1116 else
1117 temp2.src.family = AF_INET;
1118 if (dst)
1119 prefix_copy(&temp2.dst, dst);
1120 else
1121 temp2.dst.family = AF_INET;
9353fe4e
PG
1122 temp2.src_port_min = src_port ? src_port->min_port : 0;
1123 temp2.dst_port_min = dst_port ? dst_port->min_port : 0;
1124 temp2.src_port_max = src_port ? src_port->max_port : 0;
1125 temp2.dst_port_max = dst_port ? dst_port->max_port : 0;
1126 temp2.proto = protocol;
ed132710
PG
1127 if (bpm)
1128 bpme = hash_get(bpm->entry_hash, &temp2,
9353fe4e 1129 bgp_pbr_match_entry_alloc_intern);
ed132710
PG
1130 if (bpme && bpme->unique == 0) {
1131 bpme->unique = ++bgp_pbr_match_entry_counter_unique;
1132 /* 0 value is forbidden */
1133 bpme->backpointer = bpm;
1134 bpme->installed = false;
1135 bpme->install_in_progress = false;
1136 }
1137
1138 /* BGP FS: append entry to zebra
1139 * - policies are not routing entries and as such
1140 * route replace semantics don't necessarily follow
1141 * through to policy entries
1142 * - because of that, not all policing information will be stored
1143 * into zebra. and non selected policies will be suppressed from zebra
1144 * - as consequence, in order to bring consistency
1145 * a policy will be added, then ifan ecmp policy exists,
1146 * it will be suppressed subsequently
1147 */
1148 /* ip rule add */
9b269948 1149 if (!bpa->installed) {
ed132710 1150 bgp_send_pbr_rule_action(bpa, true);
9b269948
PG
1151 bgp_zebra_announce_default(bgp, nh,
1152 AFI_IP, bpa->table_id, true);
1153 }
ed132710
PG
1154
1155 /* ipset create */
1156 if (bpm && !bpm->installed)
1157 bgp_send_pbr_ipset_match(bpm, true);
1158 /* ipset add */
1159 if (bpme && !bpme->installed)
1160 bgp_send_pbr_ipset_entry_match(bpme, true);
1161
1162 /* iptables */
1163 if (bpm && !bpm->installed_in_iptable)
1164 bgp_send_pbr_iptable(bpa, bpm, true);
1165
1166 /* A previous entry may already exist
1167 * flush previous entry if necessary
1168 */
1169 bpmer.bpme_to_match = bpme;
1170 bpmer.bpme_found = NULL;
1171 hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
1172 if (bpmer.bpme_found) {
1173 static struct bgp_pbr_match *local_bpm;
1174 static struct bgp_pbr_action *local_bpa;
1175
1176 local_bpm = bpmer.bpme_found->backpointer;
1177 local_bpa = local_bpm->action;
1178 bgp_pbr_flush_entry(bgp, local_bpa,
1179 local_bpm, bpmer.bpme_found);
1180 }
1181
1182
1183}
1184
1185static void bgp_pbr_handle_entry(struct bgp *bgp,
1186 struct bgp_info *binfo,
1187 struct bgp_pbr_entry_main *api,
1188 bool add)
1189{
1190 struct nexthop nh;
1191 int i = 0;
1192 int continue_loop = 1;
1193 float rate = 0;
1194 struct prefix *src = NULL, *dst = NULL;
9353fe4e
PG
1195 uint8_t proto = 0;
1196 struct bgp_pbr_range_port *srcp = NULL, *dstp = NULL;
1197 struct bgp_pbr_range_port range;
ed132710
PG
1198
1199 if (api->match_bitmask & PREFIX_SRC_PRESENT)
1200 src = &api->src_prefix;
1201 if (api->match_bitmask & PREFIX_DST_PRESENT)
1202 dst = &api->dst_prefix;
1203 memset(&nh, 0, sizeof(struct nexthop));
1204 nh.vrf_id = VRF_UNKNOWN;
9353fe4e
PG
1205 if (api->match_protocol_num)
1206 proto = (uint8_t)api->protocol[0].value;
1207 /* if match_port is selected, then either src or dst port will be parsed
1208 * but not both at the same time
1209 */
1210 if (api->match_port_num >= 1) {
1211 bgp_pbr_extract(api->port,
1212 api->match_port_num,
1213 &range);
1214 srcp = dstp = &range;
1215 } else if (api->match_src_port_num >= 1) {
1216 bgp_pbr_extract(api->src_port,
1217 api->match_src_port_num,
1218 &range);
1219 srcp = &range;
1220 dstp = NULL;
1221 } else if (api->match_dst_port_num >= 1) {
1222 bgp_pbr_extract(api->dst_port,
1223 api->match_dst_port_num,
1224 &range);
1225 dstp = &range;
1226 srcp = NULL;
1227 }
ed132710
PG
1228 if (!add)
1229 return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo,
9353fe4e
PG
1230 api->vrf_id, src, dst,
1231 proto, srcp, dstp);
ed132710
PG
1232 /* no action for add = true */
1233 for (i = 0; i < api->action_num; i++) {
1234 switch (api->actions[i].action) {
1235 case ACTION_TRAFFICRATE:
1236 /* drop packet */
1237 if (api->actions[i].u.r.rate == 0) {
1238 nh.vrf_id = api->vrf_id;
1239 nh.type = NEXTHOP_TYPE_BLACKHOLE;
1240 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
1241 api->vrf_id, src, dst,
9353fe4e
PG
1242 &nh, &rate, proto,
1243 srcp, dstp);
ed132710
PG
1244 } else {
1245 /* update rate. can be reentrant */
1246 rate = api->actions[i].u.r.rate;
aeb8f086 1247 if (BGP_DEBUG(pbr, PBR)) {
ed132710 1248 bgp_pbr_print_policy_route(api);
aeb8f086
PG
1249 zlog_warn("PBR: ignoring Set action rate %f",
1250 api->actions[i].u.r.rate);
1251 }
ed132710
PG
1252 }
1253 break;
1254 case ACTION_TRAFFIC_ACTION:
1255 if (api->actions[i].u.za.filter
1256 & TRAFFIC_ACTION_SAMPLE) {
aeb8f086 1257 if (BGP_DEBUG(pbr, PBR)) {
ed132710 1258 bgp_pbr_print_policy_route(api);
aeb8f086
PG
1259 zlog_warn("PBR: Sample action Ignored");
1260 }
ed132710
PG
1261 }
1262#if 0
1263 if (api->actions[i].u.za.filter
1264 & TRAFFIC_ACTION_DISTRIBUTE) {
aeb8f086 1265 if (BGP_DEBUG(pbr, PBR)) {
ed132710 1266 bgp_pbr_print_policy_route(api);
aeb8f086
PG
1267 zlog_warn("PBR: Distribute action Applies");
1268 }
ed132710
PG
1269 continue_loop = 0;
1270 /* continue forwarding entry as before
1271 * no action
1272 */
1273 }
1274#endif /* XXX to confirm behaviour of traffic action. for now , ignore */
1275 /* terminate action: run other filters
1276 */
1277 break;
1278 case ACTION_REDIRECT_IP:
1279 nh.type = NEXTHOP_TYPE_IPV4;
1280 nh.gate.ipv4.s_addr =
1281 api->actions[i].u.zr.redirect_ip_v4.s_addr;
1282 nh.vrf_id = api->vrf_id;
1283 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
1284 api->vrf_id,
1285 src, dst,
9353fe4e
PG
1286 &nh, &rate, proto,
1287 srcp, dstp);
ed132710
PG
1288 /* XXX combination with REDIRECT_VRF
1289 * + REDIRECT_NH_IP not done
1290 */
1291 continue_loop = 0;
1292 break;
1293 case ACTION_REDIRECT:
1294 nh.vrf_id = api->actions[i].u.redirect_vrf;
1295 nh.type = NEXTHOP_TYPE_IPV4;
1296 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
1297 api->vrf_id,
1298 src, dst,
9353fe4e
PG
1299 &nh, &rate, proto,
1300 srcp, dstp);
ed132710
PG
1301 continue_loop = 0;
1302 break;
1303 case ACTION_MARKING:
aeb8f086 1304 if (BGP_DEBUG(pbr, PBR)) {
ed132710 1305 bgp_pbr_print_policy_route(api);
aeb8f086
PG
1306 zlog_warn("PBR: Set DSCP %u Ignored",
1307 api->actions[i].u.marking_dscp);
1308 }
ed132710
PG
1309 break;
1310 default:
1311 break;
1312 }
1313 if (continue_loop == 0)
1314 break;
1315 }
1316}
1317
2749b788
PG
1318void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p,
1319 struct bgp_info *info, afi_t afi, safi_t safi,
1320 bool nlri_update)
1321{
1322 struct bgp_pbr_entry_main api;
1323
1324 if (afi == AFI_IP6)
1325 return; /* IPv6 not supported */
1326 if (safi != SAFI_FLOWSPEC)
1327 return; /* not supported */
1328 /* Make Zebra API structure. */
1329 memset(&api, 0, sizeof(api));
1330 api.vrf_id = bgp->vrf_id;
1331 api.afi = afi;
1332
1333 if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) {
060eadea
PG
1334 if (BGP_DEBUG(pbr, PBR_ERROR))
1335 zlog_err("%s: cancel updating entry in bgp pbr",
1336 __func__);
2749b788
PG
1337 return;
1338 }
ed132710 1339 bgp_pbr_handle_entry(bgp, info, &api, nlri_update);
2749b788 1340}