]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_pbr.c
bgpd: allow flowspec entries to be announced to zebra
[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
172/* return 1 if OK, 0 if validation should stop) */
173static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
174{
175 /* because bgp pbr entry may contain unsupported
176 * combinations, a message will be displayed here if
177 * not supported.
178 * for now, only match/set supported is
179 * - combination src/dst => redirect nexthop [ + rate]
180 * - combination src/dst => redirect VRF [ + rate]
181 * - combination src/dst => drop
182 */
183 if (api->match_src_port_num || api->match_dst_port_num
184 || api->match_port_num || api->match_protocol_num
185 || api->match_icmp_type_num || api->match_icmp_type_num
186 || api->match_packet_length_num || api->match_dscp_num
187 || api->match_tcpflags_num) {
188 if (BGP_DEBUG(pbr, PBR))
189 bgp_pbr_print_policy_route(api);
190 zlog_err("BGP: some SET actions not supported by Zebra. ignoring.");
191 return 0;
192 }
193 if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
194 !(api->match_bitmask & PREFIX_DST_PRESENT)) {
195 if (BGP_DEBUG(pbr, PBR))
196 bgp_pbr_print_policy_route(api);
197 zlog_err("BGP: SET actions without src or dst address can not operate. ignoring.");
198 return 0;
199 }
200 return 1;
201}
8046aadf 202
2749b788
PG
203/* return -1 if build or validation failed */
204static int bgp_pbr_build_and_validate_entry(struct prefix *p,
205 struct bgp_info *info,
206 struct bgp_pbr_entry_main *api)
207{
208 int ret;
209 int i, action_count = 0;
210 struct ecommunity *ecom;
211 struct ecommunity_val *ecom_eval;
212 struct bgp_pbr_entry_action *api_action;
213 struct prefix *src = NULL, *dst = NULL;
214 int valid_prefix = 0;
215 afi_t afi = AFI_IP;
216
217 /* extract match from flowspec entries */
218 ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr,
219 p->u.prefix_flowspec.prefixlen, api);
220 if (ret < 0)
221 return -1;
222 /* extract actiosn from flowspec ecom list */
223 if (info && info->attr && info->attr->ecommunity) {
224 ecom = info->attr->ecommunity;
225 for (i = 0; i < ecom->size; i++) {
226 ecom_eval = (struct ecommunity_val *)
227 ecom->val + (i * ECOMMUNITY_SIZE);
228
229 if (action_count > ACTIONS_MAX_NUM) {
230 zlog_err("%s: flowspec actions exceeds limit (max %u)",
231 __func__, action_count);
232 break;
233 }
234 api_action = &api->actions[action_count];
235
236 if ((ecom_eval->val[1] ==
237 (char)ECOMMUNITY_REDIRECT_VRF) &&
238 (ecom_eval->val[0] ==
239 (char)ECOMMUNITY_ENCODE_TRANS_EXP ||
240 ecom_eval->val[0] ==
241 (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
242 ecom_eval->val[0] ==
243 (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_3)) {
244 struct ecommunity *eckey = ecommunity_new();
245 struct ecommunity_val ecom_copy;
246
247 memcpy(&ecom_copy, ecom_eval,
248 sizeof(struct ecommunity_val));
249 ecom_copy.val[0] &=
250 ~ECOMMUNITY_ENCODE_TRANS_EXP;
251 ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
252 ecommunity_add_val(eckey, &ecom_copy);
253
254 api_action->action = ACTION_REDIRECT;
255 api_action->u.redirect_vrf =
256 get_first_vrf_for_redirect_with_rt(
257 eckey);
258 ecommunity_free(&eckey);
259 } else if ((ecom_eval->val[0] ==
260 (char)ECOMMUNITY_ENCODE_REDIRECT_IP_NH) &&
261 (ecom_eval->val[1] ==
262 (char)ECOMMUNITY_REDIRECT_IP_NH)) {
263 api_action->action = ACTION_REDIRECT_IP;
264 api_action->u.zr.redirect_ip_v4.s_addr =
265 info->attr->nexthop.s_addr;
266 api_action->u.zr.duplicate = ecom_eval->val[7];
267 } else {
268 if (ecom_eval->val[0] !=
269 (char)ECOMMUNITY_ENCODE_TRANS_EXP)
270 continue;
271 ret = ecommunity_fill_pbr_action(ecom_eval,
272 api_action);
273 if (ret != 0)
274 continue;
275 }
276 api->action_num++;
277 }
278 }
279
280 /* validate if incoming matc/action is compatible
281 * with our policy routing engine
282 */
283 if (!bgp_pbr_validate_policy_route(api))
284 return -1;
285
286 /* check inconsistency in the match rule */
287 if (api->match_bitmask & PREFIX_SRC_PRESENT) {
288 src = &api->src_prefix;
289 afi = family2afi(src->family);
290 valid_prefix = 1;
291 }
292 if (api->match_bitmask & PREFIX_DST_PRESENT) {
293 dst = &api->dst_prefix;
294 if (valid_prefix && afi != family2afi(dst->family)) {
295 if (BGP_DEBUG(pbr, PBR))
296 bgp_pbr_print_policy_route(api);
297 zlog_err("%s: inconsistency: no match for afi src and dst (%u/%u)",
298 __func__, afi, family2afi(dst->family));
299 return -1;
300 }
301 }
302 return 0;
303}
304
ed132710
PG
305static void *bgp_pbr_match_alloc_intern(void *arg)
306{
307 struct bgp_pbr_match *bpm, *new;
308
309 bpm = (struct bgp_pbr_match *)arg;
310
311 new = XCALLOC(MTYPE_PBR_MATCH, sizeof(*new));
312 memcpy(new, bpm, sizeof(*bpm));
313
314 return new;
315}
316
317static void *bgp_pbr_action_alloc_intern(void *arg)
318{
319 struct bgp_pbr_action *bpa, *new;
320
321 bpa = (struct bgp_pbr_action *)arg;
322
323 new = XCALLOC(MTYPE_PBR_ACTION, sizeof(*new));
324
325 memcpy(new, bpa, sizeof(*bpa));
326
327 return new;
328}
329
330static void *bgp_pbr_match_entry_alloc_intern(void *arg)
331{
332 struct bgp_pbr_match_entry *bpme, *new;
333
334 bpme = (struct bgp_pbr_match_entry *)arg;
335
336 new = XCALLOC(MTYPE_PBR_MATCH_ENTRY, sizeof(*new));
337
338 memcpy(new, bpme, sizeof(*bpme));
339
340 return new;
341}
342
34d44f18
PG
343uint32_t bgp_pbr_match_hash_key(void *arg)
344{
345 struct bgp_pbr_match *pbm = (struct bgp_pbr_match *)arg;
346 uint32_t key;
347
348 key = jhash_1word(pbm->vrf_id, 0x4312abde);
349 key = jhash_1word(pbm->flags, key);
350 return jhash_1word(pbm->type, key);
351}
352
353int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2)
354{
355 const struct bgp_pbr_match *r1, *r2;
356
357 r1 = (const struct bgp_pbr_match *)arg1;
358 r2 = (const struct bgp_pbr_match *)arg2;
359
360 if (r1->vrf_id != r2->vrf_id)
361 return 0;
362
363 if (r1->type != r2->type)
364 return 0;
365
366 if (r1->flags != r2->flags)
367 return 0;
368
369 if (r1->action != r2->action)
370 return 0;
371
372 return 1;
373}
374
375uint32_t bgp_pbr_match_entry_hash_key(void *arg)
376{
377 struct bgp_pbr_match_entry *pbme;
378 uint32_t key;
379
380 pbme = (struct bgp_pbr_match_entry *)arg;
381 key = prefix_hash_key(&pbme->src);
382 key = jhash_1word(prefix_hash_key(&pbme->dst), key);
383
384 return key;
385}
386
387int bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2)
388{
389 const struct bgp_pbr_match_entry *r1, *r2;
390
391 r1 = (const struct bgp_pbr_match_entry *)arg1;
392 r2 = (const struct bgp_pbr_match_entry *)arg2;
393
394 /* on updates, comparing
395 * backpointer is not necessary
396 */
397
398 /* unique value is self calculated
399 */
400
401 /* rate is ignored for now
402 */
403
404 if (!prefix_same(&r1->src, &r2->src))
405 return 0;
406
407 if (!prefix_same(&r1->dst, &r2->dst))
408 return 0;
409
410 return 1;
411}
412
413uint32_t bgp_pbr_action_hash_key(void *arg)
414{
415 struct bgp_pbr_action *pbra;
416 uint32_t key;
417
418 pbra = (struct bgp_pbr_action *)arg;
419 key = jhash_1word(pbra->table_id, 0x4312abde);
420 key = jhash_1word(pbra->fwmark, key);
421 return key;
422}
423
424int bgp_pbr_action_hash_equal(const void *arg1, const void *arg2)
425{
426 const struct bgp_pbr_action *r1, *r2;
427
428 r1 = (const struct bgp_pbr_action *)arg1;
429 r2 = (const struct bgp_pbr_action *)arg2;
430
431 /* unique value is self calculated
432 * table and fwmark is self calculated
433 */
434 if (r1->rate != r2->rate)
435 return 0;
436
437 if (r1->vrf_id != r2->vrf_id)
438 return 0;
439
440 if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop)))
441 return 0;
442 return 1;
443}
8046aadf 444
2055c776
PG
445struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id,
446 uint32_t unique)
8046aadf 447{
2055c776
PG
448 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
449 struct bgp_pbr_action_unique bpau;
450
451 if (!bgp || unique == 0)
452 return NULL;
453 bpau.unique = unique;
454 bpau.bpa_found = NULL;
455 hash_walk(bgp->pbr_action_hash, bgp_pbr_action_walkcb, &bpau);
456 return bpau.bpa_found;
8046aadf
PG
457}
458
459struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
460 uint32_t unique)
461{
5bbc648d
PG
462 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
463 struct bgp_pbr_match_unique bpmu;
464
465 if (!bgp || unique == 0)
466 return NULL;
467 bpmu.unique = unique;
468 bpmu.bpm_found = NULL;
469 hash_walk(bgp->pbr_match_hash, bgp_pbr_match_walkcb, &bpmu);
470 return bpmu.bpm_found;
8046aadf
PG
471}
472
473struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(vrf_id_t vrf_id,
474 char *ipset_name,
475 uint32_t unique)
476{
5bbc648d
PG
477 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
478 struct bgp_pbr_match_entry_unique bpmeu;
479 struct bgp_pbr_match_ipsetname bpmi;
480
481 if (!bgp || unique == 0)
482 return NULL;
483 bpmi.ipsetname = XCALLOC(MTYPE_TMP, ZEBRA_IPSET_NAME_SIZE);
484 snprintf(bpmi.ipsetname, ZEBRA_IPSET_NAME_SIZE, "%s", ipset_name);
485 bpmi.bpm_found = NULL;
486 hash_walk(bgp->pbr_match_hash, bgp_pbr_match_pername_walkcb, &bpmi);
487 XFREE(MTYPE_TMP, bpmi.ipsetname);
488 if (!bpmi.bpm_found)
489 return NULL;
490 bpmeu.bpme_found = NULL;
491 bpmeu.unique = unique;
492 hash_walk(bpmi.bpm_found->entry_hash,
493 bgp_pbr_match_entry_walkcb, &bpmeu);
494 return bpmeu.bpme_found;
8046aadf
PG
495}
496
4c793038
PG
497struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id,
498 uint32_t unique)
499{
500 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
501 struct bgp_pbr_match_iptable_unique bpmiu;
502
503 if (!bgp || unique == 0)
504 return NULL;
505 bpmiu.unique = unique;
506 bpmiu.bpm_found = NULL;
507 hash_walk(bgp->pbr_match_hash, bgp_pbr_match_iptable_walkcb, &bpmiu);
508 return bpmiu.bpm_found;
509}
510
34d44f18
PG
511void bgp_pbr_init(struct bgp *bgp)
512{
513 bgp->pbr_match_hash =
514 hash_create_size(8, bgp_pbr_match_hash_key,
515 bgp_pbr_match_hash_equal,
516 "Match Hash");
517 bgp->pbr_action_hash =
518 hash_create_size(8, bgp_pbr_action_hash_key,
519 bgp_pbr_action_hash_equal,
520 "Match Hash Entry");
521}
16239153
PG
522
523void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api)
524{
525 int i = 0;
526 char return_string[512];
527 char *ptr = return_string;
528 char buff[64];
529 int nb_items = 0;
530
531 ptr += sprintf(ptr, "MATCH : ");
532 if (api->match_bitmask & PREFIX_SRC_PRESENT) {
533 struct prefix *p = &(api->src_prefix);
534
535 ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64));
536 INCREMENT_DISPLAY(ptr, nb_items);
537 }
538 if (api->match_bitmask & PREFIX_DST_PRESENT) {
539 struct prefix *p = &(api->dst_prefix);
540
541 INCREMENT_DISPLAY(ptr, nb_items);
542 ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64));
543 }
544
545 if (api->match_protocol_num)
546 INCREMENT_DISPLAY(ptr, nb_items);
547 for (i = 0; i < api->match_protocol_num; i++)
548 ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i],
549 i > 0 ? NULL : "@proto ");
550
551 if (api->match_src_port_num)
552 INCREMENT_DISPLAY(ptr, nb_items);
553 for (i = 0; i < api->match_src_port_num; i++)
554 ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i],
555 i > 0 ? NULL : "@srcport ");
556
557 if (api->match_dst_port_num)
558 INCREMENT_DISPLAY(ptr, nb_items);
559 for (i = 0; i < api->match_dst_port_num; i++)
560 ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i],
561 i > 0 ? NULL : "@dstport ");
562
563 if (api->match_port_num)
564 INCREMENT_DISPLAY(ptr, nb_items);
565 for (i = 0; i < api->match_port_num; i++)
566 ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i],
567 i > 0 ? NULL : "@port ");
568
569 if (api->match_icmp_type_num)
570 INCREMENT_DISPLAY(ptr, nb_items);
571 for (i = 0; i < api->match_icmp_type_num; i++)
572 ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i],
573 i > 0 ? NULL : "@icmptype ");
574
575 if (api->match_icmp_code_num)
576 INCREMENT_DISPLAY(ptr, nb_items);
577 for (i = 0; i < api->match_icmp_code_num; i++)
578 ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i],
579 i > 0 ? NULL : "@icmpcode ");
580
581 if (api->match_packet_length_num)
582 INCREMENT_DISPLAY(ptr, nb_items);
583 for (i = 0; i < api->match_packet_length_num; i++)
584 ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i],
585 i > 0 ? NULL : "@plen ");
586
587 if (api->match_dscp_num)
588 INCREMENT_DISPLAY(ptr, nb_items);
589 for (i = 0; i < api->match_dscp_num; i++)
590 ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i],
591 i > 0 ? NULL : "@dscp ");
592
593 if (api->match_tcpflags_num)
594 INCREMENT_DISPLAY(ptr, nb_items);
595 for (i = 0; i < api->match_tcpflags_num; i++)
596 ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i],
597 i > 0 ? NULL : "@tcpflags ");
598
599 if (api->match_bitmask & FRAGMENT_PRESENT) {
600 INCREMENT_DISPLAY(ptr, nb_items);
601 ptr += sprintf(ptr, "@fragment %u", api->fragment.bitmask);
602 }
603 if (!nb_items)
604 ptr = return_string;
605 else
606 ptr += sprintf(ptr, "; ");
607 if (api->action_num)
608 ptr += sprintf(ptr, "SET : ");
609 nb_items = 0;
610 for (i = 0; i < api->action_num; i++) {
611 switch (api->actions[i].action) {
612 case ACTION_TRAFFICRATE:
613 INCREMENT_DISPLAY(ptr, nb_items);
614 ptr += sprintf(ptr, "@set rate %f",
615 api->actions[i].u.r.rate);
616 break;
617 case ACTION_TRAFFIC_ACTION:
618 INCREMENT_DISPLAY(ptr, nb_items);
619 ptr += sprintf(ptr, "@action ");
620 if (api->actions[i].u.za.filter
621 & TRAFFIC_ACTION_TERMINATE)
622 ptr += sprintf(ptr,
623 " terminate (apply filter(s))");
624 if (api->actions[i].u.za.filter
625 & TRAFFIC_ACTION_DISTRIBUTE)
626 ptr += sprintf(ptr, " distribute");
627 if (api->actions[i].u.za.filter
628 & TRAFFIC_ACTION_SAMPLE)
629 ptr += sprintf(ptr, " sample");
630 break;
631 case ACTION_REDIRECT_IP:
632 INCREMENT_DISPLAY(ptr, nb_items);
633 char local_buff[INET_ADDRSTRLEN];
634
635 if (inet_ntop(AF_INET,
636 &api->actions[i].u.zr.redirect_ip_v4,
637 local_buff, INET_ADDRSTRLEN) != NULL)
638 ptr += sprintf(ptr,
639 "@redirect ip nh %s", local_buff);
640 break;
641 case ACTION_REDIRECT:
642 INCREMENT_DISPLAY(ptr, nb_items);
643 ptr += sprintf(ptr, "@redirect vrf %u",
644 api->actions[i].u.redirect_vrf);
645 break;
646 case ACTION_MARKING:
647 INCREMENT_DISPLAY(ptr, nb_items);
648 ptr += sprintf(ptr, "@set dscp %u",
649 api->actions[i].u.marking_dscp);
650 break;
651 default:
652 break;
653 }
654 }
655 zlog_info("%s", return_string);
656}
2749b788 657
ed132710
PG
658static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa,
659 struct bgp_pbr_match *bpm,
660 struct bgp_pbr_match_entry *bpme)
661{
662 /* if bpme is null, bpm is also null
663 */
664 if (bpme == NULL)
665 return;
666 /* ipset del entry */
667 if (bpme->installed) {
668 bgp_send_pbr_ipset_entry_match(bpme, false);
669 bpme->installed = false;
670 bpme->backpointer = NULL;
671 }
672 hash_release(bpm->entry_hash, bpme);
673 if (hashcount(bpm->entry_hash) == 0) {
674 /* delete iptable entry first */
675 /* then delete ipset match */
676 if (bpm->installed) {
677 if (bpm->installed_in_iptable) {
678 bgp_send_pbr_iptable(bpm->action,
679 bpm, false);
680 bpm->installed_in_iptable = false;
681 }
682 bgp_send_pbr_ipset_match(bpm, false);
683 bpm->installed = false;
684 bpm->action = NULL;
685 }
686 hash_release(bgp->pbr_match_hash, bpm);
687 /* XXX release pbr_match_action if not used
688 * note that drop does not need to call send_pbr_action
689 */
690 }
691}
692
693struct bgp_pbr_match_entry_remain {
694 struct bgp_pbr_match_entry *bpme_to_match;
695 struct bgp_pbr_match_entry *bpme_found;
696};
697
698static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg)
699{
700 struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
701 struct bgp_pbr_match_entry_remain *bpmer =
702 (struct bgp_pbr_match_entry_remain *)arg;
703 struct bgp_pbr_match *bpm_temp;
704 struct bgp_pbr_match_entry *bpme = bpmer->bpme_to_match;
705
706 if (!bpme->backpointer ||
707 bpm == bpme->backpointer ||
708 bpme->backpointer->action == bpm->action)
709 return HASHWALK_CONTINUE;
710 /* ensure bpm other characteristics are equal */
711 bpm_temp = bpme->backpointer;
712 if (bpm_temp->vrf_id != bpm->vrf_id ||
713 bpm_temp->type != bpm->type ||
714 bpm_temp->flags != bpm->flags)
715 return HASHWALK_CONTINUE;
716
717 /* look for remaining bpme */
718 bpmer->bpme_found = hash_lookup(bpm->entry_hash, bpme);
719 if (!bpmer->bpme_found)
720 return HASHWALK_CONTINUE;
721 return HASHWALK_ABORT;
722}
723
724static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
725 struct bgp_info *binfo,
726 vrf_id_t vrf_id,
727 struct prefix *src,
728 struct prefix *dst)
729{
730 struct bgp_pbr_match temp;
731 struct bgp_pbr_match_entry temp2;
732 struct bgp_pbr_match *bpm;
733 struct bgp_pbr_match_entry *bpme;
734 struct bgp_pbr_match_entry_remain bpmer;
735
736 /* as we don't know information from EC
737 * look for bpm that have the bpm
738 * with vrf_id characteristics
739 */
740 memset(&temp2, 0, sizeof(temp2));
741 memset(&temp, 0, sizeof(temp));
742 if (src) {
743 temp.flags |= MATCH_IP_SRC_SET;
744 prefix_copy(&temp2.src, src);
745 } else
746 temp2.src.family = AF_INET;
747 if (dst) {
748 temp.flags |= MATCH_IP_DST_SET;
749 prefix_copy(&temp2.dst, dst);
750 } else
751 temp2.dst.family = AF_INET;
752
753 if (src == NULL || dst == NULL)
754 temp.type = IPSET_NET;
755 else
756 temp.type = IPSET_NET_NET;
757 if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
758 temp.vrf_id = 0;
759 else
760 temp.vrf_id = vrf_id;
761 bpme = &temp2;
762 bpm = &temp;
763 bpme->backpointer = bpm;
764 /* right now, a previous entry may already exist
765 * flush previous entry if necessary
766 */
767 bpmer.bpme_to_match = bpme;
768 bpmer.bpme_found = NULL;
769 hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
770 if (bpmer.bpme_found) {
771 static struct bgp_pbr_match *local_bpm;
772 static struct bgp_pbr_action *local_bpa;
773
774 local_bpm = bpmer.bpme_found->backpointer;
775 local_bpa = local_bpm->action;
776 bgp_pbr_flush_entry(bgp, local_bpa,
777 local_bpm, bpmer.bpme_found);
778 }
779}
780
781static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
782 struct bgp_info *binfo,
783 vrf_id_t vrf_id,
784 struct prefix *src,
785 struct prefix *dst,
786 struct nexthop *nh,
787 float *rate)
788{
789 struct bgp_pbr_match temp;
790 struct bgp_pbr_match_entry temp2;
791 struct bgp_pbr_match *bpm;
792 struct bgp_pbr_match_entry *bpme = NULL;
793 struct bgp_pbr_action temp3;
794 struct bgp_pbr_action *bpa = NULL;
795 struct bgp_pbr_match_entry_remain bpmer;
796
797 /* look for bpa first */
798 memset(&temp3, 0, sizeof(temp3));
799 if (rate)
800 temp3.rate = *rate;
801 if (nh)
802 memcpy(&temp3.nh, nh, sizeof(struct nexthop));
803 temp3.vrf_id = vrf_id;
804 bpa = hash_get(bgp->pbr_action_hash, &temp3,
805 bgp_pbr_action_alloc_intern);
806
807 if (bpa->fwmark == 0) {
808 /* TODO: allocate new table ID using zebra */
809 static int fwmark_id;
810
811 /* drop is handled by iptable */
812 if (nh && nh->type == NEXTHOP_TYPE_BLACKHOLE) {
813 bpa->table_id = 0;
814 bpa->installed = true;
815 } else {
816 bpa->fwmark = ++fwmark_id;
817 bpa->table_id = fwmark_id;
818 bpa->installed = false;
819 }
820 bpa->unique = ++bgp_pbr_action_counter_unique;
821 /* 0 value is forbidden */
822 bpa->install_in_progress = false;
823 }
824
825 /* then look for bpm */
826 memset(&temp, 0, sizeof(temp));
827 if (src == NULL || dst == NULL)
828 temp.type = IPSET_NET;
829 else
830 temp.type = IPSET_NET_NET;
831 temp.vrf_id = vrf_id;
832 if (src)
833 temp.flags |= MATCH_IP_SRC_SET;
834 if (dst)
835 temp.flags |= MATCH_IP_DST_SET;
836 temp.action = bpa;
837 bpm = hash_get(bgp->pbr_match_hash, &temp,
838 bgp_pbr_match_alloc_intern);
839
840 /* new, then self allocate ipset_name and unique */
841 if (bpm && bpm->unique == 0) {
842 bpm->unique = ++bgp_pbr_match_counter_unique;
843 /* 0 value is forbidden */
844 sprintf(bpm->ipset_name, "match%p", bpm);
845 bpm->entry_hash = hash_create_size(8,
846 bgp_pbr_match_entry_hash_key,
847 bgp_pbr_match_entry_hash_equal,
848 "Match Entry Hash");
849 bpm->installed = false;
850
851 /* unique2 should be updated too */
852 bpm->unique2 = ++bgp_pbr_match_iptable_counter_unique;
853 bpm->installed_in_iptable = false;
854 bpm->install_in_progress = false;
855 bpm->install_iptable_in_progress = false;
856 }
857
858 memset(&temp2, 0, sizeof(temp2));
859 if (src)
860 prefix_copy(&temp2.src, src);
861 else
862 temp2.src.family = AF_INET;
863 if (dst)
864 prefix_copy(&temp2.dst, dst);
865 else
866 temp2.dst.family = AF_INET;
867 if (bpm)
868 bpme = hash_get(bpm->entry_hash, &temp2,
869 bgp_pbr_match_entry_alloc_intern);
870 if (bpme && bpme->unique == 0) {
871 bpme->unique = ++bgp_pbr_match_entry_counter_unique;
872 /* 0 value is forbidden */
873 bpme->backpointer = bpm;
874 bpme->installed = false;
875 bpme->install_in_progress = false;
876 }
877
878 /* BGP FS: append entry to zebra
879 * - policies are not routing entries and as such
880 * route replace semantics don't necessarily follow
881 * through to policy entries
882 * - because of that, not all policing information will be stored
883 * into zebra. and non selected policies will be suppressed from zebra
884 * - as consequence, in order to bring consistency
885 * a policy will be added, then ifan ecmp policy exists,
886 * it will be suppressed subsequently
887 */
888 /* ip rule add */
889 if (!bpa->installed)
890 bgp_send_pbr_rule_action(bpa, true);
891
892 /* ipset create */
893 if (bpm && !bpm->installed)
894 bgp_send_pbr_ipset_match(bpm, true);
895 /* ipset add */
896 if (bpme && !bpme->installed)
897 bgp_send_pbr_ipset_entry_match(bpme, true);
898
899 /* iptables */
900 if (bpm && !bpm->installed_in_iptable)
901 bgp_send_pbr_iptable(bpa, bpm, true);
902
903 /* A previous entry may already exist
904 * flush previous entry if necessary
905 */
906 bpmer.bpme_to_match = bpme;
907 bpmer.bpme_found = NULL;
908 hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
909 if (bpmer.bpme_found) {
910 static struct bgp_pbr_match *local_bpm;
911 static struct bgp_pbr_action *local_bpa;
912
913 local_bpm = bpmer.bpme_found->backpointer;
914 local_bpa = local_bpm->action;
915 bgp_pbr_flush_entry(bgp, local_bpa,
916 local_bpm, bpmer.bpme_found);
917 }
918
919
920}
921
922static void bgp_pbr_handle_entry(struct bgp *bgp,
923 struct bgp_info *binfo,
924 struct bgp_pbr_entry_main *api,
925 bool add)
926{
927 struct nexthop nh;
928 int i = 0;
929 int continue_loop = 1;
930 float rate = 0;
931 struct prefix *src = NULL, *dst = NULL;
932
933 if (api->match_bitmask & PREFIX_SRC_PRESENT)
934 src = &api->src_prefix;
935 if (api->match_bitmask & PREFIX_DST_PRESENT)
936 dst = &api->dst_prefix;
937 memset(&nh, 0, sizeof(struct nexthop));
938 nh.vrf_id = VRF_UNKNOWN;
939
940 if (!add)
941 return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo,
942 api->vrf_id, src, dst);
943 /* no action for add = true */
944 for (i = 0; i < api->action_num; i++) {
945 switch (api->actions[i].action) {
946 case ACTION_TRAFFICRATE:
947 /* drop packet */
948 if (api->actions[i].u.r.rate == 0) {
949 nh.vrf_id = api->vrf_id;
950 nh.type = NEXTHOP_TYPE_BLACKHOLE;
951 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
952 api->vrf_id, src, dst,
953 &nh, &rate);
954 } else {
955 /* update rate. can be reentrant */
956 rate = api->actions[i].u.r.rate;
957 if (BGP_DEBUG(pbr, PBR))
958 bgp_pbr_print_policy_route(api);
959 zlog_warn("PBR: ignoring Set action rate %f",
960 api->actions[i].u.r.rate);
961 }
962 break;
963 case ACTION_TRAFFIC_ACTION:
964 if (api->actions[i].u.za.filter
965 & TRAFFIC_ACTION_SAMPLE) {
966 if (BGP_DEBUG(pbr, PBR))
967 bgp_pbr_print_policy_route(api);
968 zlog_warn("PBR: Sample action Ignored");
969 }
970#if 0
971 if (api->actions[i].u.za.filter
972 & TRAFFIC_ACTION_DISTRIBUTE) {
973 if (BGP_DEBUG(pbr, PBR))
974 bgp_pbr_print_policy_route(api);
975 zlog_warn("PBR: Distribute action Applies");
976 continue_loop = 0;
977 /* continue forwarding entry as before
978 * no action
979 */
980 }
981#endif /* XXX to confirm behaviour of traffic action. for now , ignore */
982 /* terminate action: run other filters
983 */
984 break;
985 case ACTION_REDIRECT_IP:
986 nh.type = NEXTHOP_TYPE_IPV4;
987 nh.gate.ipv4.s_addr =
988 api->actions[i].u.zr.redirect_ip_v4.s_addr;
989 nh.vrf_id = api->vrf_id;
990 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
991 api->vrf_id,
992 src, dst,
993 &nh, &rate);
994 /* XXX combination with REDIRECT_VRF
995 * + REDIRECT_NH_IP not done
996 */
997 continue_loop = 0;
998 break;
999 case ACTION_REDIRECT:
1000 nh.vrf_id = api->actions[i].u.redirect_vrf;
1001 nh.type = NEXTHOP_TYPE_IPV4;
1002 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
1003 api->vrf_id,
1004 src, dst,
1005 &nh, &rate);
1006 continue_loop = 0;
1007 break;
1008 case ACTION_MARKING:
1009 if (BGP_DEBUG(pbr, PBR))
1010 bgp_pbr_print_policy_route(api);
1011 zlog_warn("PBR: Set DSCP %u Ignored",
1012 api->actions[i].u.marking_dscp);
1013 break;
1014 default:
1015 break;
1016 }
1017 if (continue_loop == 0)
1018 break;
1019 }
1020}
1021
2749b788
PG
1022void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p,
1023 struct bgp_info *info, afi_t afi, safi_t safi,
1024 bool nlri_update)
1025{
1026 struct bgp_pbr_entry_main api;
1027
1028 if (afi == AFI_IP6)
1029 return; /* IPv6 not supported */
1030 if (safi != SAFI_FLOWSPEC)
1031 return; /* not supported */
1032 /* Make Zebra API structure. */
1033 memset(&api, 0, sizeof(api));
1034 api.vrf_id = bgp->vrf_id;
1035 api.afi = afi;
1036
1037 if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) {
1038 zlog_err("%s: cancel updating entry in bgp pbr",
1039 __func__);
1040 return;
1041 }
ed132710 1042 bgp_pbr_handle_entry(bgp, info, &api, nlri_update);
2749b788 1043}