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