#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ip4.h" #include "udp4.h" #include "dhcp4.h" #include "eth.h" #include "packet.h" #include "interface.h" void interface_send(int sock, int interface_index, void * buf, int length, uint8_t const * eth_destination_address) { struct sockaddr_ll socket_address; socket_address.sll_ifindex = interface_index; socket_address.sll_halen = ETH_ALEN; memcpy(socket_address.sll_addr, eth_destination_address, 6); int flags = 0; int sret = sendto(sock, buf, length, flags, (struct sockaddr *)&socket_address, sizeof(struct sockaddr_ll)); if (sret < 0) { perror("sendto"); exit(EXIT_FAILURE); } } void attach_dhcp_filter(int sock) { struct sock_filter filter[] = { { 0x28, 0, 0, 0x0000000c }, { 0x15, 0, 8, 0x00000800 }, { 0x30, 0, 0, 0x00000017 }, { 0x15, 0, 6, 0x00000011 }, { 0x28, 0, 0, 0x00000014 }, { 0x45, 4, 0, 0x00001fff }, { 0xb1, 0, 0, 0x0000000e }, { 0x48, 0, 0, 0x00000010 }, { 0x15, 0, 1, 0x00000044 }, { 0x06, 0, 0, 0x00040000 }, { 0x06, 0, 0, 0000000000 }, }; struct sock_fprog fprog = { .len = sizeof(filter) / sizeof(filter[0]), .filter = filter, }; int ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, (sizeof (fprog))); if (ret < 0) { perror("setsockopt(SO_ATTACH_FILTER)"); exit(EXIT_FAILURE); } } int main() { srand(time(NULL)); uint8_t buf[16384]; ////////////////////////////////////////////////////////////////////// // socket ////////////////////////////////////////////////////////////////////// int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { perror("socket"); exit(EXIT_FAILURE); } { int enable = 1; int ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, (sizeof (int))); if (ret < 0) { perror("setsockopt(SO_BROADCAST)"); exit(EXIT_FAILURE); } } ////////////////////////////////////////////////////////////////////// // interface index ////////////////////////////////////////////////////////////////////// char const * interface_name = "wlp174s0"; struct ifreq if_index; memset(&if_index, 0, (sizeof (struct ifreq))); strncpy(if_index.ifr_name, interface_name, IFNAMSIZ - 1); int iret = ioctl(s, SIOCGIFINDEX, &if_index); if (iret < 0) { perror("ioctl(SIOCGIFINDEX)"); exit(EXIT_FAILURE); } int ifa_index = if_index.ifr_ifindex; printf("interface index %d\n", ifa_index); ////////////////////////////////////////////////////////////////////// // interface address ////////////////////////////////////////////////////////////////////// struct ifreq if_hwaddr; memset(&if_hwaddr, 0, sizeof(struct ifreq)); strncpy(if_hwaddr.ifr_name, interface_name, IFNAMSIZ - 1); int hret = ioctl(s, SIOCGIFHWADDR, &if_hwaddr); if (hret < 0) { perror("ioctl(SIOCGIFHWADDR)"); exit(EXIT_FAILURE); } printf("hardware address %02x:%02x:%02x:%02x:%02x:%02x\n", (uint8_t)if_hwaddr.ifr_hwaddr.sa_data[0], (uint8_t)if_hwaddr.ifr_hwaddr.sa_data[1], (uint8_t)if_hwaddr.ifr_hwaddr.sa_data[2], (uint8_t)if_hwaddr.ifr_hwaddr.sa_data[3], (uint8_t)if_hwaddr.ifr_hwaddr.sa_data[4], (uint8_t)if_hwaddr.ifr_hwaddr.sa_data[5]); ////////////////////////////////////////////////////////////////////// // bind ////////////////////////////////////////////////////////////////////// struct sockaddr_ll bind_addr_ll = { .sll_family = AF_PACKET, .sll_protocol = htons(ETH_P_IP), .sll_ifindex = ifa_index, }; int bret_ll = bind(s, (struct sockaddr *)&bind_addr_ll, (sizeof (struct sockaddr_ll))); if (bret_ll < 0) { perror("bind ll"); exit(EXIT_FAILURE); } attach_dhcp_filter(s); ////////////////////////////////////////////////////////////////////// // fake socket ////////////////////////////////////////////////////////////////////// int sf; { sf = socket(AF_INET, SOCK_DGRAM, 0); if (sf < 0) { perror("socket"); exit(EXIT_FAILURE); } // this has the effect of inhibiting linux's generation of icmp // "destination unreachable" packets struct sockaddr_in bind_addr_in = { .sin_family = AF_INET, .sin_port = htons(68), .sin_addr = { .s_addr = INADDR_ANY }, }; int bret_in = bind(sf, (struct sockaddr *)&bind_addr_in, (sizeof (struct sockaddr_in))); if (bret_in < 0) { perror("bind in"); exit(EXIT_FAILURE); } } ////////////////////////////////////////////////////////////////////// // dhcp send discover ////////////////////////////////////////////////////////////////////// uint32_t dhcp_transaction_id = rand(); uint8_t eth_source_address[6] = {}; memcpy(eth_source_address, if_hwaddr.ifr_hwaddr.sa_data, 6); uint8_t eth_destination_address[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; uint32_t ip4_source_address = 0x00000000; uint32_t ip4_destination_address = 0xffffffff; int length = packet::make(buf, eth_source_address, eth_destination_address, ip4_source_address, ip4_destination_address, dhcp_transaction_id, dhcp4::message_type::dhcpdiscover, 0); interface_send(s, ifa_index, buf, length, eth_destination_address); ////////////////////////////////////////////////////////////////////// // dhcp recv offer ////////////////////////////////////////////////////////////////////// packet::packet p{}; dhcp4::options options{}; uint8_t recvbuf[65536]; { struct sockaddr_ll recv_address; int flags = 0; socklen_t socklen = (sizeof (struct sockaddr_ll)); ssize_t rret = recvfrom(s, recvbuf, (sizeof (recvbuf)), flags, (struct sockaddr *)&recv_address, &socklen); if (rret < 0) { perror("recvfrom"); exit(EXIT_FAILURE); } packet::parse(recvbuf, rret, &p); printf("dhcp4 op: %d\n", p.dhcp4->op); printf("dhcp4 yiaddr: %s\n", ip4::tostr(p.dhcp4->yiaddr)); int dhcp4_options_length = rret - (packet::dhcp4_offset + (offsetof (dhcp4::message, options))); dhcp4::parse_options(p.dhcp4, dhcp4_options_length, &options); printf("message type: %s\n", dhcp4::message_type_tostr(options.message_type)); printf("subnet mask: %s\n", ip4::tostr(options.subnet_mask)); printf("router: %s\n", ip4::tostr(options.router)); printf("domain name server: %s\n", ip4::tostr(options.domain_name_server)); assert(p.dhcp4->op == dhcp4::op::bootreply); assert(options.message_type == dhcp4::message_type::dhcpoffer); } ////////////////////////////////////////////////////////////////////// // dhcp send request ////////////////////////////////////////////////////////////////////// { int length = packet::make(buf, eth_source_address, eth_destination_address, ip4_source_address, ip4_destination_address, dhcp_transaction_id, dhcp4::message_type::dhcprequest, p.dhcp4->yiaddr); interface_send(s, ifa_index, buf, length, eth_destination_address); } ////////////////////////////////////////////////////////////////////// // dhcp recv ack ////////////////////////////////////////////////////////////////////// { struct sockaddr_ll recv_address; int flags = 0; socklen_t socklen = (sizeof (struct sockaddr_ll)); ssize_t rret = recvfrom(s, recvbuf, (sizeof (recvbuf)), flags, (struct sockaddr *)&recv_address, &socklen); if (rret < 0) { perror("recvfrom"); exit(EXIT_FAILURE); } packet::parse(recvbuf, rret, &p); printf("dhcp4 op: %d\n", p.dhcp4->op); printf("dhcp4 yiaddr: %s\n", ip4::tostr(p.dhcp4->yiaddr)); int dhcp4_options_length = rret - (packet::dhcp4_offset + (offsetof (dhcp4::message, options))); dhcp4::parse_options(p.dhcp4, dhcp4_options_length, &options); printf("message type: %s\n", dhcp4::message_type_tostr(options.message_type)); printf("subnet mask: %s\n", ip4::tostr(options.subnet_mask)); printf("router: %s\n", ip4::tostr(options.router)); printf("domain name server: %s\n", ip4::tostr(options.domain_name_server)); assert(p.dhcp4->op == dhcp4::op::bootreply); assert(options.message_type == dhcp4::message_type::dhcpack); } ////////////////////////////////////////////////////////////////////// // interface configure ////////////////////////////////////////////////////////////////////// uint32_t mask = ntohl(options.subnet_mask); uint8_t prefix_length = 32 - __builtin_ctz(mask); printf("prefix_length %d\n", prefix_length); interface::configure(ifa_index, prefix_length, p.dhcp4->yiaddr, options.router); ////////////////////////////////////////////////////////////////////// // exit ////////////////////////////////////////////////////////////////////// close(s); close(sf); exit(EXIT_SUCCESS); }