peer = PAF_PEER(paf);
vec = &pkt->arr.entries[BGP_ATTR_VEC_NH];
- if (CHECK_FLAG(vec->flags, BPKT_ATTRVEC_FLAGS_UPDATED)) {
- uint8_t nhlen;
- afi_t nhafi;
- int route_map_sets_nh;
- nhlen = stream_getc_from(s, vec->offset);
- filter = &peer->filter[paf->afi][paf->safi];
-
- if (peer_cap_enhe(peer, paf->afi, paf->safi))
- nhafi = AFI_IP6;
- else
- nhafi = BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen);
-
- if (nhafi == AFI_IP) {
- struct in_addr v4nh, *mod_v4nh;
- int nh_modified = 0;
- size_t offset_nh = vec->offset + 1;
-
- route_map_sets_nh =
- (CHECK_FLAG(
- vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED)
- || CHECK_FLAG(
- vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
-
- switch (nhlen) {
- case BGP_ATTR_NHLEN_IPV4:
- break;
- case BGP_ATTR_NHLEN_VPNV4:
- offset_nh += 8;
- break;
- default:
- /* TODO: handle IPv6 nexthops */
- flog_warn(
- EC_BGP_INVALID_NEXTHOP_LENGTH,
- "%s: %s: invalid MP nexthop length (AFI IP): %u",
- __func__, peer->host, nhlen);
- stream_free(s);
- return NULL;
- }
- stream_get_from(&v4nh, s, offset_nh, IPV4_MAX_BYTELEN);
- mod_v4nh = &v4nh;
-
- /*
- * If route-map has set the nexthop, that is normally
- * used; if it is specified as peer-address, the peering
- * address is picked up. Otherwise, if NH is unavailable
- * from attribute, the peering addr is picked up; the
- * "NH unavailable" case also covers next-hop-self and
- * some other scenarios - see subgroup_announce_check().
- * In all other cases, use the nexthop carried in the
- * attribute unless it is EBGP non-multiaccess and there
- * is no next-hop-unchanged setting or the peer is EBGP
- * and the route-map that changed the next-hop value
- * was applied inbound rather than outbound. Updates to
- * an EBGP peer should only modify the next-hop if it
- * was set in an outbound route-map to that peer.
- * Note: It is assumed route-map cannot set the nexthop
- * to an invalid value.
- */
- if (route_map_sets_nh
- && ((peer->sort != BGP_PEER_EBGP)
- || ROUTE_MAP_OUT(filter))) {
- if (CHECK_FLAG(
- vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)) {
- mod_v4nh = &peer->nexthop.v4;
- nh_modified = 1;
- }
- } else if (v4nh.s_addr == INADDR_ANY) {
- mod_v4nh = &peer->nexthop.v4;
- nh_modified = 1;
- } else if (peer->sort == BGP_PEER_EBGP
- && (bgp_multiaccess_check_v4(v4nh, peer)
- == 0)
- && !CHECK_FLAG(
- vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED)
- && !peer_af_flag_check(
- peer, paf->afi, paf->safi,
- PEER_FLAG_NEXTHOP_UNCHANGED)) {
- /* NOTE: not handling case where NH has new AFI
- */
- mod_v4nh = &peer->nexthop.v4;
- nh_modified = 1;
- }
+ if (!CHECK_FLAG(vec->flags, BPKT_ATTRVEC_FLAGS_UPDATED))
+ return s;
- if (nh_modified) /* allow for VPN RD */
- stream_put_in_addr_at(s, offset_nh, mod_v4nh);
+ uint8_t nhlen;
+ afi_t nhafi;
+ int route_map_sets_nh;
- if (bgp_debug_update(peer, NULL, NULL, 0))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " %s send UPDATE w/ nexthop %s%s",
- PAF_SUBGRP(paf)->update_group->id,
- PAF_SUBGRP(paf)->id, peer->host,
- inet_ntoa(*mod_v4nh),
- (nhlen == 12 ? " and RD" : ""));
- } else if (nhafi == AFI_IP6) {
- struct in6_addr v6nhglobal, *mod_v6nhg;
- struct in6_addr v6nhlocal, *mod_v6nhl;
- int gnh_modified, lnh_modified;
- size_t offset_nhglobal = vec->offset + 1;
- size_t offset_nhlocal = vec->offset + 1;
-
- gnh_modified = lnh_modified = 0;
- mod_v6nhg = &v6nhglobal;
- mod_v6nhl = &v6nhlocal;
-
- route_map_sets_nh =
- (CHECK_FLAG(
- vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED)
- || CHECK_FLAG(
- vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
-
- /*
- * The logic here is rather similar to that for IPv4,
- * the
- * additional work being to handle 1 or 2 nexthops.
- * Also, 3rd
- * party nexthop is not propagated for EBGP right now.
- */
- switch (nhlen) {
- case BGP_ATTR_NHLEN_IPV6_GLOBAL:
- break;
- case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
- offset_nhlocal += IPV6_MAX_BYTELEN;
- break;
- case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
- offset_nhglobal += 8;
- break;
- case BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL:
- offset_nhglobal += 8;
- offset_nhlocal += 8 * 2 + IPV6_MAX_BYTELEN;
- break;
- default:
- /* TODO: handle IPv4 nexthops */
- flog_warn(
- EC_BGP_INVALID_NEXTHOP_LENGTH,
- "%s: %s: invalid MP nexthop length (AFI IP6): %u",
- __func__, peer->host, nhlen);
- stream_free(s);
- return NULL;
- }
+ nhlen = stream_getc_from(s, vec->offset);
+ filter = &peer->filter[paf->afi][paf->safi];
- stream_get_from(&v6nhglobal, s, offset_nhglobal,
- IPV6_MAX_BYTELEN);
+ if (peer_cap_enhe(peer, paf->afi, paf->safi))
+ nhafi = AFI_IP6;
+ else
+ nhafi = BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen);
+
+ if (nhafi == AFI_IP) {
+ struct in_addr v4nh, *mod_v4nh;
+ int nh_modified = 0;
+ size_t offset_nh = vec->offset + 1;
+
+ route_map_sets_nh =
+ (CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED)
+ || CHECK_FLAG(
+ vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
+
+ switch (nhlen) {
+ case BGP_ATTR_NHLEN_IPV4:
+ break;
+ case BGP_ATTR_NHLEN_VPNV4:
+ offset_nh += 8;
+ break;
+ default:
+ /* TODO: handle IPv6 nexthops */
+ flog_warn(
+ EC_BGP_INVALID_NEXTHOP_LENGTH,
+ "%s: %s: invalid MP nexthop length (AFI IP): %u",
+ __func__, peer->host, nhlen);
+ stream_free(s);
+ return NULL;
+ }
- /*
- * Updates to an EBGP peer should only modify the
- * next-hop if it was set in an outbound route-map
- * to that peer.
- */
- if (route_map_sets_nh
- && ((peer->sort != BGP_PEER_EBGP)
- || ROUTE_MAP_OUT(filter))) {
- if (CHECK_FLAG(
- vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)) {
- mod_v6nhg = &peer->nexthop.v6_global;
- gnh_modified = 1;
- }
- } else if (IN6_IS_ADDR_UNSPECIFIED(&v6nhglobal)) {
- mod_v6nhg = &peer->nexthop.v6_global;
- gnh_modified = 1;
- } else if (
- (peer->sort == BGP_PEER_EBGP)
- && (!bgp_multiaccess_check_v6(v6nhglobal, peer))
- && !CHECK_FLAG(vec->flags,
- BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED)
- && !peer_af_flag_check(
- peer, nhafi, paf->safi,
- PEER_FLAG_NEXTHOP_UNCHANGED)) {
- /* NOTE: not handling case where NH has new AFI
- */
- mod_v6nhg = &peer->nexthop.v6_global;
- gnh_modified = 1;
+ stream_get_from(&v4nh, s, offset_nh, IPV4_MAX_BYTELEN);
+ mod_v4nh = &v4nh;
+
+ /*
+ * If route-map has set the nexthop, that is normally
+ * used; if it is specified as peer-address, the peering
+ * address is picked up. Otherwise, if NH is unavailable
+ * from attribute, the peering addr is picked up; the
+ * "NH unavailable" case also covers next-hop-self and
+ * some other scenarios - see subgroup_announce_check().
+ * In all other cases, use the nexthop carried in the
+ * attribute unless it is EBGP non-multiaccess and there
+ * is no next-hop-unchanged setting or the peer is EBGP
+ * and the route-map that changed the next-hop value
+ * was applied inbound rather than outbound. Updates to
+ * an EBGP peer should only modify the next-hop if it
+ * was set in an outbound route-map to that peer.
+ * Note: It is assumed route-map cannot set the nexthop
+ * to an invalid value.
+ */
+ if (route_map_sets_nh
+ && ((peer->sort != BGP_PEER_EBGP)
+ || ROUTE_MAP_OUT(filter))) {
+ if (CHECK_FLAG(
+ vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)) {
+ mod_v4nh = &peer->nexthop.v4;
+ nh_modified = 1;
}
+ } else if (v4nh.s_addr == INADDR_ANY) {
+ mod_v4nh = &peer->nexthop.v4;
+ nh_modified = 1;
+ } else if (peer->sort == BGP_PEER_EBGP
+ && (bgp_multiaccess_check_v4(v4nh, peer) == 0)
+ && !CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED)
+ && !peer_af_flag_check(
+ peer, paf->afi, paf->safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)) {
+ /* NOTE: not handling case where NH has new AFI
+ */
+ mod_v4nh = &peer->nexthop.v4;
+ nh_modified = 1;
+ }
+ if (nh_modified) /* allow for VPN RD */
+ stream_put_in_addr_at(s, offset_nh, mod_v4nh);
- if (nhlen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
- || nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) {
- stream_get_from(&v6nhlocal, s, offset_nhlocal,
- IPV6_MAX_BYTELEN);
- if (IN6_IS_ADDR_UNSPECIFIED(&v6nhlocal)) {
- mod_v6nhl = &peer->nexthop.v6_local;
- lnh_modified = 1;
- }
- }
+ if (bgp_debug_update(peer, NULL, NULL, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64
+ " %s send UPDATE w/ nexthop %s%s",
+ PAF_SUBGRP(paf)->update_group->id,
+ PAF_SUBGRP(paf)->id, peer->host,
+ inet_ntoa(*mod_v4nh),
+ (nhlen == 12 ? " and RD" : ""));
+ } else if (nhafi == AFI_IP6) {
+ struct in6_addr v6nhglobal, *mod_v6nhg;
+ struct in6_addr v6nhlocal, *mod_v6nhl;
+ int gnh_modified, lnh_modified;
+ size_t offset_nhglobal = vec->offset + 1;
+ size_t offset_nhlocal = vec->offset + 1;
+
+ gnh_modified = lnh_modified = 0;
+ mod_v6nhg = &v6nhglobal;
+ mod_v6nhl = &v6nhlocal;
+
+ route_map_sets_nh =
+ (CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED)
+ || CHECK_FLAG(
+ vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
- if (gnh_modified)
- stream_put_in6_addr_at(s, offset_nhglobal,
- mod_v6nhg);
- if (lnh_modified)
- stream_put_in6_addr_at(s, offset_nhlocal,
- mod_v6nhl);
+ /*
+ * The logic here is rather similar to that for IPv4, the
+ * additional work being to handle 1 or 2 nexthops.
+ * Also, 3rd party nexthop is not propagated for EBGP
+ * right now.
+ */
+ switch (nhlen) {
+ case BGP_ATTR_NHLEN_IPV6_GLOBAL:
+ break;
+ case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
+ offset_nhlocal += IPV6_MAX_BYTELEN;
+ break;
+ case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
+ offset_nhglobal += 8;
+ break;
+ case BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL:
+ offset_nhglobal += 8;
+ offset_nhlocal += 8 * 2 + IPV6_MAX_BYTELEN;
+ break;
+ default:
+ /* TODO: handle IPv4 nexthops */
+ flog_warn(
+ EC_BGP_INVALID_NEXTHOP_LENGTH,
+ "%s: %s: invalid MP nexthop length (AFI IP6): %u",
+ __func__, peer->host, nhlen);
+ stream_free(s);
+ return NULL;
+ }
- if (bgp_debug_update(peer, NULL, NULL, 0)) {
- if (nhlen == 32 || nhlen == 48)
- zlog_debug(
- "u%" PRIu64 ":s%" PRIu64
- " %s send UPDATE w/ mp_nexthops %s, %s%s",
- PAF_SUBGRP(paf)
- ->update_group->id,
- PAF_SUBGRP(paf)->id, peer->host,
- inet_ntop(AF_INET6, mod_v6nhg,
- buf, BUFSIZ),
- inet_ntop(AF_INET6, mod_v6nhl,
- buf2, BUFSIZ),
- (nhlen == 48 ? " and RD" : ""));
- else
- zlog_debug(
- "u%" PRIu64 ":s%" PRIu64
- " %s send UPDATE w/ mp_nexthop %s%s",
- PAF_SUBGRP(paf)
- ->update_group->id,
- PAF_SUBGRP(paf)->id, peer->host,
- inet_ntop(AF_INET6, mod_v6nhg,
- buf, BUFSIZ),
- (nhlen == 24 ? " and RD" : ""));
- }
- } else if (paf->afi == AFI_L2VPN) {
- struct in_addr v4nh, *mod_v4nh;
- int nh_modified = 0;
+ stream_get_from(&v6nhglobal, s, offset_nhglobal,
+ IPV6_MAX_BYTELEN);
- stream_get_from(&v4nh, s, vec->offset + 1, 4);
- mod_v4nh = &v4nh;
+ /*
+ * Updates to an EBGP peer should only modify the
+ * next-hop if it was set in an outbound route-map
+ * to that peer.
+ */
+ if (route_map_sets_nh
+ && ((peer->sort != BGP_PEER_EBGP)
+ || ROUTE_MAP_OUT(filter))) {
+ if (CHECK_FLAG(
+ vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)) {
+ mod_v6nhg = &peer->nexthop.v6_global;
+ gnh_modified = 1;
+ }
+ } else if (IN6_IS_ADDR_UNSPECIFIED(&v6nhglobal)) {
+ mod_v6nhg = &peer->nexthop.v6_global;
+ gnh_modified = 1;
+ } else if ((peer->sort == BGP_PEER_EBGP)
+ && (!bgp_multiaccess_check_v6(v6nhglobal, peer))
+ && !CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED)
+ && !peer_af_flag_check(
+ peer, nhafi, paf->safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)) {
+ /* NOTE: not handling case where NH has new AFI
+ */
+ mod_v6nhg = &peer->nexthop.v6_global;
+ gnh_modified = 1;
+ }
- /* No route-map changes allowed for EVPN nexthops. */
- if (v4nh.s_addr == INADDR_ANY) {
- mod_v4nh = &peer->nexthop.v4;
- nh_modified = 1;
+ if (nhlen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
+ || nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) {
+ stream_get_from(&v6nhlocal, s, offset_nhlocal,
+ IPV6_MAX_BYTELEN);
+ if (IN6_IS_ADDR_UNSPECIFIED(&v6nhlocal)) {
+ mod_v6nhl = &peer->nexthop.v6_local;
+ lnh_modified = 1;
}
+ }
- if (nh_modified)
- stream_put_in_addr_at(s, vec->offset + 1,
- mod_v4nh);
+ if (gnh_modified)
+ stream_put_in6_addr_at(s, offset_nhglobal, mod_v6nhg);
+ if (lnh_modified)
+ stream_put_in6_addr_at(s, offset_nhlocal, mod_v6nhl);
- if (bgp_debug_update(peer, NULL, NULL, 0))
+ if (bgp_debug_update(peer, NULL, NULL, 0)) {
+ if (nhlen == 32 || nhlen == 48)
+ zlog_debug(
+ "u%" PRIu64 ":s%" PRIu64
+ " %s send UPDATE w/ mp_nexthops %s, %s%s",
+ PAF_SUBGRP(paf)->update_group->id,
+ PAF_SUBGRP(paf)->id, peer->host,
+ inet_ntop(AF_INET6, mod_v6nhg, buf,
+ BUFSIZ),
+ inet_ntop(AF_INET6, mod_v6nhl, buf2,
+ BUFSIZ),
+ (nhlen == 48 ? " and RD" : ""));
+ else
zlog_debug("u%" PRIu64 ":s%" PRIu64
- " %s send UPDATE w/ nexthop %s",
+ " %s send UPDATE w/ mp_nexthop %s%s",
PAF_SUBGRP(paf)->update_group->id,
PAF_SUBGRP(paf)->id, peer->host,
- inet_ntoa(*mod_v4nh));
+ inet_ntop(AF_INET6, mod_v6nhg, buf,
+ BUFSIZ),
+ (nhlen == 24 ? " and RD" : ""));
+ }
+ } else if (paf->afi == AFI_L2VPN) {
+ struct in_addr v4nh, *mod_v4nh;
+ int nh_modified = 0;
+
+ stream_get_from(&v4nh, s, vec->offset + 1, 4);
+ mod_v4nh = &v4nh;
+
+ /* No route-map changes allowed for EVPN nexthops. */
+ if (v4nh.s_addr == INADDR_ANY) {
+ mod_v4nh = &peer->nexthop.v4;
+ nh_modified = 1;
}
+
+ if (nh_modified)
+ stream_put_in_addr_at(s, vec->offset + 1, mod_v4nh);
+
+ if (bgp_debug_update(peer, NULL, NULL, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64
+ " %s send UPDATE w/ nexthop %s",
+ PAF_SUBGRP(paf)->update_group->id,
+ PAF_SUBGRP(paf)->id, peer->host,
+ inet_ntoa(*mod_v4nh));
}
return s;