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
index 327f93d0cd2529b3bb0d29ff842ea05386ee85bf..8b207612115c2c43585b3da51dba66f1c8ca07e6 100644 (file)
@@ -1,29 +1,53 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
 package org.opendaylight.controller.config.yang.bgp.rib.impl;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelConfig;
 import io.netty.channel.ChannelFuture;
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.GenericFutureListener;
+import io.netty.channel.epoll.Epoll;
+import io.netty.channel.epoll.EpollChannelOption;
+import io.netty.util.internal.PlatformDependent;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
+import java.security.AccessControlException;
 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
-import org.opendaylight.protocol.bgp.rib.impl.server.BGPServerSessionValidator;
+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.PeerRegistryListener;
+import org.opendaylight.protocol.concepts.KeyMapping;
+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;
 
 /**
-* BGP peer acceptor that handles incomming bgp connections.
-*/
+ * BGP peer acceptor that handles incoming bgp connections.
+ */
 public class BGPPeerAcceptorModule extends org.opendaylight.controller.config.yang.bgp.rib.impl.AbstractBGPPeerAcceptorModule {
-    public BGPPeerAcceptorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+
+    private static final int PRIVILEGED_PORTS = 1024;
+
+    public BGPPeerAcceptorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
     }
 
-    public BGPPeerAcceptorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.bgp.rib.impl.BGPPeerAcceptorModule oldModule, java.lang.AutoCloseable oldInstance) {
+    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) {
         super(identifier, dependencyResolver, oldModule, oldInstance);
     }
 
     @Override
     public void customValidation() {
+        // check if unix root user
+        if (!PlatformDependent.isWindows() && !PlatformDependent.isRoot() && getBindingPort().getValue() < PRIVILEGED_PORTS) {
+            throw new AccessControlException("Unable to bind port " + getBindingPort().getValue() + " while running as non-root user.");
+        }
         // Try to parse address
         try {
             getAddress();
@@ -32,27 +56,29 @@ public class BGPPeerAcceptorModule extends org.opendaylight.controller.config.ya
         }
     }
 
+    private AutoCloseable listenerRegistration;
+
     @Override
     public java.lang.AutoCloseable createInstance() {
-        final ChannelFuture future = getBgpDispatcherDependency().createServer(getPeerRegistryDependency(), getAddress(), new BGPServerSessionValidator());
+        final BGPPeerRegistry peerRegistry = getAcceptingPeerRegistryDependency();
+        final ChannelFuture futureChannel = getAcceptingBgpDispatcherDependency().createServer(peerRegistry, getAddress());
 
         // Validate future success
-        future.addListener(new GenericFutureListener<Future<Void>>() {
-            @Override
-            public void operationComplete(Future<Void> future) throws Exception {
-                if(future.isSuccess() == false) {
-                    throw new IllegalStateException(String.format("Unable to start bgp server on %s", getAddress()), future.cause());
-                }
+        futureChannel.addListener(future -> {
+            Preconditions.checkArgument(future.isSuccess(), "Unable to start bgp server on %s", getAddress(), future.cause());
+            final Channel channel = futureChannel.channel();
+            if (Epoll.isAvailable()) {
+                BGPPeerAcceptorModule.this.listenerRegistration = peerRegistry.registerPeerRegisterListener(new PeerRegistryListenerImpl(channel.config()));
             }
         });
 
-        return new AutoCloseable() {
-            @Override
-            public void close() throws Exception {
-                // This closes the acceptor and no new bgp connections will be accepted
-                // Connections already established will be preserved
-                future.cancel(true);
-                future.channel().close();
+        return () -> {
+            // This closes the acceptor and no new bgp connections will be accepted
+            // Connections already established will be preserved
+            futureChannel.cancel(true);
+            futureChannel.channel().close();
+            if (BGPPeerAcceptorModule.this.listenerRegistration != null) {
+                BGPPeerAcceptorModule.this.listenerRegistration.close();
             }
         };
     }
@@ -62,12 +88,40 @@ public class BGPPeerAcceptorModule extends org.opendaylight.controller.config.ya
         try {
             inetAddr = InetAddress.getByName(getBindingAddress()
                     .getIpv4Address() != null ? getBindingAddress()
-                    .getIpv4Address().getValue() : getBindingAddress()
-                    .getIpv6Address().getValue());
+                            .getIpv4Address().getValue() : getBindingAddress()
+                            .getIpv6Address().getValue());
         } catch (final UnknownHostException e) {
             throw new IllegalArgumentException("Illegal binding address " + getBindingAddress(), e);
         }
         return new InetSocketAddress(inetAddr, getBindingPort().getValue());
     }
 
+    private static final class PeerRegistryListenerImpl implements PeerRegistryListener {
+
+        private final ChannelConfig channelConfig;
+
+        private final KeyMapping keys;
+
+        PeerRegistryListenerImpl(final ChannelConfig channelConfig) {
+            this.channelConfig = channelConfig;
+            this.keys = KeyMapping.getKeyMapping();
+        }
+
+        @Override
+        public void onPeerAdded(final IpAddress ip, final BGPSessionPreferences prefs) {
+            if (prefs.getMd5Password().isPresent()) {
+                this.keys.put(IetfInetUtil.INSTANCE.inetAddressFor(ip), prefs.getMd5Password().get());
+                this.channelConfig.setOption(EpollChannelOption.TCP_MD5SIG, this.keys);
+            }
+        }
+
+        @Override
+        public void onPeerRemoved(final IpAddress ip) {
+            if (this.keys.remove(IetfInetUtil.INSTANCE.inetAddressFor(ip)) != null) {
+                this.channelConfig.setOption(EpollChannelOption.TCP_MD5SIG, this.keys);
+            }
+        }
+
+    }
+
 }