/* * Copyright (c) 2010 Peter J. Philipp * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #define _KERNEL 1 #include #undef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include void reply_icmp(char *ip6, int len, char *source, int); int open_filter(char *interface); int ip_cksum(u_int16_t *p, int len); static struct ether_addr *src, *dst; char *interface = "em0"; int main(int argc, char *argv[]) { time_t now; int len, fd; int ds, sslen = sizeof(struct sockaddr_in6); struct sockaddr_in6 sin6; char buf[2048]; char clock[512]; char abuf[INET6_ADDRSTRLEN]; u_int8_t version; struct ip6_hdr *ip6; if (argc != 4) { /* as in faketrace6 em0 00:0c:29:18:78:88 00:0c:29:6d:84:5c */ fprintf(stderr, "usage: faketrace6 [interface] [mymac] [mygatewaymac]\n"); exit(1); } interface = argv[1]; src = ether_aton(argv[2]); dst = ether_aton(argv[3]); openlog("faketrace6", LOG_PID | LOG_NDELAY, LOG_DAEMON); syslog(LOG_INFO, "faketrace6 starting up"); ds = socket(AF_INET6, SOCK_RAW, IPPROTO_DIVERT); if (ds < 0) { perror("socket"); exit(1); } memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(9999); if (bind(ds, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) { perror("bind"); exit(1); } fd = open_filter(interface); if (fd < 0) { perror("open_filter"); exit(1); } #if 1 daemon(0,0); #endif for (;;) { len = recvfrom(ds, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &sslen); if (len < 0) { perror("read"); continue; } if (len < sizeof(struct ip6_hdr)) continue; ip6 = (struct ip6_hdr *)&buf[0]; version = (ip6->ip6_vfc >> 4); if (version != 6) continue; if (ip6->ip6_nxt != IPPROTO_UDP) goto skip; switch (ip6->ip6_hops) { case 1: /* hello */ reply_icmp((char *)ip6, len, "2001:a60:f074:ff::1", fd); break; case 2: reply_icmp((char *)ip6, len, "2001:a60:f074:ff::2", fd); /* why */ break; case 3: inet_ntop(AF_INET6, (char *)&ip6->ip6_src, (char *)&abuf, sizeof(abuf)); syslog(LOG_INFO, "possible traceroute6 from %s", abuf); reply_icmp((char *)ip6, len, "2001:a60:f074:ff::3", fd); /* are */ break; case 4: reply_icmp((char *)ip6, len, "2001:a60:f074:ff::4", fd); /* you */ break; case 5: reply_icmp((char *)ip6, len, "2001:a60:f074:ff::5", fd); /* tracerouting */ break; case 6: now = time(NULL); now %= 86400; snprintf(clock, sizeof(clock), "2001:a60:f074:fe::%x:%x", (now >> 16) & 0xffff, now & 0xffff); /* clock */ reply_icmp((char *)ip6, len, clock , fd); break; default: break; } #if 0 printf("--------------------------------------------------------------------\n"); printf("VERSION: %d\n", version); inet_ntop(AF_INET6, (char *)&ip6->ip6_src, (char *)&abuf, sizeof(abuf)); printf("SOURCE: %s\n", abuf); inet_ntop(AF_INET6, (char *)&ip6->ip6_dst, (char *)&abuf, sizeof(abuf)); printf("DEST: %s\n", abuf); printf("HOPS: %d\n", ip6->ip6_hops); printf("NEXT HEADER: %s\n", (ip6->ip6_nxt == IPPROTO_ICMPV6) ? "ICMP" : (ip6->ip6_nxt == IPPROTO_TCP) ? "TCP" : (ip6->ip6_nxt == IPPROTO_UDP) ? "UDP" : "unknown"); for (i = 0; i < len ; i++) { if (i && i % 16 == 0) printf("\n"); c = buf[i]; printf("%02X", c); } printf("\n"); printf("--------------------------------------------------------------------\n"); #endif if (ip6->ip6_hops < 6) continue; skip: if (sendto(ds, buf, len, 0, (struct sockaddr*)&sin6, sslen) < 0) { perror("write"); } } /* for(;;) */ } void reply_icmp(char *ip6h, int len, char *source, int fd) { struct ether_header *eh; struct in6_addr ia6; struct ip6_hdr *rip6, *ip6; struct icmp6_hdr *icmp6; struct ip6_hdr_pseudo *pseudo; char buf[2048]; char csum[2048]; char *payload; inet_pton(AF_INET6, source, &ia6); printf("replying from %s\n", source); memset(&buf, 0, sizeof(buf)); eh = (struct ether_header *)&buf[0]; memcpy(eh->ether_shost, (char *)src, 6); memcpy(eh->ether_dhost, (char *)dst, 6); eh->ether_type = htons(ETHERTYPE_IPV6); /* IPv6 protocol */ rip6 = (struct ip6_hdr *)&buf[sizeof(struct ether_header)]; ip6 = (struct ip6_hdr *)ip6h; rip6->ip6_vfc = (6 << 4); memcpy((char *)&rip6->ip6_src, (char *)&ia6, sizeof(struct in6_addr)); memcpy((char *)&rip6->ip6_dst, (char *)&ip6->ip6_src, sizeof(struct in6_addr)); rip6->ip6_plen = htons(sizeof(struct icmp6_hdr) + len); rip6->ip6_hops = 0xff; rip6->ip6_nxt = IPPROTO_ICMPV6; icmp6 = (struct icmp6_hdr *)&buf[sizeof(struct ether_header) + sizeof(struct ip6_hdr)]; icmp6->icmp6_type = ICMP6_TIME_EXCEEDED; icmp6->icmp6_code = 0; icmp6->icmp6_cksum = 0; /* later */ payload = (char *)&buf[sizeof(struct ether_header) + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)]; memcpy(payload, ip6h, len); /* now we checksum */ pseudo = (struct ip6_hdr_pseudo *)&csum[0]; pseudo->ip6ph_nxt = IPPROTO_ICMPV6; pseudo->ip6ph_len = htons(sizeof(struct icmp6_hdr) + len); memcpy((char *)&pseudo->ip6ph_src, (char *)&rip6->ip6_src, sizeof(struct in6_addr)); memcpy((char *)&pseudo->ip6ph_dst, (char *)&rip6->ip6_dst, sizeof(struct in6_addr)); memcpy((char *)&csum[sizeof(struct ip6_hdr_pseudo)], icmp6, sizeof(struct icmp6_hdr) + len); icmp6->icmp6_cksum = ip_cksum((u_int16_t *)&csum[0], sizeof(struct ip6_hdr_pseudo) + sizeof(struct icmp6_hdr) + len); write(fd, buf, sizeof(struct ether_header) + len + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)); return; } int open_filter(char *interface) { struct ifreq ifr; char buf[PATH_MAX]; int i = 0, fd; u_int hdrcomplete, dltype; do { snprintf(buf, sizeof(buf), "/dev/bpf%d", i++); fd = open(buf, O_RDWR, 0); } while (fd < 0 && errno == EBUSY); if (fd < 0) { perror("open"); return -1; } #if 0 /* set maximum buffer length to something high */ blen = 32768; if (ioctl(fd, BIOCSBLEN, (u_int)&blen) < 0) { perror("ioctl 1"); close (fd); return -1; } #endif /* set interface on bpf */ memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name) - 1); if (ioctl(fd, BIOCSETIF, &ifr) < 0) { perror("ioctl 2"); close (fd); return -1; } /* write complete frame headers */ hdrcomplete=1; if (ioctl(fd, BIOCSHDRCMPLT, &hdrcomplete) < 0) { perror("ioctl 3"); return -1; } /* * If we're not ethernet return with -1 as there is no point opening * bpf for a utility that is a ethernet spoofer */ if (ioctl(fd, BIOCGDLT, &dltype) < 0) { perror("ioctl 4"); return -1; } if (dltype == DLT_EN10MB) return (fd); fprintf(stderr, "dltype != DLT_EN10MB, missing -l flag?\n"); errno = ENOSYS; close (fd); return -1; } /* * IP_CKSUM - compute the ones complement sum of the ones complement of 16 bit * numbers */ int ip_cksum(u_int16_t *p, int len) { register int nleft = len; register u_int16_t *w = p; register int sum = 0; u_int16_t answer; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return (answer); }