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