Compare commits
10 Commits
da6461ad47
...
3f10ae6691
Author | SHA1 | Date |
---|---|---|
![]() |
3f10ae6691 | |
![]() |
4e85a4efae | |
![]() |
4e7adb9c53 | |
![]() |
b10a4158fa | |
![]() |
0c4ef661e2 | |
![]() |
d5547c3035 | |
![]() |
da8543510a | |
![]() |
2fac47f1c7 | |
![]() |
1871098942 | |
![]() |
77542aa220 |
|
@ -1 +1,2 @@
|
|||
/ethercat
|
||||
/ethercat-static
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
Copyright 2025 Interzero Product Cycle GmbH
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
44
README.md
44
README.md
|
@ -1,3 +1,45 @@
|
|||
```
|
||||
|\ /|
|
||||
| \______/ |
|
||||
| ,,,,,,,, |
|
||||
| |||||||| |
|
||||
| |||||||| |
|
||||
| ¯¯¯¯¯¯¯¯ |
|
||||
|__________|
|
||||
\- -/
|
||||
```
|
||||
|
||||
# EtherCat
|
||||
|
||||
a tool to interface tap interfaces with stdin and stdout
|
||||
A simple tool written in C that connects a Linux tap interface to stdin and stdout
|
||||
|
||||
## How to build
|
||||
|
||||
- Prerequisites: Linux, C compiler
|
||||
- Might be easily portable to other systems that support tap interfaces, Idk.
|
||||
- Run `./build.sh`
|
||||
- Install by copying to `/usr/local/bin` or just leave it where it is
|
||||
|
||||
## How to use
|
||||
|
||||
Run as root:
|
||||
```
|
||||
ip tuntap add mode tap user USERNAME group GROUPNAME name tapN
|
||||
ip link set tapN up
|
||||
```
|
||||
Run as user:
|
||||
`ethercat tapN`
|
||||
|
||||
## Simple network tunnel:
|
||||
|
||||
Be warned: This will behave as if you had both sides connected with a (slow and less reliable) cable. This is more of a bridge than a regular VPN.
|
||||
If you bridge both sides to a real NIC and you have DHCP servers on both networks, you will end up with two DHCP servers on the same network.
|
||||
|
||||
- Set up a bridge between a physical NIC and the tap interface on one or both sides
|
||||
- Connect a port between the hosts, for example using SSH port forwarding
|
||||
- Make two FIFOs on both hosts (mkfifo)
|
||||
- Use netcat to connect the FIFOs between the hosts through the forwarded port
|
||||
- `nc -l 10000 < fifo1 > fifo2` (with some implementations apparently `nc -l -p ...` -\_- )
|
||||
- `nc localhost 10000 < fifo1 > fifo2`
|
||||
- Start an instance of EtherCat with the other ends of the FIFOs on both hosts
|
||||
- `./ethercat tapN < fifo2 > fifo1`
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2025 Interzero Product Cycle GmbH
|
||||
#
|
||||
# This program is free software under the terms of the BSD-3-Clause license.
|
||||
# A copy of this license should have been provided with the code.
|
||||
# If not, see https://opensource.org/license/BSD-3-Clause or https://spdx.org/licenses/BSD-3-Clause.html
|
||||
|
||||
set -v
|
||||
rm ethercat-static
|
||||
gcc -Wall -Wextra -fPIE -pie -static -static-libgcc ethercat.c -o ethercat-static
|
8
build.sh
8
build.sh
|
@ -1,3 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2025 Interzero Product Cycle GmbH
|
||||
#
|
||||
# This program is free software under the terms of the BSD-3-Clause license.
|
||||
# A copy of this license should have been provided with the code.
|
||||
# If not, see https://opensource.org/license/BSD-3-Clause or https://spdx.org/licenses/BSD-3-Clause.html
|
||||
|
||||
set -v
|
||||
rm ethercat
|
||||
gcc -Wall -Wextra ethercat.c -o ethercat
|
||||
|
|
132
ethercat.c
132
ethercat.c
|
@ -1,58 +1,156 @@
|
|||
/*
|
||||
Copyright 2025 Interzero Product Cycle GmbH
|
||||
|
||||
This program is free software under the terms of the BSD-3-Clause license.
|
||||
A copy of this license should have been provided with the code.
|
||||
If not, see https://opensource.org/license/BSD-3-Clause or https://spdx.org/licenses/BSD-3-Clause.html
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define DEVICE_NODE_TUN /dev/net/tun
|
||||
#define DEVICE_NODE_TUN "/dev/net/tun"
|
||||
// This number mus be larger than the MTU.
|
||||
#define BUFFER_SIZE 0x1000
|
||||
#define NUMBER_OF_POLLED_FILES 2
|
||||
#define POLL_TIMEOUT 100
|
||||
|
||||
#define EXIT_SUCCESS 0
|
||||
#define EXIT_USAGE 1
|
||||
#define EXIT_RUNTIME 2
|
||||
|
||||
int file_descriptor;
|
||||
int poll_result;
|
||||
char buffer[BUFFER_SIZE];
|
||||
struct ifreq ifr;
|
||||
bool stop_now = false;
|
||||
ssize_t read_size;
|
||||
ssize_t write_size;
|
||||
// 0 file_descriptor
|
||||
// 1 stdin
|
||||
struct pollfd polls_pls[NUMBER_OF_POLLED_FILES];
|
||||
|
||||
void stop() {
|
||||
stop_now = true;
|
||||
}
|
||||
|
||||
void yeet(int fd) {
|
||||
if (fcntl(fd, F_GETFD) != -1) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s TAP\n", argv[0]);
|
||||
return EXIT_USAGE;
|
||||
} else if ((strlen(argv[1])==2 && strncmp(argv[1], "-h", 2)==0) || (strlen(argv[1])==6 &&strncmp(argv[1], "--help", 6))) {
|
||||
} else if ((strlen(argv[1])==2 && strncmp(argv[1], "-h", 2)==0) || (strlen(argv[1])==6 && strncmp(argv[1], "--help", 6)==0)) {
|
||||
fprintf(stdout, "Usage: %s TAP\n", argv[0]);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (freopen(NULL, "rb", stdin) == NULL) {
|
||||
fprintf(stderr, "Failed to set stdin to binary mode: %s\n", strerror(errno));
|
||||
return EXIT_RUNTIME;
|
||||
}
|
||||
|
||||
polls_pls[1].fd = fileno(stdin);
|
||||
polls_pls[1].events = POLLIN;
|
||||
|
||||
if (freopen(NULL, "wb", stdout) == NULL) {
|
||||
fprintf(stderr, "Failed to set stdout to binary mode: %s\n", strerror(errno));
|
||||
return EXIT_RUNTIME;
|
||||
}
|
||||
|
||||
file_descriptor = open(DEVICE_NODE_TUN, O_RDWR);
|
||||
if (file_descriptor == -1) {
|
||||
fprintf(stderr, "Failed to open %s: %s\n", DEVICE_NODE_TUN, strerror(errno));
|
||||
return EXIT_RUNTIME;
|
||||
}
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||
strncpy(ifr.ifr_name, argv[1], IFNAMSIZ);
|
||||
|
||||
file_descriptor = open("/dev/net/tun", O_RDWR);
|
||||
if (file_descriptor == -1) {
|
||||
fprintf(stderr, "An error occurred while trying to open DEVICE_NODE_TUN: %i\n", errno);
|
||||
return EXIT_RUNTIME;
|
||||
}
|
||||
|
||||
if (ioctl(file_descriptor, TUNSETIFF, &ifr) == -1) {
|
||||
fprintf(stderr, "An error occurred while trying to ioctl: %i\n", errno);
|
||||
fprintf(stderr, "An error occurred while trying to ioctl: %s\n", strerror(errno));
|
||||
close(file_descriptor);
|
||||
return EXIT_RUNTIME;
|
||||
}
|
||||
|
||||
//TODO:
|
||||
// while input not EOF
|
||||
// and the tap is still a valid device (it might go away)
|
||||
// read from tap to stdout
|
||||
// read from stdin to tap
|
||||
polls_pls[0].fd = file_descriptor;
|
||||
polls_pls[0].events = POLLIN;
|
||||
|
||||
//TODO: signal handler for SIGINT (and SIGHUP? maybe others?)
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
|
||||
if (fcntl(fd, F_GETFD) != -1 || errno != EBADF) {
|
||||
close(file_descriptor);
|
||||
while (fcntl(file_descriptor, F_GETFD) != -1 && !stop_now) {
|
||||
poll_result = poll(polls_pls, NUMBER_OF_POLLED_FILES, POLL_TIMEOUT);
|
||||
if (poll_result == -1 && errno != EINTR) {
|
||||
fprintf(stderr, "An error occurred while polling: %s\n", strerror(errno));
|
||||
yeet(file_descriptor);
|
||||
return EXIT_RUNTIME;
|
||||
}
|
||||
if (poll_result == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (polls_pls[0].revents & POLLIN) {
|
||||
read_size = read(file_descriptor, buffer, BUFFER_SIZE);
|
||||
if (read_size == -1) {
|
||||
fprintf(stderr, "Failed to read from tap: %s\n", strerror(errno));
|
||||
yeet(file_descriptor);
|
||||
return EXIT_RUNTIME;
|
||||
}
|
||||
for (
|
||||
write_size = 0;
|
||||
read_size > 0 && !stop_now;
|
||||
read_size = read_size - write_size
|
||||
) {
|
||||
// using read_size as the amount of remaining bytes
|
||||
write_size = write(fileno(stdout), buffer, read_size);
|
||||
if (write_size == -1) {
|
||||
fprintf(stderr, "Failed to write stdout: %s\n", strerror(errno));
|
||||
yeet(file_descriptor);
|
||||
return EXIT_RUNTIME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (polls_pls[1].revents & POLLIN) {
|
||||
read_size = read(fileno(stdin), buffer, BUFFER_SIZE);
|
||||
if (read_size == -1) {
|
||||
fprintf(stderr, "Failed to read from stdin: %s\n", strerror(errno));
|
||||
yeet(file_descriptor);
|
||||
return EXIT_RUNTIME;
|
||||
}
|
||||
if (read_size == 0) {
|
||||
//TODO: is this still how to check for eof with poll?
|
||||
// or is there another way?
|
||||
break;
|
||||
}
|
||||
|
||||
//TODO: How to properly deal with partial reads?
|
||||
|
||||
// accepts exactly one complete packet (Ethernet Frame?) at a time
|
||||
write_size = write(file_descriptor, buffer, read_size);
|
||||
if (write_size == -1) {
|
||||
fprintf(stderr, "Failed to write tap: %s\n", strerror(errno));
|
||||
yeet(file_descriptor);
|
||||
return EXIT_RUNTIME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yeet(file_descriptor);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue