icmp: Work around possible failure on bind() due to e.g. broken SELinux policy
If we can't bind() ping sockets, the echo identifier sent out from the socket won't be the original one seen from the tap. Binding a ping socket doesn't require any security capability, but it might still fail due to a broken SELinux policy, see for example: https://bugzilla.redhat.com/show_bug.cgi?id=1848929 Track the ICMP echo identifier as part of the epoll reference for the socket and replace it in the reply on mismatch. We won't send out the original identifier as sent from the guest, but still better than missing replies. Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
parent
0279ec8eae
commit
9663378d6d
3 changed files with 22 additions and 5 deletions
18
icmp.c
18
icmp.c
|
@ -87,10 +87,17 @@ void icmp_sock_handler(struct ctx *c, union epoll_ref ref, uint32_t events,
|
||||||
struct sockaddr_in6 *sr6 = (struct sockaddr_in6 *)&sr;
|
struct sockaddr_in6 *sr6 = (struct sockaddr_in6 *)&sr;
|
||||||
struct icmp6hdr *ih = (struct icmp6hdr *)buf;
|
struct icmp6hdr *ih = (struct icmp6hdr *)buf;
|
||||||
|
|
||||||
|
id = ntohs(ih->icmp6_identifier);
|
||||||
|
|
||||||
|
/* If bind() fails e.g. because of a broken SELinux policy, this
|
||||||
|
* might happen. Fix up the identifier to match the sent one.
|
||||||
|
*/
|
||||||
|
if (id != ref.icmp.id)
|
||||||
|
ih->icmp6_identifier = htons(ref.icmp.id);
|
||||||
|
|
||||||
/* In PASTA mode, we'll get any reply we send, discard them. */
|
/* In PASTA mode, we'll get any reply we send, discard them. */
|
||||||
if (c->mode == MODE_PASTA) {
|
if (c->mode == MODE_PASTA) {
|
||||||
seq = ntohs(ih->icmp6_sequence);
|
seq = ntohs(ih->icmp6_sequence);
|
||||||
id = ntohs(ih->icmp6_identifier);
|
|
||||||
|
|
||||||
if (icmp_id_map[V6][id].seq == seq)
|
if (icmp_id_map[V6][id].seq == seq)
|
||||||
return;
|
return;
|
||||||
|
@ -103,9 +110,12 @@ void icmp_sock_handler(struct ctx *c, union epoll_ref ref, uint32_t events,
|
||||||
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;
|
||||||
|
|
||||||
|
id = ntohs(ih->un.echo.id);
|
||||||
|
if (id != ref.icmp.id)
|
||||||
|
ih->un.echo.id = htons(ref.icmp.id);
|
||||||
|
|
||||||
if (c->mode == MODE_PASTA) {
|
if (c->mode == MODE_PASTA) {
|
||||||
seq = ntohs(ih->un.echo.sequence);
|
seq = ntohs(ih->un.echo.sequence);
|
||||||
id = ntohs(ih->un.echo.id);
|
|
||||||
|
|
||||||
if (icmp_id_map[V4][id].seq == seq)
|
if (icmp_id_map[V4][id].seq == seq)
|
||||||
return;
|
return;
|
||||||
|
@ -148,7 +158,7 @@ int icmp_tap_handler(struct ctx *c, int af, void *addr,
|
||||||
if (msg[0].l4_len < sizeof(*ih) || ih->type != ICMP_ECHO)
|
if (msg[0].l4_len < sizeof(*ih) || ih->type != ICMP_ECHO)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
id = ntohs(ih->un.echo.id);
|
iref.id = id = ntohs(ih->un.echo.id);
|
||||||
|
|
||||||
if ((s = icmp_id_map[V4][id].sock) <= 0) {
|
if ((s = icmp_id_map[V4][id].sock) <= 0) {
|
||||||
s = sock_l4(c, AF_INET, IPPROTO_ICMP, id, 0, iref.u32);
|
s = sock_l4(c, AF_INET, IPPROTO_ICMP, id, 0, iref.u32);
|
||||||
|
@ -177,7 +187,7 @@ int icmp_tap_handler(struct ctx *c, int af, void *addr,
|
||||||
(ih->icmp6_type != 128 && ih->icmp6_type != 129))
|
(ih->icmp6_type != 128 && ih->icmp6_type != 129))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
id = ntohs(ih->icmp6_identifier);
|
iref.id = id = ntohs(ih->icmp6_identifier);
|
||||||
if ((s = icmp_id_map[V6][id].sock) <= 0) {
|
if ((s = icmp_id_map[V6][id].sock) <= 0) {
|
||||||
s = sock_l4(c, AF_INET6, IPPROTO_ICMPV6, id, 0,
|
s = sock_l4(c, AF_INET6, IPPROTO_ICMPV6, id, 0,
|
||||||
iref.u32);
|
iref.u32);
|
||||||
|
|
4
icmp.h
4
icmp.h
|
@ -15,10 +15,12 @@ void icmp_timer(struct ctx *c, struct timespec *ts);
|
||||||
* union icmp_epoll_ref - epoll reference portion for ICMP tracking
|
* union icmp_epoll_ref - epoll reference portion for ICMP tracking
|
||||||
* @v6: Set for IPv6 sockets or connections
|
* @v6: Set for IPv6 sockets or connections
|
||||||
* @u32: Opaque u32 value of reference
|
* @u32: Opaque u32 value of reference
|
||||||
|
* @id: Associated echo identifier, needed if bind() fails
|
||||||
*/
|
*/
|
||||||
union icmp_epoll_ref {
|
union icmp_epoll_ref {
|
||||||
struct {
|
struct {
|
||||||
uint32_t v6:1;
|
uint32_t v6:1,
|
||||||
|
id:16;
|
||||||
};
|
};
|
||||||
uint32_t u32;
|
uint32_t u32;
|
||||||
};
|
};
|
||||||
|
|
5
tap.c
5
tap.c
|
@ -134,6 +134,11 @@ void tap_ip_send(struct ctx *c, struct in6_addr *src, uint8_t proto,
|
||||||
struct udphdr *uh = (struct udphdr *)(iph + 1);
|
struct udphdr *uh = (struct udphdr *)(iph + 1);
|
||||||
|
|
||||||
uh->check = 0;
|
uh->check = 0;
|
||||||
|
} else if (iph->protocol == IPPROTO_ICMP) {
|
||||||
|
struct icmphdr *ih = (struct icmphdr *)(iph + 1);
|
||||||
|
|
||||||
|
ih->checksum = 0;
|
||||||
|
ih->checksum = csum_unaligned(ih, len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
tap_send(c, buf, len + sizeof(*iph) + sizeof(*eh), 1);
|
tap_send(c, buf, len + sizeof(*iph) + sizeof(*eh), 1);
|
||||||
|
|
Loading…
Reference in a new issue