All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.snice.networking.examples.gtp.Sgi Maven / Gradle / Ivy

There is a newer version: 0.0.13
Show newest version
package io.snice.networking.examples.gtp;

import io.snice.codecs.codec.gtp.Teid;
import io.snice.codecs.codec.gtp.gtpc.v1.Gtp1Message;
import io.snice.codecs.codec.gtp.gtpc.v1.Gtp1MessageType;
import io.snice.codecs.codec.gtp.gtpc.v1.impl.ImmutableGtp1Message;
import io.snice.codecs.codec.internet.IpMessage;
import io.snice.codecs.codec.transport.UdpMessage;
import io.snice.networking.app.NetworkBootstrap;
import io.snice.networking.app.buffer.BufferApplication;
import io.snice.networking.app.buffer.BufferEnvironment;
import io.snice.networking.bundles.buffer.BufferConnection;
import io.snice.networking.bundles.buffer.BufferEvent;
import io.snice.networking.bundles.buffer.BufferReadEvent;
import io.snice.networking.common.ConnectionEndpointId;
import io.snice.networking.common.Transport;
import io.snice.networking.core.NetworkInterface;
import io.snice.networking.gtp.GtpTunnel;
import io.snice.networking.gtp.PdnSessionContext;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;

import static io.snice.networking.app.NetworkBootstrap.ACCEPT_ALL;

public class Sgi extends BufferApplication {

    private BufferEnvironment environment;
    // private final ConcurrentMap nats = new ConcurrentHashMap<>();

    // Make sure we don't need to re-hash...
    private final ConcurrentMap sessions = new ConcurrentHashMap(10000, 0.75f);
    private final ConcurrentMap nats = new ConcurrentHashMap(10000, 0.75f);

    @Override
    public void initialize(final NetworkBootstrap bootstrap) {
        bootstrap.onConnection(ACCEPT_ALL).accept(b -> {
            b.match(buffer -> true).map(BufferEvent::toReadEvent).consume(this::processBuffer);
        });
    }

    @Override
    public void run(final SgiConfig configuration, final BufferEnvironment environment) {
        this.environment = environment;
    }

    /**
     * Process a PDU packet that arrived over the given {@link GtpTunnel}.
     * 

* What we should do, but currently are not, is to allocate a new socket for this tunnel so all * traffic associated with it is correctly NAT:ed and tunneled back to it. However, currently this * is a simple SGi example and as such, if two different tunnels (and therefore different "NAT" settings) * happen to send traffic to the exact same destination (remote ip:port pair) then we will NOT * be able to accurately match any responses. *

* Also, if the TEID identifying the tunnel over which these messages are coming in (over S5/S8), * we'll calculate a new {@link NatEntry}, which means that, again, if two or more UE's send * traffic to the same remote endpoint, it won't work. However, for testing purposes we want this since * if you keep restarting the UE you want the new "PdnSession" to be remembered and not the old. *

* Again, this is a very simple SGi example so perhaps we'll do the above at some point. Also, we can * only do UDP (could use sockraw, a java wrapper for unit raw sockets but don't care for now and * didn't want to have that dependency since it is not published on a public maven repo) *

* Also note that we are going to be leaking these {@link NatEntry}s right now since we currently * do not "listen" to when the {@link GtpTunnel} goes down/is destroyed. But whatever, it's an example. *

* * @param tunnel * @param pdu */ public void processPdu(final GtpTunnel tunnel, final PdnSessionContext session, final Gtp1Message pdu) { final var payload = pdu.getPayload().get(); final var ipv4 = IpMessage.frame(payload).toIPv4(); final var udp = UdpMessage.frame(ipv4.getPayload()); final var remoteEndpoint = ConnectionEndpointId.create(Transport.udp, ipv4.getDestinationIp(), udp.getDestinationPort()); final var teid = session.getLocalBearerTeid(); final var natEntry = sessions.computeIfAbsent(teid, key -> { final var connection = connect(key.toString(), remoteEndpoint); final var deviceEndpoint = ConnectionEndpointId.create(Transport.udp, ipv4.getSourceIp(), udp.getSourcePort()); final var nat = new NatEntry(session, tunnel, connection, deviceEndpoint, remoteEndpoint); nats.put(connection.id().getLocalConnectionEndpointId(), nat); return nat; }); natEntry.externalConnection.send(udp.getPayload()); } // TODO: allocate new local "NIC" for this mapping... public void onNewPdnSession(final GtpTunnel tunnel, final PdnSessionContext session) { } public void deleteSession(final PdnSessionContext session) { final var teid = session.getLocalBearerTeid(); final var natEntry = sessions.remove(teid); if (natEntry != null) { final var localEndpoint = natEntry.externalConnection.id().getLocalConnectionEndpointId(); nats.remove(localEndpoint); environment.getNetworkInterface(teid.toString()).ifPresent(NetworkInterface::down); } else { System.err.println("WTF - wrong TEID"); } } private BufferConnection connect(final String nicName, final ConnectionEndpointId remote) { try { return environment.connect(nicName, Transport.udp, 0, remote.getAddress()).toCompletableFuture().get(); } catch (final InterruptedException | ExecutionException e) { throw new RuntimeException("We are cheating right now but seems like it didn't work", e); } } private void processBuffer(final BufferConnection connection, final BufferReadEvent msg) { final var connectionId = msg.getConnectionId(); final var remote = connectionId.getRemoteConnectionEndpointId(); final var local = connectionId.getLocalConnectionEndpointId(); final var natEntry = nats.get(local); if (natEntry == null) { System.err.println("WTF - the NAT Entry is null. Guess we removed it before we processed the last data???"); } final var content = msg.getBuffer(); final var udp = UdpMessage.createUdpIPv4(content) .withSourcePort(remote.getPort()) .withDestinationPort(natEntry.deviceEndpoint.getPort()) .withSourceIp(remote.getIpAddress()) .withDestinationIp(natEntry.deviceEndpoint.getIpAddress()) .withTTL(64) // TODO: should probably grab this from the incoming UDP packet. .build(); final var gtpU = ImmutableGtp1Message.create(Gtp1MessageType.G_PDU) .withTeid(natEntry.session.getLocalBearerTeid()) .withPayload(udp.getBuffer()) .build(); natEntry.tunnel.send(gtpU); } private static class NatEntry { public final PdnSessionContext session; /** * The GTP-U tunnel that is "pointing" back to the actual device on the GRX network */ public final GtpTunnel tunnel; /** * The "internet" connection pointing to the remote external party. * I.e., this is where the UE (user equipment - device - your cell phone if you will) * is sending (some) traffic) */ public final BufferConnection externalConnection; public final ConnectionEndpointId deviceEndpoint; public final ConnectionEndpointId remoteEndpoint; private NatEntry(final PdnSessionContext session, final GtpTunnel tunnel, final BufferConnection externalConnection, final ConnectionEndpointId deviceEndpoint, final ConnectionEndpointId remoteEndpoint) { this.session = session; this.tunnel = tunnel; this.externalConnection = externalConnection; this.deviceEndpoint = deviceEndpoint; this.remoteEndpoint = remoteEndpoint; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy