]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_pbr.c
bgpd: add 3 fields to ipset_entry : src,dst port, and proto
[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
a6b07429
PG
312static void bgp_pbr_match_entry_free(void *arg)
313{
314 struct bgp_pbr_match_entry *bpme;
315
316 bpme = (struct bgp_pbr_match_entry *)arg;
317
318 if (bpme->installed) {
319 bgp_send_pbr_ipset_entry_match(bpme, false);
320 bpme->installed = false;
321 bpme->backpointer = NULL;
322 }
323 XFREE(MTYPE_PBR_MATCH_ENTRY, bpme);
324}
325
326static void bgp_pbr_match_free(void *arg)
327{
328 struct bgp_pbr_match *bpm;
329
330 bpm = (struct bgp_pbr_match *)arg;
331
332 hash_clean(bpm->entry_hash, bgp_pbr_match_entry_free);
333
334 if (hashcount(bpm->entry_hash) == 0) {
335 /* delete iptable entry first */
336 /* then delete ipset match */
337 if (bpm->installed) {
338 if (bpm->installed_in_iptable) {
339 bgp_send_pbr_iptable(bpm->action,
340 bpm, false);
341 bpm->installed_in_iptable = false;
342 bpm->action->refcnt--;
343 }
344 bgp_send_pbr_ipset_match(bpm, false);
345 bpm->installed = false;
346 bpm->action = NULL;
347 }
348 }
349 hash_free(bpm->entry_hash);
350
351 XFREE(MTYPE_PBR_MATCH, bpm);
352}
353
d114b0d7
PG
354static void *bgp_pbr_match_alloc_intern(void *arg)
355{
356 struct bgp_pbr_match *bpm, *new;
357
358 bpm = (struct bgp_pbr_match *)arg;
359
360 new = XCALLOC(MTYPE_PBR_MATCH, sizeof(*new));
361 memcpy(new, bpm, sizeof(*bpm));
362
363 return new;
364}
365
a6b07429
PG
366static void bgp_pbr_action_free(void *arg)
367{
368 struct bgp_pbr_action *bpa;
369
370 bpa = (struct bgp_pbr_action *)arg;
371
372 if (bpa->refcnt == 0) {
373 if (bpa->installed && bpa->table_id != 0) {
374 bgp_send_pbr_rule_action(bpa, false);
375 bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
376 AFI_IP,
377 bpa->table_id,
378 false);
379 }
380 }
381 XFREE(MTYPE_PBR_ACTION, bpa);
382}
383
d114b0d7
PG
384static void *bgp_pbr_action_alloc_intern(void *arg)
385{
386 struct bgp_pbr_action *bpa, *new;
387
388 bpa = (struct bgp_pbr_action *)arg;
389
390 new = XCALLOC(MTYPE_PBR_ACTION, sizeof(*new));
391
392 memcpy(new, bpa, sizeof(*bpa));
393
394 return new;
395}
396
397static void *bgp_pbr_match_entry_alloc_intern(void *arg)
398{
399 struct bgp_pbr_match_entry *bpme, *new;
400
401 bpme = (struct bgp_pbr_match_entry *)arg;
402
403 new = XCALLOC(MTYPE_PBR_MATCH_ENTRY, sizeof(*new));
404
405 memcpy(new, bpme, sizeof(*bpme));
406
407 return new;
408}
409
f3d32faa
PG
410uint32_t bgp_pbr_match_hash_key(void *arg)
411{
412 struct bgp_pbr_match *pbm = (struct bgp_pbr_match *)arg;
413 uint32_t key;
414
415 key = jhash_1word(pbm->vrf_id, 0x4312abde);
416 key = jhash_1word(pbm->flags, key);
417 return jhash_1word(pbm->type, key);
418}
419
420int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2)
421{
422 const struct bgp_pbr_match *r1, *r2;
423
424 r1 = (const struct bgp_pbr_match *)arg1;
425 r2 = (const struct bgp_pbr_match *)arg2;
426
427 if (r1->vrf_id != r2->vrf_id)
428 return 0;
429
430 if (r1->type != r2->type)
431 return 0;
432
433 if (r1->flags != r2->flags)
434 return 0;
435
436 if (r1->action != r2->action)
437 return 0;
438
439 return 1;
440}
441
442uint32_t bgp_pbr_match_entry_hash_key(void *arg)
443{
444 struct bgp_pbr_match_entry *pbme;
445 uint32_t key;
446
447 pbme = (struct bgp_pbr_match_entry *)arg;
448 key = prefix_hash_key(&pbme->src);
449 key = jhash_1word(prefix_hash_key(&pbme->dst), key);
450
451 return key;
452}
453
454int bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2)
455{
456 const struct bgp_pbr_match_entry *r1, *r2;
457
458 r1 = (const struct bgp_pbr_match_entry *)arg1;
459 r2 = (const struct bgp_pbr_match_entry *)arg2;
460
461 /* on updates, comparing
462 * backpointer is not necessary
463 */
464
465 /* unique value is self calculated
466 */
467
468 /* rate is ignored for now
469 */
470
471 if (!prefix_same(&r1->src, &r2->src))
472 return 0;
473
474 if (!prefix_same(&r1->dst, &r2->dst))
475 return 0;
476
477 return 1;
478}
479
480uint32_t bgp_pbr_action_hash_key(void *arg)
481{
482 struct bgp_pbr_action *pbra;
483 uint32_t key;
484
485 pbra = (struct bgp_pbr_action *)arg;
486 key = jhash_1word(pbra->table_id, 0x4312abde);
487 key = jhash_1word(pbra->fwmark, key);
488 return key;
489}
490
491int bgp_pbr_action_hash_equal(const void *arg1, const void *arg2)
492{
493 const struct bgp_pbr_action *r1, *r2;
494
495 r1 = (const struct bgp_pbr_action *)arg1;
496 r2 = (const struct bgp_pbr_action *)arg2;
497
498 /* unique value is self calculated
499 * table and fwmark is self calculated
500 */
501 if (r1->rate != r2->rate)
502 return 0;
503
504 if (r1->vrf_id != r2->vrf_id)
505 return 0;
506
507 if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop)))
508 return 0;
509 return 1;
510}
bbe6ffd6 511
70eabd12
PG
512struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id,
513 uint32_t unique)
bbe6ffd6 514{
70eabd12
PG
515 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
516 struct bgp_pbr_action_unique bpau;
517
518 if (!bgp || unique == 0)
519 return NULL;
520 bpau.unique = unique;
521 bpau.bpa_found = NULL;
522 hash_walk(bgp->pbr_action_hash, bgp_pbr_action_walkcb, &bpau);
523 return bpau.bpa_found;
bbe6ffd6
PG
524}
525
526struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
527 uint32_t unique)
528{
c5d429e1
PG
529 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
530 struct bgp_pbr_match_unique bpmu;
531
532 if (!bgp || unique == 0)
533 return NULL;
534 bpmu.unique = unique;
535 bpmu.bpm_found = NULL;
536 hash_walk(bgp->pbr_match_hash, bgp_pbr_match_walkcb, &bpmu);
537 return bpmu.bpm_found;
bbe6ffd6
PG
538}
539
540struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(vrf_id_t vrf_id,
541 char *ipset_name,
542 uint32_t unique)
543{
c5d429e1
PG
544 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
545 struct bgp_pbr_match_entry_unique bpmeu;
546 struct bgp_pbr_match_ipsetname bpmi;
547
548 if (!bgp || unique == 0)
549 return NULL;
550 bpmi.ipsetname = XCALLOC(MTYPE_TMP, ZEBRA_IPSET_NAME_SIZE);
551 snprintf(bpmi.ipsetname, ZEBRA_IPSET_NAME_SIZE, "%s", ipset_name);
552 bpmi.bpm_found = NULL;
553 hash_walk(bgp->pbr_match_hash, bgp_pbr_match_pername_walkcb, &bpmi);
554 XFREE(MTYPE_TMP, bpmi.ipsetname);
555 if (!bpmi.bpm_found)
556 return NULL;
557 bpmeu.bpme_found = NULL;
558 bpmeu.unique = unique;
559 hash_walk(bpmi.bpm_found->entry_hash,
560 bgp_pbr_match_entry_walkcb, &bpmeu);
561 return bpmeu.bpme_found;
bbe6ffd6
PG
562}
563
1815c6fc
PG
564struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id,
565 uint32_t unique)
566{
567 struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
568 struct bgp_pbr_match_iptable_unique bpmiu;
569
570 if (!bgp || unique == 0)
571 return NULL;
572 bpmiu.unique = unique;
573 bpmiu.bpm_found = NULL;
574 hash_walk(bgp->pbr_match_hash, bgp_pbr_match_iptable_walkcb, &bpmiu);
575 return bpmiu.bpm_found;
576}
577
a6b07429
PG
578void bgp_pbr_cleanup(struct bgp *bgp)
579{
580 if (bgp->pbr_match_hash) {
581 hash_clean(bgp->pbr_match_hash, bgp_pbr_match_free);
582 hash_free(bgp->pbr_match_hash);
583 bgp->pbr_match_hash = NULL;
584 }
585 if (bgp->pbr_action_hash) {
586 hash_clean(bgp->pbr_action_hash, bgp_pbr_action_free);
587 hash_free(bgp->pbr_action_hash);
588 bgp->pbr_action_hash = NULL;
589 }
590}
591
f3d32faa
PG
592void bgp_pbr_init(struct bgp *bgp)
593{
594 bgp->pbr_match_hash =
595 hash_create_size(8, bgp_pbr_match_hash_key,
596 bgp_pbr_match_hash_equal,
597 "Match Hash");
598 bgp->pbr_action_hash =
599 hash_create_size(8, bgp_pbr_action_hash_key,
600 bgp_pbr_action_hash_equal,
601 "Match Hash Entry");
602}
b46b6f1a
PG
603
604void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api)
605{
606 int i = 0;
607 char return_string[512];
608 char *ptr = return_string;
609 char buff[64];
610 int nb_items = 0;
611
612 ptr += sprintf(ptr, "MATCH : ");
613 if (api->match_bitmask & PREFIX_SRC_PRESENT) {
614 struct prefix *p = &(api->src_prefix);
615
616 ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64));
617 INCREMENT_DISPLAY(ptr, nb_items);
618 }
619 if (api->match_bitmask & PREFIX_DST_PRESENT) {
620 struct prefix *p = &(api->dst_prefix);
621
622 INCREMENT_DISPLAY(ptr, nb_items);
623 ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64));
624 }
625
626 if (api->match_protocol_num)
627 INCREMENT_DISPLAY(ptr, nb_items);
628 for (i = 0; i < api->match_protocol_num; i++)
629 ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i],
630 i > 0 ? NULL : "@proto ");
631
632 if (api->match_src_port_num)
633 INCREMENT_DISPLAY(ptr, nb_items);
634 for (i = 0; i < api->match_src_port_num; i++)
635 ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i],
636 i > 0 ? NULL : "@srcport ");
637
638 if (api->match_dst_port_num)
639 INCREMENT_DISPLAY(ptr, nb_items);
640 for (i = 0; i < api->match_dst_port_num; i++)
641 ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i],
642 i > 0 ? NULL : "@dstport ");
643
644 if (api->match_port_num)
645 INCREMENT_DISPLAY(ptr, nb_items);
646 for (i = 0; i < api->match_port_num; i++)
647 ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i],
648 i > 0 ? NULL : "@port ");
649
650 if (api->match_icmp_type_num)
651 INCREMENT_DISPLAY(ptr, nb_items);
652 for (i = 0; i < api->match_icmp_type_num; i++)
653 ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i],
654 i > 0 ? NULL : "@icmptype ");
655
656 if (api->match_icmp_code_num)
657 INCREMENT_DISPLAY(ptr, nb_items);
658 for (i = 0; i < api->match_icmp_code_num; i++)
659 ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i],
660 i > 0 ? NULL : "@icmpcode ");
661
662 if (api->match_packet_length_num)
663 INCREMENT_DISPLAY(ptr, nb_items);
664 for (i = 0; i < api->match_packet_length_num; i++)
665 ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i],
666 i > 0 ? NULL : "@plen ");
667
668 if (api->match_dscp_num)
669 INCREMENT_DISPLAY(ptr, nb_items);
670 for (i = 0; i < api->match_dscp_num; i++)
671 ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i],
672 i > 0 ? NULL : "@dscp ");
673
674 if (api->match_tcpflags_num)
675 INCREMENT_DISPLAY(ptr, nb_items);
676 for (i = 0; i < api->match_tcpflags_num; i++)
677 ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i],
678 i > 0 ? NULL : "@tcpflags ");
679
680 if (api->match_bitmask & FRAGMENT_PRESENT) {
681 INCREMENT_DISPLAY(ptr, nb_items);
682 ptr += sprintf(ptr, "@fragment %u", api->fragment.bitmask);
683 }
684 if (!nb_items)
685 ptr = return_string;
686 else
687 ptr += sprintf(ptr, "; ");
688 if (api->action_num)
689 ptr += sprintf(ptr, "SET : ");
690 nb_items = 0;
691 for (i = 0; i < api->action_num; i++) {
692 switch (api->actions[i].action) {
693 case ACTION_TRAFFICRATE:
694 INCREMENT_DISPLAY(ptr, nb_items);
695 ptr += sprintf(ptr, "@set rate %f",
696 api->actions[i].u.r.rate);
697 break;
698 case ACTION_TRAFFIC_ACTION:
699 INCREMENT_DISPLAY(ptr, nb_items);
700 ptr += sprintf(ptr, "@action ");
701 if (api->actions[i].u.za.filter
702 & TRAFFIC_ACTION_TERMINATE)
703 ptr += sprintf(ptr,
704 " terminate (apply filter(s))");
705 if (api->actions[i].u.za.filter
706 & TRAFFIC_ACTION_DISTRIBUTE)
707 ptr += sprintf(ptr, " distribute");
708 if (api->actions[i].u.za.filter
709 & TRAFFIC_ACTION_SAMPLE)
710 ptr += sprintf(ptr, " sample");
711 break;
712 case ACTION_REDIRECT_IP:
713 INCREMENT_DISPLAY(ptr, nb_items);
714 char local_buff[INET_ADDRSTRLEN];
715
716 if (inet_ntop(AF_INET,
717 &api->actions[i].u.zr.redirect_ip_v4,
718 local_buff, INET_ADDRSTRLEN) != NULL)
719 ptr += sprintf(ptr,
720 "@redirect ip nh %s", local_buff);
721 break;
722 case ACTION_REDIRECT:
723 INCREMENT_DISPLAY(ptr, nb_items);
724 ptr += sprintf(ptr, "@redirect vrf %u",
725 api->actions[i].u.redirect_vrf);
726 break;
727 case ACTION_MARKING:
728 INCREMENT_DISPLAY(ptr, nb_items);
729 ptr += sprintf(ptr, "@set dscp %u",
730 api->actions[i].u.marking_dscp);
731 break;
732 default:
733 break;
734 }
735 }
736 zlog_info("%s", return_string);
737}
45918cfb 738
d114b0d7
PG
739static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa,
740 struct bgp_pbr_match *bpm,
741 struct bgp_pbr_match_entry *bpme)
742{
743 /* if bpme is null, bpm is also null
744 */
745 if (bpme == NULL)
746 return;
747 /* ipset del entry */
748 if (bpme->installed) {
749 bgp_send_pbr_ipset_entry_match(bpme, false);
750 bpme->installed = false;
751 bpme->backpointer = NULL;
752 }
753 hash_release(bpm->entry_hash, bpme);
754 if (hashcount(bpm->entry_hash) == 0) {
755 /* delete iptable entry first */
756 /* then delete ipset match */
757 if (bpm->installed) {
758 if (bpm->installed_in_iptable) {
759 bgp_send_pbr_iptable(bpm->action,
760 bpm, false);
761 bpm->installed_in_iptable = false;
a6b07429 762 bpm->action->refcnt--;
d114b0d7
PG
763 }
764 bgp_send_pbr_ipset_match(bpm, false);
765 bpm->installed = false;
766 bpm->action = NULL;
767 }
768 hash_release(bgp->pbr_match_hash, bpm);
769 /* XXX release pbr_match_action if not used
770 * note that drop does not need to call send_pbr_action
771 */
772 }
a6b07429
PG
773 if (bpa->refcnt == 0) {
774 if (bpa->installed && bpa->table_id != 0) {
775 bgp_send_pbr_rule_action(bpa, false);
776 bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
777 AFI_IP,
778 bpa->table_id,
779 false);
780 }
781 }
d114b0d7
PG
782}
783
784struct bgp_pbr_match_entry_remain {
785 struct bgp_pbr_match_entry *bpme_to_match;
786 struct bgp_pbr_match_entry *bpme_found;
787};
788
789static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg)
790{
791 struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
792 struct bgp_pbr_match_entry_remain *bpmer =
793 (struct bgp_pbr_match_entry_remain *)arg;
794 struct bgp_pbr_match *bpm_temp;
795 struct bgp_pbr_match_entry *bpme = bpmer->bpme_to_match;
796
797 if (!bpme->backpointer ||
798 bpm == bpme->backpointer ||
799 bpme->backpointer->action == bpm->action)
800 return HASHWALK_CONTINUE;
801 /* ensure bpm other characteristics are equal */
802 bpm_temp = bpme->backpointer;
803 if (bpm_temp->vrf_id != bpm->vrf_id ||
804 bpm_temp->type != bpm->type ||
805 bpm_temp->flags != bpm->flags)
806 return HASHWALK_CONTINUE;
807
808 /* look for remaining bpme */
809 bpmer->bpme_found = hash_lookup(bpm->entry_hash, bpme);
810 if (!bpmer->bpme_found)
811 return HASHWALK_CONTINUE;
812 return HASHWALK_ABORT;
813}
814
815static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
816 struct bgp_info *binfo,
817 vrf_id_t vrf_id,
818 struct prefix *src,
819 struct prefix *dst)
820{
821 struct bgp_pbr_match temp;
822 struct bgp_pbr_match_entry temp2;
823 struct bgp_pbr_match *bpm;
824 struct bgp_pbr_match_entry *bpme;
825 struct bgp_pbr_match_entry_remain bpmer;
826
827 /* as we don't know information from EC
828 * look for bpm that have the bpm
829 * with vrf_id characteristics
830 */
831 memset(&temp2, 0, sizeof(temp2));
832 memset(&temp, 0, sizeof(temp));
833 if (src) {
834 temp.flags |= MATCH_IP_SRC_SET;
835 prefix_copy(&temp2.src, src);
836 } else
837 temp2.src.family = AF_INET;
838 if (dst) {
839 temp.flags |= MATCH_IP_DST_SET;
840 prefix_copy(&temp2.dst, dst);
841 } else
842 temp2.dst.family = AF_INET;
843
844 if (src == NULL || dst == NULL)
845 temp.type = IPSET_NET;
846 else
847 temp.type = IPSET_NET_NET;
848 if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
849 temp.vrf_id = 0;
850 else
851 temp.vrf_id = vrf_id;
852 bpme = &temp2;
853 bpm = &temp;
854 bpme->backpointer = bpm;
855 /* right now, a previous entry may already exist
856 * flush previous entry if necessary
857 */
858 bpmer.bpme_to_match = bpme;
859 bpmer.bpme_found = NULL;
860 hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
861 if (bpmer.bpme_found) {
862 static struct bgp_pbr_match *local_bpm;
863 static struct bgp_pbr_action *local_bpa;
864
865 local_bpm = bpmer.bpme_found->backpointer;
866 local_bpa = local_bpm->action;
867 bgp_pbr_flush_entry(bgp, local_bpa,
868 local_bpm, bpmer.bpme_found);
869 }
870}
871
872static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
873 struct bgp_info *binfo,
874 vrf_id_t vrf_id,
875 struct prefix *src,
876 struct prefix *dst,
877 struct nexthop *nh,
878 float *rate)
879{
880 struct bgp_pbr_match temp;
881 struct bgp_pbr_match_entry temp2;
882 struct bgp_pbr_match *bpm;
883 struct bgp_pbr_match_entry *bpme = NULL;
884 struct bgp_pbr_action temp3;
885 struct bgp_pbr_action *bpa = NULL;
886 struct bgp_pbr_match_entry_remain bpmer;
887
888 /* look for bpa first */
889 memset(&temp3, 0, sizeof(temp3));
890 if (rate)
891 temp3.rate = *rate;
892 if (nh)
893 memcpy(&temp3.nh, nh, sizeof(struct nexthop));
894 temp3.vrf_id = vrf_id;
895 bpa = hash_get(bgp->pbr_action_hash, &temp3,
896 bgp_pbr_action_alloc_intern);
897
898 if (bpa->fwmark == 0) {
d114b0d7
PG
899 /* drop is handled by iptable */
900 if (nh && nh->type == NEXTHOP_TYPE_BLACKHOLE) {
901 bpa->table_id = 0;
902 bpa->installed = true;
903 } else {
31c28cd7
PG
904 bpa->fwmark = bgp_zebra_tm_get_id();
905 bpa->table_id = bpa->fwmark;
d114b0d7
PG
906 bpa->installed = false;
907 }
a6b07429 908 bpa->bgp = bgp;
d114b0d7
PG
909 bpa->unique = ++bgp_pbr_action_counter_unique;
910 /* 0 value is forbidden */
911 bpa->install_in_progress = false;
912 }
913
914 /* then look for bpm */
915 memset(&temp, 0, sizeof(temp));
916 if (src == NULL || dst == NULL)
917 temp.type = IPSET_NET;
918 else
919 temp.type = IPSET_NET_NET;
920 temp.vrf_id = vrf_id;
921 if (src)
922 temp.flags |= MATCH_IP_SRC_SET;
923 if (dst)
924 temp.flags |= MATCH_IP_DST_SET;
925 temp.action = bpa;
926 bpm = hash_get(bgp->pbr_match_hash, &temp,
927 bgp_pbr_match_alloc_intern);
928
929 /* new, then self allocate ipset_name and unique */
930 if (bpm && bpm->unique == 0) {
931 bpm->unique = ++bgp_pbr_match_counter_unique;
932 /* 0 value is forbidden */
933 sprintf(bpm->ipset_name, "match%p", bpm);
934 bpm->entry_hash = hash_create_size(8,
935 bgp_pbr_match_entry_hash_key,
936 bgp_pbr_match_entry_hash_equal,
937 "Match Entry Hash");
938 bpm->installed = false;
939
940 /* unique2 should be updated too */
941 bpm->unique2 = ++bgp_pbr_match_iptable_counter_unique;
942 bpm->installed_in_iptable = false;
943 bpm->install_in_progress = false;
944 bpm->install_iptable_in_progress = false;
945 }
946
947 memset(&temp2, 0, sizeof(temp2));
948 if (src)
949 prefix_copy(&temp2.src, src);
950 else
951 temp2.src.family = AF_INET;
952 if (dst)
953 prefix_copy(&temp2.dst, dst);
954 else
955 temp2.dst.family = AF_INET;
956 if (bpm)
957 bpme = hash_get(bpm->entry_hash, &temp2,
958 bgp_pbr_match_entry_alloc_intern);
959 if (bpme && bpme->unique == 0) {
960 bpme->unique = ++bgp_pbr_match_entry_counter_unique;
961 /* 0 value is forbidden */
962 bpme->backpointer = bpm;
963 bpme->installed = false;
964 bpme->install_in_progress = false;
965 }
966
967 /* BGP FS: append entry to zebra
968 * - policies are not routing entries and as such
969 * route replace semantics don't necessarily follow
970 * through to policy entries
971 * - because of that, not all policing information will be stored
972 * into zebra. and non selected policies will be suppressed from zebra
973 * - as consequence, in order to bring consistency
974 * a policy will be added, then ifan ecmp policy exists,
975 * it will be suppressed subsequently
976 */
977 /* ip rule add */
f7df1907 978 if (!bpa->installed) {
d114b0d7 979 bgp_send_pbr_rule_action(bpa, true);
f7df1907
PG
980 bgp_zebra_announce_default(bgp, nh,
981 AFI_IP, bpa->table_id, true);
982 }
d114b0d7
PG
983
984 /* ipset create */
985 if (bpm && !bpm->installed)
986 bgp_send_pbr_ipset_match(bpm, true);
987 /* ipset add */
988 if (bpme && !bpme->installed)
989 bgp_send_pbr_ipset_entry_match(bpme, true);
990
991 /* iptables */
992 if (bpm && !bpm->installed_in_iptable)
993 bgp_send_pbr_iptable(bpa, bpm, true);
994
995 /* A previous entry may already exist
996 * flush previous entry if necessary
997 */
998 bpmer.bpme_to_match = bpme;
999 bpmer.bpme_found = NULL;
1000 hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
1001 if (bpmer.bpme_found) {
1002 static struct bgp_pbr_match *local_bpm;
1003 static struct bgp_pbr_action *local_bpa;
1004
1005 local_bpm = bpmer.bpme_found->backpointer;
1006 local_bpa = local_bpm->action;
1007 bgp_pbr_flush_entry(bgp, local_bpa,
1008 local_bpm, bpmer.bpme_found);
1009 }
1010
1011
1012}
1013
1014static void bgp_pbr_handle_entry(struct bgp *bgp,
1015 struct bgp_info *binfo,
1016 struct bgp_pbr_entry_main *api,
1017 bool add)
1018{
1019 struct nexthop nh;
1020 int i = 0;
1021 int continue_loop = 1;
1022 float rate = 0;
1023 struct prefix *src = NULL, *dst = NULL;
1024
1025 if (api->match_bitmask & PREFIX_SRC_PRESENT)
1026 src = &api->src_prefix;
1027 if (api->match_bitmask & PREFIX_DST_PRESENT)
1028 dst = &api->dst_prefix;
1029 memset(&nh, 0, sizeof(struct nexthop));
1030 nh.vrf_id = VRF_UNKNOWN;
1031
1032 if (!add)
1033 return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo,
1034 api->vrf_id, src, dst);
1035 /* no action for add = true */
1036 for (i = 0; i < api->action_num; i++) {
1037 switch (api->actions[i].action) {
1038 case ACTION_TRAFFICRATE:
1039 /* drop packet */
1040 if (api->actions[i].u.r.rate == 0) {
1041 nh.vrf_id = api->vrf_id;
1042 nh.type = NEXTHOP_TYPE_BLACKHOLE;
1043 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
1044 api->vrf_id, src, dst,
1045 &nh, &rate);
1046 } else {
1047 /* update rate. can be reentrant */
1048 rate = api->actions[i].u.r.rate;
ac7c35f8 1049 if (BGP_DEBUG(pbr, PBR)) {
d114b0d7 1050 bgp_pbr_print_policy_route(api);
ac7c35f8
PG
1051 zlog_warn("PBR: ignoring Set action rate %f",
1052 api->actions[i].u.r.rate);
1053 }
d114b0d7
PG
1054 }
1055 break;
1056 case ACTION_TRAFFIC_ACTION:
1057 if (api->actions[i].u.za.filter
1058 & TRAFFIC_ACTION_SAMPLE) {
ac7c35f8 1059 if (BGP_DEBUG(pbr, PBR)) {
d114b0d7 1060 bgp_pbr_print_policy_route(api);
ac7c35f8
PG
1061 zlog_warn("PBR: Sample action Ignored");
1062 }
d114b0d7
PG
1063 }
1064#if 0
1065 if (api->actions[i].u.za.filter
1066 & TRAFFIC_ACTION_DISTRIBUTE) {
ac7c35f8 1067 if (BGP_DEBUG(pbr, PBR)) {
d114b0d7 1068 bgp_pbr_print_policy_route(api);
ac7c35f8
PG
1069 zlog_warn("PBR: Distribute action Applies");
1070 }
d114b0d7
PG
1071 continue_loop = 0;
1072 /* continue forwarding entry as before
1073 * no action
1074 */
1075 }
1076#endif /* XXX to confirm behaviour of traffic action. for now , ignore */
1077 /* terminate action: run other filters
1078 */
1079 break;
1080 case ACTION_REDIRECT_IP:
1081 nh.type = NEXTHOP_TYPE_IPV4;
1082 nh.gate.ipv4.s_addr =
1083 api->actions[i].u.zr.redirect_ip_v4.s_addr;
1084 nh.vrf_id = api->vrf_id;
1085 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
1086 api->vrf_id,
1087 src, dst,
1088 &nh, &rate);
1089 /* XXX combination with REDIRECT_VRF
1090 * + REDIRECT_NH_IP not done
1091 */
1092 continue_loop = 0;
1093 break;
1094 case ACTION_REDIRECT:
1095 nh.vrf_id = api->actions[i].u.redirect_vrf;
1096 nh.type = NEXTHOP_TYPE_IPV4;
1097 bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
1098 api->vrf_id,
1099 src, dst,
1100 &nh, &rate);
1101 continue_loop = 0;
1102 break;
1103 case ACTION_MARKING:
ac7c35f8 1104 if (BGP_DEBUG(pbr, PBR)) {
d114b0d7 1105 bgp_pbr_print_policy_route(api);
ac7c35f8
PG
1106 zlog_warn("PBR: Set DSCP %u Ignored",
1107 api->actions[i].u.marking_dscp);
1108 }
d114b0d7
PG
1109 break;
1110 default:
1111 break;
1112 }
1113 if (continue_loop == 0)
1114 break;
1115 }
1116}
1117
45918cfb
PG
1118void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p,
1119 struct bgp_info *info, afi_t afi, safi_t safi,
1120 bool nlri_update)
1121{
1122 struct bgp_pbr_entry_main api;
1123
1124 if (afi == AFI_IP6)
1125 return; /* IPv6 not supported */
1126 if (safi != SAFI_FLOWSPEC)
1127 return; /* not supported */
1128 /* Make Zebra API structure. */
1129 memset(&api, 0, sizeof(api));
1130 api.vrf_id = bgp->vrf_id;
1131 api.afi = afi;
1132
1133 if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) {
f146bb54
PG
1134 if (BGP_DEBUG(pbr, PBR_ERROR))
1135 zlog_err("%s: cancel updating entry in bgp pbr",
1136 __func__);
45918cfb
PG
1137 return;
1138 }
d114b0d7 1139 bgp_pbr_handle_entry(bgp, info, &api, nlri_update);
45918cfb 1140}