Split tap_ip_send() into IPv4 and IPv6 specific functions
The IPv4 and IPv6 paths in tap_ip_send() have very little in common, and it turns out that every caller (statically) knows if it is using IPv4 or IPv6. So split into separate tap_ip4_send() and tap_ip6_send() functions. Use a new tap_l2_hdr() function for the very small common part. While we're there, make some minor cleanups: - We were double writing some fields in the IPv6 header, so that it temporary matched the pseudo-header for checksum calculation. With recent checksum reworks, this isn't neccessary any more. - We don't use any IPv4 header options, so use some sizeof() constructs instead of some open coded values for header length. - The comment used to say that the flow label was for TCP over IPv6, but in fact the only thing we used it for was DHCPv6 over UDP traffic Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
parent
fb5d1c5d7d
commit
f616ca231e
4 changed files with 101 additions and 94 deletions
6
dhcpv6.c
6
dhcpv6.c
|
@ -531,8 +531,8 @@ int dhcpv6(struct ctx *c, const struct pool *p,
|
||||||
|
|
||||||
resp_not_on_link.hdr.xid = mh->xid;
|
resp_not_on_link.hdr.xid = mh->xid;
|
||||||
|
|
||||||
tap_ip_send(c, src, IPPROTO_UDP,
|
tap_ip6_send(c, src, IPPROTO_UDP,
|
||||||
(char *)&resp_not_on_link, n, mh->xid);
|
(char *)&resp_not_on_link, n, mh->xid);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -580,7 +580,7 @@ int dhcpv6(struct ctx *c, const struct pool *p,
|
||||||
|
|
||||||
resp.hdr.xid = mh->xid;
|
resp.hdr.xid = mh->xid;
|
||||||
|
|
||||||
tap_ip_send(c, src, IPPROTO_UDP, (char *)&resp, n, mh->xid);
|
tap_ip6_send(c, src, IPPROTO_UDP, (char *)&resp, n, mh->xid);
|
||||||
c->ip6.addr_seen = c->ip6.addr;
|
c->ip6.addr_seen = c->ip6.addr;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
10
icmp.c
10
icmp.c
|
@ -69,10 +69,6 @@ static uint8_t icmp_act[IP_VERSIONS][DIV_ROUND_UP(ICMP_NUM_IDS, 8)];
|
||||||
void icmp_sock_handler(const struct ctx *c, union epoll_ref ref,
|
void icmp_sock_handler(const struct ctx *c, union epoll_ref ref,
|
||||||
uint32_t events, const struct timespec *now)
|
uint32_t events, const struct timespec *now)
|
||||||
{
|
{
|
||||||
struct in6_addr a6 = { .s6_addr = { 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0xff, 0xff,
|
|
||||||
0, 0, 0, 0 } };
|
|
||||||
union icmp_epoll_ref *iref = &ref.r.p.icmp;
|
union icmp_epoll_ref *iref = &ref.r.p.icmp;
|
||||||
struct sockaddr_storage sr;
|
struct sockaddr_storage sr;
|
||||||
socklen_t sl = sizeof(sr);
|
socklen_t sl = sizeof(sr);
|
||||||
|
@ -109,7 +105,7 @@ void icmp_sock_handler(const struct ctx *c, union epoll_ref ref,
|
||||||
icmp_id_map[V6][id].seq = seq;
|
icmp_id_map[V6][id].seq = seq;
|
||||||
}
|
}
|
||||||
|
|
||||||
tap_ip_send(c, &sr6->sin6_addr, IPPROTO_ICMPV6, buf, n, 0);
|
tap_ip6_send(c, &sr6->sin6_addr, IPPROTO_ICMPV6, buf, n, 0);
|
||||||
} else {
|
} else {
|
||||||
struct sockaddr_in *sr4 = (struct sockaddr_in *)&sr;
|
struct sockaddr_in *sr4 = (struct sockaddr_in *)&sr;
|
||||||
struct icmphdr *ih = (struct icmphdr *)buf;
|
struct icmphdr *ih = (struct icmphdr *)buf;
|
||||||
|
@ -127,9 +123,7 @@ void icmp_sock_handler(const struct ctx *c, union epoll_ref ref,
|
||||||
icmp_id_map[V4][id].seq = seq;
|
icmp_id_map[V4][id].seq = seq;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&a6.s6_addr[12], &sr4->sin_addr, sizeof(sr4->sin_addr));
|
tap_ip4_send(c, sr4->sin_addr.s_addr, IPPROTO_ICMP, buf, n);
|
||||||
|
|
||||||
tap_ip_send(c, &a6, IPPROTO_ICMP, buf, n, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
173
tap.c
173
tap.c
|
@ -113,100 +113,111 @@ const struct in6_addr *tap_ip6_daddr(const struct ctx *c,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tap_ip_send() - Send IP packet, with L2 headers, calculating L3/L4 checksums
|
* tap_push_l2h() - Build an L2 header for an inbound packet
|
||||||
* @c: Execution context
|
* @c: Execution context
|
||||||
* @src: IPv6 source address, IPv4-mapped for IPv4 sources
|
* @buf: Buffer address at which to generate header
|
||||||
* @proto: L4 protocol number
|
* @proto: Ethernet protocol number for L3
|
||||||
* @in: Payload
|
*
|
||||||
* @len: L4 payload length
|
* Return: pointer at which to write the packet's payload
|
||||||
* @flow: Flow label for TCP over IPv6
|
|
||||||
*/
|
*/
|
||||||
void tap_ip_send(const struct ctx *c, const struct in6_addr *src, uint8_t proto,
|
static void *tap_push_l2h(const struct ctx *c, void *buf, uint16_t proto)
|
||||||
const char *in, size_t len, uint32_t flow)
|
|
||||||
{
|
{
|
||||||
char buf[USHRT_MAX];
|
struct ethhdr *eh = (struct ethhdr *)buf;
|
||||||
struct ethhdr *eh;
|
|
||||||
|
|
||||||
eh = (struct ethhdr *)buf;
|
|
||||||
|
|
||||||
/* TODO: ARP table lookup */
|
/* TODO: ARP table lookup */
|
||||||
memcpy(eh->h_dest, c->mac_guest, ETH_ALEN);
|
memcpy(eh->h_dest, c->mac_guest, ETH_ALEN);
|
||||||
memcpy(eh->h_source, c->mac, ETH_ALEN);
|
memcpy(eh->h_source, c->mac, ETH_ALEN);
|
||||||
|
eh->h_proto = ntohs(proto);
|
||||||
|
return eh + 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (IN6_IS_ADDR_V4MAPPED(src)) {
|
/**
|
||||||
struct iphdr *iph = (struct iphdr *)(eh + 1);
|
* tap_ip4_send() - Send IPv4 packet, with L2 headers, calculating L3/L4 checksums
|
||||||
char *data = (char *)(iph + 1);
|
* @c: Execution context
|
||||||
|
* @src: IPv4 source address
|
||||||
|
* @proto: L4 protocol number
|
||||||
|
* @in: Payload
|
||||||
|
* @len: L4 payload length
|
||||||
|
*/
|
||||||
|
void tap_ip4_send(const struct ctx *c, in_addr_t src, uint8_t proto,
|
||||||
|
const char *in, size_t len)
|
||||||
|
{
|
||||||
|
char buf[USHRT_MAX];
|
||||||
|
struct iphdr *ip4h = (struct iphdr *)tap_push_l2h(c, buf, ETH_P_IP);
|
||||||
|
char *data = (char *)(ip4h + 1);
|
||||||
|
|
||||||
eh->h_proto = ntohs(ETH_P_IP);
|
ip4h->version = 4;
|
||||||
|
ip4h->ihl = sizeof(struct iphdr) / 4;
|
||||||
|
ip4h->tos = 0;
|
||||||
|
ip4h->tot_len = htons(len + sizeof(*ip4h));
|
||||||
|
ip4h->id = 0;
|
||||||
|
ip4h->frag_off = 0;
|
||||||
|
ip4h->ttl = 255;
|
||||||
|
ip4h->protocol = proto;
|
||||||
|
ip4h->saddr = src;
|
||||||
|
ip4h->daddr = tap_ip4_daddr(c);
|
||||||
|
csum_ip4_header(ip4h);
|
||||||
|
|
||||||
iph->version = 4;
|
memcpy(data, in, len);
|
||||||
iph->ihl = 5;
|
|
||||||
iph->tos = 0;
|
|
||||||
iph->tot_len = htons(len + 20);
|
|
||||||
iph->id = 0;
|
|
||||||
iph->frag_off = 0;
|
|
||||||
iph->ttl = 255;
|
|
||||||
iph->protocol = proto;
|
|
||||||
iph->daddr = tap_ip4_daddr(c);
|
|
||||||
memcpy(&iph->saddr, &src->s6_addr[12], 4);
|
|
||||||
|
|
||||||
csum_ip4_header(iph);
|
if (ip4h->protocol == IPPROTO_UDP) {
|
||||||
|
struct udphdr *uh = (struct udphdr *)(ip4h + 1);
|
||||||
|
|
||||||
memcpy(data, in, len);
|
csum_udp4(uh, ip4h->saddr, ip4h->daddr,
|
||||||
|
uh + 1, len - sizeof(*uh));
|
||||||
if (iph->protocol == IPPROTO_UDP) {
|
} else if (ip4h->protocol == IPPROTO_ICMP) {
|
||||||
struct udphdr *uh = (struct udphdr *)(iph + 1);
|
struct icmphdr *ih = (struct icmphdr *)(ip4h + 1);
|
||||||
|
csum_icmp4(ih, ih + 1, len - sizeof(*ih));
|
||||||
csum_udp4(uh, iph->saddr, iph->daddr, uh + 1, len - sizeof(*uh));
|
|
||||||
} else if (iph->protocol == IPPROTO_ICMP) {
|
|
||||||
struct icmphdr *ih = (struct icmphdr *)(iph + 1);
|
|
||||||
csum_icmp4(ih, ih + 1, len - sizeof(*ih));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tap_send(c, buf, len + sizeof(*iph) + sizeof(*eh)) < 0)
|
|
||||||
debug("tap: failed to send %lu bytes (IPv4)", len);
|
|
||||||
} else {
|
|
||||||
struct ipv6hdr *ip6h = (struct ipv6hdr *)(eh + 1);
|
|
||||||
char *data = (char *)(ip6h + 1);
|
|
||||||
|
|
||||||
eh->h_proto = ntohs(ETH_P_IPV6);
|
|
||||||
|
|
||||||
memset(ip6h->flow_lbl, 0, 3);
|
|
||||||
ip6h->payload_len = htons(len);
|
|
||||||
ip6h->priority = 0;
|
|
||||||
|
|
||||||
ip6h->saddr = *src;
|
|
||||||
ip6h->daddr = *tap_ip6_daddr(c, src);
|
|
||||||
|
|
||||||
memcpy(data, in, len);
|
|
||||||
|
|
||||||
ip6h->hop_limit = proto;
|
|
||||||
ip6h->version = 0;
|
|
||||||
ip6h->nexthdr = 0;
|
|
||||||
|
|
||||||
if (proto == IPPROTO_UDP) {
|
|
||||||
struct udphdr *uh = (struct udphdr *)(ip6h + 1);
|
|
||||||
|
|
||||||
csum_udp6(uh, &ip6h->saddr, &ip6h->daddr,
|
|
||||||
uh + 1, len - sizeof(*uh));
|
|
||||||
} else if (proto == IPPROTO_ICMPV6) {
|
|
||||||
struct icmp6hdr *ih = (struct icmp6hdr *)(ip6h + 1);
|
|
||||||
|
|
||||||
csum_icmp6(ih, &ip6h->saddr, &ip6h->daddr,
|
|
||||||
ih + 1, len - sizeof(*ih));
|
|
||||||
}
|
|
||||||
ip6h->version = 6;
|
|
||||||
ip6h->nexthdr = proto;
|
|
||||||
ip6h->hop_limit = 255;
|
|
||||||
if (flow) {
|
|
||||||
ip6h->flow_lbl[0] = (flow >> 16) & 0xf;
|
|
||||||
ip6h->flow_lbl[1] = (flow >> 8) & 0xff;
|
|
||||||
ip6h->flow_lbl[2] = (flow >> 0) & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tap_send(c, buf, len + sizeof(*ip6h) + sizeof(*eh)) < 1)
|
|
||||||
debug("tap: failed to send %lu bytes (IPv6)", len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tap_send(c, buf, len + (data - buf)) < 0)
|
||||||
|
debug("tap: failed to send %lu bytes (IPv4)", len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tap_ip6_send() - Send IPv6 packet, with L2 headers, calculating L3/L4 checksums
|
||||||
|
* @c: Execution context
|
||||||
|
* @src: IPv6 source address
|
||||||
|
* @proto: L4 protocol number
|
||||||
|
* @in: Payload
|
||||||
|
* @len: L4 payload length
|
||||||
|
* @flow: Flow label
|
||||||
|
*/
|
||||||
|
void tap_ip6_send(const struct ctx *c, const struct in6_addr *src,
|
||||||
|
uint8_t proto, const char *in, size_t len, uint32_t flow)
|
||||||
|
{
|
||||||
|
char buf[USHRT_MAX];
|
||||||
|
struct ipv6hdr *ip6h =
|
||||||
|
(struct ipv6hdr *)tap_push_l2h(c, buf, ETH_P_IPV6);
|
||||||
|
char *data = (char *)(ip6h + 1);
|
||||||
|
|
||||||
|
ip6h->payload_len = htons(len);
|
||||||
|
ip6h->priority = 0;
|
||||||
|
ip6h->version = 6;
|
||||||
|
ip6h->nexthdr = proto;
|
||||||
|
ip6h->hop_limit = 255;
|
||||||
|
ip6h->saddr = *src;
|
||||||
|
ip6h->daddr = *tap_ip6_daddr(c, src);
|
||||||
|
ip6h->flow_lbl[0] = (flow >> 16) & 0xf;
|
||||||
|
ip6h->flow_lbl[1] = (flow >> 8) & 0xff;
|
||||||
|
ip6h->flow_lbl[2] = (flow >> 0) & 0xff;
|
||||||
|
|
||||||
|
memcpy(data, in, len);
|
||||||
|
|
||||||
|
if (proto == IPPROTO_UDP) {
|
||||||
|
struct udphdr *uh = (struct udphdr *)(ip6h + 1);
|
||||||
|
|
||||||
|
csum_udp6(uh, &ip6h->saddr, &ip6h->daddr,
|
||||||
|
uh + 1, len - sizeof(*uh));
|
||||||
|
} else if (proto == IPPROTO_ICMPV6) {
|
||||||
|
struct icmp6hdr *ih = (struct icmp6hdr *)(ip6h + 1);
|
||||||
|
|
||||||
|
csum_icmp6(ih, &ip6h->saddr, &ip6h->daddr,
|
||||||
|
ih + 1, len - sizeof(*ih));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tap_send(c, buf, len + (data - buf)) < 1)
|
||||||
|
debug("tap: failed to send %lu bytes (IPv6)", len);
|
||||||
}
|
}
|
||||||
|
|
||||||
PACKET_POOL_DECL(pool_l4, UIO_MAXIOV, pkt_buf);
|
PACKET_POOL_DECL(pool_l4, UIO_MAXIOV, pkt_buf);
|
||||||
|
|
6
tap.h
6
tap.h
|
@ -9,8 +9,10 @@
|
||||||
in_addr_t tap_ip4_daddr(const struct ctx *c);
|
in_addr_t tap_ip4_daddr(const struct ctx *c);
|
||||||
const struct in6_addr *tap_ip6_daddr(const struct ctx *c,
|
const struct in6_addr *tap_ip6_daddr(const struct ctx *c,
|
||||||
const struct in6_addr *src);
|
const struct in6_addr *src);
|
||||||
void tap_ip_send(const struct ctx *c, const struct in6_addr *src, uint8_t proto,
|
void tap_ip4_send(const struct ctx *c, in_addr_t src, uint8_t proto,
|
||||||
const char *in, size_t len, uint32_t flow);
|
const char *in, size_t len);
|
||||||
|
void tap_ip6_send(const struct ctx *c, const struct in6_addr *src,
|
||||||
|
uint8_t proto, const char *in, size_t len, uint32_t flow);
|
||||||
int tap_send(const struct ctx *c, const void *data, size_t len);
|
int tap_send(const struct ctx *c, const void *data, size_t len);
|
||||||
void tap_handler(struct ctx *c, int fd, uint32_t events,
|
void tap_handler(struct ctx *c, int fd, uint32_t events,
|
||||||
const struct timespec *now);
|
const struct timespec *now);
|
||||||
|
|
Loading…
Reference in a new issue