Improve compatibility with Java 8 javadoc
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / StrictBGPPeerRegistry.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.protocol.bgp.rib.impl;
10
11 import com.google.common.base.MoreObjects;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.Maps;
14 import com.google.common.net.InetAddresses;
15 import com.google.common.primitives.UnsignedInts;
16 import java.net.Inet4Address;
17 import java.net.Inet6Address;
18 import java.net.InetAddress;
19 import java.net.InetSocketAddress;
20 import java.net.SocketAddress;
21 import java.util.Map;
22 import javax.annotation.concurrent.GuardedBy;
23 import javax.annotation.concurrent.ThreadSafe;
24 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
25 import org.opendaylight.protocol.bgp.parser.BGPError;
26 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
27 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
28 import org.opendaylight.protocol.bgp.rib.impl.spi.ReusableBGPPeer;
29 import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * BGP peer registry that allows only 1 session per BGP peer.
39  * If second session with peer is established, one of the sessions will be dropped.
40  * The session with lower source BGP id will be dropped.
41  */
42 @ThreadSafe
43 public final class StrictBGPPeerRegistry implements BGPPeerRegistry {
44
45     private static final Logger LOG = LoggerFactory.getLogger(StrictBGPPeerRegistry.class);
46
47     // TODO remove backwards compatibility
48     public static final StrictBGPPeerRegistry GLOBAL = new StrictBGPPeerRegistry();
49
50     @GuardedBy("this")
51     private final Map<IpAddress, ReusableBGPPeer> peers = Maps.newHashMap();
52     @GuardedBy("this")
53     private final Map<IpAddress, BGPSessionId> sessionIds = Maps.newHashMap();
54     @GuardedBy("this")
55     private final Map<IpAddress, BGPSessionPreferences> peerPreferences = Maps.newHashMap();
56
57     @Override
58     public synchronized void addPeer(final IpAddress ip, final ReusableBGPPeer peer, final BGPSessionPreferences preferences) {
59         Preconditions.checkNotNull(ip);
60         Preconditions.checkArgument(!this.peers.containsKey(ip), "Peer for %s already present", ip);
61         this.peers.put(ip, Preconditions.checkNotNull(peer));
62         this.peerPreferences.put(ip, Preconditions.checkNotNull(preferences));
63     }
64
65     @Override
66     public synchronized void removePeer(final IpAddress ip) {
67         Preconditions.checkNotNull(ip);
68         this.peers.remove(ip);
69     }
70
71     @Override
72     public synchronized void removePeerSession(final IpAddress ip) {
73         Preconditions.checkNotNull(ip);
74         this.sessionIds.remove(ip);
75     }
76
77     @Override
78     public boolean isPeerConfigured(final IpAddress ip) {
79         Preconditions.checkNotNull(ip);
80         return this.peers.containsKey(ip);
81     }
82
83     private void checkPeerConfigured(final IpAddress ip) {
84         Preconditions.checkState(isPeerConfigured(ip), "BGP peer with ip: %s not configured, configured peers are: %s", ip, this.peers.keySet());
85     }
86
87     @Override
88     public synchronized BGPSessionListener getPeer(final IpAddress ip,
89         final Ipv4Address sourceId, final Ipv4Address remoteId, final AsNumber asNumber)
90             throws BGPDocumentedException {
91         Preconditions.checkNotNull(ip);
92         Preconditions.checkNotNull(sourceId);
93         Preconditions.checkNotNull(remoteId);
94
95         checkPeerConfigured(ip);
96
97         final BGPSessionId currentConnection = new BGPSessionId(sourceId, remoteId, asNumber);
98         final BGPSessionListener p = this.peers.get(ip);
99
100         final BGPSessionId previousConnection = this.sessionIds.get(ip);
101
102         if (previousConnection != null) {
103
104             LOG.warn("Duplicate BGP session established with {}", ip);
105
106             // Session reestablished with different ids
107             if (!previousConnection.equals(currentConnection)) {
108                 LOG.warn("BGP session with {} {} has to be dropped. Same session already present {}", ip, currentConnection, previousConnection);
109                 throw new BGPDocumentedException(
110                     String.format("BGP session with %s %s has to be dropped. Same session already present %s",
111                         ip, currentConnection, previousConnection),
112                         BGPError.CEASE);
113
114                 // Session reestablished with lower source bgp id, dropping current
115             } else if (previousConnection.isHigherDirection(currentConnection)) {
116                 LOG.warn("BGP session with {} {} has to be dropped. Opposite session already present", ip, currentConnection);
117                 throw new BGPDocumentedException(
118                     String.format("BGP session with %s initiated %s has to be dropped. Opposite session already present",
119                         ip, currentConnection),
120                         BGPError.CEASE);
121
122                 // Session reestablished with higher source bgp id, dropping previous
123             } else if (currentConnection.isHigherDirection(previousConnection)) {
124                 LOG.warn("BGP session with {} {} released. Replaced by opposite session", ip, previousConnection);
125                 this.peers.get(ip).releaseConnection();
126                 return this.peers.get(ip);
127
128             } else if (previousConnection.hasHigherAsNumber(currentConnection)) {
129                 LOG.warn("BGP session with {} {} has to be dropped. Opposite session already present", ip, currentConnection);
130                 throw new BGPDocumentedException(
131                     String.format("BGP session with %s initiated %s has to be dropped. Opposite session already present",
132                         ip, currentConnection),
133                         BGPError.CEASE);
134             } else if (currentConnection.hasHigherAsNumber(previousConnection)) {
135                 LOG.warn("BGP session with {} {} released. Replaced by opposite session", ip, previousConnection);
136                 this.peers.get(ip).releaseConnection();
137                 return this.peers.get(ip);
138             // Session reestablished with same source bgp id, dropping current as duplicate
139             } else {
140                 LOG.warn("BGP session with %s initiated from %s to %s has to be dropped. Same session already present", ip, sourceId, remoteId);
141                 throw new BGPDocumentedException(
142                     String.format("BGP session with %s initiated %s has to be dropped. Same session already present",
143                         ip, currentConnection),
144                         BGPError.CEASE);
145             }
146         }
147
148         // Map session id to peer IP address
149         this.sessionIds.put(ip, currentConnection);
150         return p;
151     }
152
153     @Override
154     public BGPSessionPreferences getPeerPreferences(final IpAddress ip) {
155         Preconditions.checkNotNull(ip);
156         checkPeerConfigured(ip);
157         return this.peerPreferences.get(ip);
158     }
159
160     /**
161      * Creates IpAddress from SocketAddress. Only InetSocketAddress is accepted with inner address: Inet4Address and Inet6Address.
162      *
163      * @param socketAddress socket address to transform
164      * @throws IllegalArgumentException if submitted socket address is not InetSocketAddress[ipv4 | ipv6]
165      */
166     public static IpAddress getIpAddress(final SocketAddress socketAddress) {
167         Preconditions.checkNotNull(socketAddress);
168         Preconditions.checkArgument(socketAddress instanceof InetSocketAddress, "Expecting InetSocketAddress but was %s", socketAddress.getClass());
169         final InetAddress inetAddress = ((InetSocketAddress) socketAddress).getAddress();
170
171         if(inetAddress instanceof Inet4Address) {
172             return new IpAddress(new Ipv4Address(inetAddress.getHostAddress()));
173         } else if(inetAddress instanceof Inet6Address) {
174             return new IpAddress(new Ipv6Address(inetAddress.getHostAddress()));
175         }
176
177         throw new IllegalArgumentException("Expecting " + Inet4Address.class + " or " + Inet6Address.class + " but was " + inetAddress.getClass());
178     }
179
180     @Override
181     public synchronized void close() {
182         this.peers.clear();
183         this.sessionIds.clear();
184     }
185
186     @Override
187     public String toString() {
188         return MoreObjects.toStringHelper(this)
189             .add("peers", this.peers.keySet())
190             .toString();
191     }
192
193     /**
194      * Session identifier that contains (source Bgp Id) -> (destination Bgp Id)
195      */
196     private static final class BGPSessionId {
197
198         private final Ipv4Address from, to;
199         private final AsNumber asNumber;
200
201         BGPSessionId(final Ipv4Address from, final Ipv4Address to, final AsNumber asNumber) {
202             this.from = Preconditions.checkNotNull(from);
203             this.to = Preconditions.checkNotNull(to);
204             this.asNumber = Preconditions.checkNotNull(asNumber);
205         }
206
207         /**
208          * Equals does not take direction of connection into account id1 -> id2 and id2 -> id1 are equal
209          */
210         @Override
211         public boolean equals(final Object o) {
212             if (this == o) {
213                 return true;
214             }
215             if (o == null || getClass() != o.getClass()) {
216                 return false;
217             }
218
219             final BGPSessionId bGPSessionId = (BGPSessionId) o;
220
221             if (!this.from.equals(bGPSessionId.from) && !this.from.equals(bGPSessionId.to)) {
222                 return false;
223             }
224             if (!this.to.equals(bGPSessionId.to) && !this.to.equals(bGPSessionId.from)) {
225                 return false;
226             }
227
228             return true;
229         }
230
231         @Override
232         public int hashCode() {
233             final int prime = 31;
234             int result = this.from.hashCode() + this.to.hashCode();
235             result = prime * result;
236             return result;
237         }
238
239         /**
240          * Check if this connection is equal to other and if it contains higher source bgp id
241          */
242         boolean isHigherDirection(final BGPSessionId other) {
243             return toLong(this.from) > toLong(other.from);
244         }
245
246         boolean hasHigherAsNumber(final BGPSessionId other) {
247             return this.asNumber.getValue() > other.asNumber.getValue();
248         }
249
250         private long toLong(final Ipv4Address from) {
251             final int i = InetAddresses.coerceToInteger(InetAddresses.forString(from.getValue()));
252             return UnsignedInts.toLong(i);
253         }
254
255         @Override
256         public String toString() {
257             return MoreObjects.toStringHelper(this)
258                 .add("from", this.from)
259                 .add("to", this.to)
260                 .toString();
261         }
262     }
263 }