diff options
Diffstat (limited to 'src/ipcpd/eth')
| -rw-r--r-- | src/ipcpd/eth/eth.c | 125 |
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(ð_data.stat.kern_rcv), LOAD_RELAXED(ð_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(ð_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(ð_data.flows_lock); + + flow = ð_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(ð_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(ð_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(ð_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(ð_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 *) ð_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, |
