<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <haka/packet_module.h>
#include <haka/log.h>
#include <haka/types.h>
#include <haka/parameters.h>
#include <haka/thread.h>
#include <haka/error.h>
#include <haka/engine.h>

static REGISTER_LOG_SECTION(bridge_ethernet);

struct ethernet_packet {
	struct packet               core_packet;
	struct time                 timestamp;
	uint64                      id;
	struct packet_module_state *state;
	int                         orig;
};

struct packet_module_state {
	int                if_fd[2];
	uint64             id;
	unsigned char     *buffer; // Generic buffer for reading
};

/* Init parameters */
static int       nb_inputs = 0;
static char     *interfaces[2] = { NULL, NULL }; // At most two interfaces
static int       max_size = 0;
static int       bypass = 0; // If 1 everything received is immediately sent to other end. Implies two interfaces

static void cleanup()
{
	free(interfaces[0]);
	free(interfaces[1]);
}

static int ethernet_open(const char *interface)
{
	int fd;

	fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

	if (fd < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to create socket for interface '%s'. %s", interface, errno_error(errno));
	    return -1;
	}

	struct ifreq ifr;

	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);

	int ret;

	ret = ioctl(fd, SIOCGIFINDEX, &ifr);

	if (ret < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to retrieve interface index for %s. %s", interface, errno_error(errno));
	    goto bailout;
	}

	struct sockaddr_ll sock_address;

	memset(&sock_address, 0, sizeof(sock_address));
	sock_address.sll_family = AF_PACKET;
	sock_address.sll_protocol = htons(ETH_P_ALL);
	sock_address.sll_ifindex = ifr.ifr_ifindex;

	ret = bind(fd, (struct sockaddr*) &sock_address, sizeof(sock_address));

	if (ret < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to bind to interface (%s). %s", interface, errno_error(errno));
	    goto bailout;
	}

	// Get IFF flags
	ret = ioctl(fd, SIOCGIFFLAGS, &ifr);

	// Promiscuous mode
	ifr.ifr_flags |= IFF_PROMISC;
	ret = ioctl (fd, SIOCSIFFLAGS, &ifr);

	if (ret < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to set ethernet interface (%s) to promiscuous mode. %s", interface, errno_error(errno));
	    goto bailout;
	}

	// Retrieve MTU
	ret = ioctl(fd, SIOCGIFMTU, &ifr);

	if (ret < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to get MTU on (%s). %s", interface, errno_error(errno));
	    goto bailout;
	}

// Ethernet header is not included in MTU size. 22 = Max that VLan can accept
#define ETHER_HEADERSIZE 22

	if (max_size) {
		// Check if MTU matches the one from the other interface, if any
		if (max_size != ifr.ifr_mtu + ETHER_HEADERSIZE) {
			// It is just a warning, it may cause unexpected packet structured if sent packet is larger than output MTU
			LOG_WARNING(bridge_ethernet, "MTU values don't match between interfaces.");
		}
	} else {
		max_size = ifr.ifr_mtu + ETHER_HEADERSIZE;
		LOG_INFO(bridge_ethernet, "Max frame size: %d bytes", max_size);
	}

	return fd;

bailout:
	close(fd);
	return -1;
}

static void ethernet_close(struct packet_module_state *state, int i)
{
	struct ifreq ifr;
	int ret;

	assert(i >= 0 && i < 2);

	if (state->if_fd[i] < 0) return;

	// Get IFF flags
	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_ifrn.ifrn_name, interfaces[i], IFNAMSIZ);

	ret = ioctl(state->if_fd[i], SIOCGIFFLAGS, &ifr);

	if (ret < 0) {
	    LOG_WARNING(bridge_ethernet, "Failed to get IFF Flags on %s. %s", interfaces[i], errno_error(errno));
	    goto end;
	}

	// Reset promiscuous mode
	ifr.ifr_flags &= ~IFF_PROMISC;
	ret = ioctl(state->if_fd[i], SIOCSIFFLAGS, &ifr);

	if (ret < 0) {
	    LOG_WARNING(bridge_ethernet, "Failed to reset promiscuous mode on %s. %s", interfaces[i], errno_error(errno));
	    goto end;
	}

end:
	close(state->if_fd[i]);
	state->if_fd[i] = -1;
}

static void ethernet_close(struct packet_module_state *state, int i) {
	struct ifreq ifr;
	int ret;

	if (state->if_fd[i] < 0) return;

	// Get IFF flags
	memset(&ifr,0,sizeof(ifr));
	strncpy(ifr.ifr_ifrn.ifrn_name, interfaces[i], IFNAMSIZ);

	ret = ioctl(state->if_fd[i], SIOCGIFFLAGS, &ifr);

	if (ret < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to get IFF Flags on %s. %s", interfaces[i], errno_error(errno));
	    return;
	}

	// Reset promiscuous mode
	ifr.ifr_flags &= ~IFF_PROMISC;
	ret = ioctl(state->if_fd[i], SIOCSIFFLAGS, &ifr);

	if (ret < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to set IFF Flags on %s. %s", interfaces[i], errno_error(errno));
	    return;
	}

	close(state->if_fd[i]);
}

