diff --git a/Makefile b/Makefile index 04c7bce..1aa4c67 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,8 @@ OBJS = \ packet.o \ udp4.o \ eth.o \ - rtnetlink.o + rtnetlink.o \ + interface.o all: client test2 diff --git a/client.cpp b/client.cpp index 4f63fe5..18ca6ff 100644 --- a/client.cpp +++ b/client.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -17,64 +18,8 @@ #include "udp4.h" #include "dhcp4.h" #include "eth.h" - -int make_packet(uint8_t * buf, - uint8_t const * eth_source_address, - uint8_t const * eth_destination_address, - uint32_t const ip4_source_address, - uint32_t const ip4_destination_address, - uint32_t const dhcp_transaction_id, - uint32_t const dhcp_message_type) -{ - ////////////////////////////////////////////////////////////////////// - // dhcp4 - ////////////////////////////////////////////////////////////////////// - - int dhcp4_offset = (sizeof (eth::header)) + (sizeof (ip4::header)) + (sizeof (udp4::header)); - int dhcp4_length = dhcp4::make_message(reinterpret_cast(&buf[dhcp4_offset]), - dhcp4::op::bootrequest, - dhcp_transaction_id, - eth_source_address, - dhcp_message_type); - - ////////////////////////////////////////////////////////////////////// - // eth - ////////////////////////////////////////////////////////////////////// - - int eth_offset = 0; - eth::header * eth_header = reinterpret_cast(&buf[eth_offset]); - memcpy(eth_header->destination_address, eth_destination_address, 6); - memcpy(eth_header->source_address, eth_source_address, 6); - eth_header->ether_type = htons(0x0800); - - ////////////////////////////////////////////////////////////////////// - // ip4 - ////////////////////////////////////////////////////////////////////// - - int ip4_offset = (sizeof (eth::header)); - int ip4_payload_length = dhcp4_length + (sizeof (udp4::header)); - int ip4_identification = 55179; - ip4::make_header(reinterpret_cast(&buf[ip4_offset]), - ip4_payload_length, - ip4_identification, - ip4_source_address, - ip4_destination_address); - - ////////////////////////////////////////////////////////////////////// - // udp4 - ////////////////////////////////////////////////////////////////////// - - int udp4_offset = (sizeof (eth::header)) + (sizeof (ip4::header)); - udp4::make_header(reinterpret_cast(&buf[udp4_offset]), - htons(68), - htons(67), - ip4_source_address, - ip4_destination_address, - &buf[dhcp4_offset], - dhcp4_length); - - return dhcp4_offset + dhcp4_length; -} +#include "packet.h" +#include "interface.h" void interface_send(int sock, int interface_index, @@ -161,7 +106,8 @@ int main() perror("ioctl(SIOCGIFINDEX)"); exit(EXIT_FAILURE); } - printf("interface index %d\n", if_index.ifr_ifindex); + int ifa_index = if_index.ifr_ifindex; + printf("interface index %d\n", ifa_index); ////////////////////////////////////////////////////////////////////// // interface address @@ -184,21 +130,46 @@ int main() // bind ////////////////////////////////////////////////////////////////////// - struct sockaddr_ll bind_addr = { + struct sockaddr_ll bind_addr_ll = { .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_ALL), - .sll_ifindex = if_index.ifr_ifindex, + .sll_protocol = htons(ETH_P_IP), + .sll_ifindex = ifa_index, }; - int bret = bind(s, (struct sockaddr *)&bind_addr, (sizeof (struct sockaddr_ll))); - if (bret < 0) { - perror("bind"); + 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); ////////////////////////////////////////////////////////////////////// - // send + // 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(); @@ -210,24 +181,118 @@ int main() uint32_t ip4_source_address = 0x00000000; uint32_t ip4_destination_address = 0xffffffff; - int length = make_packet(buf, - eth_source_address, - eth_destination_address, - ip4_source_address, - ip4_destination_address, - dhcp_transaction_id, - dhcp4::message_type::dhcpdiscover); + 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, if_index.ifr_ifindex, buf, length, eth_destination_address); + 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 ret = recvfrom(s, recvbuf, 16384, flags, - (struct sockaddr *)&recv_address, &socklen); - printf("recv ret %ld\n", ret); + + { + 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); } diff --git a/dhcp4.cpp b/dhcp4.cpp index 0420078..b98b954 100644 --- a/dhcp4.cpp +++ b/dhcp4.cpp @@ -1,5 +1,6 @@ #include #include +#include #include @@ -11,7 +12,8 @@ namespace dhcp4 { uint32_t bootp_message_type, uint32_t transaction_id, uint8_t const * chaddr, - uint32_t dhcp_message_type) + uint32_t dhcp_message_type, + uint32_t requested_ip_address) { memset(buf, 0, (sizeof (message))); message * msg = reinterpret_cast(buf); @@ -48,6 +50,15 @@ namespace dhcp4 { prl[4] = option::domain_name_server; } + if (requested_ip_address != 0) { + uint8_t * addr = &msg->options[index]; + int length = 4; + index += 2 + length; + addr[0] = option::requested_ip_address; + addr[1] = length; + memcpy(&addr[2], &requested_ip_address, 4); + } + uint8_t * end = &msg->options[index]; index += 1; end[0] = 255; diff --git a/dhcp4.h b/dhcp4.h index 476c4aa..6a0da42 100644 --- a/dhcp4.h +++ b/dhcp4.h @@ -51,6 +51,7 @@ namespace dhcp4 { router = 3, domain_name_server = 6, broadcast_address = 28, + requested_ip_address = 50, ip_address_lease_time = 51, option_overload = 52, message_type = 53, @@ -78,7 +79,8 @@ namespace dhcp4 { uint32_t bootp_message_type, uint32_t transaction_id, uint8_t const * chaddr, - uint32_t dhcp_message_type); + uint32_t dhcp_message_type, + uint32_t requested_ip_address); char const * message_type_tostr(int message_type); diff --git a/interface.cpp b/interface.cpp new file mode 100644 index 0000000..6ccbff3 --- /dev/null +++ b/interface.cpp @@ -0,0 +1,114 @@ +#include +#include +#include + +#include +#include +#include + +#include "ip4.h" +#include "rtnetlink.h" +#include "interface.h" + +namespace interface { + static void newaddr(int fd, + uint32_t ifa_index, uint8_t ifa_prefixlen, uint32_t local, uint32_t address) + { + int dret = rtnetlink::send_typeaddr(fd, RTM_NEWADDR, ifa_index, + ifa_prefixlen, local, address); + if (dret < 0) { + exit(EXIT_FAILURE); + } + int eret = rtnetlink::handle_error(fd); + if (eret < 0) { + exit(EXIT_FAILURE); + } + } + + static void deladdr(int fd, + uint32_t ifa_index, uint8_t ifa_prefixlen, uint32_t local, uint32_t address) + { + int dret = rtnetlink::send_typeaddr(fd, RTM_DELADDR, ifa_index, + ifa_prefixlen, local, address); + if (dret < 0) { + exit(EXIT_FAILURE); + } + int eret = rtnetlink::handle_error(fd); + if (eret < 0) { + exit(EXIT_FAILURE); + } + } + + static void newroute(int fd, + uint32_t ifa_index, uint32_t gateway) + { + int dret = rtnetlink::send_newroute(fd, ifa_index, gateway); + if (dret < 0) { + exit(EXIT_FAILURE); + } + int eret = rtnetlink::handle_error(fd); + if (eret < 0) { + exit(EXIT_FAILURE); + } + } + + static void flush_addresses(int fd, + uint32_t ifa_index) + { + int grets = rtnetlink::send_getaddr(fd, ifa_index); + if (grets < 0) { + exit(EXIT_FAILURE); + } + + const int out_length = 64; + rtnetlink::newaddr out[out_length]; + int greth = rtnetlink::handle_getaddr(fd, ifa_index, out, out_length); + if (greth < 0) { + exit(EXIT_FAILURE); + } + + for (int i = 0; i < greth; i++) { + printf("deladdr[%d]:\n", i); + printf(" prefixlen %d\n", out[i].ifa_prefixlen); + printf(" local %s\n", ip4::tostr(out[i].local)); + printf(" address %s\n", ip4::tostr(out[i].address)); + + deladdr(fd, ifa_index, out[i].ifa_prefixlen, out[i].local, out[i].address); + } + } + + void configure(uint32_t ifa_index, + uint8_t ifa_prefixlen, + uint32_t address, + uint32_t gateway) + { + ////////////////////////////////////////////////////////////////////// + // netlink socket + ////////////////////////////////////////////////////////////////////// + + int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + struct sockaddr_nl bsa = { .nl_family = AF_NETLINK }; + int ret = bind(fd, (struct sockaddr *)&bsa, (sizeof (sockaddr_nl))); + if (ret < 0) { + perror("bind"); + exit(EXIT_FAILURE); + } + + ////////////////////////////////////////////////////////////////////// + // + ////////////////////////////////////////////////////////////////////// + + flush_addresses(fd, ifa_index); + + newaddr(fd, + ifa_index, ifa_prefixlen, address, address); + + newroute(fd, + ifa_index, gateway); + } +} diff --git a/interface.h b/interface.h new file mode 100644 index 0000000..6421857 --- /dev/null +++ b/interface.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace interface { + + void configure(uint32_t ifa_index, + uint8_t ifa_prefixlen, + uint32_t address, + uint32_t gateway); + +} diff --git a/netlink.cpp b/netlink.cpp index 3fedaf1..91b8952 100644 --- a/netlink.cpp +++ b/netlink.cpp @@ -44,7 +44,6 @@ int main() // interface index ////////////////////////////////////////////////////////////////////// - char const * interface_name = "wlp174s0"; struct ifreq if_index = {}; strncpy(if_index.ifr_name, interface_name, IFNAMSIZ - 1); int iret = ioctl(fd, SIOCGIFINDEX, &if_index); diff --git a/packet.cpp b/packet.cpp index 9c79e53..c6634c5 100644 --- a/packet.cpp +++ b/packet.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -59,4 +60,60 @@ namespace packet { p->udp4 = udp4_header; p->dhcp4 = dhcp4_message; } + + int make(uint8_t * buf, + uint8_t const * eth_source_address, + uint8_t const * eth_destination_address, + uint32_t const ip4_source_address, + uint32_t const ip4_destination_address, + uint32_t const dhcp_transaction_id, + uint32_t const dhcp_message_type, + uint32_t const requested_ip_address) + { + ////////////////////////////////////////////////////////////////////// + // dhcp4 + ////////////////////////////////////////////////////////////////////// + + int dhcp4_length = dhcp4::make_message(reinterpret_cast(&buf[dhcp4_offset]), + dhcp4::op::bootrequest, + dhcp_transaction_id, + eth_source_address, + dhcp_message_type, + requested_ip_address); + + ////////////////////////////////////////////////////////////////////// + // eth + ////////////////////////////////////////////////////////////////////// + + eth::header * eth_header = reinterpret_cast(&buf[eth_offset]); + memcpy(eth_header->destination_address, eth_destination_address, 6); + memcpy(eth_header->source_address, eth_source_address, 6); + eth_header->ether_type = htons(0x0800); + + ////////////////////////////////////////////////////////////////////// + // ip4 + ////////////////////////////////////////////////////////////////////// + + int ip4_payload_length = dhcp4_length + (sizeof (udp4::header)); + int ip4_identification = 55179; + ip4::make_header(reinterpret_cast(&buf[ip4_offset]), + ip4_payload_length, + ip4_identification, + ip4_source_address, + ip4_destination_address); + + ////////////////////////////////////////////////////////////////////// + // udp4 + ////////////////////////////////////////////////////////////////////// + + udp4::make_header(reinterpret_cast(&buf[udp4_offset]), + htons(68), + htons(67), + ip4_source_address, + ip4_destination_address, + &buf[dhcp4_offset], + dhcp4_length); + + return dhcp4_offset + dhcp4_length; + } } diff --git a/packet.h b/packet.h index 377d7b0..faed5ed 100644 --- a/packet.h +++ b/packet.h @@ -19,4 +19,13 @@ namespace packet { const int ip4_offset = (sizeof (eth::header)); const int udp4_offset = (sizeof (eth::header)) + (sizeof (ip4::header)); const int dhcp4_offset = (sizeof (eth::header)) + (sizeof (ip4::header)) + (sizeof (udp4::header)); + + int make(uint8_t * buf, + uint8_t const * eth_source_address, + uint8_t const * eth_destination_address, + uint32_t const ip4_source_address, + uint32_t const ip4_destination_address, + uint32_t const dhcp_transaction_id, + uint32_t const dhcp_message_type, + uint32_t const requested_ip_address); };