}
static int
-path_segment_contains(byte *p, int bs, u32 asn)
+bgp_encode_u32s(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
{
- int i;
- int len = p[1];
- p += 2;
+ uint len = a->u.ptr->length;
- for(i=0; i<len; i++)
- {
- u32 asn2 = (bs == 4) ? get_u32(p) : get_u16(p);
- if (asn2 == asn)
- return 1;
- p += bs;
- }
+ if (size < (4+len))
+ return -1;
- return 0;
+ uint hdr = bgp_put_attr_hdr(buf, EA_ID(a->id), a->flags, len);
+ put_u32s(buf + hdr, (u32 *) a->u.ptr->data, len / 4);
+
+ return hdr + len;
}
-/* Validates path attribute, removes AS_CONFED_* segments, and also returns path length */
static int
-validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, uint *ilength)
+bgp_put_attr(byte *buf, uint size, uint code, uint flags, byte *data, uint len)
{
- int res = 0;
- u8 *a, *dst;
- int len, plen;
+ if (size < (4+len))
+ return -1;
- dst = a = idata;
- len = *ilength;
+ uint hdr = bgp_put_attr_hdr(buf, code, flags, len);
+ memcpy(buf + hdr, data, len);
- while (len)
- {
- if (len < 2)
- return -1;
-
- plen = 2 + bs * a[1];
- if (len < plen)
- return -1;
-
- if (a[1] == 0)
- {
- log(L_WARN "%s: %s_PATH attribute contains empty segment, skipping it",
- p->p.name, as_path ? "AS" : "AS4");
- goto skip;
- }
-
- switch (a[0])
- {
- case AS_PATH_SET:
- res++;
- break;
-
- case AS_PATH_SEQUENCE:
- res += a[1];
- break;
-
- case AS_PATH_CONFED_SEQUENCE:
- case AS_PATH_CONFED_SET:
- if (as_path && path_segment_contains(a, bs, p->remote_as))
- {
- log(L_WARN "%s: AS_CONFED_* segment with peer ASN found, misconfigured confederation?", p->p.name);
- return -1;
- }
-
- log(L_WARN "%s: %s_PATH attribute contains AS_CONFED_* segment, skipping segment",
- p->p.name, as_path ? "AS" : "AS4");
- goto skip;
-
- default:
- return -1;
- }
-
- if (dst != a)
- memmove(dst, a, plen);
- dst += plen;
-
- skip:
- len -= plen;
- a += plen;
- }
+ return hdr + len;
+}
- *ilength = dst - idata;
- return res;
+static int
+bgp_encode_raw(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
+{
+ return bgp_put_attr(buf, size, EA_ID(a->id), a->flags, a->u.ptr->data, a->u.ptr->length);
}
-static inline int
-validate_as_path(struct bgp_proto *p, byte *a, int *len)
+
+/*
+ * Attribute hooks
+ */
+
+static void
+bgp_export_origin(struct bgp_export_state *s, eattr *a)
{
- return validate_path(p, 1, p->as4_session ? 4 : 2, a, len);
+ if (a->u.data > 2)
+ WITHDRAW(BAD_VALUE, "ORIGIN", a->u.data);
}
-static inline int
-validate_as4_path(struct bgp_proto *p, struct adata *path)
+static void
+bgp_decode_origin(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- return validate_path(p, 0, 4, path->data, &path->length);
+ if (len != 1)
+ WITHDRAW(BAD_LENGTH, "ORIGIN", len);
+
+ if (data[0] > 2)
+ WITHDRAW(BAD_VALUE, "ORIGIN", data[0]);
+
+ bgp_set_attr_u32(to, s->pool, BA_ORIGIN, flags, data[0]);
}
+static void
+bgp_format_origin(eattr *a, byte *buf, uint size UNUSED)
+{
+ static const char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" };
+
+ bsprintf(buf, (a->u.data <= 2) ? bgp_origin_names[a->u.data] : "?");
+}
+
+
static int
-bgp_check_next_hop(struct bgp_proto *p UNUSED, byte *a UNUSED6, int len UNUSED6)
+bgp_encode_as_path(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
{
-#ifdef IPV6
- return IGNORE;
-#else
- ip_addr addr;
+ byte *data = a->u.ptr->data;
+ uint len = a->u.ptr->length;
- memcpy(&addr, a, len);
- ipa_ntoh(addr);
- if (ipa_classify(addr) & IADDR_HOST)
+ if (!s->as4_session)
+ {
+ /* Prepare 16-bit AS_PATH (from 32-bit one) in a temporary buffer */
+ byte *src = data;
+ data = alloca(len);
+ len = as_path_32to16(data, src, len);
+ }
+
+ return bgp_put_attr(buf, size, BA_AS_PATH, a->flags, data, len);
+}
+
+static void
+bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
+{
+ struct bgp_proto *p = s->proto;
+ int as_length = s->as4_session ? 4 : 2;
+ int as_confed = p->cf->confederation && p->is_interior;
+ char err[128];
+
+ if (!as_path_valid(data, len, as_length, as_confed, err, sizeof(err)))
+ WITHDRAW("Malformed AS_PATH attribute - %s", err);
+
+ /* In some circumstances check for initial AS_CONFED_SEQUENCE; RFC 5065 5.0 */
+ if (p->is_interior && !p->is_internal &&
+ ((len < 2) || (data[0] != AS_PATH_CONFED_SEQUENCE)))
+ WITHDRAW("Malformed AS_PATH attribute - %s", "missing initial AS_CONFED_SEQUENCE");
+
+ if (!s->as4_session)
+ {
+ /* Prepare 32-bit AS_PATH (from 16-bit one) in a temporary buffer */
+ byte *src = data;
+ data = alloca(2*len);
+ len = as_path_16to32(data, src, len);
+ }
+
+ bgp_set_attr_data(to, s->pool, BA_AS_PATH, flags, data, len);
+}
+
+
+static int
+bgp_encode_next_hop(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
+{
+ /*
+ * The NEXT_HOP attribute is used only in traditional (IPv4) BGP. In MP-BGP,
+ * the next hop is encoded as a part of the MP_REACH_NLRI attribute, so we
+ * store it and encode it later by AFI-specific hooks.
+ */
+
+ if (s->channel->afi == BGP_AF_IPV4)
+ {
+ ASSERT(a->u.ptr->length == sizeof(ip_addr));
+
+ if (size < (3+4))
+ return -1;
+
+ bgp_put_attr_hdr3(buf, BA_NEXT_HOP, a->flags, 4);
+ put_ip4(buf+3, ipa_to_ip4( *(ip_addr *) a->u.ptr->data ));
+
+ return 3+4;
+ }
+ else
+ {
+ s->mp_next_hop = a;
return 0;
+ }
+}
+
+static void
+bgp_decode_next_hop(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUSED, byte *data, uint len, ea_list **to UNUSED)
+{
+ if (len != 4)
+ WITHDRAW(BAD_LENGTH, "NEXT_HOP", len);
+
+ /* Semantic checks are done later */
+ s->ip_next_hop_len = len;
+ s->ip_next_hop_data = data;
+}
+
+/* TODO: This function should use AF-specific hook */
+static void
+bgp_format_next_hop(eattr *a, byte *buf, uint size UNUSED)
+{
+ ip_addr *nh = (void *) a->u.ptr->data;
+ uint len = a->u.ptr->length;
+
+ ASSERT((len == 16) || (len == 32));
+
+ /* in IPv6, we may have two addresses in NEXT HOP */
+ if ((len == 16) || ipa_zero(nh[1]))
+ bsprintf(buf, "%I", nh[0]);
else
- return 8;
-#endif
+ bsprintf(buf, "%I %I", nh[0], nh[1]);
}
+
static void
-bgp_format_next_hop(eattr *a, byte *buf, int buflen UNUSED)
+bgp_decode_med(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- ip_addr *ipp = (ip_addr *) a->u.ptr->data;
-#ifdef IPV6
- /* in IPv6, we might have two addresses in NEXT HOP */
- if ((a->u.ptr->length == NEXT_HOP_LENGTH) && ipa_nonzero(ipp[1]))
- {
- bsprintf(buf, "%I %I", ipp[0], ipp[1]);
- return;
- }
-#endif
+ if (len != 4)
+ WITHDRAW(BAD_LENGTH, "MULTI_EXIT_DISC", len);
- bsprintf(buf, "%I", ipp[0]);
+ u32 val = get_u32(data);
+ bgp_set_attr_u32(to, s->pool, BA_MULTI_EXIT_DISC, flags, val);
}
-static int
-bgp_check_aggregator(struct bgp_proto *p, byte *a UNUSED, int len)
+
+static void
+bgp_export_local_pref(struct bgp_export_state *s, eattr *a)
{
- if (!s->proto->is_interior)
- int exp_len = p->as4_session ? 8 : 6;
-
- return (len == exp_len) ? 0 : WITHDRAW;
++ if (!s->proto->is_interior && !s->proto->cf->allow_local_pref)
+ UNSET(a);
}
static void
-bgp_format_aggregator(eattr *a, byte *buf, int buflen UNUSED)
+bgp_decode_local_pref(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
- if (!s->proto->is_interior)
- struct adata *ad = a->u.ptr;
- byte *data = ad->data;
- u32 as;
++ if (!s->proto->is_interior && !s->proto->cf->allow_local_pref)
+ DISCARD(BAD_EBGP, "LOCAL_PREF");
- as = get_u32(data);
- data += 4;
+ if (len != 4)
+ WITHDRAW(BAD_LENGTH, "LOCAL_PREF", len);
- bsprintf(buf, "%d.%d.%d.%d AS%u", data[0], data[1], data[2], data[3], as);
+ u32 val = get_u32(data);
+ bgp_set_attr_u32(to, s->pool, BA_LOCAL_PREF, flags, val);
}
-static int
-bgp_check_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
+
+static void
+bgp_decode_atomic_aggr(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data UNUSED, uint len, ea_list **to)
{
- return ((len % 4) == 0) ? 0 : WITHDRAW;
+ if (len != 0)
+ DISCARD(BAD_LENGTH, "ATOMIC_AGGR", len);
+
+ bgp_set_attr_data(to, s->pool, BA_ATOMIC_AGGR, flags, NULL, 0);
}
static int
static struct bgp_proto *
bgp_find_proto(sock *sk)
{
- struct proto_config *pc;
+ struct bgp_proto *p;
- WALK_LIST(pc, config->protos)
- if ((pc->protocol == &proto_bgp) && pc->proto)
- {
- struct bgp_proto *p = (struct bgp_proto *) pc->proto;
- if (ipa_equal(p->cf->remote_ip, sk->daddr) &&
- (!p->cf->iface || (p->cf->iface == sk->iface)))
- return p;
- }
+ WALK_LIST(p, proto_list)
+ if ((p->p.proto == &proto_bgp) &&
+ ipa_equal(p->cf->remote_ip, sk->daddr) &&
- (!ipa_is_link_local(sk->daddr) || (p->cf->iface == sk->iface)) &&
++ (!p->cf->iface || (p->cf->iface == sk->iface)) &&
+ (ipa_zero(p->cf->local_ip) || ipa_equal(p->cf->local_ip, sk->saddr)) &&
+ (p->cf->local_port == sk->sport))
+ return p;
return NULL;
}
/* EBGP direct by default, IBGP multihop by default */
- if (c->multihop < 0)
- c->multihop = internal ? 64 : 0;
-
- /* Different default for gw_mode */
- if (!c->gw_mode)
- c->gw_mode = c->multihop ? GW_RECURSIVE : GW_DIRECT;
-
- /* Different default based on rs_client */
- if (!c->missing_lladdr)
- c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
-
- /* Disable after error incompatible with restart limit action */
- if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
- c->c.in_limit->action = PLA_DISABLE;
+ if (cf->multihop < 0)
+ cf->multihop = internal ? 64 : 0;
- if (!c->local_as)
+ if (!cf->local_as)
cf_error("Local AS number must be set");
- if (ipa_zero(c->remote_ip))
+ if (ipa_zero(cf->remote_ip))
cf_error("Neighbor must be configured");
- if (!c->remote_as)
+ if (!cf->remote_as)
cf_error("Remote AS number must be set");
- // if (ipa_is_link_local(c->remote_ip) && !c->iface)
- // cf_error("Link-local neighbor address requires specified interface");
-
- if (!ipa_is_link_local(cf->remote_ip) != !cf->iface)
- cf_error("Link-local address and interface scope must be used together");
- if (ipa_is_link_local(c->remote_ip) && !c->iface)
++ if (ipa_is_link_local(cf->remote_ip) && !cf->iface)
+ cf_error("Link-local neighbor address requires specified interface");
- if (!(c->capabilities && c->enable_as4) && (c->remote_as > 0xFFFF))
+ if (!(cf->capabilities && cf->enable_as4) && (cf->remote_as > 0xFFFF))
cf_error("Neighbor AS number out of range (AS4 not available)");
- if (!internal && c->rr_client)
+ if (!internal && cf->rr_client)
cf_error("Only internal neighbor can be RR client");
- if (internal && c->rs_client)
+ if (internal && cf->rs_client)
cf_error("Only external neighbor can be RS client");
- if (c->multihop && (c->gw_mode == GW_DIRECT))
- cf_error("Multihop BGP cannot use direct gateway mode");
+ if (!cf->confederation && cf->confederation_member)
+ cf_error("Confederation ID must be set for member sessions");
- if (c->multihop && (ipa_is_link_local(c->remote_ip) ||
- ipa_is_link_local(c->source_addr)))
+ if (cf->multihop && (ipa_is_link_local(cf->local_ip) ||
+ ipa_is_link_local(cf->remote_ip)))
cf_error("Multihop BGP cannot be used with link-local addresses");
- if (c->multihop && c->iface)
++ if (cf->multihop && cf->iface)
+ cf_error("Multihop BGP cannot be bound to interface");
+
- if (c->multihop && c->check_link)
+ if (cf->multihop && cf->check_link)
cf_error("Multihop BGP cannot depend on link state");
- if (c->multihop && c->bfd && ipa_zero(c->source_addr))
- cf_error("Multihop BGP with BFD requires specified source address");
+ if (cf->multihop && cf->bfd && ipa_zero(cf->local_ip))
+ cf_error("Multihop BGP with BFD requires specified local address");
- if ((c->gw_mode == GW_RECURSIVE) && c->c.table->sorted)
- cf_error("BGP in recursive mode prohibits sorted table");
- if (c->deterministic_med && c->c.table->sorted)
- cf_error("BGP with deterministic MED prohibits sorted table");
+ struct bgp_channel_config *cc;
+ WALK_LIST(cc, CF->channels)
+ {
+ /* Disable after error incompatible with restart limit action */
+ if ((cc->c.in_limit.action == PLA_RESTART) && cf->disable_after_error)
+ cc->c.in_limit.action = PLA_DISABLE;
+
+ /* Different default based on rs_client */
+ if (!cc->missing_lladdr)
+ cc->missing_lladdr = cf->rs_client ? MLL_IGNORE : MLL_SELF;
+
+ /* Different default for gw_mode */
+ if (!cc->gw_mode)
+ cc->gw_mode = cf->multihop ? GW_RECURSIVE : GW_DIRECT;
+
+ /* Default based on proto config */
+ if (cc->gr_able == 0xff)
+ cc->gr_able = (cf->gr_mode == BGP_GR_ABLE);
+
+ /* Default values of IGP tables */
+ if ((cc->gw_mode == GW_RECURSIVE) && !cc->desc->no_igp)
+ {
+ if (!cc->igp_table_ip4 && (bgp_cc_is_ipv4(cc) || cc->ext_next_hop))
+ cc->igp_table_ip4 = bgp_default_igp_table(cf, cc, NET_IP4);
+
+ if (!cc->igp_table_ip6 && (bgp_cc_is_ipv6(cc) || cc->ext_next_hop))
+ cc->igp_table_ip6 = bgp_default_igp_table(cf, cc, NET_IP6);
- if (c->secondary && !c->c.table->sorted)
- cf_error("BGP with secondary option requires sorted table");
+ if (cc->igp_table_ip4 && bgp_cc_is_ipv6(cc) && !cc->ext_next_hop)
+ cf_error("Mismatched IGP table type");
+
+ if (cc->igp_table_ip6 && bgp_cc_is_ipv4(cc) && !cc->ext_next_hop)
+ cf_error("Mismatched IGP table type");
+ }
+
+ if (cf->multihop && (cc->gw_mode == GW_DIRECT))
+ cf_error("Multihop BGP cannot use direct gateway mode");
+
+ if ((cc->gw_mode == GW_RECURSIVE) && cc->c.table->sorted)
+ cf_error("BGP in recursive mode prohibits sorted table");
+
+ if (cf->deterministic_med && cc->c.table->sorted)
+ cf_error("BGP with deterministic MED prohibits sorted table");
+
+ if (cc->secondary && !cc->c.table->sorted)
+ cf_error("BGP with secondary option requires sorted table");
+ }
}
static int