Bug 5566: BGP listener TCP MD5 support is not working
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / controller / config / yang / bgp / rib / impl / BGPPeerAcceptorModule.java
1 /*
2  * Copyright (c) 2016 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 package org.opendaylight.controller.config.yang.bgp.rib.impl;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.Lists;
12 import io.netty.channel.Channel;
13 import io.netty.channel.ChannelConfig;
14 import io.netty.channel.ChannelFuture;
15 import io.netty.channel.epoll.Epoll;
16 import io.netty.channel.epoll.EpollChannelOption;
17 import io.netty.util.internal.PlatformDependent;
18 import java.net.InetAddress;
19 import java.net.InetSocketAddress;
20 import java.net.UnknownHostException;
21 import java.security.AccessControlException;
22 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
23 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
24 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
25 import org.opendaylight.protocol.bgp.rib.impl.spi.PeerRegistryListener;
26 import org.opendaylight.protocol.concepts.KeyMapping;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
29
30 /**
31  * BGP peer acceptor that handles incoming bgp connections.
32  */
33 public class BGPPeerAcceptorModule extends org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractBGPPeerAcceptorModule {
34
35     private static final int PRIVILEGED_PORTS = 1024;
36
37     public BGPPeerAcceptorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
38         super(identifier, dependencyResolver);
39     }
40
41     public BGPPeerAcceptorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.bgp.rib.impl.BGPPeerAcceptorModule oldModule, final java.lang.AutoCloseable oldInstance) {
42         super(identifier, dependencyResolver, oldModule, oldInstance);
43     }
44
45     @Override
46     public void customValidation() {
47         // check if unix root user
48         if (!PlatformDependent.isWindows() && !PlatformDependent.isRoot() && getBindingPort().getValue() < PRIVILEGED_PORTS) {
49             throw new AccessControlException("Unable to bind port " + getBindingPort().getValue() + " while running as non-root user.");
50         }
51         // Try to parse address
52         try {
53             getAddress();
54         } catch (final IllegalArgumentException e) {
55             throw new JmxAttributeValidationException("Unable to resolve configured address", e, Lists.newArrayList(bindingAddressJmxAttribute, bindingPortJmxAttribute));
56         }
57     }
58
59     private AutoCloseable listenerRegistration;
60
61     @Override
62     public java.lang.AutoCloseable createInstance() {
63         final BGPPeerRegistry peerRegistry = getAcceptingPeerRegistryDependency();
64         final ChannelFuture futureChannel = getAcceptingBgpDispatcherDependency().createServer(peerRegistry, getAddress());
65
66         // Validate future success
67         futureChannel.addListener(future -> {
68             Preconditions.checkArgument(future.isSuccess(), "Unable to start bgp server on %s", getAddress(), future.cause());
69             final Channel channel = futureChannel.channel();
70             if (Epoll.isAvailable()) {
71                 BGPPeerAcceptorModule.this.listenerRegistration = peerRegistry.registerPeerRegisterListener(new PeerRegistryListenerImpl(channel.config()));
72             }
73         });
74
75         return () -> {
76             // This closes the acceptor and no new bgp connections will be accepted
77             // Connections already established will be preserved
78             futureChannel.cancel(true);
79             futureChannel.channel().close();
80             if (BGPPeerAcceptorModule.this.listenerRegistration != null) {
81                 BGPPeerAcceptorModule.this.listenerRegistration.close();
82             }
83         };
84     }
85
86     private InetSocketAddress getAddress() {
87         final InetAddress inetAddr;
88         try {
89             inetAddr = InetAddress.getByName(getBindingAddress()
90                     .getIpv4Address() != null ? getBindingAddress()
91                             .getIpv4Address().getValue() : getBindingAddress()
92                             .getIpv6Address().getValue());
93         } catch (final UnknownHostException e) {
94             throw new IllegalArgumentException("Illegal binding address " + getBindingAddress(), e);
95         }
96         return new InetSocketAddress(inetAddr, getBindingPort().getValue());
97     }
98
99     private static final class PeerRegistryListenerImpl implements PeerRegistryListener {
100
101         private final ChannelConfig channelConfig;
102
103         private final KeyMapping keys;
104
105         PeerRegistryListenerImpl(final ChannelConfig channelConfig) {
106             this.channelConfig = channelConfig;
107             this.keys = KeyMapping.getKeyMapping();
108         }
109
110         @Override
111         public void onPeerAdded(final IpAddress ip, final BGPSessionPreferences prefs) {
112             if (prefs.getMd5Password().isPresent()) {
113                 this.keys.put(IetfInetUtil.INSTANCE.inetAddressFor(ip), prefs.getMd5Password().get());
114                 this.channelConfig.setOption(EpollChannelOption.TCP_MD5SIG, this.keys);
115             }
116         }
117
118         @Override
119         public void onPeerRemoved(final IpAddress ip) {
120             if (this.keys.remove(IetfInetUtil.INSTANCE.inetAddressFor(ip)) != null) {
121                 this.channelConfig.setOption(EpollChannelOption.TCP_MD5SIG, this.keys);
122             }
123         }
124
125     }
126
127 }