/* * $Id: sasp.c,v 0.1-alpha 2004/03/06 17:29:17 sviat Exp $ * * * Copyright (c) 2004 Luigi Pizzirani * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * */ #define _BSD_SOURCE 1 #include #include #include #include #include #define HASHSIZE 65535 #define FNV_prime 16777619 struct ifreqlist { struct ifaliasreq req; struct ifreqlist *ifl_next; } *hashtable[HASHSIZE]; unsigned long pos, pos_table[HASHSIZE]; int k, entries; struct ifreqlist * alias(char*, struct sockaddr_in, struct sockaddr_in, struct sockaddr_in); void inject_packet(libnet_t *, u_char *, u_char *, u_char *, u_char *); unsigned long hash(const char *, int); void initialise_hash_table(void); void initialise_pos_table(void); void create_hash_table(char *, struct ifreqlist *); struct ifreqlist * ifaddrs(struct ifreqlist *); void remove_alias(void); int main(int argc, char **argv) { bpf_u_int32 net, their_net, alias_net, alias_broadnet, mask, alias_mask, alias_broadmask, ip, myip, their_ip; pcap_t *handle; char device[IFNAMSIZ], errbuf[PCAP_ERRBUF_SIZE], err_buf[LIBNET_ERRBUF_SIZE], buf[16]; const u_char *packet; struct bpf_program filter; struct pcap_pkthdr pkthdr; const struct libnet_ethernet_hdr *ether_header; struct ether_arp *arphdr; struct libnet_ethernet_hdr *ether_header2; libnet_t *lt; struct libnet_ether_addr *ether_addr; struct sockaddr_in sin, sin2, sin3; struct ifreqlist *request, ifrequest; unsigned long *ptr; char aliasmask[] ="0.0.0.254"; char aliasbroadmask[] = "0.0.0.255"; char fil[] = "arp"; int on = 1; if (argc != 2) { printf("Usage: %s interface.\n", argv[0]); exit(1); } strlcpy(device, argv[1], IFNAMSIZ); if (pcap_lookupnet(device, &net, &mask, errbuf) == -1) { printf("pcap_lookupnet(): %s\n", errbuf); exit(1); } initialise_hash_table(); initialise_pos_table(); request = NULL; for( ; ; ) { if (entries > HASHSIZE) remove_alias(); if ( (handle = pcap_open_live(device, 200, 0, 10, errbuf)) == NULL) { printf("pcap_open_live(): %s\n", errbuf); exit(1); } if (ioctl(pcap_fileno(handle), BIOCIMMEDIATE, &on) == -1) { perror("ioctl"); exit(1); } if (pcap_compile(handle, &filter, fil, 0, net) == -1) { printf("pcap_compile(): %s\n", pcap_geterr(handle)); exit(1); } if (pcap_setfilter(handle, &filter) == -1) { printf("pcap_setfilter(): %s\n", pcap_geterr(handle)); exit(1); } memset(&sin.sin_addr, 0, sizeof(sin.sin_addr)); memset(&sin2.sin_addr, 0, sizeof(sin2.sin_addr)); memset(&sin3.sin_addr, 0, sizeof(sin3.sin_addr)); sin.sin_len = sin2.sin_len = sin3.sin_len = sizeof(struct sockaddr); sin.sin_family = sin2.sin_family = sin3.sin_family = AF_INET; while ( (packet = pcap_next(handle, &pkthdr)) == NULL) ; lt = libnet_init(LIBNET_LINK, device, err_buf); ether_addr = libnet_get_hwaddr(lt); myip = libnet_get_ipaddr4(lt); ip = ntohl(myip); ether_header = (struct libnet_ethernet_hdr *) packet; arphdr = (struct ether_arp *) (packet + sizeof(struct libnet_ethernet_hdr)); if (arphdr->arp_tpa[3] != arphdr->arp_spa[3]) { snprintf(buf, sizeof(buf), "%d.%d.%d.%d", arphdr->arp_spa[0], arphdr->arp_spa[1], arphdr->arp_spa[2], arphdr->arp_spa[3]); inet_pton(AF_INET, buf, &sin.sin_addr); their_ip = sin.sin_addr.s_addr; their_net = their_ip & mask; inet_pton(AF_INET, aliasmask, &sin.sin_addr); inet_pton(AF_INET, aliasbroadmask, &sin2.sin_addr); alias_mask = sin.sin_addr.s_addr; alias_broadmask = sin2.sin_addr.s_addr; alias_net = alias_mask | their_net; alias_broadnet = alias_broadmask | their_net; sin.sin_addr.s_addr = alias_net; sin2.sin_addr.s_addr = alias_broadnet; sin3.sin_addr.s_addr = mask; if (their_net == net) inject_packet(lt, ether_addr->ether_addr_octet,arphdr->arp_tpa, arphdr->arp_sha, arphdr->arp_spa); else { request = alias(device, sin, sin2, sin3); create_hash_table(request->req.ifra_addr.sa_data, request); inject_packet(lt, ether_addr->ether_addr_octet, arphdr->arp_tpa, arphdr->arp_sha, arphdr->arp_spa); inject_packet(lt, arphdr->arp_sha, arphdr->arp_spa, ether_addr->ether_addr_octet, (u_char *) &myip); } } pcap_close(handle); libnet_destroy(lt); } } struct ifreqlist * alias(char *ifdevice, struct sockaddr_in sin, struct sockaddr_in sin2, struct sockaddr_in sin3) { int sockfd; struct ifaliasreq aliasreq; struct ifreqlist *reqlist; if ( (reqlist = (struct ifreqlist *) malloc(sizeof(struct ifreqlist))) == NULL) { perror("malloc"); exit(1); } memset(&aliasreq, 0, sizeof(aliasreq)); strlcpy(aliasreq.ifra_name, ifdevice, IFNAMSIZ); memcpy(&aliasreq.ifra_addr, &sin, sizeof(sin)); memcpy(&aliasreq.ifra_broadaddr, &sin2, sizeof(sin2)); memcpy(&aliasreq.ifra_mask, &sin3, sizeof(sin3)); memset(&reqlist->req, 0, sizeof(reqlist->req)); memcpy(&reqlist->req, &aliasreq, sizeof(aliasreq)); if ( (sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(1); } if (ioctl(sockfd, SIOCAIFADDR, &aliasreq) == -1) { perror("ioctl"); exit(1); } close(sockfd); return reqlist; } void inject_packet(libnet_t *lt, u_char *hwsrc, u_char *psrc, u_char *hwdst, u_char *pdst) { int w; libnet_build_arp(ARPHRD_ETHER, ETHERTYPE_IP, 6, 4, ARPOP_REPLY, hwsrc, psrc, hwdst, pdst, NULL, 0, lt, 0); libnet_build_ethernet(hwdst, hwsrc, ETHERTYPE_ARP, NULL, 0, lt, 0); w = libnet_write(lt); if (w == -1) { printf("libnet_write(): %s\n", libnet_geterror(lt)); exit(1); } } void create_hash_table(char *key, struct ifreqlist *structure) { if (ifaddrs(structure) == NULL) { struct ifreqlist *previous_ptr, *current_ptr, *next_ptr; next_ptr = structure; pos = hash(key, strlen(key)); if (hashtable[pos] == NULL) { hashtable[pos] = next_ptr; pos_table[k++] = pos; } else { current_ptr = hashtable[pos]; while (current_ptr != NULL) { previous_ptr = current_ptr; current_ptr = current_ptr->ifl_next; } previous_ptr->ifl_next = next_ptr; } next_ptr->ifl_next = NULL; ++entries; } } struct ifreqlist * ifaddrs(struct ifreqlist *structure) { int sockfd; register int i = 0; struct ifreqlist *list; struct ifreq delreq; for (list = hashtable[hash(structure->req.ifra_addr.sa_data, strlen(structure->req.ifra_addr.sa_data))]; list != NULL; list = list->ifl_next) { memset(&delreq, 0, sizeof(delreq)); memcpy(delreq.ifr_name, list->req.ifra_name, IFNAMSIZ); memcpy(&delreq.ifr_addr, &list->req.ifra_addr, sizeof(list->req.ifra_addr)); if ( (sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(1); } if ( (ioctl(sockfd, SIOCGIFADDR, &delreq)) == -1) { perror("ioctl"); exit(1); } close(sockfd); if (!strncmp(structure->req.ifra_addr.sa_data, delreq.ifr_addr.sa_data, sizeof(delreq.ifr_addr.sa_data))) return structure; } return NULL; } void remove_alias(void) { int sockfd; register int i = 0; struct ifreqlist *list, *ptr; while(i < HASHSIZE) { for (list = hashtable[pos_table[i]]; list != NULL; list = ptr) { if ( (sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(1); } if ( (ioctl(sockfd, SIOCDIFADDR, &list->req)) == -1) { perror("ioctl"); exit(1); } close(sockfd); ptr = list->ifl_next; free(list); } ++i; } initialise_hash_table(); initialise_pos_table(); entries = 0; k = 0; } /* Fowler / Noll / Vo (FNV) Hash algorythm */ unsigned long hash(const char *p, int s) { unsigned long h = 2166136261UL;/* FNV-1a hash */ int i = 0; for (; i < s; i++) h = ((h ^ p[i]) * FNV_prime); return (h % HASHSIZE); } void initialise_hash_table(void) { register int count; for (count = 0; count < HASHSIZE; count++) hashtable[count] = NULL; } void initialise_pos_table(void) { register int count; for (count = 0; count < HASHSIZE; count++) pos_table[count] = -1; }