/****************************************************************************** (c) 2002-2004 Patrick Caulfield patrick@debian.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 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. ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for the glibc version number */ #if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || __GLIBC__ >= 3 #include #include /* the L2 protocols */ #include #include #else #include #include #include #include #include /* The L2 protocols */ #endif #include #include #include #include #include #include #include #include #include "utils.h" #include "interfaces.h" #include "interfaces-linux.h" int LATinterfaces::ProtoLAT = ETH_P_LAT; int LATinterfaces::ProtoMOP = ETH_P_DNA_RC; int LinuxInterfaces::Start(int proto) { protocol = proto; // Open raw socket on specified protocol fd = socket(PF_PACKET, SOCK_DGRAM, htons(protocol)); if (fd < 0) { syslog(LOG_ERR, "Can't create protocol socket: %m\n"); return -1; } return 0; } // Return a list of valid interface numbers and the count void LinuxInterfaces::get_all_interfaces(int *ifs, int &num) { struct ifreq ifr; int iindex = 1; int sock = socket(PF_PACKET, SOCK_RAW, 0); num = 0; for (iindex = 1; iindex < 256; iindex++) { ifr.ifr_ifindex = iindex; if (ioctl(sock, SIOCGIFNAME, &ifr) == 0) { // Only use ethernet interfaces ioctl(sock, SIOCGIFHWADDR, &ifr); if (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER) { debuglog(("interface %d: %d\n", num, iindex)); ifs[num++] = iindex; } } } close(sock); } // Print the name of an interface std::string LinuxInterfaces::ifname(int ifn) { struct ifreq ifr; int sock = socket(PF_PACKET, SOCK_RAW, 0); ifr.ifr_ifindex = ifn; if (ioctl(sock, SIOCGIFNAME, &ifr) == 0) { close(sock); return std::string((char *)ifr.ifr_name); } // Didn't find it close(sock); return std::string(""); } // Bind a socket to an interface int LinuxInterfaces::bind_socket(int interface) { struct sockaddr_ll sock_info; sock_info.sll_family = AF_PACKET; sock_info.sll_protocol = htons(protocol); sock_info.sll_ifindex = interface; if (bind(fd, (struct sockaddr *)&sock_info, sizeof(sock_info))) { perror("can't bind socket to i/f %m\n"); return -1; } return 0; } // Find an interface number by name, or // use the first one if name is NULL. int LinuxInterfaces::find_interface(char *name) { struct ifreq ifr; int iindex = 1; int sock = socket(PF_PACKET, SOCK_RAW, 0); // Default "1st" interface if (!name) name = "eth0"; ifr.ifr_ifindex = iindex; for (iindex = 1; iindex < 256; iindex++) { ifr.ifr_ifindex = iindex; if (ioctl(sock, SIOCGIFNAME, &ifr) == 0) { if (strcmp(ifr.ifr_name, name) == 0) { // Also check it's ethernet while we are here ioctl(sock, SIOCGIFHWADDR, &ifr); if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { syslog(LOG_ERR, "Device %s is not ethernet\n", name); return -1; } close(sock); return iindex; } } } // Didn't find it close(sock); return -1; } // true if this class defines one FD for each active // interface, false if one fd is used for all interfaces. bool LinuxInterfaces::one_fd_per_interface() { return false; } // Return the FD for this interface (will only be called once for // select if above returns false) int LinuxInterfaces::get_fd(int ifn) { return fd; } // Send a packet to a given macaddr int LinuxInterfaces::send_packet(int ifn, unsigned char macaddr[], unsigned char *data, int len) { struct sockaddr_ll sock_info; /* Build the sockaddr_ll structure */ sock_info.sll_family = AF_PACKET; sock_info.sll_protocol = htons(protocol); sock_info.sll_ifindex = ifn; sock_info.sll_hatype = 0;//ARPHRD_ETHER; sock_info.sll_pkttype = PACKET_MULTICAST; sock_info.sll_halen = 6; memcpy(sock_info.sll_addr, macaddr, 6); return sendto(fd, data, len, 0, (struct sockaddr *)&sock_info, sizeof(sock_info)); } // Receive a packet from a given interface int LinuxInterfaces::recv_packet(int sockfd, int &ifn, unsigned char macaddr[], unsigned char *data, int maxlen, bool &more) { struct msghdr msg; struct iovec iov; struct sockaddr_ll sock_info; int len; /* Linux only returns 1 packet at a time */ more = false; memset(&msg, 0, sizeof(msg)); msg.msg_name = &sock_info; msg.msg_namelen = sizeof(sock_info); msg.msg_iovlen = 1; msg.msg_iov = &iov; iov.iov_len = maxlen; iov.iov_base = data; len = recvmsg(sockfd, &msg, 0); ifn = sock_info.sll_ifindex; memcpy(macaddr, sock_info.sll_addr, 6); // Ignore packets captured in promiscuous mode. if (sock_info.sll_pkttype == PACKET_OTHERHOST) { debuglog(("Got a rogue packet .. interface probably in promiscuous mode\n")); return 0; } return len; } // Open a connection on an interface // Only necessary for LAT sockets. int LinuxInterfaces::set_lat_multicast(int ifn) { // Add Multicast membership for LAT on socket struct packet_mreq pack_info; /* Fill in socket options */ pack_info.mr_type = PACKET_MR_MULTICAST; pack_info.mr_alen = 6; pack_info.mr_ifindex = ifn; /* This is the LAT multicast address */ pack_info.mr_address[0] = 0x09; pack_info.mr_address[1] = 0x00; pack_info.mr_address[2] = 0x2b; pack_info.mr_address[3] = 0x00; pack_info.mr_address[4] = 0x00; pack_info.mr_address[5] = 0x0f; if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &pack_info, sizeof(pack_info))) { syslog(LOG_ERR, "can't add lat socket multicast : %m\n"); return -1; } /* This is the LAT solicit address */ pack_info.mr_address[0] = 0x09; pack_info.mr_address[1] = 0x00; pack_info.mr_address[2] = 0x2b; pack_info.mr_address[3] = 0x02; pack_info.mr_address[4] = 0x01; pack_info.mr_address[5] = 0x04; if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &pack_info, sizeof(pack_info))) { syslog(LOG_ERR, "can't add lat socket multicast : %m\n"); return -1; } return 0; } // Close an interface. int LinuxInterfaces::remove_lat_multicast(int ifn) { // Add Multicast membership for LAT on socket struct packet_mreq pack_info; /* Fill in socket options */ pack_info.mr_type = PACKET_MR_MULTICAST; pack_info.mr_alen = 6; pack_info.mr_ifindex = ifn; /* This is the LAT multicast address */ pack_info.mr_address[0] = 0x09; pack_info.mr_address[1] = 0x00; pack_info.mr_address[2] = 0x2b; pack_info.mr_address[3] = 0x00; pack_info.mr_address[4] = 0x00; pack_info.mr_address[5] = 0x0f; if (setsockopt(fd, SOL_PACKET, PACKET_DROP_MEMBERSHIP, &pack_info, sizeof(pack_info))) { syslog(LOG_ERR, "can't remove socket multicast : %m\n"); return -1; } /* This is the LAT solicit address */ pack_info.mr_address[0] = 0x09; pack_info.mr_address[1] = 0x00; pack_info.mr_address[2] = 0x2b; pack_info.mr_address[3] = 0x02; pack_info.mr_address[4] = 0x01; pack_info.mr_address[5] = 0x04; if (setsockopt(fd, SOL_PACKET, PACKET_DROP_MEMBERSHIP, &pack_info, sizeof(pack_info))) { syslog(LOG_ERR, "can't remove socket multicast : %m\n"); return -1; } return 0; } // Here's where we know how to instantiate the class. LATinterfaces *LATinterfaces::Create() { return new LinuxInterfaces(); }