299 lines
9.4 KiB
C++
299 lines
9.4 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <time.h>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/if_packet.h>
|
|
#include <linux/if_ether.h>
|
|
#include <linux/if.h>
|
|
#include <linux/sockios.h>
|
|
#include <linux/filter.h>
|
|
|
|
#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);
|
|
}
|