frr/zebra/zebra_pbr.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1492 lines
40 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/* Zebra Policy Based Routing (PBR) main handling.
* Copyright (C) 2018 Cumulus Networks, Inc.
* Portions:
* Copyright (c) 2021 The MITRE Corporation.
* Copyright (c) 2023 LabN Consulting, L.L.C.
*/
#include <zebra.h>
#include <jhash.h>
#include <hash.h>
#include <memory.h>
#include <hook.h>
#include "zebra/zebra_router.h"
#include "zebra/zebra_pbr.h"
#include "zebra/rt.h"
#include "zebra/zapi_msg.h"
#include "zebra/zserv.h"
#include "zebra/debug.h"
#include "zebra/zebra_neigh.h"
/* definitions */
DEFINE_MTYPE_STATIC(ZEBRA, PBR_IPTABLE_IFNAME, "PBR interface list");
DEFINE_MTYPE_STATIC(ZEBRA, PBR_RULE, "PBR rule");
DEFINE_MTYPE_STATIC(ZEBRA, PBR_IPSET, "PBR ipset");
DEFINE_MTYPE_STATIC(ZEBRA, PBR_IPSET_ENTRY, "PBR ipset entry");
DEFINE_MTYPE(ZEBRA, PBR_IPTABLE, "PBR iptable");
/* definitions */
static const struct message ipset_type_msg[] = {
{IPSET_NET_PORT_NET, "net,port,net"},
{IPSET_NET_PORT, "net,port"},
{IPSET_NET_NET, "net,net"},
{IPSET_NET, "net"},
{0}
};
const struct message icmp_typecode_str[] = {
{ 0 << 8, "echo-reply"},
{ 0 << 8, "pong"},
{ 3 << 8, "network-unreachable"},
{ (3 << 8) + 1, "host-unreachable"},
{ (3 << 8) + 2, "protocol-unreachable"},
{ (3 << 8) + 3, "port-unreachable"},
{ (3 << 8) + 4, "fragmentation-needed"},
{ (3 << 8) + 5, "source-route-failed"},
{ (3 << 8) + 6, "network-unknown"},
{ (3 << 8) + 7, "host-unknown"},
{ (3 << 8) + 9, "network-prohibited"},
{ (3 << 8) + 10, "host-prohibited"},
{ (3 << 8) + 11, "TOS-network-unreachable"},
{ (3 << 8) + 12, "TOS-host-unreachable"},
{ (3 << 8) + 13, "communication-prohibited"},
{ (3 << 8) + 14, "host-precedence-violation"},
{ (3 << 8) + 15, "precedence-cutoff"},
{ 4 << 8, "source-quench"},
{ 5 << 8, "network-redirect"},
{ (5 << 8) + 1, "host-redirect"},
{ (5 << 8) + 2, "TOS-network-redirect"},
{ (5 << 8) + 3, "TOS-host-redirect"},
{ 8 << 8, "echo-request"},
{ 8 << 8, "ping"},
{ 9 << 8, "router-advertisement"},
{ 10 << 8, "router-solicitation"},
{ 11 << 8, "ttl-zero-during-transit"},
{ (11 << 8) + 1, "ttl-zero-during-reassembly"},
{ 12 << 8, "ip-header-bad"},
{ (12 << 8) + 1, "required-option-missing"},
{ 13 << 8, "timestamp-request"},
{ 14 << 8, "timestamp-reply"},
{ 17 << 8, "address-mask-request"},
{ 18 << 8, "address-mask-reply"},
{0}
};
const struct message icmpv6_typecode_str[] = {
{ 128 << 8, "echo-request"},
{ 129 << 8, "echo-reply"},
{ 1 << 8, "no-route"},
{ (1 << 8) + 1, "communication-prohibited"},
{ (1 << 8) + 3, "address-unreachable"},
{ (1 << 8) + 4, "port-unreachable"},
{ (2 << 8), "packet-too-big"},
{ 3 << 0, "ttl-zero-during-transit"},
{ (3 << 8) + 1, "ttl-zero-during-reassembly"},
{ 4 << 0, "bad-header"},
{ (4 << 0) + 1, "unknown-header-type"},
{ (4 << 0) + 2, "unknown-option"},
{ 133 << 8, "router-solicitation"},
{ 134 << 8, "router-advertisement"},
{ 135 << 8, "neighbor-solicitation"},
{ 136 << 8, "neighbor-advertisement"},
{ 137 << 8, "redirect"},
{0}
};
/* definitions */
static const struct message tcp_value_str[] = {
{TCP_HEADER_FIN, "FIN"},
{TCP_HEADER_SYN, "SYN"},
{TCP_HEADER_RST, "RST"},
{TCP_HEADER_PSH, "PSH"},
{TCP_HEADER_ACK, "ACK"},
{TCP_HEADER_URG, "URG"},
{0}
};
static const struct message fragment_value_str[] = {
{1, "dont-fragment"},
{2, "is-fragment"},
{4, "first-fragment"},
{8, "last-fragment"},
{0}
};
struct zebra_pbr_env_display {
struct zebra_ns *zns;
struct vty *vty;
char *name;
};
/* static function declarations */
DEFINE_HOOK(zebra_pbr_ipset_entry_get_stat,
(struct zebra_pbr_ipset_entry *ipset, uint64_t *pkts,
uint64_t *bytes),
(ipset, pkts, bytes));
DEFINE_HOOK(zebra_pbr_iptable_get_stat,
(struct zebra_pbr_iptable *iptable, uint64_t *pkts,
uint64_t *bytes),
(iptable, pkts, bytes));
DEFINE_HOOK(zebra_pbr_iptable_update,
(int cmd, struct zebra_pbr_iptable *iptable), (cmd, iptable));
DEFINE_HOOK(zebra_pbr_ipset_entry_update,
(int cmd, struct zebra_pbr_ipset_entry *ipset), (cmd, ipset));
DEFINE_HOOK(zebra_pbr_ipset_update,
(int cmd, struct zebra_pbr_ipset *ipset), (cmd, ipset));
/* resolve nexthop for dataplane (dpdk) programming */
static bool zebra_pbr_expand_action;
/* Private functions */
/* Public functions */
void zebra_pbr_rules_free(void *arg)
{
struct zebra_pbr_rule *rule;
rule = (struct zebra_pbr_rule *)arg;
(void)dplane_pbr_rule_delete(rule);
XFREE(MTYPE_PBR_RULE, rule);
}
uint32_t zebra_pbr_rules_hash_key(const void *arg)
{
const struct zebra_pbr_rule *rule;
uint32_t key;
rule = arg;
key = jhash_3words(rule->rule.seq, rule->rule.priority,
rule->rule.action.table,
prefix_hash_key(&rule->rule.filter.src_ip));
key = jhash_3words(rule->rule.filter.fwmark, rule->vrf_id,
rule->rule.filter.ip_proto, key);
key = jhash(rule->ifname, strlen(rule->ifname), key);
key = jhash_3words(rule->rule.filter.pcp, rule->rule.filter.vlan_id,
rule->rule.filter.vlan_flags, key);
key = jhash_3words(rule->rule.filter.src_port,
rule->rule.filter.dst_port,
prefix_hash_key(&rule->rule.filter.dst_ip), key);
key = jhash_2words(rule->rule.unique, rule->sock, key);
return key;
}
bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2)
{
const struct zebra_pbr_rule *r1, *r2;
r1 = (const struct zebra_pbr_rule *)arg1;
r2 = (const struct zebra_pbr_rule *)arg2;
if (r1->rule.seq != r2->rule.seq)
return false;
if (r1->rule.priority != r2->rule.priority)
return false;
if (r1->sock != r2->sock)
return false;
if (r1->rule.unique != r2->rule.unique)
return false;
if (r1->rule.action.table != r2->rule.action.table)
return false;
if (r1->rule.filter.src_port != r2->rule.filter.src_port)
return false;
if (r1->rule.filter.dst_port != r2->rule.filter.dst_port)
return false;
if (r1->rule.filter.fwmark != r2->rule.filter.fwmark)
return false;
if (r1->rule.filter.ip_proto != r2->rule.filter.ip_proto)
return false;
if (!prefix_same(&r1->rule.filter.src_ip, &r2->rule.filter.src_ip))
return false;
if (!prefix_same(&r1->rule.filter.dst_ip, &r2->rule.filter.dst_ip))
return false;
if (strcmp(r1->rule.ifname, r2->rule.ifname) != 0)
return false;
if (r1->vrf_id != r2->vrf_id)
return false;
return true;
}
struct pbr_rule_unique_lookup {
struct zebra_pbr_rule *rule;
int sock;
uint32_t unique;
char ifname[IFNAMSIZ + 1];
vrf_id_t vrf_id;
};
static int pbr_rule_lookup_unique_walker(struct hash_bucket *b, void *data)
{
struct pbr_rule_unique_lookup *pul = data;
struct zebra_pbr_rule *rule = b->data;
if (pul->sock == rule->sock && pul->unique == rule->rule.unique &&
strmatch(pul->ifname, rule->rule.ifname) &&
pul->vrf_id == rule->vrf_id) {
pul->rule = rule;
return HASHWALK_ABORT;
}
return HASHWALK_CONTINUE;
}
static struct zebra_pbr_rule *
pbr_rule_lookup_unique(struct zebra_pbr_rule *zrule)
{
struct pbr_rule_unique_lookup pul;
pul.unique = zrule->rule.unique;
strlcpy(pul.ifname, zrule->rule.ifname, IFNAMSIZ);
pul.rule = NULL;
pul.vrf_id = zrule->vrf_id;
pul.sock = zrule->sock;
hash_walk(zrouter.rules_hash, &pbr_rule_lookup_unique_walker, &pul);
return pul.rule;
}
void zebra_pbr_ipset_free(void *arg)
{
struct zebra_pbr_ipset *ipset;
ipset = (struct zebra_pbr_ipset *)arg;
hook_call(zebra_pbr_ipset_update, 0, ipset);
XFREE(MTYPE_PBR_IPSET, ipset);
}
uint32_t zebra_pbr_ipset_hash_key(const void *arg)
{
const struct zebra_pbr_ipset *ipset = arg;
uint32_t *pnt = (uint32_t *)&ipset->ipset_name;
uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de);
key = jhash_1word(ipset->family, key);
return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, key);
}
bool zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2)
{
const struct zebra_pbr_ipset *r1, *r2;
r1 = (const struct zebra_pbr_ipset *)arg1;
r2 = (const struct zebra_pbr_ipset *)arg2;
if (r1->type != r2->type)
return false;
if (r1->unique != r2->unique)
return false;
if (r1->vrf_id != r2->vrf_id)
return false;
if (r1->family != r2->family)
return false;
if (strncmp(r1->ipset_name, r2->ipset_name,
ZEBRA_IPSET_NAME_SIZE))
return false;
return true;
}
void zebra_pbr_ipset_entry_free(void *arg)
{
struct zebra_pbr_ipset_entry *ipset;
ipset = (struct zebra_pbr_ipset_entry *)arg;
hook_call(zebra_pbr_ipset_entry_update, 0, ipset);
XFREE(MTYPE_PBR_IPSET_ENTRY, ipset);
}
uint32_t zebra_pbr_ipset_entry_hash_key(const void *arg)
{
const struct zebra_pbr_ipset_entry *ipset;
uint32_t key;
ipset = arg;
key = prefix_hash_key(&ipset->src);
key = jhash_1word(ipset->unique, key);
key = jhash_1word(prefix_hash_key(&ipset->dst), key);
key = jhash(&ipset->dst_port_min, 2, key);
key = jhash(&ipset->dst_port_max, 2, key);
key = jhash(&ipset->src_port_min, 2, key);
key = jhash(&ipset->src_port_max, 2, key);
key = jhash(&ipset->proto, 1, key);
return key;
}
bool zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2)
{
const struct zebra_pbr_ipset_entry *r1, *r2;
r1 = (const struct zebra_pbr_ipset_entry *)arg1;
r2 = (const struct zebra_pbr_ipset_entry *)arg2;
if (r1->unique != r2->unique)
return false;
if (!prefix_same(&r1->src, &r2->src))
return false;
if (!prefix_same(&r1->dst, &r2->dst))
return false;
if (r1->src_port_min != r2->src_port_min)
return false;
if (r1->src_port_max != r2->src_port_max)
return false;
if (r1->dst_port_min != r2->dst_port_min)
return false;
if (r1->dst_port_max != r2->dst_port_max)
return false;
if (r1->proto != r2->proto)
return false;
return true;
}
/* this function gives option to flush plugin memory contexts
* with all parameter. set it to true to flush all
* set it to false to flush only passed arg argument
*/
static void _zebra_pbr_iptable_free_all(void *arg, bool all)
{
struct zebra_pbr_iptable *iptable;
iptable = (struct zebra_pbr_iptable *)arg;
if (all)
hook_call(zebra_pbr_iptable_update, 0, iptable);
if (iptable->interface_name_list)
list_delete(&iptable->interface_name_list);
XFREE(MTYPE_PBR_IPTABLE, iptable);
}
void zebra_pbr_iptable_free(void *arg)
{
_zebra_pbr_iptable_free_all(arg, false);
}
uint32_t zebra_pbr_iptable_hash_key(const void *arg)
{
const struct zebra_pbr_iptable *iptable = arg;
uint32_t *pnt = (uint32_t *)&(iptable->ipset_name);
uint32_t key;
key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE,
0x63ab42de);
key = jhash_1word(iptable->fwmark, key);
key = jhash_1word(iptable->family, key);
key = jhash_1word(iptable->flow_label, key);
key = jhash_1word(iptable->pkt_len_min, key);
key = jhash_1word(iptable->pkt_len_max, key);
key = jhash_1word(iptable->tcp_flags, key);
key = jhash_1word(iptable->tcp_mask_flags, key);
key = jhash_1word(iptable->dscp_value, key);
key = jhash_1word(iptable->protocol, key);
key = jhash_1word(iptable->fragment, key);
key = jhash_1word(iptable->vrf_id, key);
return jhash_3words(iptable->filter_bm, iptable->type,
iptable->unique, key);
}
bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2)
{
const struct zebra_pbr_iptable *r1, *r2;
r1 = (const struct zebra_pbr_iptable *)arg1;
r2 = (const struct zebra_pbr_iptable *)arg2;
if (r1->vrf_id != r2->vrf_id)
return false;
if (r1->type != r2->type)
return false;
if (r1->unique != r2->unique)
return false;
if (r1->filter_bm != r2->filter_bm)
return false;
if (r1->fwmark != r2->fwmark)
return false;
if (r1->action != r2->action)
return false;
if (strncmp(r1->ipset_name, r2->ipset_name,
ZEBRA_IPSET_NAME_SIZE))
return false;
if (r1->family != r2->family)
return false;
if (r1->flow_label != r2->flow_label)
return false;
if (r1->pkt_len_min != r2->pkt_len_min)
return false;
if (r1->pkt_len_max != r2->pkt_len_max)
return false;
if (r1->tcp_flags != r2->tcp_flags)
return false;
if (r1->tcp_mask_flags != r2->tcp_mask_flags)
return false;
if (r1->dscp_value != r2->dscp_value)
return false;
if (r1->fragment != r2->fragment)
return false;
if (r1->protocol != r2->protocol)
return false;
return true;
}
static void *pbr_rule_alloc_intern(void *arg)
{
struct zebra_pbr_rule *zpr;
struct zebra_pbr_rule *new;
zpr = (struct zebra_pbr_rule *)arg;
new = XCALLOC(MTYPE_PBR_RULE, sizeof(*new));
memcpy(new, zpr, sizeof(*zpr));
return new;
}
static struct zebra_pbr_rule *pbr_rule_free(struct zebra_pbr_rule *hash_data,
bool free_data)
{
if (hash_data->action.neigh)
zebra_neigh_deref(hash_data);
hash_release(zrouter.rules_hash, hash_data);
if (free_data) {
XFREE(MTYPE_PBR_RULE, hash_data);
return NULL;
}
return hash_data;
}
static struct zebra_pbr_rule *pbr_rule_release(struct zebra_pbr_rule *rule,
bool free_data)
{
struct zebra_pbr_rule *lookup;
lookup = hash_lookup(zrouter.rules_hash, rule);
if (!lookup)
return NULL;
return pbr_rule_free(lookup, free_data);
}
void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty)
{
struct pbr_rule *prule = &rule->rule;
struct zebra_pbr_action *zaction = &rule->action;
struct pbr_action *pa = &prule->action;
vty_out(vty, "Rules if %s\n", rule->ifname);
vty_out(vty, " Seq %u pri %u\n", prule->seq, prule->priority);
if (prule->filter.filter_bm & PBR_FILTER_SRC_IP)
vty_out(vty, " SRC IP Match: %pFX\n", &prule->filter.src_ip);
if (prule->filter.filter_bm & PBR_FILTER_DST_IP)
vty_out(vty, " DST IP Match: %pFX\n", &prule->filter.dst_ip);
if (prule->filter.filter_bm & PBR_FILTER_IP_PROTOCOL)
vty_out(vty, " IP protocol Match: %u\n",
prule->filter.ip_proto);
if (prule->filter.filter_bm & PBR_FILTER_SRC_PORT)
vty_out(vty, " SRC Port Match: %u\n", prule->filter.src_port);
if (prule->filter.filter_bm & PBR_FILTER_DST_PORT)
vty_out(vty, " DST Port Match: %u\n", prule->filter.dst_port);
if (prule->filter.filter_bm & PBR_FILTER_DSCP)
vty_out(vty, " DSCP Match: %u\n",
(prule->filter.dsfield & PBR_DSFIELD_DSCP) >> 2);
if (prule->filter.filter_bm & PBR_FILTER_ECN)
vty_out(vty, " ECN Match: %u\n",
prule->filter.dsfield & PBR_DSFIELD_ECN);
if (prule->filter.filter_bm & PBR_FILTER_FWMARK)
vty_out(vty, " MARK Match: %u\n", prule->filter.fwmark);
if (prule->filter.filter_bm & PBR_FILTER_PCP)
vty_out(vty, " PCP Match: %u\n", prule->filter.pcp);
if (prule->filter.filter_bm & PBR_FILTER_VLAN_ID)
vty_out(vty, " VLAN ID Match: %u\n", prule->filter.vlan_id);
if (prule->filter.filter_bm & PBR_FILTER_VLAN_FLAGS) {
vty_out(vty, " VLAN Flags Match:");
if (CHECK_FLAG(prule->filter.vlan_flags, PBR_VLAN_FLAGS_TAGGED))
vty_out(vty, " tagged");
if (CHECK_FLAG(prule->filter.vlan_flags,
PBR_VLAN_FLAGS_UNTAGGED))
vty_out(vty, " untagged");
if (CHECK_FLAG(prule->filter.vlan_flags,
PBR_VLAN_FLAGS_UNTAGGED_0))
vty_out(vty, " untagged-or-zero");
vty_out(vty, "\n");
}
if (CHECK_FLAG(pa->flags, PBR_ACTION_ECN))
vty_out(vty, " Action: Set ECN: %u\n", pa->ecn);
if (CHECK_FLAG(pa->flags, PBR_ACTION_DSCP))
vty_out(vty, " Action: Set DSCP: %u\n", pa->dscp >> 2);
if (CHECK_FLAG(pa->flags, PBR_ACTION_SRC_IP))
vty_out(vty, " Action: Set SRC IP: %pSU\n", &pa->src_ip);
if (CHECK_FLAG(pa->flags, PBR_ACTION_DST_IP))
vty_out(vty, " Action: Set DST IP: %pSU\n", &pa->dst_ip);
if (CHECK_FLAG(pa->flags, PBR_ACTION_SRC_PORT))
vty_out(vty, " Action: Set SRC PORT: %u\n", pa->src_port);
if (CHECK_FLAG(pa->flags, PBR_ACTION_DST_PORT))
vty_out(vty, " Action: Set DST PORT: %u\n", pa->dst_port);
if (CHECK_FLAG(pa->flags, PBR_ACTION_QUEUE_ID))
vty_out(vty, " Action: Set Queue ID: %u\n", pa->queue_id);
if (CHECK_FLAG(pa->flags, PBR_ACTION_PCP))
vty_out(vty, " Action: Set PCP: %u\n", pa->pcp);
if (CHECK_FLAG(pa->flags, PBR_ACTION_VLAN_ID))
vty_out(vty, " Action: Set VLAN ID: %u\n", pa->vlan_id);
if (CHECK_FLAG(pa->flags, PBR_ACTION_VLAN_STRIP_INNER_ANY))
vty_out(vty, " Action: Strip VLAN ID\n");
vty_out(vty, " Tableid: %u\n", prule->action.table);
if (zaction->afi == AFI_IP)
vty_out(vty, " Action: nh: %pI4 intf: %s\n",
&zaction->gate.ipv4,
ifindex2ifname(zaction->ifindex, rule->vrf_id));
if (zaction->afi == AFI_IP6)
vty_out(vty, " Action: nh: %pI6 intf: %s\n",
&zaction->gate.ipv6,
ifindex2ifname(zaction->ifindex, rule->vrf_id));
if (zaction->neigh && (zaction->neigh->flags & ZEBRA_NEIGH_ENT_ACTIVE))
vty_out(vty, " Action: mac: %pEA\n", &zaction->neigh->mac);
}
static int zebra_pbr_show_rules_walkcb(struct hash_bucket *bucket, void *arg)
{
struct zebra_pbr_rule *rule = (struct zebra_pbr_rule *)bucket->data;
struct zebra_pbr_env_display *env = (struct zebra_pbr_env_display *)arg;
struct vty *vty = env->vty;
zebra_pbr_show_rule_unit(rule, vty);
return HASHWALK_CONTINUE;
}
void zebra_pbr_show_rule(struct vty *vty)
{
struct zebra_pbr_env_display env;
env.vty = vty;
hash_walk(zrouter.rules_hash, zebra_pbr_show_rules_walkcb, &env);
}
void zebra_pbr_config_write(struct vty *vty)
{
if (zebra_pbr_expand_action)
vty_out(vty, "pbr nexthop-resolve\n");
}
void zebra_pbr_expand_action_update(bool enable)
{
zebra_pbr_expand_action = enable;
}
static void zebra_pbr_expand_rule(struct zebra_pbr_rule *rule)
{
struct prefix p;
struct route_table *table;
struct route_node *rn;
rib_dest_t *dest;
struct route_entry *re;
const struct nexthop_group *nhg;
const struct nexthop *nexthop;
struct zebra_pbr_action *action = &rule->action;
struct ipaddr ip;
if (!zebra_pbr_expand_action)
return;
table = zebra_vrf_get_table_with_table_id(
AFI_IP, SAFI_UNICAST, VRF_DEFAULT, rule->rule.action.table);
if (!table)
return;
memset(&p, 0, sizeof(p));
p.family = AF_INET;
rn = route_node_lookup(table, &p);
if (!rn)
return;
dest = rib_dest_from_rnode(rn);
re = dest->selected_fib;
if (!re) {
route_unlock_node(rn);
return;
}
nhg = rib_get_fib_nhg(re);
if (!nhg) {
route_unlock_node(rn);
return;
}
nexthop = nhg->nexthop;
if (nexthop) {
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
action->afi = AFI_IP;
action->gate.ipv4 = nexthop->gate.ipv4;
action->ifindex = nexthop->ifindex;
ip.ipa_type = AF_INET;
ip.ipaddr_v4 = action->gate.ipv4;
zebra_neigh_ref(action->ifindex, &ip, rule);
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
action->afi = AFI_IP6;
action->gate.ipv6 = nexthop->gate.ipv6;
action->ifindex = nexthop->ifindex;
ip.ipa_type = AF_INET6;
ip.ipaddr_v6 = action->gate.ipv6;
zebra_neigh_ref(action->ifindex, &ip, rule);
break;
case NEXTHOP_TYPE_BLACKHOLE:
case NEXTHOP_TYPE_IFINDEX:
action->afi = AFI_UNSPEC;
}
}
route_unlock_node(rn);
}
void zebra_pbr_add_rule(struct zebra_pbr_rule *rule)
{
struct zebra_pbr_rule *found;
struct zebra_pbr_rule *old;
struct zebra_pbr_rule *new;
/**
* Check if we already have it (this checks via a unique ID, walking
* over the hash table, not via a hash operation).
*/
found = pbr_rule_lookup_unique(rule);
/* If found, this is an update */
if (found) {
if (IS_ZEBRA_DEBUG_PBR)
zlog_debug(
"%s: seq: %d, prior: %d, unique: %d, ifname: %s -- update",
__func__, rule->rule.seq, rule->rule.priority,
rule->rule.unique, rule->rule.ifname);
/* remove the old entry from the hash but don't free the hash
* data yet as we need it for the dplane update
*/
old = pbr_rule_release(found, false);
/* insert new entry into hash */
new = hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern);
/* expand the action if needed */
zebra_pbr_expand_rule(new);
/* update dataplane */
(void)dplane_pbr_rule_update(found, new);
/* release the old hash data */
if (old)
XFREE(MTYPE_PBR_RULE, old);
} else {
if (IS_ZEBRA_DEBUG_PBR)
zlog_debug(
"%s: seq: %d, prior: %d, unique: %d, ifname: %s -- new",
__func__, rule->rule.seq, rule->rule.priority,
rule->rule.unique, rule->rule.ifname);
/* insert new entry into hash */
new = hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern);
/* expand the action if needed */
zebra_pbr_expand_rule(new);
(void)dplane_pbr_rule_add(new);
}
}
void zebra_pbr_del_rule(struct zebra_pbr_rule *rule)
{
if (IS_ZEBRA_DEBUG_PBR)
zlog_debug("%s: seq: %d, prior: %d, unique: %d, ifname: %s",
__func__, rule->rule.seq, rule->rule.priority,
rule->rule.unique, rule->rule.ifname);
(void)dplane_pbr_rule_delete(rule);
if (pbr_rule_release(rule, true))
zlog_debug("%s: Rule being deleted we know nothing about",
__func__);
}
void zebra_pbr_process_iptable(struct zebra_dplane_ctx *ctx)
{
int mode, ret = 0;
struct zebra_pbr_iptable ipt;
if (dplane_ctx_get_op(ctx) == DPLANE_OP_IPTABLE_ADD)
mode = 1;
else
mode = 0;
dplane_ctx_get_pbr_iptable(ctx, &ipt);
ret = hook_call(zebra_pbr_iptable_update, mode, &ipt);
if (ret)
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
else
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
}
void zebra_pbr_process_ipset(struct zebra_dplane_ctx *ctx)
{
int mode, ret = 0;
struct zebra_pbr_ipset ipset;
if (dplane_ctx_get_op(ctx) == DPLANE_OP_IPSET_ADD)
mode = 1;
else
mode = 0;
dplane_ctx_get_pbr_ipset(ctx, &ipset);
ret = hook_call(zebra_pbr_ipset_update, mode, &ipset);
if (ret)
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
else
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
}
void zebra_pbr_process_ipset_entry(struct zebra_dplane_ctx *ctx)
{
int mode, ret = 0;
struct zebra_pbr_ipset_entry ipset_entry;
struct zebra_pbr_ipset ipset;
if (dplane_ctx_get_op(ctx) == DPLANE_OP_IPSET_ENTRY_ADD)
mode = 1;
else
mode = 0;
dplane_ctx_get_pbr_ipset_entry(ctx, &ipset_entry);
dplane_ctx_get_pbr_ipset(ctx, &ipset);
ipset_entry.backpointer = &ipset;
ret = hook_call(zebra_pbr_ipset_entry_update, mode, &ipset_entry);
if (ret)
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
else
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
}
static void zebra_pbr_cleanup_rules(struct hash_bucket *b, void *data)
{
struct zebra_pbr_rule *rule = b->data;
int *sock = data;
if (rule->sock == *sock) {
(void)dplane_pbr_rule_delete(rule);
pbr_rule_free(rule, true);
}
}
static void zebra_pbr_cleanup_ipset(struct hash_bucket *b, void *data)
{
struct zebra_pbr_ipset *ipset = b->data;
int *sock = data;
if (ipset->sock == *sock) {
if (hash_release(zrouter.ipset_hash, ipset))
zebra_pbr_ipset_free(ipset);
else
hook_call(zebra_pbr_ipset_update, 0, ipset);
}
}
static void zebra_pbr_cleanup_ipset_entry(struct hash_bucket *b, void *data)
{
struct zebra_pbr_ipset_entry *ipset = b->data;
int *sock = data;
if (ipset->sock == *sock) {
if (hash_release(zrouter.ipset_entry_hash, ipset))
zebra_pbr_ipset_entry_free(ipset);
else
hook_call(zebra_pbr_ipset_entry_update, 0, ipset);
}
}
static void zebra_pbr_cleanup_iptable(struct hash_bucket *b, void *data)
{
struct zebra_pbr_iptable *iptable = b->data;
int *sock = data;
if (iptable->sock == *sock) {
if (hash_release(zrouter.iptable_hash, iptable))
_zebra_pbr_iptable_free_all(iptable, true);
else
hook_call(zebra_pbr_iptable_update, 0, iptable);
}
}
static int zebra_pbr_client_close_cleanup(struct zserv *client)
{
int sock = client->sock;
if (!sock)
return 0;
hash_iterate(zrouter.rules_hash, zebra_pbr_cleanup_rules, &sock);
hash_iterate(zrouter.iptable_hash, zebra_pbr_cleanup_iptable, &sock);
hash_iterate(zrouter.ipset_entry_hash, zebra_pbr_cleanup_ipset_entry,
&sock);
hash_iterate(zrouter.ipset_hash, zebra_pbr_cleanup_ipset, &sock);
return 1;
}
void zebra_pbr_init(void)
{
hook_register(zserv_client_close, zebra_pbr_client_close_cleanup);
}
static void *pbr_ipset_alloc_intern(void *arg)
{
struct zebra_pbr_ipset *zpi;
struct zebra_pbr_ipset *new;
zpi = (struct zebra_pbr_ipset *)arg;
new = XCALLOC(MTYPE_PBR_IPSET, sizeof(struct zebra_pbr_ipset));
memcpy(new, zpi, sizeof(*zpi));
return new;
}
void zebra_pbr_create_ipset(struct zebra_pbr_ipset *ipset)
{
(void)hash_get(zrouter.ipset_hash, ipset, pbr_ipset_alloc_intern);
(void)dplane_pbr_ipset_add(ipset);
}
void zebra_pbr_destroy_ipset(struct zebra_pbr_ipset *ipset)
{
struct zebra_pbr_ipset *lookup;
lookup = hash_lookup(zrouter.ipset_hash, ipset);
(void)dplane_pbr_ipset_delete(ipset);
if (lookup) {
hash_release(zrouter.ipset_hash, lookup);
XFREE(MTYPE_PBR_IPSET, lookup);
} else
zlog_debug(
"%s: IPSet Entry being deleted we know nothing about",
__func__);
}
struct pbr_ipset_name_lookup {
struct zebra_pbr_ipset *ipset;
char ipset_name[ZEBRA_IPSET_NAME_SIZE];
};
const char *zebra_pbr_ipset_type2str(uint32_t type)
{
return lookup_msg(ipset_type_msg, type,
"Unrecognized IPset Type");
}
static int zebra_pbr_ipset_pername_walkcb(struct hash_bucket *bucket, void *arg)
{
struct pbr_ipset_name_lookup *pinl =
(struct pbr_ipset_name_lookup *)arg;
struct zebra_pbr_ipset *zpi = (struct zebra_pbr_ipset *)bucket->data;
if (!strncmp(pinl->ipset_name, zpi->ipset_name,
ZEBRA_IPSET_NAME_SIZE)) {
pinl->ipset = zpi;
return HASHWALK_ABORT;
}
return HASHWALK_CONTINUE;
}
struct zebra_pbr_ipset *zebra_pbr_lookup_ipset_pername(char *ipsetname)
{
struct pbr_ipset_name_lookup pinl;
struct pbr_ipset_name_lookup *ptr = &pinl;
if (!ipsetname)
return NULL;
memset(ptr, 0, sizeof(struct pbr_ipset_name_lookup));
snprintf((char *)ptr->ipset_name, ZEBRA_IPSET_NAME_SIZE, "%s",
ipsetname);
hash_walk(zrouter.ipset_hash, zebra_pbr_ipset_pername_walkcb, ptr);
return ptr->ipset;
}
static void *pbr_ipset_entry_alloc_intern(void *arg)
{
struct zebra_pbr_ipset_entry *zpi;
struct zebra_pbr_ipset_entry *new;
zpi = (struct zebra_pbr_ipset_entry *)arg;
new = XCALLOC(MTYPE_PBR_IPSET_ENTRY, sizeof(struct zebra_pbr_ipset_entry));
memcpy(new, zpi, sizeof(*zpi));
return new;
}
void zebra_pbr_add_ipset_entry(struct zebra_pbr_ipset_entry *ipset)
{
(void)hash_get(zrouter.ipset_entry_hash, ipset,
pbr_ipset_entry_alloc_intern);
(void)dplane_pbr_ipset_entry_add(ipset);
}
void zebra_pbr_del_ipset_entry(struct zebra_pbr_ipset_entry *ipset)
{
struct zebra_pbr_ipset_entry *lookup;
lookup = hash_lookup(zrouter.ipset_entry_hash, ipset);
(void)dplane_pbr_ipset_entry_delete(ipset);
if (lookup) {
hash_release(zrouter.ipset_entry_hash, lookup);
XFREE(MTYPE_PBR_IPSET_ENTRY, lookup);
} else
zlog_debug("%s: IPSet being deleted we know nothing about",
__func__);
}
void zebra_pbr_iptable_interface_name_list_free(void *arg)
{
char *name = arg;
XFREE(MTYPE_PBR_IPTABLE_IFNAME, name);
}
static void *pbr_iptable_alloc_intern(void *arg)
{
struct zebra_pbr_iptable *zpi;
struct zebra_pbr_iptable *new;
struct listnode *ln;
char *ifname;
zpi = (struct zebra_pbr_iptable *)arg;
new = XCALLOC(MTYPE_PBR_IPTABLE, sizeof(struct zebra_pbr_iptable));
/* Deep structure copy */
memcpy(new, zpi, sizeof(*zpi));
new->interface_name_list = list_new();
new->interface_name_list->del = zebra_pbr_iptable_interface_name_list_free;
if (zpi->interface_name_list) {
for (ALL_LIST_ELEMENTS_RO(zpi->interface_name_list, ln, ifname))
listnode_add(new->interface_name_list,
XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifname));
}
return new;
}
void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable)
{
zebra: avoid pbr iptable added twice when used with flowspec The usage of zebra dplane makes the job asyncronous which implies that a given job will try to add an iptable, while the second job will not know that its iptable is the same as the former one. The below exabgp rules stand for two bgp flowspec rules sent to the bgp device: flow { route {match { source 185.228.172.73/32; destination 0.0.0.0/0; source-port >=49156&<=49159; }then {redirect 213.242.114.113;}} route {match { source 185.228.172.73/32; destination 0.0.0.0/0; source-port >=49160&<=49163; }then {redirect 213.242.114.113;}} } This rule creates a single iptable, but in fact, the same iptable name is appended twice. This results in duplicated entries in the iptables context. This also results in contexts not flushed, when BGP session or 'flush' operation is performed. iptables-save: [..] -A PREROUTING -m set --match-set match0x55baf4c25cb0 src,src -g match0x55baf4c25cb0 -A PREROUTING -m set --match-set match0x55baf4c25cb0 src,src -g match0x55baf4c25cb0 -A match0x55baf4c25cb0 -j MARK --set-xmark 0x100/0xffffffff -A match0x55baf4c25cb0 -j ACCEPT -A match0x55baf4c25cb0 -j MARK --set-xmark 0x100/0xffffffff -A match0x55baf4c25cb0 -j ACCEPT [..] This commit addresses this issue, by checking that an iptable context is not already being processed. A flag is added in the original iptable context, and a check is done if the iptable context is not already being processed for install or uinstall. Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
2022-05-23 10:21:16 +02:00
struct zebra_pbr_iptable *ipt_hash;
ipt_hash = hash_get(zrouter.iptable_hash, iptable,
pbr_iptable_alloc_intern);
(void)dplane_pbr_iptable_add(ipt_hash);
}
void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable)
{
struct zebra_pbr_iptable *lookup;
lookup = hash_lookup(zrouter.iptable_hash, iptable);
(void)dplane_pbr_iptable_delete(iptable);
if (lookup) {
hash_release(zrouter.iptable_hash, lookup);
zebra: fix pbr_iptable memory leak We are obviously doing deleting on wrong object. > Direct leak of 40 byte(s) in 1 object(s) allocated from: > #0 0x7fcf718b4a57 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 > #1 0x7fcf7126f8dd in qcalloc lib/memory.c:105 > #2 0x7fcf7124401a in list_new lib/linklist.c:49 > #3 0x55771621d86d in pbr_iptable_alloc_intern zebra/zebra_pbr.c:1015 > #4 0x7fcf71217d79 in hash_get lib/hash.c:147 > #5 0x55771621dad3 in zebra_pbr_add_iptable zebra/zebra_pbr.c:1030 > #6 0x55771614d00c in zread_iptable zebra/zapi_msg.c:4131 > #7 0x55771614e586 in zserv_handle_commands zebra/zapi_msg.c:4424 > #8 0x5577162dae2c in zserv_process_messages zebra/zserv.c:521 > #9 0x7fcf7137798e in event_call lib/event.c:2011 > #10 0x7fcf71242ff1 in frr_run lib/libfrr.c:1216 > #11 0x5577160e4d6d in main zebra/main.c:540 > #12 0x7fcf70c29d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 > > Indirect leak of 24 byte(s) in 1 object(s) allocated from: > #0 0x7fcf718b4a57 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 > #1 0x7fcf7126f8dd in qcalloc lib/memory.c:105 > #2 0x7fcf71244129 in listnode_new lib/linklist.c:71 > #3 0x7fcf71244238 in listnode_add lib/linklist.c:92 > #4 0x55771621d938 in pbr_iptable_alloc_intern zebra/zebra_pbr.c:1019 > #5 0x7fcf71217d79 in hash_get lib/hash.c:147 > #6 0x55771621dad3 in zebra_pbr_add_iptable zebra/zebra_pbr.c:1030 > #7 0x55771614d00c in zread_iptable zebra/zapi_msg.c:4131 > #8 0x55771614e586 in zserv_handle_commands zebra/zapi_msg.c:4424 > #9 0x5577162dae2c in zserv_process_messages zebra/zserv.c:521 > #10 0x7fcf7137798e in event_call lib/event.c:2011 > #11 0x7fcf71242ff1 in frr_run lib/libfrr.c:1216 > #12 0x5577160e4d6d in main zebra/main.c:540 > #13 0x7fcf70c29d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 Fixes: f80ec7e3d6 ("zebra: handle iptable list of interfaces") Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2025-04-11 15:20:13 +02:00
list_delete(&lookup->interface_name_list);
XFREE(MTYPE_PBR_IPTABLE, lookup);
} else
zlog_debug("%s: IPTable being deleted we know nothing about",
__func__);
}
/*
* Handle success or failure of rule (un)install in the kernel.
*/
void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx)
{
enum zebra_dplane_result res;
enum dplane_op_e op;
res = dplane_ctx_get_status(ctx);
op = dplane_ctx_get_op(ctx);
if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE)
zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_RULE_INSTALLED
: ZAPI_RULE_FAIL_INSTALL);
else if (op == DPLANE_OP_RULE_DELETE)
zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_RULE_REMOVED
: ZAPI_RULE_FAIL_REMOVE);
else if (op == DPLANE_OP_IPTABLE_ADD)
zsend_iptable_notify_owner(ctx,
res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_IPTABLE_INSTALLED
: ZAPI_IPTABLE_FAIL_INSTALL);
else if (op == DPLANE_OP_IPTABLE_DELETE)
zsend_iptable_notify_owner(ctx,
res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_IPTABLE_REMOVED
: ZAPI_IPTABLE_FAIL_REMOVE);
else if (op == DPLANE_OP_IPSET_ADD)
zsend_ipset_notify_owner(ctx,
res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_IPSET_INSTALLED
: ZAPI_IPSET_FAIL_INSTALL);
else if (op == DPLANE_OP_IPSET_DELETE)
zsend_ipset_notify_owner(ctx,
res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_IPSET_REMOVED
: ZAPI_IPSET_FAIL_REMOVE);
else if (op == DPLANE_OP_IPSET_ENTRY_ADD)
zsend_ipset_entry_notify_owner(
ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_IPSET_ENTRY_INSTALLED
: ZAPI_IPSET_ENTRY_FAIL_INSTALL);
else if (op == DPLANE_OP_IPSET_ENTRY_DELETE)
zsend_ipset_entry_notify_owner(
ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_IPSET_ENTRY_REMOVED
: ZAPI_IPSET_ENTRY_FAIL_REMOVE);
else
flog_err(
EC_ZEBRA_PBR_RULE_UPDATE,
"Context received in pbr rule dplane result handler with incorrect OP code (%u)",
op);
}
/*
* Handle rule delete notification from kernel.
*/
int kernel_pbr_rule_del(struct zebra_pbr_rule *rule)
{
return 0;
}
struct zebra_pbr_ipset_entry_unique_display {
struct zebra_pbr_ipset *zpi;
struct vty *vty;
struct zebra_ns *zns;
};
static const char *zebra_pbr_prefix2str(union prefixconstptr pu,
char *str, int size)
{
const struct prefix *p = pu.p;
char buf[PREFIX2STR_BUFFER];
if ((p->family == AF_INET && p->prefixlen == IPV4_MAX_BITLEN)
|| (p->family == AF_INET6 && p->prefixlen == IPV6_MAX_BITLEN)) {
snprintf(str, size, "%s", inet_ntop(p->family, &p->u.prefix,
buf, PREFIX2STR_BUFFER));
return str;
}
return prefix2str(pu, str, size);
}
static void zebra_pbr_display_icmp(struct vty *vty,
struct zebra_pbr_ipset_entry *zpie)
{
char decoded_str[20];
uint16_t port;
struct zebra_pbr_ipset *zpi;
zpi = zpie->backpointer;
/* range icmp type */
if (zpie->src_port_max || zpie->dst_port_max) {
vty_out(vty, ":icmp:[type <%u:%u>;code <%u:%u>",
zpie->src_port_min, zpie->src_port_max,
zpie->dst_port_min, zpie->dst_port_max);
} else {
port = ((zpie->src_port_min << 8) & 0xff00) +
(zpie->dst_port_min & 0xff);
memset(decoded_str, 0, sizeof(decoded_str));
snprintf(decoded_str, sizeof(decoded_str), "%u/%u",
zpie->src_port_min, zpie->dst_port_min);
vty_out(vty, ":%s:%s",
zpi->family == AF_INET6 ? "ipv6-icmp" : "icmp",
lookup_msg(zpi->family == AF_INET6 ?
icmpv6_typecode_str : icmp_typecode_str,
port, decoded_str));
}
}
static void zebra_pbr_display_port(struct vty *vty, uint32_t filter_bm,
uint16_t port_min, uint16_t port_max,
uint8_t proto)
{
if (!(filter_bm & PBR_FILTER_IP_PROTOCOL)) {
if (port_max)
vty_out(vty, ":udp/tcp:%d-%d",
port_min, port_max);
else
vty_out(vty, ":udp/tcp:%d",
port_min);
} else {
if (port_max)
vty_out(vty, ":proto %d:%d-%d",
proto, port_min, port_max);
else
vty_out(vty, ":proto %d:%d",
proto, port_min);
}
}
static int zebra_pbr_show_ipset_entry_walkcb(struct hash_bucket *bucket,
void *arg)
{
struct zebra_pbr_ipset_entry_unique_display *unique =
(struct zebra_pbr_ipset_entry_unique_display *)arg;
struct zebra_pbr_ipset *zpi = unique->zpi;
struct vty *vty = unique->vty;
struct zebra_pbr_ipset_entry *zpie =
(struct zebra_pbr_ipset_entry *)bucket->data;
uint64_t pkts = 0, bytes = 0;
int ret = 0;
if (zpie->backpointer != zpi)
return HASHWALK_CONTINUE;
if ((zpi->type == IPSET_NET_NET) ||
(zpi->type == IPSET_NET_PORT_NET)) {
char buf[PREFIX_STRLEN];
zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf));
vty_out(vty, "\tfrom %s", buf);
if (zpie->filter_bm & PBR_FILTER_SRC_PORT &&
zpie->proto != IPPROTO_ICMP)
zebra_pbr_display_port(vty, zpie->filter_bm,
zpie->src_port_min,
zpie->src_port_max,
zpie->proto);
vty_out(vty, " to ");
zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf));
vty_out(vty, "%s", buf);
if (zpie->filter_bm & PBR_FILTER_DST_PORT &&
zpie->proto != IPPROTO_ICMP)
zebra_pbr_display_port(vty, zpie->filter_bm,
zpie->dst_port_min,
zpie->dst_port_max,
zpie->proto);
if (zpie->proto == IPPROTO_ICMP)
zebra_pbr_display_icmp(vty, zpie);
} else if ((zpi->type == IPSET_NET) ||
(zpi->type == IPSET_NET_PORT)) {
char buf[PREFIX_STRLEN];
if (zpie->filter_bm & PBR_FILTER_SRC_IP) {
zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf));
vty_out(vty, "\tfrom %s", buf);
}
if (zpie->filter_bm & PBR_FILTER_SRC_PORT &&
zpie->proto != IPPROTO_ICMP)
zebra_pbr_display_port(vty, zpie->filter_bm,
zpie->src_port_min,
zpie->src_port_max,
zpie->proto);
if (zpie->filter_bm & PBR_FILTER_DST_IP) {
zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf));
vty_out(vty, "\tto %s", buf);
}
if (zpie->filter_bm & PBR_FILTER_DST_PORT &&
zpie->proto != IPPROTO_ICMP)
zebra_pbr_display_port(vty, zpie->filter_bm,
zpie->dst_port_min,
zpie->dst_port_max,
zpie->proto);
if (zpie->proto == IPPROTO_ICMP)
zebra_pbr_display_icmp(vty, zpie);
}
vty_out(vty, " (%u)\n", zpie->unique);
ret = hook_call(zebra_pbr_ipset_entry_get_stat, zpie, &pkts,
&bytes);
if (ret && pkts > 0)
vty_out(vty, "\t pkts %" PRIu64 ", bytes %" PRIu64"\n",
pkts, bytes);
return HASHWALK_CONTINUE;
}
static int zebra_pbr_show_ipset_walkcb(struct hash_bucket *bucket, void *arg)
{
struct zebra_pbr_env_display *uniqueipset =
(struct zebra_pbr_env_display *)arg;
struct zebra_pbr_ipset *zpi = (struct zebra_pbr_ipset *)bucket->data;
struct zebra_pbr_ipset_entry_unique_display unique;
struct vty *vty = uniqueipset->vty;
struct zebra_ns *zns = uniqueipset->zns;
vty_out(vty, "IPset %s type %s family %s\n", zpi->ipset_name,
zebra_pbr_ipset_type2str(zpi->type),
family2str(zpi->family));
unique.vty = vty;
unique.zpi = zpi;
unique.zns = zns;
hash_walk(zrouter.ipset_entry_hash, zebra_pbr_show_ipset_entry_walkcb,
&unique);
vty_out(vty, "\n");
return HASHWALK_CONTINUE;
}
size_t zebra_pbr_tcpflags_snprintf(char *buffer, size_t len,
uint16_t tcp_val)
{
size_t len_written = 0;
static struct message nt = {0};
const struct message *pnt;
int incr = 0;
for (pnt = tcp_value_str;
memcmp(pnt, &nt, sizeof(struct message)); pnt++)
if (pnt->key & tcp_val) {
len_written += snprintf(buffer + len_written,
len - len_written,
"%s%s", incr ?
",":"", pnt->str);
incr++;
}
return len_written;
}
/*
*/
void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname)
{
struct zebra_pbr_ipset *zpi;
struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
struct zebra_pbr_ipset_entry_unique_display unique;
struct zebra_pbr_env_display uniqueipset;
if (ipsetname) {
zpi = zebra_pbr_lookup_ipset_pername(ipsetname);
if (!zpi) {
vty_out(vty, "No IPset %s found\n", ipsetname);
return;
}
vty_out(vty, "IPset %s type %s family %s\n", ipsetname,
zebra_pbr_ipset_type2str(zpi->type),
family2str(zpi->family));
unique.vty = vty;
unique.zpi = zpi;
unique.zns = zns;
hash_walk(zrouter.ipset_entry_hash,
zebra_pbr_show_ipset_entry_walkcb, &unique);
return;
}
uniqueipset.zns = zns;
uniqueipset.vty = vty;
uniqueipset.name = NULL;
hash_walk(zrouter.ipset_hash, zebra_pbr_show_ipset_walkcb,
&uniqueipset);
}
struct pbr_rule_fwmark_lookup {
struct zebra_pbr_rule *ptr;
uint32_t fwmark;
};
static int zebra_pbr_rule_lookup_fwmark_walkcb(struct hash_bucket *bucket,
void *arg)
{
struct pbr_rule_fwmark_lookup *iprule =
(struct pbr_rule_fwmark_lookup *)arg;
struct zebra_pbr_rule *zpr = (struct zebra_pbr_rule *)bucket->data;
if (iprule->fwmark == zpr->rule.filter.fwmark) {
iprule->ptr = zpr;
return HASHWALK_ABORT;
}
return HASHWALK_CONTINUE;
}
static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable,
struct vty *vty,
struct zebra_ns *zns)
{
int ret;
uint64_t pkts = 0, bytes = 0;
vty_out(vty, "IPtable %s family %s action %s (%u)\n",
iptable->ipset_name,
family2str(iptable->family),
iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect",
iptable->unique);
if (iptable->type == IPSET_NET_PORT ||
iptable->type == IPSET_NET_PORT_NET) {
if (!(iptable->filter_bm & MATCH_ICMP_SET)) {
if (iptable->filter_bm & PBR_FILTER_DST_PORT)
vty_out(vty, "\t lookup dst port\n");
else if (iptable->filter_bm & PBR_FILTER_SRC_PORT)
vty_out(vty, "\t lookup src port\n");
}
}
if (iptable->pkt_len_min || iptable->pkt_len_max) {
if (!iptable->pkt_len_max)
vty_out(vty, "\t pkt len %u\n",
iptable->pkt_len_min);
else
vty_out(vty, "\t pkt len [%u;%u]\n",
iptable->pkt_len_min,
iptable->pkt_len_max);
}
if (iptable->tcp_flags || iptable->tcp_mask_flags) {
char tcp_flag_str[64];
char tcp_flag_mask_str[64];
zebra_pbr_tcpflags_snprintf(tcp_flag_str,
sizeof(tcp_flag_str),
iptable->tcp_flags);
zebra_pbr_tcpflags_snprintf(tcp_flag_mask_str,
sizeof(tcp_flag_mask_str),
iptable->tcp_mask_flags);
vty_out(vty, "\t tcpflags [%s/%s]\n",
tcp_flag_str, tcp_flag_mask_str);
}
if (iptable->filter_bm & (MATCH_DSCP_SET | MATCH_DSCP_INVERSE_SET)) {
vty_out(vty, "\t dscp %s %d\n",
iptable->filter_bm & MATCH_DSCP_INVERSE_SET ?
"not" : "", iptable->dscp_value);
}
if (iptable->filter_bm & (MATCH_FLOW_LABEL_SET |
MATCH_FLOW_LABEL_INVERSE_SET)) {
vty_out(vty, "\t flowlabel %s %d\n",
iptable->filter_bm & MATCH_FLOW_LABEL_INVERSE_SET ?
"not" : "", iptable->flow_label);
}
if (iptable->fragment) {
char val_str[10];
snprintf(val_str, sizeof(val_str), "%d", iptable->fragment);
vty_out(vty, "\t fragment%s %s\n",
iptable->filter_bm & MATCH_FRAGMENT_INVERSE_SET ?
" not" : "", lookup_msg(fragment_value_str,
iptable->fragment, val_str));
}
if (iptable->protocol) {
vty_out(vty, "\t protocol %d\n",
iptable->protocol);
}
ret = hook_call(zebra_pbr_iptable_get_stat, iptable, &pkts,
&bytes);
if (ret && pkts > 0)
vty_out(vty, "\t pkts %" PRIu64 ", bytes %" PRIu64"\n",
pkts, bytes);
if (iptable->action != ZEBRA_IPTABLES_DROP) {
struct pbr_rule_fwmark_lookup prfl;
prfl.fwmark = iptable->fwmark;
prfl.ptr = NULL;
hash_walk(zrouter.rules_hash,
&zebra_pbr_rule_lookup_fwmark_walkcb, &prfl);
if (prfl.ptr) {
struct zebra_pbr_rule *zpr = prfl.ptr;
vty_out(vty, "\t table %u, fwmark %u\n",
zpr->rule.action.table,
prfl.fwmark);
}
}
}
static int zebra_pbr_show_iptable_walkcb(struct hash_bucket *bucket, void *arg)
{
struct zebra_pbr_iptable *iptable =
(struct zebra_pbr_iptable *)bucket->data;
struct zebra_pbr_env_display *env = (struct zebra_pbr_env_display *)arg;
struct vty *vty = env->vty;
struct zebra_ns *zns = env->zns;
char *iptable_name = env->name;
if (!iptable_name)
zebra_pbr_show_iptable_unit(iptable, vty, zns);
else if (!strncmp(iptable_name,
iptable->ipset_name,
ZEBRA_IPSET_NAME_SIZE))
zebra_pbr_show_iptable_unit(iptable, vty, zns);
return HASHWALK_CONTINUE;
}
void zebra_pbr_show_iptable(struct vty *vty, char *iptable_name)
{
struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
struct zebra_pbr_env_display env;
env.vty = vty;
env.zns = zns;
env.name = iptable_name;
hash_walk(zrouter.iptable_hash, zebra_pbr_show_iptable_walkcb, &env);
}
void zebra_pbr_iptable_update_interfacelist(struct stream *s,
struct zebra_pbr_iptable *zpi)
{
uint32_t i = 0, index;
struct interface *ifp;
char *name;
for (i = 0; i < zpi->nb_interface; i++) {
STREAM_GETL(s, index);
ifp = if_lookup_by_index(index, zpi->vrf_id);
if (!ifp)
continue;
name = XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifp->name);
listnode_add(zpi->interface_name_list, name);
}
stream_failure:
return;
}