aboutgitcodebugslistschat
path: root/icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'icmp.c')
-rw-r--r--icmp.c83
1 files changed, 52 insertions, 31 deletions
diff --git a/icmp.c b/icmp.c
index cb81c76..9564c49 100644
--- a/icmp.c
+++ b/icmp.c
@@ -15,7 +15,6 @@
#include <errno.h>
#include <net/ethernet.h>
#include <net/if.h>
-#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <stdio.h>
@@ -23,10 +22,8 @@
#include <stdint.h>
#include <stddef.h>
#include <string.h>
-#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <unistd.h>
#include <time.h>
#include <linux/icmpv6.h>
@@ -41,9 +38,11 @@
#include "inany.h"
#include "icmp.h"
#include "flow_table.h"
+#include "epoll_ctl.h"
#define ICMP_ECHO_TIMEOUT 60 /* s, timeout for ICMP socket activity */
#define ICMP_NUM_IDS (1U << 16)
+#define MAX_IOV_ICMP 16 /* Arbitrary, should be enough */
/**
* ping_at_sidx() - Get ping specific flow at given sidx
@@ -85,7 +84,7 @@ void icmp_sock_handler(const struct ctx *c, union epoll_ref ref)
n = recvfrom(ref.fd, buf, sizeof(buf), 0, &sr.sa, &sl);
if (n < 0) {
- flow_err(pingf, "recvfrom() error: %s", strerror(errno));
+ flow_perror(pingf, "recvfrom() error");
return;
}
@@ -124,17 +123,21 @@ void icmp_sock_handler(const struct ctx *c, union epoll_ref ref)
flow_dbg(pingf, "echo reply to tap, ID: %"PRIu16", seq: %"PRIu16,
ini->eport, seq);
+ /* Check if neighbour table has a recorded MAC address */
+ if (MAC_IS_UNDEF(pingf->f.tap_omac))
+ fwd_neigh_mac_get(c, &ini->oaddr, pingf->f.tap_omac);
+
if (pingf->f.type == FLOW_PING4) {
- const struct in_addr *saddr = inany_v4(&ini->faddr);
+ const struct in_addr *saddr = inany_v4(&ini->oaddr);
const struct in_addr *daddr = inany_v4(&ini->eaddr);
ASSERT(saddr && daddr); /* Must have IPv4 addresses */
- tap_icmp4_send(c, *saddr, *daddr, buf, n);
+ tap_icmp4_send(c, *saddr, *daddr, buf, pingf->f.tap_omac, n);
} else if (pingf->f.type == FLOW_PING6) {
- const struct in6_addr *saddr = &ini->faddr.a6;
+ const struct in6_addr *saddr = &ini->oaddr.a6;
const struct in6_addr *daddr = &ini->eaddr.a6;
- tap_icmp6_send(c, saddr, daddr, buf, n);
+ tap_icmp6_send(c, saddr, daddr, buf, pingf->f.tap_omac, n);
}
return;
@@ -150,7 +153,7 @@ unexpected:
static void icmp_ping_close(const struct ctx *c,
const struct icmp_ping_flow *pingf)
{
- epoll_ctl(c->epollfd, EPOLL_CTL_DEL, pingf->sock, NULL);
+ epoll_del(flow_epollfd(&pingf->f), pingf->sock);
close(pingf->sock);
flow_hash_remove(c, FLOW_SIDX(pingf, INISIDE));
}
@@ -163,7 +166,7 @@ static void icmp_ping_close(const struct ctx *c,
* @saddr: Source address
* @daddr: Destination address
*
- * Return: Newly opened ping flow, or NULL on failure
+ * Return: newly opened ping flow, or NULL on failure
*/
static struct icmp_ping_flow *icmp_ping_new(const struct ctx *c,
sa_family_t af, uint16_t id,
@@ -171,10 +174,10 @@ static struct icmp_ping_flow *icmp_ping_new(const struct ctx *c,
{
uint8_t proto = af == AF_INET ? IPPROTO_ICMP : IPPROTO_ICMPV6;
uint8_t flowtype = af == AF_INET ? FLOW_PING4 : FLOW_PING6;
- union epoll_ref ref = { .type = EPOLL_TYPE_PING };
union flow *flow = flow_alloc();
struct icmp_ping_flow *pingf;
const struct flowside *tgt;
+ union epoll_ref ref;
if (!flow)
return NULL;
@@ -195,9 +198,7 @@ static struct icmp_ping_flow *icmp_ping_new(const struct ctx *c,
pingf->seq = -1;
- ref.flowside = FLOW_SIDX(flow, TGTSIDE);
- pingf->sock = flowside_sock_l4(c, EPOLL_TYPE_PING, PIF_HOST,
- tgt, ref.data);
+ pingf->sock = flowside_sock_l4(c, EPOLL_TYPE_PING, PIF_HOST, tgt);
if (pingf->sock < 0) {
warn("Cannot open \"ping\" socket. You might need to:");
@@ -209,6 +210,17 @@ static struct icmp_ping_flow *icmp_ping_new(const struct ctx *c,
if (pingf->sock > FD_REF_MAX)
goto cancel;
+ flow_epollid_set(&pingf->f, EPOLLFD_ID_DEFAULT);
+
+ ref.type = EPOLL_TYPE_PING;
+ ref.flowside = FLOW_SIDX(flow, TGTSIDE);
+ ref.fd = pingf->sock;
+
+ if (epoll_add(flow_epollfd(&pingf->f), EPOLLIN, ref) < 0) {
+ close(pingf->sock);
+ goto cancel;
+ }
+
flow_dbg(pingf, "new socket %i for echo ID %"PRIu16, pingf->sock, id);
flow_hash_insert(c, FLOW_SIDX(pingf, INISIDE));
@@ -229,37 +241,36 @@ cancel:
* @af: Address family, AF_INET or AF_INET6
* @saddr: Source address
* @daddr: Destination address
- * @p: Packet pool, single packet with ICMP/ICMPv6 header
+ * @data: Single packet with ICMP/ICMPv6 header
* @now: Current timestamp
*
* Return: count of consumed packets (always 1, even if malformed)
*/
int icmp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
const void *saddr, const void *daddr,
- const struct pool *p, const struct timespec *now)
+ struct iov_tail *data, const struct timespec *now)
{
+ struct iovec iov[MAX_IOV_ICMP];
struct icmp_ping_flow *pingf;
const struct flowside *tgt;
union sockaddr_inany sa;
- size_t dlen, l4len;
+ struct msghdr msh;
uint16_t id, seq;
union flow *flow;
uint8_t proto;
- socklen_t sl;
- void *pkt;
+ int cnt;
(void)saddr;
ASSERT(pif == PIF_TAP);
if (af == AF_INET) {
+ struct icmphdr ih_storage;
const struct icmphdr *ih;
- if (!(pkt = packet_get(p, 0, 0, sizeof(*ih), &dlen)))
+ ih = IOV_PEEK_HEADER(data, ih_storage);
+ if (!ih)
return 1;
- ih = (struct icmphdr *)pkt;
- l4len = dlen + sizeof(*ih);
-
if (ih->type != ICMP_ECHO)
return 1;
@@ -267,14 +278,13 @@ int icmp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
id = ntohs(ih->un.echo.id);
seq = ntohs(ih->un.echo.sequence);
} else if (af == AF_INET6) {
+ struct icmp6hdr ih_storage;
const struct icmp6hdr *ih;
- if (!(pkt = packet_get(p, 0, 0, sizeof(*ih), &dlen)))
+ ih = IOV_PEEK_HEADER(data, ih_storage);
+ if (!ih)
return 1;
- ih = (struct icmp6hdr *)pkt;
- l4len = dlen + sizeof(*ih);
-
if (ih->icmp6_type != ICMPV6_ECHO_REQUEST)
return 1;
@@ -285,6 +295,10 @@ int icmp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
ASSERT(0);
}
+ cnt = iov_tail_clone(&iov[0], MAX_IOV_ICMP, data);
+ if (cnt < 0)
+ return 1;
+
flow = flow_at_sidx(flow_lookup_af(c, proto, PIF_TAP,
af, saddr, daddr, id, id));
@@ -298,10 +312,17 @@ int icmp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af,
ASSERT(flow_proto[pingf->f.type] == proto);
pingf->ts = now->tv_sec;
- pif_sockaddr(c, &sa, &sl, PIF_HOST, &tgt->eaddr, 0);
- if (sendto(pingf->sock, pkt, l4len, MSG_NOSIGNAL, &sa.sa, sl) < 0) {
- flow_dbg(pingf, "failed to relay request to socket: %s",
- strerror(errno));
+ pif_sockaddr(c, &sa, PIF_HOST, &tgt->eaddr, 0);
+ msh.msg_name = &sa;
+ msh.msg_namelen = socklen_inany(&sa);
+ msh.msg_iov = iov;
+ msh.msg_iovlen = cnt;
+ msh.msg_control = NULL;
+ msh.msg_controllen = 0;
+ msh.msg_flags = 0;
+
+ if (sendmsg(pingf->sock, &msh, MSG_NOSIGNAL) < 0) {
+ flow_dbg_perror(pingf, "failed to relay request to socket");
} else {
flow_dbg(pingf,
"echo request to socket, ID: %"PRIu16", seq: %"PRIu16,