package com.lumaserv.bgp; import com.lumaserv.bgp.protocol.AFI; import com.lumaserv.bgp.protocol.BGPPacket; import com.lumaserv.bgp.protocol.SAFI; import com.lumaserv.bgp.protocol.message.BGPNotification; import com.lumaserv.bgp.protocol.message.BGPOpen; import com.lumaserv.bgp.protocol.message.BGPUpdate; import lombok.Getter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; public class BGPSession implements Runnable { @Getter final BGPServer server; @Getter final BGPSessionConfiguration configuration; final String host; final int port; Socket socket; InputStream inputStream; OutputStream outputStream; @Getter boolean closed; boolean outgoing; @Getter BGPFsm fsm; Thread thread; boolean isAdvAS4Capability; @Getter boolean isAS4Capability; @Getter byte[] remoteIdentifier; public BGPSession(BGPServer server, Socket socket, BGPSessionConfiguration configuration) throws IOException { this.server = server; this.configuration = configuration; this.socket = socket; this.inputStream = socket.getInputStream(); this.outputStream = socket.getOutputStream(); this.fsm = new BGPFsm(this); this.host = null; this.port = -1; } public BGPSession(BGPServer server, BGPSessionConfiguration configuration, String host, int port) { this.server = server; this.configuration = configuration; this.fsm = new BGPFsm(this); this.host = host; this.port = port; } public InetAddress getInetAddress() { return socket.getInetAddress(); } public InetAddress getLocalAddress() { return socket.getLocalAddress(); } protected void finalize() throws Throwable { System.out.println("Finalize BGPSession"); super.finalize(); } public void cleanup() { fsm.cleanup(); } public void automaticStop() { fsm.getCurrentState().automaticStop(); } public void keepAlive() { try { outputStream.write(new BGPPacket().setType(BGPPacket.Type.KEEPALIVE).setMessage(new byte[0]).build()); } catch (IOException e) { e.printStackTrace(); } } private void handle(BGPPacket packet) { switch (packet.getType()) { case NOTIFICATION: BGPNotification notif = new BGPNotification(packet.getMessage()); if (notif.getMajorErrorCode() == BGPNotification.Error.MESSAGE_HEADER_ERROR.getCode() && notif.getMinorErrorCode() == BGPNotification.OpenMessageError.UNSUPPORTED_VERSION_NUMBER.getCode()) fsm.getCurrentState().notifMsgVerErr(notif); else fsm.getCurrentState().notifMsg(notif); break; case KEEPALIVE: fsm.getCurrentState().keepAliveMsg(); //keepAlive(); break; case OPEN: BGPOpen open = new BGPOpen(packet.getMessage()); if (server.hasCollided(this, open)) { BGPNotification notification = new BGPNotification() .setMajorErrorCode(BGPNotification.Error.CEASE.getCode()); try { sendNotification(notification); } catch(IOException ex) { } dropConnection(); cleanup(); } else { isAS4Capability = isAdvAS4Capability && open.getAS4Capability().isPresent(); remoteIdentifier = open.getIdentifier(); fsm.getCurrentState().openMsg(open); } break; case UPDATE: { BGPUpdate update = new BGPUpdate(this, packet.getMessage()); fsm.getCurrentState().updateMsg(update); configuration.getListener().onUpdate(this, update); break; } } } public void startIncoming() { outgoing = false; thread = new Thread(this); thread.start(); } public void startOutgoing() { outgoing = true; thread = new Thread(this); thread.start(); } void sendNotification(BGPNotification notification) throws IOException { outputStream.write(new BGPPacket().setType(BGPPacket.Type.NOTIFICATION).setMessage(notification.build()).build()); } void sendOpen(BGPOpen request) throws IOException { isAdvAS4Capability = request.getAS4Capability().isPresent(); outputStream.write(new BGPPacket().setType(BGPPacket.Type.OPEN).setMessage(request.build()).build()); System.out.println("Sent open"); } void sendOpen() throws IOException { BGPOpen.Capabilities caps = new BGPOpen.Capabilities(); caps.getCapabilities().add(new BGPOpen.MultiprotocolExtensionCapability(AFI.IPV4.getValue(), SAFI.UNICAST.getValue())); caps.getCapabilities().add(new BGPOpen.MultiprotocolExtensionCapability(AFI.IPV6.getValue(), SAFI.UNICAST.getValue())); caps.getCapabilities().add(new BGPOpen.AS4Capability(configuration.getLocalAs())); BGPOpen request = new BGPOpen() .setHoldTime(BGPFsm.CONFIG_HOLD_TIME) .setVersion(BGPFsm.CONFIG_VERSION) .setIdentifier(configuration.getLocalIdentifier()); if ((configuration.getLocalAs() & 0xFFFFFFFFL) < 0xFFFF) request.setAsn(configuration.getLocalAs()); request.getOptionalParameters().add(caps); sendOpen(request); } public void sendUpdate(BGPUpdate request) throws IOException { if (fsm.getCurrentState() != fsm.ESTABLISHED) { throw new RuntimeException("Can't send UPDATE in state " + fsm.getCurrentState()); } outputStream.write(new BGPPacket().setType(BGPPacket.Type.UPDATE).setMessage(request.build()).build()); fsm.setKeepAliveTimer(); } public void retryConnection() { if (!socket.isConnected()) { try { socket.close(); } catch (IOException ex) { ex.printStackTrace(); throw new RuntimeException(ex); } } } public void dropConnection() { closed = true; try { if (outputStream != null) outputStream.close(); if (inputStream != null) inputStream.close(); socket.close(); } catch (IOException ex) { ex.printStackTrace(); throw new RuntimeException(ex); } } public void run() { try { if (socket == null) { while (true) { try { System.out.println("Outgoing: " + host + ":" + port); socket = configuration.getSocketFactory().createSocket(host, port); break; } catch(IOException ex) { System.out.println("Exception: " + ex); if (closed) { throw new IOException("Closed", ex); } } } System.out.println("Outgoing accepted: " + socket.isConnected()); inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); fsm.getCurrentState().tcpCRAcked(); } else { System.out.println("Incoming"); fsm.getCurrentState().tcpConnectionConfirmed(); } while (!closed) { handle(BGPPacket.read(inputStream)); } System.out.println("Closed!"); } catch (IOException ex) { if (!closed) { fsm.getCurrentState().tcpConnectionFails(); } closed = true; configuration.getListener().onClose(this); } } }