X-Git-Url: https://www.tinc-vpn.org/git/browse?a=blobdiff_plain;ds=sidebyside;f=test%2Fpong.c;fp=test%2Fpong.c;h=65a9075cd77ba744a66a6c3e68bcfaa637c6954e;hb=b80cbaba040775ba20159b20d02c8c903c84e0e1;hp=0000000000000000000000000000000000000000;hpb=fe1d0043c81b26f337bdce63dd290d882b01cf21;p=tinc diff --git a/test/pong.c b/test/pong.c new file mode 100644 index 00000000..65a9075c --- /dev/null +++ b/test/pong.c @@ -0,0 +1,194 @@ +/* + pong.c -- ICMP echo reply generator + Copyright (C) 2013 Guus Sliepen + + 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 \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; +}