summaryrefslogtreecommitdiff
path: root/src/ipcpd/eth/eth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipcpd/eth/eth.c')
-rw-r--r--src/ipcpd/eth/eth.c125
1 files changed, 117 insertions, 8 deletions
diff --git a/src/ipcpd/eth/eth.c b/src/ipcpd/eth/eth.c
index d6f476f2..2d3bd249 100644
--- a/src/ipcpd/eth/eth.c
+++ b/src/ipcpd/eth/eth.c
@@ -49,6 +49,7 @@
#include <ouroboros/ipcp-dev.h>
#include <ouroboros/fqueue.h>
#include <ouroboros/logs.h>
+#include <ouroboros/np1_flow.h>
#include <ouroboros/time.h>
#include <ouroboros/fccntl.h>
#include <ouroboros/pthread.h>
@@ -82,6 +83,7 @@
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#include <linux/sockios.h>
#endif
#ifdef __FreeBSD__
@@ -160,6 +162,7 @@
#define FLOW_REPLY 1
#define NAME_QUERY_REQ 2
#define NAME_QUERY_REPLY 3
+#define FLOW_IRM_UPDATE 4
struct mgmt_msg {
#if defined(BUILD_ETH_DIX)
@@ -435,6 +438,7 @@ static int eth_rib_read(const char * path,
int queued = 0;
socklen_t optlen = sizeof(rcvbuf);
# if defined(__linux__)
+ int outq = 0;
struct tpacket_stats tp_stats;
socklen_t tp_len = sizeof(tp_stats);
# endif
@@ -446,6 +450,7 @@ static int eth_rib_read(const char * path,
SO_SNDBUF, &sndbuf, &optlen);
ioctl(eth_data.s_fd, FIONREAD, &queued);
# if defined(__linux__)
+ ioctl(eth_data.s_fd, SIOCOUTQ, &outq);
if (getsockopt(eth_data.s_fd, SOL_PACKET,
PACKET_STATISTICS,
&tp_stats, &tp_len) == 0) {
@@ -481,12 +486,14 @@ static int eth_rib_read(const char * path,
n += sprintf(buf + n,
"Socket rcvbuf (bytes): %20d\n"
"Socket sndbuf (bytes): %20d\n"
- "Socket queued (bytes): %20d\n",
+ "Socket ingress (bytes): %20d\n",
rcvbuf, sndbuf, queued);
# if defined(__linux__)
n += sprintf(buf + n,
+ "Socket egress (bytes): %20d\n"
"Kernel frames received: %20zu\n"
"Kernel frames dropped: %20zu\n",
+ outq,
LOAD_RELAXED(&eth_data.stat.kern_rcv),
LOAD_RELAXED(&eth_data.stat.kern_drp));
# endif
@@ -574,6 +581,7 @@ static int eth_rib_readdir(char *** buf)
fail_entry:
while (idx-- > 0)
free((*buf)[idx]);
+
free(*buf);
fail_entries:
pthread_rwlock_unlock(&eth_data.flows_lock);
@@ -844,6 +852,58 @@ static int eth_ipcp_alloc_resp(uint8_t * dst_addr,
return 0;
}
+static int eth_ipcp_flow_update(int fd,
+ const buffer_t * data)
+{
+ struct mgmt_msg * msg;
+ struct ef * flow;
+ uint8_t * buf;
+ uint8_t r_addr[MAC_SIZE];
+ int ret;
+
+ buf = malloc(sizeof(*msg) + ETH_HEADER_TOT_SIZE + data->len);
+ if (buf == NULL)
+ return -1;
+
+ memset(buf, 0, sizeof(*msg) + ETH_HEADER_TOT_SIZE + data->len);
+
+ msg = (struct mgmt_msg *) (buf + ETH_HEADER_TOT_SIZE);
+
+ msg->code = FLOW_IRM_UPDATE;
+
+ pthread_rwlock_rdlock(&eth_data.flows_lock);
+
+ flow = &eth_data.fd_to_ef[fd];
+#if defined(BUILD_ETH_DIX)
+ msg->seid = htons((uint16_t) fd);
+ msg->deid = htons((uint16_t) flow->r_eid);
+#elif defined(BUILD_ETH_LLC)
+ msg->ssap = flow->sap;
+ msg->dsap = (uint8_t) flow->r_sap;
+#endif
+ memcpy(r_addr, flow->r_addr, MAC_SIZE);
+
+ pthread_rwlock_unlock(&eth_data.flows_lock);
+
+ if (data->len > 0)
+ memcpy(msg + 1, data->data, data->len);
+
+ ret = eth_ipcp_send_frame(r_addr,
+#if defined(BUILD_ETH_DIX)
+ MGMT_EID,
+#elif defined(BUILD_ETH_LLC)
+ reverse_bits(MGMT_SAP),
+ reverse_bits(MGMT_SAP),
+#endif
+ buf, sizeof(*msg) + data->len);
+ free(buf);
+
+ if (ret == 0)
+ FETCH_ADD_RELAXED(&eth_data.stat.n_mgmt_snd, 1);
+
+ return ret;
+}
+
static int eth_ipcp_req(uint8_t * r_addr,
#if defined(BUILD_ETH_DIX)
uint16_t r_eid,
@@ -994,6 +1054,44 @@ static int eth_ipcp_name_query_reply(const uint8_t * hash,
return 0;
}
+static int eth_ipcp_flow_update_arr(const uint8_t * buf,
+ size_t len)
+{
+ struct mgmt_msg * msg;
+ buffer_t data;
+ int fd;
+ int flow_id;
+
+ msg = (struct mgmt_msg *) buf;
+
+ data.data = (uint8_t *) buf + sizeof(*msg);
+ data.len = len - sizeof(*msg);
+
+ pthread_rwlock_rdlock(&eth_data.flows_lock);
+#if defined(BUILD_ETH_DIX)
+ fd = ntohs(msg->deid);
+#elif defined(BUILD_ETH_LLC)
+ fd = eth_data.ef_to_fd[msg->dsap];
+#endif
+ pthread_rwlock_unlock(&eth_data.flows_lock);
+
+ if (fd < 0 || fd >= SYS_MAX_FLOWS) {
+ log_err("Flow update for unknown endpoint.");
+ return -1;
+ }
+
+ flow_id = np1_flow_id(fd);
+ if (flow_id < 0)
+ return -1;
+
+ if (ipcp_flow_update_arr(flow_id, &data) < 0) {
+ log_err("Failed to relay flow update on fd %d.", fd);
+ return -1;
+ }
+
+ return 0;
+}
+
static int eth_ipcp_mgmt_frame(const uint8_t * buf,
size_t len,
uint8_t * r_addr)
@@ -1055,6 +1153,9 @@ static int eth_ipcp_mgmt_frame(const uint8_t * buf,
ntoh32(msg->response),
&data);
break;
+ case FLOW_IRM_UPDATE:
+ eth_ipcp_flow_update_arr(buf, len);
+ break;
case NAME_QUERY_REQ:
if (len < sizeof(*msg) + ipcp_dir_hash_len())
return -1;
@@ -1847,15 +1948,22 @@ static int eth_init_raw_socket(struct ifreq * ifr)
#endif
sndbuf = IPCP_ETH_SNDBUF;
- if (sndbuf > 0 && setsockopt(eth_data.s_fd, SOL_SOCKET, SO_SNDBUF,
- &sndbuf, sizeof(sndbuf))) {
- log_info("Failed to set SO_SNDBUF to %d.", sndbuf);
+ if (sndbuf > 0) {
+ /* Never below one frame or sendto() cannot fit an SDU. */
+ sndbuf = MAX(sndbuf, (int) (ETH_HEADER_TOT_SIZE + ETH_MTU));
+ if (setsockopt(eth_data.s_fd, SOL_SOCKET, SO_SNDBUF,
+ &sndbuf, sizeof(sndbuf)))
+ log_info("Failed to set SO_SNDBUF to %d.", sndbuf);
}
rcvbuf = IPCP_ETH_RCVBUF;
- if (rcvbuf > 0 && setsockopt(eth_data.s_fd, SOL_SOCKET, SO_RCVBUF,
- &rcvbuf, sizeof(rcvbuf))) {
- log_info("Failed to set SO_RCVBUF to %d.", rcvbuf);
+ if (rcvbuf > 0) {
+ /* SO_RCVBUFFORCE bypasses rmem_max; SO_RCVBUF is fallback. */
+ if (setsockopt(eth_data.s_fd, SOL_SOCKET, SO_RCVBUFFORCE,
+ &rcvbuf, sizeof(rcvbuf))
+ && setsockopt(eth_data.s_fd, SOL_SOCKET, SO_RCVBUF,
+ &rcvbuf, sizeof(rcvbuf)))
+ log_info("Failed to set SO_RCVBUF to %d.", rcvbuf);
}
if (bind(eth_data.s_fd, (struct sockaddr *) &eth_data.device,
@@ -2289,7 +2397,8 @@ static struct ipcp_ops eth_ops = {
.ipcp_flow_alloc = eth_ipcp_flow_alloc,
.ipcp_flow_join = NULL,
.ipcp_flow_alloc_resp = eth_ipcp_flow_alloc_resp,
- .ipcp_flow_dealloc = eth_ipcp_flow_dealloc
+ .ipcp_flow_dealloc = eth_ipcp_flow_dealloc,
+ .ipcp_flow_update = eth_ipcp_flow_update
};
int main(int argc,