%type <time> expr_us time
%type <a> ipa
%type <net> net_ip4_ net_ip4 net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
-%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_ net_aspa_
+%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_ net_aspa_ net_nbr_
%type <mls> label_stack_start label_stack
%type <t> text opttext
/* See r_args */
%expect 2
-CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM, MAX, AS)
+CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM, MAX, AS, NEIGHBOR)
CF_GRAMMAR
net_fill_aspa($$, $2);
}
+net_nbr_: NEIGHBOR ipa '%' expr
+{
+ $$ = cfg_alloc(sizeof(net_addr_nbr));
+ net_fill_nbr($$, $2, $4);
+}
+
net_ip_: net_ip4_ | net_ip6_ ;
net_vpn_: net_vpn4_ | net_vpn6_ ;
net_roa_: net_roa4_ | net_roa6_ ;
| net_ip6_sadr_
| net_mpls_
| net_aspa_
+ | net_nbr_
;
<item>Route next hops
</itemize>
+<sect1>Neighbor entries
+<label id="neighbor-routes">
+
+<p>Neighbor entries represent discovered network neighbors. These are used to
+exchange neighbor information between protocols. For example, in automatic BGP
+peering scenarios, protocols responsible for router discovery (such as RAdv)
+announce these entries, which are then exported to a dynamic BGP instance to
+automatically establish peering sessions. The same type is used for both IPv4
+and IPv6 neighbors. Configuration keyword is <cf/neighbor/.
+
+<itemize>
+ <item>(PK) IP address
+ <item>(PK) Interface index
+</itemize>
+
<sect1>Route next hops
<label id="route-next-hop">
<cf/NET_MPLS/ holds a single MPLS label and its handling is currently
not implemented.
+ <cf/NET_NEIGHBOR/ holds a single IP address and an interface index.
+ The IP address can be accessed by operator <cf/.ip/, while the access
+ to the interface index is not implemented.
+
<tag><label id="type-rd"><label id="type-vpnrd">rd</tag>
This is a route distinguisher according to <rfc id="4364">. There are
three kinds of RDs: <cf><m/asn/:<m/32bit int/</cf>, <cf><m/asn4/:<m/16bit int/</cf>
+/*
+ * Testing neighbor nets
+ * ----------------------
+ */
+
+function t_net_nbr()
+{
+ prefix p;
+
+ p = neighbor 10.1.2.3%10;
+ bt_assert(format(p) = "10.1.2.3%10");
+ bt_assert(p.type = NET_NEIGHBOR);
+ bt_assert(p.ip = 10.1.2.3);
+ bt_assert(p.len = 0);
+
+ p = neighbor 2001:db8:1:13::1%20;
+ bt_assert(format(p) = "2001:db8:1:13::1%20");
+ bt_assert(p.type = NET_NEIGHBOR);
+ bt_assert(p.ip = 2001:db8:1:13::1);
+ bt_assert(p.len = 0);
+}
+
+bt_test_suite(t_net_nbr, "Testing neighbor nets");
+
+
+
+
/*
* Testing defined() function
* --------------------------
[NET_IP6_SADR]= "ipv6-sadr",
[NET_MPLS] = "mpls",
[NET_ASPA] = "aspa",
+ [NET_NEIGHBOR]= "neighbor",
};
const u16 net_addr_length[] = {
[NET_IP6_SADR]= sizeof(net_addr_ip6_sadr),
[NET_MPLS] = sizeof(net_addr_mpls),
[NET_ASPA] = sizeof(net_addr_aspa),
+ [NET_NEIGHBOR]= sizeof(net_addr_nbr),
};
const u8 net_max_prefix_length[] = {
[NET_IP6_SADR]= IP6_MAX_PREFIX_LENGTH,
[NET_MPLS] = 0,
[NET_ASPA] = 0,
+ [NET_NEIGHBOR]= 0,
};
const u16 net_max_text_length[] = {
[NET_IP6_SADR]= 92, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 from ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
[NET_MPLS] = 7, /* "1048575" */
[NET_ASPA] = 10, /* "4294967295" */
+ [NET_NEIGHBOR]= 50, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967295" */
};
/* There should be no implicit padding in net_addr structures */
STATIC_ASSERT(sizeof(net_addr_ip6_sadr) == 40);
STATIC_ASSERT(sizeof(net_addr_mpls) == 8);
STATIC_ASSERT(sizeof(net_addr_aspa) == 8);
+STATIC_ASSERT(sizeof(net_addr_nbr) == 24);
/* Ensure that all net_addr structures have the same alignment */
STATIC_ASSERT(alignof(net_addr_ip4) == alignof(net_addr));
STATIC_ASSERT(alignof(net_addr_ip6_sadr) == alignof(net_addr));
STATIC_ASSERT(alignof(net_addr_mpls) == alignof(net_addr));
STATIC_ASSERT(alignof(net_addr_aspa) == alignof(net_addr));
+STATIC_ASSERT(alignof(net_addr_nbr) == alignof(net_addr));
int
return bsnprintf(buf, buflen, "%u", n->mpls.label);
case NET_ASPA:
return bsnprintf(buf, buflen, "%u", n->aspa.asn);
+ case NET_NEIGHBOR:
+ return bsnprintf(buf, buflen, "%I%%%u", n->nbr.addr, n->nbr.ifindex);
}
bug("unknown network type");
case NET_MPLS:
case NET_ASPA:
+ case NET_NEIGHBOR:
default:
return IPA_NONE;
}
return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b);
case NET_ASPA:
return net_compare_aspa((const net_addr_aspa *) a, (const net_addr_aspa *) b);
+ case NET_NEIGHBOR:
+ return net_compare_nbr((const net_addr_nbr *) a, (const net_addr_nbr *) b);
}
return 0;
}
case NET_IP6_SADR: return NET_HASH(n, ip6_sadr);
case NET_MPLS: return NET_HASH(n, mpls);
case NET_ASPA: return NET_HASH(n, aspa);
+ case NET_NEIGHBOR: return NET_HASH(n, nbr);
default: bug("invalid type");
}
}
case NET_IP6_SADR: return NET_VALIDATE(n, ip6_sadr);
case NET_MPLS: return NET_VALIDATE(n, mpls);
case NET_ASPA: return NET_VALIDATE(n, aspa);
+ case NET_NEIGHBOR: return NET_VALIDATE(n, nbr);
default: return 0;
}
}
case NET_MPLS:
case NET_ASPA:
+ case NET_NEIGHBOR:
return;
}
}
case NET_MPLS:
case NET_ASPA:
+ case NET_NEIGHBOR:
return IADDR_HOST | SCOPE_UNIVERSE;
}
case NET_MPLS:
case NET_ASPA:
+ case NET_NEIGHBOR:
default:
return 0;
}
#define NET_IP6_SADR 9
#define NET_MPLS 10
#define NET_ASPA 11
-#define NET_MAX 12
+#define NET_NEIGHBOR 12
+#define NET_MAX 13
#define NB_IP4 (1 << NET_IP4)
#define NB_IP6 (1 << NET_IP6)
#define NB_IP6_SADR (1 << NET_IP6_SADR)
#define NB_MPLS (1 << NET_MPLS)
#define NB_ASPA (1 << NET_ASPA)
+#define NB_NEIGHBOR (1 << NET_NEIGHBOR)
#define NB_IP (NB_IP4 | NB_IP6)
#define NB_VPN (NB_VPN4 | NB_VPN6)
u32 asn;
} net_addr_aspa;
+typedef struct net_addr_nbr {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ ip_addr addr; /* Peer IP address (IPv6 or IPv4) */
+ u32 ifindex; /* Interface index */
+} net_addr_nbr;
+
typedef struct net_addr_ip6_sadr {
u8 type;
u8 dst_pxlen;
net_addr_ip6_sadr ip6_sadr;
net_addr_mpls mpls;
net_addr_aspa aspa;
+ net_addr_nbr nbr;
} net_addr_union;
#define NET_ADDR_MPLS(label) \
((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
+#define NET_ADDR_NBR(addr, ifindex) \
+ ((net_addr_nbr) { NET_NEIGHBOR, 0, sizeof(net_addr_nbr), addr, ifindex })
+
static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
{ *(net_addr_ip4 *)a = NET_ADDR_IP4(prefix, pxlen); }
static inline void net_fill_aspa(net_addr *a, u32 asn)
{ *(net_addr_aspa *)a = NET_ADDR_ASPA(asn); }
+static inline void net_fill_nbr(net_addr *a, ip_addr addr, u32 ifindex)
+{ *(net_addr_nbr *)a = NET_ADDR_NBR(addr, ifindex); }
+
static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen)
{
if (ipa_is_ip4(prefix))
case NET_IP6_SADR:
return ipa_from_ip6(net6_prefix(a));
+ case NET_NEIGHBOR:
+ /* Arguably it is not exactly prefix */
+ return ((net_addr_nbr *) a)->addr;
+
case NET_MPLS:
case NET_ASPA:
default:
static inline int net_equal_aspa(const net_addr_aspa *a, const net_addr_aspa *b)
{ return !memcmp(a, b, sizeof(net_addr_aspa)); }
+static inline int net_equal_nbr(const net_addr_nbr *a, const net_addr_nbr *b)
+{ return !memcmp(a, b, sizeof(net_addr_nbr)); }
+
static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
{ return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
static inline int net_zero_aspa(const net_addr_aspa *a)
{ return !a->asn; }
+static inline int net_zero_nbr(const net_addr_nbr *a)
+{ return ipa_zero(a->addr) && !a->ifindex; }
+
static inline int net_compare_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
static inline int net_compare_aspa(const net_addr_aspa *a, const net_addr_aspa *b)
{ return uint_cmp(a->asn, b->asn); }
+static inline int net_compare_nbr(const net_addr_nbr *a, const net_addr_nbr *b)
+{ return ipa_compare(a->addr, b->addr) ?: uint_cmp(a->ifindex, b->ifindex); }
+
int net_compare(const net_addr *a, const net_addr *b);
static inline void net_copy_aspa(net_addr_aspa *dst, const net_addr_aspa *src)
{ memcpy(dst, src, sizeof(net_addr_aspa)); }
+static inline void net_copy_nbr(net_addr_nbr *dst, const net_addr_nbr *src)
+{ memcpy(dst, src, sizeof(net_addr_nbr)); }
+
static inline u32 px4_hash(ip4_addr prefix, u32 pxlen)
{ return ip4_hash(prefix) ^ (pxlen << 26); }
static inline u32 net_hash_aspa(const net_addr_aspa *n)
{ return u32_hash(n->asn); }
+static inline u32 net_hash_nbr(const net_addr_nbr *n)
+{ return ipa_hash(n->addr) ^ u32_hash(n->ifindex); }
+
u32 net_hash(const net_addr *a);
static inline int net_validate_aspa(const net_addr_aspa *n)
{ return n->asn > 0; }
+static inline int net_validate_nbr(const net_addr_nbr *n)
+{ return ipa_nonzero(n->addr); }
+
static inline int net_validate_ip6_sadr(const net_addr_ip6_sadr *n)
{ return net_validate_px6(n->dst_prefix, n->dst_pxlen) && net_validate_px6(n->src_prefix, n->src_pxlen); }
CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, TABLES, STATES, ROUTES, FILTERS)
-CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS, ASPA)
+CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS, ASPA, NEIGHBOR, NEIGHBORS)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
CF_KEYWORDS(PASSWORD, KEY, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, CHANNELS, INTERFACES)
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512, BLAKE2S128, BLAKE2S256, BLAKE2B256, BLAKE2B512)
/* For r_args_channel */
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC, ASPA)
-CF_ENUM(T_ENUM_NET_TYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR, MPLS, ASPA)
+CF_ENUM(T_ENUM_NET_TYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR, MPLS, ASPA, NEIGHBOR)
CF_ENUM(T_ENUM_RTS, RTS_, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL, RPKI, L3VPN,
AGGREGATED)
%type <sd> sym_args
%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type net_type_base tos password_algorithm
%type <ps> proto_patt proto_patt2
-%type <cc> channel_start proto_channel
+%type <cc> channel_start proto_channel nbrs_channel_start nbrs_channel
%type <cl> limit_spec
%type <tf> timeformat_spec
%type <tfp> timeformat_which
net_type:
net_type_base
| MPLS { $$ = NET_MPLS; }
+ | NEIGHBOR { $$ = NET_NEIGHBOR; }
;
proto_channel: channel_start channel_opt_list channel_end;
+nbrs_channel_start: NEIGHBORS
+{ $$ = this_channel = channel_config_get(NULL, "neighbors", NET_NEIGHBOR, this_proto); };
+
+nbrs_channel: nbrs_channel_start channel_opt_list channel_end;
+
+
rtable: CF_SYM_KNOWN { cf_assert_symbol($1, SYM_TABLE); $$ = $1->table; } ;
imexport:
| FLOW6 { $$ = "flow6"; }
| MPLS { $$ = "mpls"; }
| ASPA { $$ = "aspa"; }
+ | NEIGHBORS { $$ = "neighbors"; }
| PRI { $$ = "pri"; }
| SEC { $$ = "sec"; }
;
if (!net_val_match(net_type, proto->protocol->channel_mask))
cf_error("Unsupported channel type");
- if (proto->net_type && (net_type != proto->net_type) && (net_type != NET_MPLS))
+ if (proto->net_type && (net_type != proto->net_type) && (net_type != NET_MPLS) && (net_type != NET_NEIGHBOR))
cf_error("Different channel type");
tab = new_config->def_tables[net_type];
case NET_IP6_SADR: return FIB_FIND(f, a, ip6_sadr);
case NET_MPLS: return FIB_FIND(f, a, mpls);
case NET_ASPA: return FIB_FIND(f, a, aspa);
+ case NET_NEIGHBOR: return FIB_FIND(f, a, nbr);
default: bug("invalid type");
}
}
case NET_IP6_SADR: FIB_INSERT(f, a, e, ip6_sadr); return;
case NET_MPLS: FIB_INSERT(f, a, e, mpls); return;
case NET_ASPA: FIB_INSERT(f, a, e, aspa); return;
+ case NET_NEIGHBOR: FIB_INSERT(f, a, e, nbr); return;
default: bug("invalid type");
}
}
static_proto_start proto_name '{'
| static_proto proto_item ';'
| static_proto proto_channel ';' { this_proto->net_type = $2->net_type; }
+ | static_proto nbrs_channel ';' { this_proto->net_type = $2->net_type; }
| static_proto mpls_channel ';'
| static_proto CHECK LINK bool ';' { STATIC_CFG->check_link = $4; }
| static_proto IGP TABLE rtable ';' {