b80cbaba04
This is a more complicated test with one tinc daemon using a tap interface (therefore requiring root), and a second one using a multicast interface. A separate program "pong" is listening on the same multicast address, and waits for ARP and ICMP packets, responding to ICMP echo packets with replies. This test doesn't require any configuration of the tap interface.
194 lines
5.4 KiB
C
194 lines
5.4 KiB
C
/*
|
|
pong.c -- ICMP echo reply generator
|
|
Copyright (C) 2013 Guus Sliepen <guus@tinc-vpn.org>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "../src/system.h"
|
|
|
|
uint8_t mymac[6] = {6, 5, 5, 6, 5, 5};
|
|
|
|
static ssize_t do_arp(uint8_t *buf, ssize_t len, struct sockaddr_in *in) {
|
|
struct ether_arp arp;
|
|
memcpy(&arp, buf + 14, sizeof arp);
|
|
|
|
// Is it a valid ARP request?
|
|
if(ntohs(arp.arp_hrd) != ARPHRD_ETHER || ntohs(arp.arp_pro) != ETH_P_IP || arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof in->sin_addr.s_addr || ntohs(arp.arp_op) != ARPOP_REQUEST)
|
|
return 0;
|
|
|
|
// Does it match our address?
|
|
if(memcmp(&in->sin_addr.s_addr, arp.arp_tpa, 4))
|
|
return 0;
|
|
|
|
// Swap addresses
|
|
memcpy(buf, buf + 6, 6);
|
|
memcpy(buf + 6, mymac, 6);
|
|
|
|
arp.arp_op = htons(ARPOP_REPLY);
|
|
memcpy(arp.arp_tpa, arp.arp_spa, sizeof arp.arp_tpa);
|
|
memcpy(arp.arp_tha, arp.arp_sha, sizeof arp.arp_tha);
|
|
memcpy(arp.arp_spa, &in->sin_addr.s_addr, sizeof in->sin_addr.s_addr);
|
|
memcpy(arp.arp_sha, mymac, 6);
|
|
|
|
memcpy(buf + 14, &arp, sizeof arp);
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t do_ipv4(uint8_t *buf, ssize_t len, struct sockaddr_in *in) {
|
|
struct ip ip;
|
|
struct icmp icmp;
|
|
|
|
// Does it match our address?
|
|
if(memcmp(buf, mymac, 6))
|
|
return 0;
|
|
|
|
memcpy(&ip, buf + 14, sizeof ip);
|
|
if(memcmp(&ip.ip_dst, &in->sin_addr.s_addr, 4))
|
|
return 0;
|
|
|
|
// Is it an ICMP echo request?
|
|
if(ip.ip_p != IPPROTO_ICMP)
|
|
return 0;
|
|
|
|
memcpy(&icmp, buf + 14 + sizeof ip, sizeof icmp);
|
|
if(icmp.icmp_type != ICMP_ECHO)
|
|
return 0;
|
|
|
|
// Return an echo reply
|
|
memcpy(buf, buf + 6, 6);
|
|
memcpy(buf + 6, mymac, 6);
|
|
|
|
ip.ip_dst = ip.ip_src;
|
|
memcpy(&ip.ip_src, &in->sin_addr.s_addr, 4);
|
|
|
|
icmp.icmp_type = ICMP_ECHOREPLY;
|
|
|
|
memcpy(buf + 14, &ip, sizeof ip);
|
|
memcpy(buf + 14 + sizeof ip, &icmp, sizeof icmp);
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t do_ipv6(uint8_t *buf, ssize_t len, struct sockaddr_in6 *in) {
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
if(argc != 4) {
|
|
fprintf(stderr, "Usage: %s <multicast address> <port> <ping address>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
struct addrinfo hints = {}, *ai = NULL;
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
hints.ai_flags = AI_ADDRCONFIG;
|
|
|
|
errno = ENOENT;
|
|
if(getaddrinfo(argv[1], argv[2], &hints, &ai) || !ai) {
|
|
fprintf(stderr, "Could not resolve %s port %s: %s\n", argv[1], argv[2], strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
int fd;
|
|
fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
|
if(!fd) {
|
|
fprintf(stderr, "Could not create socket: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
static const int one = 1;
|
|
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof one);
|
|
|
|
if(bind(fd, ai->ai_addr, ai->ai_addrlen)) {
|
|
fprintf(stderr, "Could not bind socket: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
switch(ai->ai_family) {
|
|
case AF_INET: {
|
|
struct ip_mreq mreq;
|
|
struct sockaddr_in in;
|
|
memcpy(&in, ai->ai_addr, sizeof in);
|
|
mreq.imr_multiaddr.s_addr = in.sin_addr.s_addr;
|
|
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
|
if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof mreq)) {
|
|
fprintf(stderr, "Cannot join multicast group: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
#ifdef IP_MULTICAST_LOOP
|
|
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&one, sizeof one);
|
|
#endif
|
|
} break;
|
|
|
|
#ifdef IPV6_JOIN_GROUP
|
|
case AF_INET6: {
|
|
struct ipv6_mreq mreq;
|
|
struct sockaddr_in6 in6;
|
|
memcpy(&in6, ai->ai_addr, sizeof in6);
|
|
memcpy(&mreq.ipv6mr_multiaddr, &in6.sin6_addr, sizeof mreq.ipv6mr_multiaddr);
|
|
mreq.ipv6mr_interface = in6.sin6_scope_id;
|
|
if(setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void *)&mreq, sizeof mreq)) {
|
|
fprintf(stderr, "Cannot join multicast group: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
#ifdef IPV6_MULTICAST_LOOP
|
|
setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const void *)&one, sizeof one);
|
|
#endif
|
|
} break;
|
|
#endif
|
|
|
|
default:
|
|
fprintf(stderr, "Multicast for address family %hx unsupported\n", ai->ai_family);
|
|
return 1;
|
|
}
|
|
|
|
errno = ENOENT;
|
|
struct addrinfo *ai2 = NULL;
|
|
if(getaddrinfo(argv[3], NULL, &hints, &ai2) || !ai2) {
|
|
fprintf(stderr, "Could not resolve %s: %s\n", argv[3], strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
while(true) {
|
|
uint8_t buf[10000];
|
|
struct sockaddr src;
|
|
socklen_t srclen;
|
|
ssize_t len = recvfrom(fd, buf, sizeof buf, 0, &src, &srclen);
|
|
if(len <= 0)
|
|
break;
|
|
|
|
// Ignore short packets.
|
|
if(len < 14)
|
|
continue;
|
|
|
|
uint16_t type = buf[12] << 8 | buf[13];
|
|
|
|
if(ai2->ai_family == AF_INET && type == ETH_P_IP)
|
|
len = do_ipv4(buf, len, (struct sockaddr_in *)ai2->ai_addr);
|
|
else if(ai2->ai_family == AF_INET && type == ETH_P_ARP)
|
|
len = do_arp(buf, len, (struct sockaddr_in *)ai2->ai_addr);
|
|
else if(ai2->ai_family == AF_INET6 && type == ETH_P_IPV6)
|
|
len = do_ipv6(buf, len, (struct sockaddr_in6 *)ai2->ai_addr);
|
|
else
|
|
continue;
|
|
|
|
if(len > 0)
|
|
sendto(fd, buf, len, 0, ai->ai_addr, ai->ai_addrlen);
|
|
}
|
|
|
|
return 0;
|
|
}
|