X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=bgp%2Frib-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fprotocol%2Fbgp%2Frib%2Fimpl%2FStrictBGPPeerRegistry.java;h=332d4768efb330ef18b56bb37491e21703b6f295;hb=962b0b9dab61baea3019aaf6ddff0fd5fe84cd72;hp=895a9b4990226c7e92e925e535529517b40e7811;hpb=b5264eb26b84e7b443935429e46699e4f3e0d012;p=bgpcep.git diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistry.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistry.java index 895a9b4990..332d4768ef 100644 --- a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistry.java +++ b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistry.java @@ -8,29 +8,49 @@ package org.opendaylight.protocol.bgp.rib.impl; +import static java.util.Objects.requireNonNull; + import com.google.common.base.MoreObjects; +import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.net.InetAddresses; import com.google.common.primitives.UnsignedInts; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.protocol.bgp.parser.AsNumberUtil; import org.opendaylight.protocol.bgp.parser.BGPDocumentedException; import org.opendaylight.protocol.bgp.parser.BGPError; +import org.opendaylight.protocol.bgp.parser.impl.message.open.As4CapabilityHandler; import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry; import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences; -import org.opendaylight.protocol.bgp.rib.impl.spi.ReusableBGPPeer; +import org.opendaylight.protocol.bgp.rib.impl.spi.PeerRegistryListener; +import org.opendaylight.protocol.bgp.rib.impl.spi.PeerRegistrySessionListener; import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address; +import org.opendaylight.protocol.util.Ipv6Util; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.Open; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.open.message.BgpParameters; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.open.message.bgp.parameters.OptionalCapabilities; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.open.message.bgp.parameters.optional.capabilities.CParameters; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.open.message.bgp.parameters.optional.capabilities.CParametersBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.open.message.bgp.parameters.optional.capabilities.c.parameters.As4BytesCapability; +import org.opendaylight.yangtools.concepts.AbstractRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,57 +64,89 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry { private static final Logger LOG = LoggerFactory.getLogger(StrictBGPPeerRegistry.class); - // TODO remove backwards compatibility - public static final StrictBGPPeerRegistry GLOBAL = new StrictBGPPeerRegistry(); - @GuardedBy("this") - private final Map peers = Maps.newHashMap(); + private final Map peers = Maps.newHashMap(); @GuardedBy("this") private final Map sessionIds = Maps.newHashMap(); @GuardedBy("this") private final Map peerPreferences = Maps.newHashMap(); + @GuardedBy("this") + private final Set listeners = new HashSet<>(); + @GuardedBy("this") + private final Set sessionListeners = new HashSet<>(); + + public static BGPPeerRegistry instance() { + return new StrictBGPPeerRegistry(); + } @Override - public synchronized void addPeer(final IpAddress ip, final ReusableBGPPeer peer, final BGPSessionPreferences preferences) { - Preconditions.checkNotNull(ip); - Preconditions.checkArgument(!this.peers.containsKey(ip), "Peer for %s already present", ip); - this.peers.put(ip, Preconditions.checkNotNull(peer)); - this.peerPreferences.put(ip, Preconditions.checkNotNull(preferences)); + public synchronized void addPeer(final IpAddress oldIp, final BGPSessionListener peer, + final BGPSessionPreferences preferences) { + IpAddress fullIp = getFullIp(oldIp); + Preconditions.checkArgument(!this.peers.containsKey(fullIp), + "Peer for %s already present", fullIp); + this.peers.put(fullIp, requireNonNull(peer)); + requireNonNull(preferences.getMyAs()); + requireNonNull(preferences.getParams()); + requireNonNull(preferences.getBgpId()); + this.peerPreferences.put(fullIp, preferences); + for (final PeerRegistryListener peerRegistryListener : this.listeners) { + peerRegistryListener.onPeerAdded(fullIp, preferences); + } + } + + private IpAddress getFullIp(final IpAddress ip) { + requireNonNull(ip); + if (ip.getIpv6Address() != null) { + return new IpAddress(Ipv6Util.getFullForm(ip.getIpv6Address())); + } + return ip; } @Override - public synchronized void removePeer(final IpAddress ip) { - Preconditions.checkNotNull(ip); - this.peers.remove(ip); + public synchronized void removePeer(final IpAddress oldIp) { + IpAddress fullIp = getFullIp(oldIp); + this.peers.remove(fullIp); + for (final PeerRegistryListener peerRegistryListener : this.listeners) { + peerRegistryListener.onPeerRemoved(fullIp); + } } @Override - public synchronized void removePeerSession(final IpAddress ip) { - Preconditions.checkNotNull(ip); - this.sessionIds.remove(ip); + public synchronized void removePeerSession(final IpAddress oldIp) { + IpAddress fullIp = getFullIp(oldIp); + this.sessionIds.remove(fullIp); + for (final PeerRegistrySessionListener peerRegistrySessionListener : this.sessionListeners) { + peerRegistrySessionListener.onSessionRemoved(fullIp); + } } @Override - public boolean isPeerConfigured(final IpAddress ip) { - Preconditions.checkNotNull(ip); - return this.peers.containsKey(ip); + public boolean isPeerConfigured(final IpAddress oldIp) { + IpAddress fullIp = getFullIp(oldIp); + return this.peers.containsKey(fullIp); } private void checkPeerConfigured(final IpAddress ip) { - Preconditions.checkState(isPeerConfigured(ip), "BGP peer with ip: %s not configured, configured peers are: %s", ip, this.peers.keySet()); + Preconditions.checkState(isPeerConfigured(ip), + "BGP peer with ip: %s not configured, configured peers are: %s", + ip, this.peers.keySet()); } @Override - public synchronized BGPSessionListener getPeer(final IpAddress ip, - final Ipv4Address sourceId, final Ipv4Address remoteId, final AsNumber asNumber) - throws BGPDocumentedException { - Preconditions.checkNotNull(ip); - Preconditions.checkNotNull(sourceId); - Preconditions.checkNotNull(remoteId); + public synchronized BGPSessionListener getPeer(final IpAddress ip, final Ipv4Address sourceId, + final Ipv4Address remoteId, final Open openObj) throws BGPDocumentedException { + requireNonNull(ip); + requireNonNull(sourceId); + requireNonNull(remoteId); + final AsNumber remoteAsNumber = AsNumberUtil.advertizedAsNumber(openObj); + requireNonNull(remoteAsNumber); + + final BGPSessionPreferences prefs = getPeerPreferences(ip); checkPeerConfigured(ip); - final BGPSessionId currentConnection = new BGPSessionId(sourceId, remoteId, asNumber); + final BGPSessionId currentConnection = new BGPSessionId(sourceId, remoteId, remoteAsNumber); final BGPSessionListener p = this.peers.get(ip); final BGPSessionId previousConnection = this.sessionIds.get(ip); @@ -105,76 +157,121 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry { // Session reestablished with different ids if (!previousConnection.equals(currentConnection)) { - LOG.warn("BGP session with {} {} has to be dropped. Same session already present {}", ip, currentConnection, previousConnection); + LOG.warn("BGP session with {} {} has to be dropped. Same session already present {}", ip, + currentConnection, previousConnection); throw new BGPDocumentedException( - String.format("BGP session with %s %s has to be dropped. Same session already present %s", - ip, currentConnection, previousConnection), + String.format("BGP session with %s %s has to be dropped. Same session already present %s", + ip, currentConnection, previousConnection), BGPError.CEASE); // Session reestablished with lower source bgp id, dropping current - } else if (previousConnection.isHigherDirection(currentConnection)) { - LOG.warn("BGP session with {} {} has to be dropped. Opposite session already present", ip, currentConnection); + } else if (previousConnection.isHigherDirection(currentConnection) || + previousConnection.hasHigherAsNumber(currentConnection)) { + LOG.warn("BGP session with {} {} has to be dropped. Opposite session already present", + ip, currentConnection); throw new BGPDocumentedException( - String.format("BGP session with %s initiated %s has to be dropped. Opposite session already present", - ip, currentConnection), - BGPError.CEASE); + String.format("BGP session with %s initiated %s has to be dropped. " + + "Opposite session already present", ip, currentConnection), BGPError.CEASE); // Session reestablished with higher source bgp id, dropping previous - } else if (currentConnection.isHigherDirection(previousConnection)) { - LOG.warn("BGP session with {} {} released. Replaced by opposite session", ip, previousConnection); - this.peers.get(ip).releaseConnection(); - return this.peers.get(ip); - - } else if (previousConnection.hasHigherAsNumber(currentConnection)) { - LOG.warn("BGP session with {} {} has to be dropped. Opposite session already present", ip, currentConnection); - throw new BGPDocumentedException( - String.format("BGP session with %s initiated %s has to be dropped. Opposite session already present", - ip, currentConnection), - BGPError.CEASE); - } else if (currentConnection.hasHigherAsNumber(previousConnection)) { + } else if (currentConnection.isHigherDirection(previousConnection) || + currentConnection.hasHigherAsNumber(previousConnection)) { LOG.warn("BGP session with {} {} released. Replaced by opposite session", ip, previousConnection); this.peers.get(ip).releaseConnection(); return this.peers.get(ip); - // Session reestablished with same source bgp id, dropping current as duplicate + // Session reestablished with same source bgp id, dropping current as duplicate } else { - LOG.warn("BGP session with %s initiated from %s to %s has to be dropped. Same session already present", ip, sourceId, remoteId); + LOG.warn("BGP session with %s initiated from %s to %s has to be dropped. Same session already present", + ip, sourceId, remoteId); throw new BGPDocumentedException( - String.format("BGP session with %s initiated %s has to be dropped. Same session already present", - ip, currentConnection), - BGPError.CEASE); + String.format("BGP session with %s initiated %s has to be dropped. " + + "Same session already present", ip, currentConnection), BGPError.CEASE); } } + validateAs(remoteAsNumber, openObj, prefs); // Map session id to peer IP address this.sessionIds.put(ip, currentConnection); + for (final PeerRegistrySessionListener peerRegistrySessionListener : this.sessionListeners) { + peerRegistrySessionListener.onSessionCreated(ip); + } return p; } + private static void validateAs(final AsNumber remoteAs, final Open openObj, final BGPSessionPreferences localPref) + throws BGPDocumentedException { + if (!remoteAs.equals(localPref.getExpectedRemoteAs())) { + LOG.warn("Unexpected remote AS number. Expecting {}, got {}", localPref.getExpectedRemoteAs(), remoteAs); + throw new BGPDocumentedException("Peer AS number mismatch", BGPError.BAD_PEER_AS); + } + + // https://tools.ietf.org/html/rfc6286#section-2.2 + if (openObj.getBgpIdentifier() != null + && openObj.getBgpIdentifier().getValue().equals(localPref.getBgpId().getValue())) { + LOG.warn("Remote and local BGP Identifiers are the same: {}", openObj.getBgpIdentifier()); + throw new BGPDocumentedException("Remote and local BGP Identifiers are the same.", BGPError.BAD_BGP_ID); + } + final List prefs = openObj.getBgpParameters(); + if (prefs != null) { + if (getAs4BytesCapability(localPref.getParams()).isPresent() && !getAs4BytesCapability(prefs).isPresent()) { + throw new BGPDocumentedException("The peer must advertise AS4Bytes capability.", + BGPError.UNSUPPORTED_CAPABILITY, + serializeAs4BytesCapability(getAs4BytesCapability(localPref.getParams()).get())); + } + if (!prefs.containsAll(localPref.getParams())) { + LOG.info("BGP Open message session parameters differ, session still accepted."); + } + } else { + throw new BGPDocumentedException("Open message unacceptable. Check the configuration of BGP speaker.", + BGPError.UNSPECIFIC_OPEN_ERROR); + } + } + + private static Optional getAs4BytesCapability(final List prefs) { + for (final BgpParameters param : prefs) { + for (final OptionalCapabilities capa : param.getOptionalCapabilities()) { + final CParameters cParam = capa.getCParameters(); + if (cParam.getAs4BytesCapability() != null) { + return Optional.of(cParam.getAs4BytesCapability()); + } + } + } + return Optional.absent(); + } + + private static byte[] serializeAs4BytesCapability(final As4BytesCapability as4Capability) { + final ByteBuf buffer = Unpooled.buffer(1 /*CODE*/ + 1 /*LENGTH*/ + + Integer.SIZE / Byte.SIZE /*4 byte value*/); + final As4CapabilityHandler serializer = new As4CapabilityHandler(); + serializer.serializeCapability(new CParametersBuilder().setAs4BytesCapability(as4Capability).build(), buffer); + return buffer.array(); + } + @Override public BGPSessionPreferences getPeerPreferences(final IpAddress ip) { - Preconditions.checkNotNull(ip); + requireNonNull(ip); checkPeerConfigured(ip); return this.peerPreferences.get(ip); } /** - * Creates IpAddress from SocketAddress. Only InetSocketAddress is accepted with inner address: Inet4Address and Inet6Address. + * Creates IpAddress from SocketAddress. Only InetSocketAddress + * is accepted with inner address: Inet4Address and Inet6Address. * * @param socketAddress socket address to transform + * @return IpAddress equivalent to given socket address * @throws IllegalArgumentException if submitted socket address is not InetSocketAddress[ipv4 | ipv6] */ public static IpAddress getIpAddress(final SocketAddress socketAddress) { - Preconditions.checkNotNull(socketAddress); - Preconditions.checkArgument(socketAddress instanceof InetSocketAddress, "Expecting InetSocketAddress but was %s", socketAddress.getClass()); + requireNonNull(socketAddress); + Preconditions.checkArgument(socketAddress instanceof InetSocketAddress, + "Expecting InetSocketAddress but was %s", socketAddress.getClass()); final InetAddress inetAddress = ((InetSocketAddress) socketAddress).getAddress(); - if(inetAddress instanceof Inet4Address) { - return new IpAddress(new Ipv4Address(inetAddress.getHostAddress())); - } else if(inetAddress instanceof Inet6Address) { - return new IpAddress(new Ipv6Address(inetAddress.getHostAddress())); - } - - throw new IllegalArgumentException("Expecting " + Inet4Address.class + " or " + Inet6Address.class + " but was " + inetAddress.getClass()); + Preconditions.checkArgument(inetAddress instanceof Inet4Address + || inetAddress instanceof Inet6Address, "Expecting %s or %s but was %s", + Inet4Address.class, Inet6Address.class, inetAddress.getClass()); + return IetfInetUtil.INSTANCE.ipAddressFor(inetAddress); } @Override @@ -191,7 +288,8 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry { } /** - * Session identifier that contains (source Bgp Id) -> (destination Bgp Id) + * Session identifier that contains (source Bgp Id) -> (destination Bgp Id) AsNumber is the remoteAs coming from + * remote Open message */ private static final class BGPSessionId { @@ -199,9 +297,9 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry { private final AsNumber asNumber; BGPSessionId(final Ipv4Address from, final Ipv4Address to, final AsNumber asNumber) { - this.from = Preconditions.checkNotNull(from); - this.to = Preconditions.checkNotNull(to); - this.asNumber = Preconditions.checkNotNull(asNumber); + this.from = requireNonNull(from); + this.to = requireNonNull(to); + this.asNumber = requireNonNull(asNumber); } /** @@ -247,7 +345,7 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry { return this.asNumber.getValue() > other.asNumber.getValue(); } - private long toLong(final Ipv4Address from) { + private static long toLong(final Ipv4Address from) { final int i = InetAddresses.coerceToInteger(InetAddresses.forString(from.getValue())); return UnsignedInts.toLong(i); } @@ -260,4 +358,36 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry { .toString(); } } + + @Override + public synchronized AutoCloseable registerPeerRegisterListener(final PeerRegistryListener listener) { + this.listeners.add(listener); + for (final Entry entry : this.peerPreferences.entrySet()) { + listener.onPeerAdded(entry.getKey(), entry.getValue()); + } + return new AbstractRegistration() { + @Override + protected void removeRegistration() { + synchronized (StrictBGPPeerRegistry.this) { + StrictBGPPeerRegistry.this.listeners.remove(listener); + } + } + }; + } + + @Override + public synchronized AutoCloseable registerPeerSessionListener(final PeerRegistrySessionListener listener) { + this.sessionListeners.add(listener); + for (final IpAddress ipAddress : this.sessionIds.keySet()) { + listener.onSessionCreated(ipAddress); + } + return new AbstractRegistration() { + @Override + protected void removeRegistration() { + synchronized (StrictBGPPeerRegistry.this) { + StrictBGPPeerRegistry.this.sessionListeners.remove(listener); + } + } + }; + } }