]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_pbr.c
bgpd: inject policy route entry from bgp into zebra pbr entries.
[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
PG
32#include "bgpd/bgp_zebra.h"
33
34DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH_ENTRY, "PBR match entry")
35DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH, "PBR match")
36DEFINE_MTYPE_STATIC(BGPD, PBR_ACTION, "PBR action")
37
38static int bgp_pbr_match_counter_unique;
39static int bgp_pbr_match_entry_counter_unique;
40static int bgp_pbr_action_counter_unique;
41static int bgp_pbr_match_iptable_counter_unique;
16239153
PG
42
43static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval,
44 const char *prepend)
45{
46 char *ptr = str;
47
48 if (prepend)
49 ptr += sprintf(ptr, "%s", prepend);
50 else {
51 if (mval->unary_operator & OPERATOR_UNARY_OR)
52 ptr += sprintf(ptr, ", or ");
53 if (mval->unary_operator & OPERATOR_UNARY_AND)
54 ptr += sprintf(ptr, ", and ");
55 }
56 if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN)
57 ptr += sprintf(ptr, "<");
58 if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN)
59 ptr += sprintf(ptr, ">");
60 if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO)
61 ptr += sprintf(ptr, "=");
62 if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH)
63 ptr += sprintf(ptr, "match");
64 ptr += sprintf(ptr, " %u", mval->value);
65 return (int)(ptr - str);
66}
67
68#define INCREMENT_DISPLAY(_ptr, _cnt) do { \
69 if (_cnt) \
70 (_ptr) += sprintf((_ptr), "; "); \
71 _cnt++; \
72 } while (0)
73
74/* return 1 if OK, 0 if validation should stop) */
75static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
76{
77 /* because bgp pbr entry may contain unsupported
78 * combinations, a message will be displayed here if
79 * not supported.
80 * for now, only match/set supported is
81 * - combination src/dst => redirect nexthop [ + rate]
82 * - combination src/dst => redirect VRF [ + rate]
83 * - combination src/dst => drop
84 */
85 if (api->match_src_port_num || api->match_dst_port_num
86 || api->match_port_num || api->match_protocol_num
87 || api->match_icmp_type_num || api->match_icmp_type_num
88 || api->match_packet_length_num || api->match_dscp_num
89 || api->match_tcpflags_num) {
90 if (BGP_DEBUG(pbr, PBR))
91 bgp_pbr_print_policy_route(api);
92 zlog_err("BGP: some SET actions not supported by Zebra. ignoring.");
93 return 0;
94 }
95 if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
96 !(api->match_bitmask & PREFIX_DST_PRESENT)) {
97 if (BGP_DEBUG(pbr, PBR))
98 bgp_pbr_print_policy_route(api);
99 zlog_err("BGP: SET actions without src or dst address can not operate. ignoring.");
100 return 0;
101 }
102 return 1;
103}
8046aadf 104
2749b788
PG
105/* return -1 if build or validation failed */
106static int bgp_pbr_build_and_validate_entry(struct prefix *p,
107 struct bgp_info *info,
108 struct bgp_pbr_entry_main *api)
109{
110 int ret;
111 int i, action_count = 0;
112 struct ecommunity *ecom;
113 struct ecommunity_val *ecom_eval;
114 struct bgp_pbr_entry_action *api_action;
115 struct prefix *src = NULL, *dst = NULL;
116 int valid_prefix = 0;
117 afi_t afi = AFI_IP;
118
119 /* extract match from flowspec entries */
120 ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr,
121 p->u.prefix_flowspec.prefixlen, api);
122 if (ret < 0)
123 return -1;
124 /* extract actiosn from flowspec ecom list */
125 if (info && info->attr && info->attr->ecommunity) {
126 ecom = info->attr->ecommunity;
127 for (i = 0; i < ecom->size; i++) {
128 ecom_eval = (struct ecommunity_val *)
129 ecom->val + (i * ECOMMUNITY_SIZE);
130
131 if (action_count > ACTIONS_MAX_NUM) {
132 zlog_err("%s: flowspec actions exceeds limit (max %u)",
133 __func__, action_count);
134 break;
135 }
136 api_action = &api->actions[action_count];
137
138 if ((ecom_eval->val[1] ==
139 (char)ECOMMUNITY_REDIRECT_VRF) &&
140 (ecom_eval->val[0] ==
141 (char)ECOMMUNITY_ENCODE_TRANS_EXP ||
142 ecom_eval->val[0] ==
143 (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
144 ecom_eval->val[0] ==
145 (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_3)) {
146 struct ecommunity *eckey = ecommunity_new();
147 struct ecommunity_val ecom_copy;
148
149 memcpy(&ecom_copy, ecom_eval,
150 sizeof(struct ecommunity_val));
151 ecom_copy.val[0] &=
152 ~ECOMMUNITY_ENCODE_TRANS_EXP;
153 ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
154 ecommunity_add_val(eckey, &ecom_copy);
155
156 api_action->action = ACTION_REDIRECT;
157 api_action->u.redirect_vrf =
158 get_first_vrf_for_redirect_with_rt(
159 eckey);
160 ecommunity_free(&eckey);
161 } else if ((ecom_eval->val[0] ==
162 (char)ECOMMUNITY_ENCODE_REDIRECT_IP_NH) &&
163 (ecom_eval->val[1] ==
164 (char)ECOMMUNITY_REDIRECT_IP_NH)) {
165 api_action->action = ACTION_REDIRECT_IP;
166 api_action->u.zr.redirect_ip_v4.s_addr =
167 info->attr->nexthop.s_addr;
168 api_action->u.zr.duplicate = ecom_eval->val[7];
169 } else {
170 if (ecom_eval->val[0] !=
171 (char)ECOMMUNITY_ENCODE_TRANS_EXP)
172 continue;
173 ret = ecommunity_fill_pbr_action(ecom_eval,
174 api_action);
175 if (ret != 0)
176 continue;
177 }
178 api->action_num++;
179 }
180 }
181
182 /* validate if incoming matc/action is compatible
183 * with our policy routing engine
184 */
185 if (!bgp_pbr_validate_policy_route(api))
186 return -1;
187
188 /* check inconsistency in the match rule */
189 if (api->match_bitmask & PREFIX_SRC_PRESENT) {
190 src = &api->src_prefix;
191 afi = family2afi(src->family);
192 valid_prefix = 1;
193 }
194 if (api->match_bitmask & PREFIX_DST_PRESENT) {
195 dst = &api->dst_prefix;
196 if (valid_prefix && afi != family2afi(dst->family)) {
197 if (BGP_DEBUG(pbr, PBR))
198 bgp_pbr_print_policy_route(api);
199 zlog_err("%s: inconsistency: no match for afi src and dst (%u/%u)",
200 __func__, afi, family2afi(dst->family));
201 return -1;
202 }
203 }
204 return 0;
205}
206
ed132710
PG
207static void *bgp_pbr_match_alloc_intern(void *arg)
208{
209 struct bgp_pbr_match *bpm, *new;
210
211 bpm = (struct bgp_pbr_match *)arg;
212
213 new = XCALLOC(MTYPE_PBR_MATCH, sizeof(*new));
214 memcpy(new, bpm, sizeof(*bpm));
215
216 return new;
217}
218
219static void *bgp_pbr_action_alloc_intern(void *arg)
220{
221 struct bgp_pbr_action *bpa, *new;
222
223 bpa = (struct bgp_pbr_action *)arg;
224
225 new = XCALLOC(MTYPE_PBR_ACTION, sizeof(*new));
226
227 memcpy(new, bpa, sizeof(*bpa));
228
229 return new;
230}
231
232static void *bgp_pbr_match_entry_alloc_intern(void *arg)
233{
234 struct bgp_pbr_match_entry *bpme, *new;
235
236 bpme = (struct bgp_pbr_match_entry *)arg;
237
238 new = XCALLOC(MTYPE_PBR_MATCH_ENTRY, sizeof(*new));
239
240 memcpy(new, bpme, sizeof(*bpme));
241
242 return new;
243}
244
34d44f18
PG
245uint32_t bgp_pbr_match_hash_key(void *arg)
246{
247 struct bgp_pbr_match *pbm = (struct bgp_pbr_match *)arg;
248 uint32_t key;
249
250 key = jhash_1word(pbm->vrf_id, 0x4312abde);
251 key = jhash_1word(pbm->flags, key);
252 return jhash_1word(pbm->type, key);
253}
254
255int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2)
256{
257 const struct bgp_pbr_match *r1, *r2;
258
259 r1 = (const struct bgp_pbr_match *)arg1;
260 r2 = (const struct bgp_pbr_match *)arg2;
261
262 if (r1->vrf_id != r2->vrf_id)
263 return 0;
264
265 if (r1->type != r2->type)
266 return 0;
267
268 if (r1->flags != r2->flags)
269 return 0;
270
271 if (r1->action != r2->action)
272 return 0;
273
274 return 1;
275}
276
277uint32_t bgp_pbr_match_entry_hash_key(void *arg)
278{
279 struct bgp_pbr_match_entry *pbme;
280 uint32_t key;
281
282 pbme = (struct bgp_pbr_match_entry *)arg;
283 key = prefix_hash_key(&pbme->src);
284 key = jhash_1word(prefix_hash_key(&pbme->dst), key);
285
286 return key;
287}
288
289int bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2)
290{
291 const struct bgp_pbr_match_entry *r1, *r2;
292
293 r1 = (const struct bgp_pbr_match_entry *)arg1;
294 r2 = (const struct bgp_pbr_match_entry *)arg2;
295
296 /* on updates, comparing
297 * backpointer is not necessary
298 */
299
300 /* unique value is self calculated
301 */
302
303 /* rate is ignored for now
304 */
305
306 if (!prefix_same(&r1->src, &r2->src))
307 return 0;
308
309 if (!prefix_same(&r1->dst, &r2->dst))
310 return 0;
311
312 return 1;
313}
314
315uint32_t bgp_pbr_action_hash_key(void *arg)
316{
317 struct bgp_pbr_action *pbra;
318 uint32_t key;
319
320 pbra = (struct bgp_pbr_action *)arg;
321 key = jhash_1word(pbra->table_id, 0x4312abde);
322 key = jhash_1word(pbra->fwmark, key);
323 return key;
324}
325
326int bgp_pbr_action_hash_equal(const void *arg1, const void *arg2)
327{
328 const struct bgp_pbr_action *r1, *r2;
329
330 r1 = (const struct bgp_pbr_action *)arg1;
331 r2 = (const struct bgp_pbr_action *)arg2;
332
333 /* unique value is self calculated
334 * table and fwmark is self calculated
335 */
336 if (r1->rate != r2->rate)
337 return 0;
338
339 if (r1->vrf_id != r2->vrf_id)
340 return 0;
341
342 if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop)))
343 return 0;
344 return 1;
345}
8046aadf
PG
346
347struct bgp_pbr_action *bgp_pbr_action_rule_lookup(uint32_t unique)
348{
349 return NULL;
350}
351
352struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
353 uint32_t unique)
354{
355 return NULL;
356}
357
358struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(vrf_id_t vrf_id,
359 char *ipset_name,
360 uint32_t unique)
361{
362 return NULL;
363}
364
34d44f18
PG
365void bgp_pbr_init(struct bgp *bgp)
366{
367 bgp->pbr_match_hash =
368 hash_create_size(8, bgp_pbr_match_hash_key,
369 bgp_pbr_match_hash_equal,
370 "Match Hash");
371 bgp->pbr_action_hash =
372 hash_create_size(8, bgp_pbr_action_hash_key,
373 bgp_pbr_action_hash_equal,
374 "Match Hash Entry");
375}
16239153
PG
376
377void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api)
378{
379 int i = 0;
380 char return_string[512];
381 char *ptr = return_string;
382 char buff[64];
383 int nb_items = 0;
384
385 ptr += sprintf(ptr, "MATCH : ");
386 if (api->match_bitmask & PREFIX_SRC_PRESENT) {
387 struct prefix *p = &(api->src_prefix);
388
389 ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64));
390 INCREMENT_DISPLAY(ptr, nb_items);
391 }
392 if (api->match_bitmask & PREFIX_DST_PRESENT) {
393 struct prefix *p = &(api->dst_prefix);
394
395 INCREMENT_DISPLAY(ptr, nb_items);
396 ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64));
397 }
398
399 if (api->match_protocol_num)
400 INCREMENT_DISPLAY(ptr, nb_items);
401 for (i = 0; i < api->match_protocol_num; i++)
402 ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i],
403 i > 0 ? NULL : "@proto ");
404
405 if (api->match_src_port_num)
406 INCREMENT_DISPLAY(ptr, nb_items);
407 for (i = 0; i < api->match_src_port_num; i++)
408 ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i],
409 i > 0 ? NULL : "@srcport ");
410
411 if (api->match_dst_port_num)
412 INCREMENT_DISPLAY(ptr, nb_items);
413 for (i = 0; i < api->match_dst_port_num; i++)
414 ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i],
415 i > 0 ? NULL : "@dstport ");
416
417 if (api->match_port_num)
418 INCREMENT_DISPLAY(ptr, nb_items);
419 for (i = 0; i < api->match_port_num; i++)
420 ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i],
421 i > 0 ? NULL : "@port ");
422
423 if (api->match_icmp_type_num)
424 INCREMENT_DISPLAY(ptr, nb_items);
425 for (i = 0; i < api->match_icmp_type_num; i++)
426 ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i],
427 i > 0 ? NULL : "@icmptype ");
428
429 if (api->match_icmp_code_num)
430 INCREMENT_DISPLAY(ptr, nb_items);
431 for (i = 0; i < api->match_icmp_code_num; i++)
432 ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i],
433 i > 0 ? NULL : "@icmpcode ");
434
435 if (api->match_packet_length_num)
436 INCREMENT_DISPLAY(ptr, nb_items);
437 for (i = 0; i < api->match_packet_length_num; i++)
438 ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i],
439 i > 0 ? NULL : "@plen ");
440
441 if (api->match_dscp_num)
442 INCREMENT_DISPLAY(ptr, nb_items);
443 for (i = 0; i < api->match_dscp_num; i++)
444 ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i],
445 i > 0 ? NULL : "@dscp ");
446
447 if (api->match_tcpflags_num)
448 INCREMENT_DISPLAY(ptr, nb_items);
449 for (i = 0; i < api->match_tcpflags_num; i++)
450 ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i],
451 i > 0 ? NULL : "@tcpflags ");
452
453 if (api->match_bitmask & FRAGMENT_PRESENT) {
454 INCREMENT_DISPLAY(ptr, nb_items);
455 ptr += sprintf(ptr, "@fragment %u", api->fragment.bitmask);
456 }
457 if (!nb_items)
458 ptr = return_string;
459 else
460 ptr += sprintf(ptr, "; ");
461 if (api->action_num)
462 ptr += sprintf(ptr, "SET : ");
463 nb_items = 0;
464 for (i = 0; i < api->action_num; i++) {
465 switch (api->actions[i].action) {
466 case ACTION_TRAFFICRATE:
467 INCREMENT_DISPLAY(ptr, nb_items);
468 ptr += sprintf(ptr, "@set rate %f",
469 api->actions[i].u.r.rate);
470 break;
471 case ACTION_TRAFFIC_ACTION:
472 INCREMENT_DISPLAY(ptr, nb_items);
473 ptr += sprintf(ptr, "@action ");
474 if (api->actions[i].u.za.filter
475 & TRAFFIC_ACTION_TERMINATE)
476 ptr += sprintf(ptr,
477 " terminate (apply filter(s))");
478 if (api->actions[i].u.za.filter
479 & TRAFFIC_ACTION_DISTRIBUTE)
480 ptr += sprintf(ptr, " distribute");
481 if (api->actions[i].u.za.filter
482 & TRAFFIC_ACTION_SAMPLE)
483 ptr += sprintf(ptr, " sample");
484 break;
485 case ACTION_REDIRECT_IP:
486 INCREMENT_DISPLAY(ptr, nb_items);
487 char local_buff[INET_ADDRSTRLEN];
488
489 if (inet_ntop(AF_INET,
490 &api->actions[i].u.zr.redirect_ip_v4,
491 local_buff, INET_ADDRSTRLEN) != NULL)
492 ptr += sprintf(ptr,
493 "@redirect ip nh %s", local_buff);
494 break;
495 case ACTION_REDIRECT:
496 INCREMENT_DISPLAY(ptr, nb_items);
497 ptr += sprintf(ptr, "@redirect vrf %u",
498 api->actions[i].u.redirect_vrf);
499 break;
500 case ACTION_MARKING:
501 INCREMENT_DISPLAY(ptr, nb_items);
502 ptr += sprintf(ptr, "@set dscp %u",
503 api->actions[i].u.marking_dscp);
504 break;
505 default:
506 break;
507 }
508 }
509 zlog_info("%s", return_string);
510}
2749b788 511
ed132710
PG
512static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa,
513 struct bgp_pbr_match *bpm,
514 struct bgp_pbr_match_entry *bpme)
515{
516 /* if bpme is null, bpm is also null
517 */
518 if (bpme == NULL)
519 return;
520 /* ipset del entry */
521 if (bpme->installed) {
522 bgp_send_pbr_ipset_entry_match(bpme, false);
523 bpme->installed = false;
524 bpme->backpointer = NULL;
525 }
526 hash_release(bpm->entry_hash, bpme);
527 if (hashcount(bpm->entry_hash) == 0) {
528 /* delete iptable entry first */
529 /* then delete ipset match */
530 if (bpm->installed) {
531 if (bpm->installed_in_iptable) {
532 bgp_send_pbr_iptable(bpm->action,
533 bpm, false);
534 bpm->installed_in_iptable = false;
535 }
536 bgp_send_pbr_ipset_match(bpm, false);
537 bpm->installed = false;
538 bpm->action = NULL;
539 }
540 hash_release(bgp->pbr_match_hash, bpm);
541 /* XXX release pbr_match_action if not used
542 * note that drop does not need to call send_pbr_action
543 */
544 }
545}
546
547struct bgp_pbr_match_entry_remain {
548 struct bgp_pbr_match_entry *bpme_to_match;
549 struct bgp_pbr_match_entry *bpme_found;
550};
551
552static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg)
553{
554 struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
555 struct bgp_pbr_match_entry_remain *bpmer =
556 (struct bgp_pbr_match_entry_remain *)arg;
557 struct bgp_pbr_match *bpm_temp;
558 struct bgp_pbr_match_entry *bpme = bpmer->bpme_to_match;
559
560 if (!bpme->backpointer ||
561 bpm == bpme->backpointer ||
562 bpme->backpointer->action == bpm->action)
563 return HASHWALK_CONTINUE;
564 /* ensure bpm other characteristics are equal */
565 bpm_temp = bpme->backpointer;
566 if (bpm_temp->vrf_id != bpm->vrf_id ||
567 bpm_temp->type != bpm->type ||
568 bpm_temp->flags != bpm->flags)
569 return HASHWALK_CONTINUE;
570
571 /* look for remaining bpme */
572 bpmer->bpme_found = hash_lookup(bpm->entry_hash, bpme);
573 if (!bpmer->bpme_found)
574 return HASHWALK_CONTINUE;
575 return HASHWALK_ABORT;
576}
577
578static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
579 struct bgp_info *binfo,
580 vrf_id_t vrf_id,
581 struct prefix *src,
582 struct prefix *dst)
583{
584 struct bgp_pbr_match temp;
585 struct bgp_pbr_match_entry temp2;
586 struct bgp_pbr_match *bpm;
587 struct bgp_pbr_match_entry *bpme;
588 struct bgp_pbr_match_entry_remain bpmer;
589
590 /* as we don't know information from EC
591 * look for bpm that have the bpm
592 * with vrf_id characteristics
593 */
594 memset(&temp2, 0, sizeof(temp2));
595 memset(&temp, 0, sizeof(temp));
596 if (src) {
597 temp.flags |= MATCH_IP_SRC_SET;
598 prefix_copy(&temp2.src, src);
599 } else
600 temp2.src.family = AF_INET;
601 if (dst) {
602 temp.flags |= MATCH_IP_DST_SET;
603 prefix_copy(&temp2.dst, dst);
604 } else
605 temp2.dst.family = AF_INET;
606
607 if (src == NULL || dst == NULL)
608 temp.type = IPSET_NET;
609 else
610 temp.type = IPSET_NET_NET;
611 if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
612 temp.vrf_id = 0;
613 else
614 temp.vrf_id = vrf_id;
615 bpme = &temp2;
616 bpm = &temp;
617 bpme->backpointer = bpm;
618 /* right now, a previous entry may already exist
619 * flush previous entry if necessary
620 */
621 bpmer.bpme_to_match = bpme;
622 bpmer.bpme_found = NULL;
623 hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
624 if (bpmer.bpme_found) {
625 static struct bgp_pbr_match *local_bpm;
626 static struct bgp_pbr_action *local_bpa;
627
628 local_bpm = bpmer.bpme_found->backpointer;
629 local_bpa = local_bpm->action;
630 bgp_pbr_flush_entry(bgp, local_bpa,
631 local_bpm, bpmer.bpme_found);
632 }
633}
634
635static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
636 struct bgp_info *binfo,
637 vrf_id_t vrf_id,
638 struct prefix *src,
639 struct prefix *dst,
640 struct nexthop *nh,
641 float *rate)
642{
643 struct bgp_pbr_match temp;
644 struct bgp_pbr_match_entry temp2;
645 struct bgp_pbr_match *bpm;
646 struct bgp_pbr_match_entry *bpme = NULL;
647 struct bgp_pbr_action temp3;
648 struct bgp_pbr_action *bpa = NULL;
649 struct bgp_pbr_match_entry_remain bpmer;
650
651 /* look for bpa first */
652 memset(&temp3, 0, sizeof(temp3));
653 if (rate)
654 temp3.rate = *rate;
655 if (nh)
656 memcpy(&temp3.nh, nh, sizeof(struct nexthop));
657 temp3.vrf_id = vrf_id;
658 bpa = hash_get(bgp->pbr_action_hash, &temp3,
659 bgp_pbr_action_alloc_intern);
660
661 if (bpa->fwmark == 0) {
662 /* TODO: allocate new table ID using zebra */
663 static int fwmark_id;
664
665 /* drop is handled by iptable */
666 if (nh && nh->type == NEXTHOP_TYPE_BLACKHOLE) {
667 bpa->table_id = 0;
668 bpa->installed = true;
669 } else {
670 bpa->fwmark = ++fwmark_id;
671 bpa->table_id = fwmark_id;
672 bpa->installed = false;
673 }
674 bpa->unique = ++bgp_pbr_action_counter_unique;
675 /* 0 value is forbidden */
676 bpa->install_in_progress = false;
677 }
678
679 /* then look for bpm */
680 memset(&temp, 0, sizeof(temp));
681 if (src == NULL || dst == NULL)
682 temp.type = IPSET_NET;
683 else
684 temp.type = IPSET_NET_NET;
685 temp.vrf_id = vrf_id;
686 if (src)
687 temp.flags |= MATCH_IP_SRC_SET;
688 if (dst)
689 temp.flags |= MATCH_IP_DST_SET;
690 temp.action = bpa;
691 bpm = hash_get(bgp->pbr_match_hash, &temp,
692 bgp_pbr_match_alloc_intern);
693
694 /* new, then self allocate ipset_name and unique */
695 if (bpm && bpm->unique == 0) {
696 bpm->unique = ++bgp_pbr_match_counter_unique;
697 /* 0 value is forbidden */
698 sprintf(bpm->ipset_name, "match%p", bpm);
699 bpm->entry_hash = hash_create_size(8,
700 bgp_pbr_match_entry_hash_key,
701 bgp_pbr_match_entry_hash_equal,
702 "Match Entry Hash");
703 bpm->installed = false;
704
705 /* unique2 should be updated too */
706 bpm->unique2 = ++bgp_pbr_match_iptable_counter_unique;
707 bpm->installed_in_iptable = false;
708 bpm->install_in_progress = false;
709 bpm->install_iptable_in_progress = false;
710 }
711
712 memset(&temp2, 0, sizeof(temp2));
713 if (src)
714 prefix_copy(&temp2.src, src);
715 else
716 temp2.src.family = AF_INET;
717 if (dst)
718 prefix_copy(&temp2.dst, dst);
719 else
720 temp2.dst.family = AF_INET;
721 if (bpm)
722 bpme = hash_get(bpm->entry_hash, &temp2,
723 bgp_pbr_match_entry_alloc_intern);
724 if (bpme && bpme->unique == 0) {
725 bpme->unique = ++bgp_pbr_match_entry_counter_unique;
726 /* 0 value is forbidden */
727 bpme->backpointer = bpm;
728 bpme->installed = false;
729 bpme->install_in_progress = false;
730 }
731
732 /* BGP FS: append entry to zebra
733 * - policies are not routing entries and as such
734 * route replace semantics don't necessarily follow
735 * through to policy entries
736 * - because of that, not all policing information will be stored
737 * into zebra. and non selected policies will be suppressed from zebra
738 * - as consequence, in order to bring consistency
739 * a policy will be added, then ifan ecmp policy exists,
740 * it will be suppressed subsequently
741 */
742 /* ip rule add */
743 if (!bpa->installed)
744 bgp_send_pbr_rule_action(bpa, true);
745
746 /* ipset create */
747 if (bpm && !bpm->installed)
748 bgp_send_pbr_ipset_match(bpm, true);
749 /* ipset add */
750 if (bpme && !bpme->installed)
751 bgp_send_pbr_ipset_entry_match(bpme, true);
752
753 /* iptables */
754 if (bpm && !bpm->installed_in_iptable)
755 bgp_send_pbr_iptable(bpa, bpm, true);
756
757 /* A previous entry may already exist
758 * flush previous entry if necessary
759 */
760 bpmer.bpme_to_match = bpme;
761 bpmer.bpme_found = NULL;
762 hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
763 if (bpmer.bpme_found) {
764 static struct bgp_pbr_match *local_bpm;
765 static struct bgp_pbr_action *local_bpa;
766
767 local_bpm = bpmer.bpme_found->backpointer;
768 local_bpa = local_bpm->action;
769 bgp_pbr_flush_entry(bgp, local_bpa,
770 local_bpm, bpmer.bpme_found);
771 }
772
773
774}
775
776static void bgp_pbr_handle_entry(struct bgp *bgp,
777 struct bgp_info *binfo,
778 struct bgp_pbr_entry_main *api,
779 bool add)
780{
781 struct nexthop nh;
782 int i = 0;
783 int continue_loop = 1;
784 float rate = 0;
785 struct prefix *src = NULL, *dst = NULL;
786
787 if (api->match_bitmask & PREFIX_SRC_PRESENT)
788 src = &api->src_prefix;
789 if (api->match_bitmask & PREFIX_DST_PRESENT)
790 dst = &api->dst_prefix;
791 memset(&nh, 0, sizeof(struct nexthop));
792 nh.vrf_id = VRF_UNKNOWN;
793
794 if (!add)
795 return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo,
796 api->vrf_id, src, dst);
797 /* no action for add = true */
798 for (i = 0; i < api->action_num; i++) {
799 switch (api->actions[i].action) {
800 case ACTION_TRAFFICRATE:
801 /* drop packet */
802 if (api->actions[i].u.r.rate == 0) {
803 nh.vrf_id = api->vrf_id;
804 nh.type = NEXTHOP_TYPE_BLACKHOLE;
805 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
806 api->vrf_id, src, dst,
807 &nh, &rate);
808 } else {
809 /* update rate. can be reentrant */
810 rate = api->actions[i].u.r.rate;
811 if (BGP_DEBUG(pbr, PBR))
812 bgp_pbr_print_policy_route(api);
813 zlog_warn("PBR: ignoring Set action rate %f",
814 api->actions[i].u.r.rate);
815 }
816 break;
817 case ACTION_TRAFFIC_ACTION:
818 if (api->actions[i].u.za.filter
819 & TRAFFIC_ACTION_SAMPLE) {
820 if (BGP_DEBUG(pbr, PBR))
821 bgp_pbr_print_policy_route(api);
822 zlog_warn("PBR: Sample action Ignored");
823 }
824#if 0
825 if (api->actions[i].u.za.filter
826 & TRAFFIC_ACTION_DISTRIBUTE) {
827 if (BGP_DEBUG(pbr, PBR))
828 bgp_pbr_print_policy_route(api);
829 zlog_warn("PBR: Distribute action Applies");
830 continue_loop = 0;
831 /* continue forwarding entry as before
832 * no action
833 */
834 }
835#endif /* XXX to confirm behaviour of traffic action. for now , ignore */
836 /* terminate action: run other filters
837 */
838 break;
839 case ACTION_REDIRECT_IP:
840 nh.type = NEXTHOP_TYPE_IPV4;
841 nh.gate.ipv4.s_addr =
842 api->actions[i].u.zr.redirect_ip_v4.s_addr;
843 nh.vrf_id = api->vrf_id;
844 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
845 api->vrf_id,
846 src, dst,
847 &nh, &rate);
848 /* XXX combination with REDIRECT_VRF
849 * + REDIRECT_NH_IP not done
850 */
851 continue_loop = 0;
852 break;
853 case ACTION_REDIRECT:
854 nh.vrf_id = api->actions[i].u.redirect_vrf;
855 nh.type = NEXTHOP_TYPE_IPV4;
856 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
857 api->vrf_id,
858 src, dst,
859 &nh, &rate);
860 continue_loop = 0;
861 break;
862 case ACTION_MARKING:
863 if (BGP_DEBUG(pbr, PBR))
864 bgp_pbr_print_policy_route(api);
865 zlog_warn("PBR: Set DSCP %u Ignored",
866 api->actions[i].u.marking_dscp);
867 break;
868 default:
869 break;
870 }
871 if (continue_loop == 0)
872 break;
873 }
874}
875
2749b788
PG
876void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p,
877 struct bgp_info *info, afi_t afi, safi_t safi,
878 bool nlri_update)
879{
880 struct bgp_pbr_entry_main api;
881
882 if (afi == AFI_IP6)
883 return; /* IPv6 not supported */
884 if (safi != SAFI_FLOWSPEC)
885 return; /* not supported */
886 /* Make Zebra API structure. */
887 memset(&api, 0, sizeof(api));
888 api.vrf_id = bgp->vrf_id;
889 api.afi = afi;
890
891 if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) {
892 zlog_err("%s: cancel updating entry in bgp pbr",
893 __func__);
894 return;
895 }
ed132710 896 bgp_pbr_handle_entry(bgp, info, &api, nlri_update);
2749b788 897}