static int init(struct parameters *args)
{
	const char *if_s;

	assert(args);

	if_s = parameters_get_string(args, "interfaces", NULL); // Get devices list

	// Inputs
	if (if_s == NULL) {
	    LOG_ERROR(bridge_ethernet, "Please specify 'interfaces' parameter in configuration file.");
	    cleanup();
	    return 1;
	}

	char *save = NULL;
	char *in = (char *)if_s;
	while (nb_inputs < 2) {
	    char *token = strtok_r(in, ", \t", &save);
	    if (token == NULL) break;
	    interfaces[nb_inputs++] = strdup(token);
	    LOG_INFO(bridge_ethernet, "Using ethernet interface %s", token);
	    in = NULL;<commit_msg>Modifications after review<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <haka/packet_module.h>
#include <haka/log.h>
#include <haka/types.h>
#include <haka/parameters.h>
#include <haka/thread.h>
#include <haka/error.h>
#include <haka/engine.h>

static REGISTER_LOG_SECTION(bridge_ethernet);

struct ethernet_packet {
	struct packet               core_packet;
	struct time                 timestamp;
	uint64                      id;
	struct packet_module_state *state;
	int                         orig;
};

struct packet_module_state {
	int                if_fd[2];
	uint64             id;
	unsigned char     *buffer; // Generic buffer for reading
};

/* Init parameters */
static int       nb_inputs = 0;
static char     *interfaces[2] = { NULL, NULL }; // At most two interfaces
static int       max_size = 0;
static int       bypass = 0; // If 1 everything received is immediately sent to other end. Implies two interfaces

static void cleanup()
{
	free(interfaces[0]);
	free(interfaces[1]);
}

static int ethernet_open(const char *interface)
{
	int fd;

	fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

	if (fd < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to create socket for interface '%s'. %s", interface, errno_error(errno));
	    return -1;
	}

	struct ifreq ifr;

	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);

	int ret;

	ret = ioctl(fd, SIOCGIFINDEX, &ifr);

	if (ret < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to retrieve interface index for %s. %s", interface, errno_error(errno));
	    goto bailout;
	}

	struct sockaddr_ll sock_address;

	memset(&sock_address, 0, sizeof(sock_address));
	sock_address.sll_family = AF_PACKET;
	sock_address.sll_protocol = htons(ETH_P_ALL);
	sock_address.sll_ifindex = ifr.ifr_ifindex;

	ret = bind(fd, (struct sockaddr*) &sock_address, sizeof(sock_address));

	if (ret < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to bind to interface (%s). %s", interface, errno_error(errno));
	    goto bailout;
	}

	// Get IFF flags
	ret = ioctl(fd, SIOCGIFFLAGS, &ifr);

	// Promiscuous mode
	ifr.ifr_flags |= IFF_PROMISC;
	ret = ioctl (fd, SIOCSIFFLAGS, &ifr);

	if (ret < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to set ethernet interface (%s) to promiscuous mode. %s", interface, errno_error(errno));
	    goto bailout;
	}

	// Retrieve MTU
	ret = ioctl(fd, SIOCGIFMTU, &ifr);

	if (ret < 0) {
	    LOG_ERROR(bridge_ethernet, "Failed to get MTU on (%s). %s", interface, errno_error(errno));
	    goto bailout;
	}

// Ethernet header is not included in MTU size. 22 = Max that VLan can accept
#define ETHER_HEADERSIZE 22

	if (max_size) {
		// Check if MTU matches the one from the other interface, if any
		if (max_size != ifr.ifr_mtu + ETHER_HEADERSIZE) {
			// It is just a warning, it may cause unexpected packet structured if sent packet is larger than output MTU
			LOG_WARNING(bridge_ethernet, "MTU values don't match between interfaces.");
		}
	} else {
		max_size = ifr.ifr_mtu + ETHER_HEADERSIZE;
		LOG_INFO(bridge_ethernet, "Max frame size: %d bytes", max_size);
	}

	return fd;

bailout:
	close(fd);
	return -1;
}

static void ethernet_close(struct packet_module_state *state, int i)
{
	struct ifreq ifr;
	int ret;

	assert(i >= 0 && i < 2);

	if (state->if_fd[i] < 0) return;

	// Get IFF flags
	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_ifrn.ifrn_name, interfaces[i], IFNAMSIZ);

	ret = ioctl(state->if_fd[i], SIOCGIFFLAGS, &ifr);

	if (ret < 0) {
	    LOG_WARNING(bridge_ethernet, "Failed to get IFF Flags on %s. %s", interfaces[i], errno_error(errno));
	    goto end;
	}

	// Reset promiscuous mode
	ifr.ifr_flags &= ~IFF_PROMISC;
	ret = ioctl(state->if_fd[i], SIOCSIFFLAGS, &ifr);

	if (ret < 0) {
	    LOG_WARNING(bridge_ethernet, "Failed to reset promiscuous mode on %s. %s", interfaces[i], errno_error(errno));
	    goto end;
	}

end:
	close(state->if_fd[i]);
	state->if_fd[i] = -1;
}

static int init(struct parameters *args)
{
	const char *if_s;

	assert(args);

	if_s = parameters_get_string(args, "interfaces", NULL); // Get devices list

	// Inputs
	if (if_s == NULL) {
	    LOG_ERROR(bridge_ethernet, "Please specify 'interfaces' parameter in configuration file.");
	    cleanup();
	    return 1;
	}

	char *save = NULL;
	char *in = (char *)if_s;
	while (nb_inputs < 2) {
	    char *token = strtok_r(in, ", \t", &save);
	    if (token == NULL) break;
	    interfaces[nb_inputs++] = strdup(token);
	    LOG_INFO(bridge_ethernet, "Using ethernet interface %s", token);
	    in = NULL;
{'fraction_non_alphanumeric': 0.1342176050459141, 'fraction_numerical': 0.007420461923754754, 'mean_word_length': 3.6354256233877904, 'pattern_counts': {'":': 0, '<': 72, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '394553', 'n_tokens_mistral': 4213, 'n_tokens_neox': 3979, 'n_words': 1096}