This is an automated email from the ASF dual-hosted git repository. cdutz pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-plc4x.git
The following commit(s) were added to refs/heads/master by this push: new 9e0f79c - Renamed RawSocket to RawIpSocket and added a new RawEthernetSocket, which allows to create raw sockets on ethernet frame level. 9e0f79c is described below commit 9e0f79c8881afe5dbb07b44cfaec5e44f76c6900 Author: Christofer Dutz <christofer.d...@c-ware.de> AuthorDate: Mon Mar 12 14:03:18 2018 +0100 - Renamed RawSocket to RawIpSocket and added a new RawEthernetSocket, which allows to create raw sockets on ethernet frame level. --- .../java/utils/rawsockets/RawEthernetSocket.java | 149 +++++++++++++++++++++ .../{RawSocket.java => RawIpSocket.java} | 6 +- .../{RawSocketTest.java => RawIpSocketTest.java} | 12 +- 3 files changed, 158 insertions(+), 9 deletions(-) diff --git a/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/RawEthernetSocket.java b/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/RawEthernetSocket.java new file mode 100644 index 0000000..07975c8 --- /dev/null +++ b/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/RawEthernetSocket.java @@ -0,0 +1,149 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.apache.plc4x.java.utils.rawsockets; + +import org.pcap4j.core.*; +import org.pcap4j.packet.*; +import org.pcap4j.packet.namednumber.*; +import org.pcap4j.util.LinkLayerAddress; +import org.pcap4j.util.MacAddress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.*; + +/** + * The raw ethernet socket relies on a layer 3 IP protocol implementation for finding the + * Network device able to connect to a given mac address. In Contrast to the {@link RawIpSocket} + * the Layer 2 protocol does not support gateways and routers, so we don't need the logic + * for looking up the default gateway, which makes this implementation a lot simpler. + */ +public class RawEthernetSocket { + + private static final Logger logger = LoggerFactory.getLogger(RawEthernetSocket.class); + + private static final int SNAPLEN = 65536; + private static final int READ_TIMEOUT = 10; + + // The EtherType of the protocol we will be communicating in. + private final EtherType etherType; + + private PcapNetworkInterface nif; + private MacAddress remoteMacAddress; + private MacAddress localMacAddress; + private ExecutorService pool = Executors.newSingleThreadExecutor(); + private PcapHandle receiveHandle; + + private final List<RawSocketListener> listeners = new LinkedList<>(); + + public RawEthernetSocket(EtherType etherType) { + this.etherType = etherType; + } + + public void connect(String localMacAddress, String remoteMacAddress) throws RawSocketException { + try { + pool = Executors.newScheduledThreadPool(2); + + this.localMacAddress = MacAddress.getByName(localMacAddress); + this.remoteMacAddress = MacAddress.getByName(remoteMacAddress); + // Find out which network device is able to connect to the given mac address. + nif = null; + for (PcapNetworkInterface dev : Pcaps.findAllDevs()) { + if(!dev.isLoopBack()) { + for (LinkLayerAddress macAddress : dev.getLinkLayerAddresses()) { + if(Arrays.equals(macAddress.getAddress(), this.localMacAddress.getAddress())) { + nif = dev; + } + } + } + } + if(nif == null) { + throw new RawSocketException( + "Unable to find local network device with mac address " + remoteMacAddress); + } + + // Setup receiving of packets and redirecting them to the corresponding listeners. + // Filter packets to contain only the ip protocol number of the current protocol. + receiveHandle = nif.openLive(SNAPLEN, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, READ_TIMEOUT); + + // Set the filter. + String filterString = "ether proto " + etherType.valueAsString() + + " and ether src " + this.remoteMacAddress.toString() + + " and ether dst " + this.localMacAddress.toString(); + + receiveHandle.setFilter(filterString, BpfProgram.BpfCompileMode.OPTIMIZE); + PacketListener packetListener = packet -> { + for (RawSocketListener listener : listeners) { + listener.packetReceived(packet.getRawData()); + } + }; + + pool.execute(() -> { + try { + receiveHandle.loop(-1, packetListener); + } catch (PcapNativeException | InterruptedException | NotOpenException e) { + logger.error("Error receiving packet for protocol {} from MAC address {}", + etherType.valueAsString(), remoteMacAddress, e); + } + }); + } catch (PcapNativeException | NotOpenException e) { + throw new RawSocketException("Error setting up RawSocket", e); + } + } + + public void disconnect() throws RawSocketException { + // TODO: Terminate all the listeners and the thread pool. + } + + public void write(byte[] rawData) throws RawSocketException { + try (PcapHandle sendHandle = + nif.openLive(SNAPLEN, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, READ_TIMEOUT)) { + UnknownPacket.Builder packetBuilder = new UnknownPacket.Builder(); + packetBuilder.rawData(rawData); + + EthernetPacket.Builder etherBuilder = new EthernetPacket.Builder(); + etherBuilder.dstAddr(remoteMacAddress) + .srcAddr(localMacAddress) + .type(etherType) + .paddingAtBuild(true); + etherBuilder.payloadBuilder( + new AbstractPacket.AbstractBuilder() { + @Override + public Packet build() { + return packetBuilder.build(); + } + } + ); + + Packet p = etherBuilder.build(); + sendHandle.sendPacket(p); + } catch (PcapNativeException | NotOpenException e) { + throw new RawSocketException("Error sending packet.", e); + } + } + + public void addListener(RawSocketListener listener) { + listeners.add(listener); + } + + public void removeListener(RawSocketListener listener) { + listeners.remove(listener); + } + +} diff --git a/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/RawSocket.java b/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/RawIpSocket.java similarity index 99% rename from plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/RawSocket.java rename to plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/RawIpSocket.java index 3d23d1f..62b5643 100644 --- a/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/RawSocket.java +++ b/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/RawIpSocket.java @@ -37,9 +37,9 @@ import java.util.List; import java.util.Map; import java.util.concurrent.*; -public class RawSocket { +public class RawIpSocket { - private static final Logger logger = LoggerFactory.getLogger(RawSocket.class); + private static final Logger logger = LoggerFactory.getLogger(RawIpSocket.class); private static final int SNAPLEN = 65536; private static final int READ_TIMEOUT = 10; @@ -59,7 +59,7 @@ public class RawSocket { private final List<RawSocketListener> listeners = new LinkedList<>(); - public RawSocket(int protocolNumber) { + public RawIpSocket(int protocolNumber) { this.protocolNumber = protocolNumber; } diff --git a/plc4j/utils/raw-sockets/src/test/java/org/apache/plc4x/java/utils/rawsockets/RawSocketTest.java b/plc4j/utils/raw-sockets/src/test/java/org/apache/plc4x/java/utils/rawsockets/RawIpSocketTest.java similarity index 92% rename from plc4j/utils/raw-sockets/src/test/java/org/apache/plc4x/java/utils/rawsockets/RawSocketTest.java rename to plc4j/utils/raw-sockets/src/test/java/org/apache/plc4x/java/utils/rawsockets/RawIpSocketTest.java index b15f0d2..fca6d62 100644 --- a/plc4j/utils/raw-sockets/src/test/java/org/apache/plc4x/java/utils/rawsockets/RawSocketTest.java +++ b/plc4j/utils/raw-sockets/src/test/java/org/apache/plc4x/java/utils/rawsockets/RawIpSocketTest.java @@ -31,7 +31,7 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.fail; import static org.junit.Assume.assumeThat; -public class RawSocketTest { +public class RawIpSocketTest { @Test @Ignore("Need to make tests run in Docker container first as this test requires libpcap or the entrie application to be run as 'root'") @@ -39,11 +39,11 @@ public class RawSocketTest { // TODO: cdutz: jenkins won't allow access on the inet device. Maybe try to fix this on a branch. assumeThat(System.getenv("PLC4X_BUILD_ON_JENKINS"), is(not(equalToIgnoringCase("true")))); // Protocol number 1 = ICMP (Ping) - RawSocket rawSocket = new RawSocket(1); + RawIpSocket rawIpSocket = new RawIpSocket(1); CompletableFuture<Boolean> result = new CompletableFuture<>(); // Simply print the result to the console - rawSocket.addListener(rawData -> { + rawIpSocket.addListener(rawData -> { System.out.println("Got response:"); System.out.println(Arrays.toString(rawData)); result.complete(true); @@ -63,13 +63,13 @@ public class RawSocketTest { for (PcapAddress pcapAddress : dev.getAddresses()) { if (pcapAddress.getAddress() instanceof Inet4Address) { System.out.println("Trying to connect on PcapAddress " + pcapAddress); - rawSocket.connect("plc4x.apache.org"); + rawIpSocket.connect("plc4x.apache.org"); } } } } // On travis we won't have any interface at all so we don't need to run there - assumeThat(rawSocket, notNullValue()); + assumeThat(rawIpSocket, notNullValue()); // Simple ICMP (Ping packet) byte[] rawData = new byte[]{ @@ -85,7 +85,7 @@ public class RawSocketTest { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09}; // Write the raw packet to the remote host. - rawSocket.write(rawData); + rawIpSocket.write(rawData); try { result.get(3, TimeUnit.SECONDS); -- To stop receiving notification emails like this one, please contact cd...@apache.org.