This patch modifies the DHCP client to support sending DHCP requests through an IPv6 GRE tunnel.
The sendto system call on a SOCK_DGRAM packet socket fails when used with an IPv6 GRE tunnel. This occurs because the GRE tunnel implementation in the Linux kernel does not add a hardware header to the packet, resulting in an "Invalid Argument" error. Using a raw socket, the patch ensures proper functionality when using DHCP over IPv6 GRE tunnels. Signed-off-by: Emanuele Santini <[email protected]> --- networking/udhcp/common.h | 2 +- networking/udhcp/dhcpc.c | 2 +- networking/udhcp/dhcpd.c | 2 +- networking/udhcp/packet.c | 84 +++++++++++++++++++++++++-------------- 4 files changed, 58 insertions(+), 32 deletions(-) diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h index 3ef371a7c..805aeff73 100644 --- a/networking/udhcp/common.h +++ b/networking/udhcp/common.h @@ -367,7 +367,7 @@ int udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd) FAST_FUNC; int udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt, uint32_t source_nip, int source_port, uint32_t dest_nip, int dest_port, const uint8_t *dest_arp, - int ifindex) FAST_FUNC; + const char *if_name) FAST_FUNC; int udhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt, uint32_t source_nip, int source_port, diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index e44086c2e..d38118b53 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -693,7 +693,7 @@ static int raw_bcast_from_client_data_ifindex(struct dhcp_packet *packet, uint32 return udhcp_send_raw_packet(packet, /*src*/ src_nip, CLIENT_PORT, /*dst*/ INADDR_BROADCAST, SERVER_PORT, MAC_BCAST_ADDR, - client_data.ifindex); + client_data.interface); } static int bcast_or_ucast(struct dhcp_packet *packet, uint32_t ciaddr, uint32_t server) diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c index 2904119e5..ca904542a 100644 --- a/networking/udhcp/dhcpd.c +++ b/networking/udhcp/dhcpd.c @@ -603,7 +603,7 @@ static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadc udhcp_send_raw_packet(dhcp_pkt, /*src*/ server_data.server_nip, SERVER_PORT, /*dst*/ ciaddr, CLIENT_PORT, chaddr, - server_data.ifindex); + server_data.interface); } /* Send a packet to gateway_nip using the kernel ip stack */ diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c index f9dc11d01..678040b55 100644 --- a/networking/udhcp/packet.c +++ b/networking/udhcp/packet.c @@ -11,6 +11,7 @@ #include <netinet/in.h> #include <netinet/if_ether.h> #include <netpacket/packet.h> +#include <net/if.h> #if ENABLE_UDHCPC || ENABLE_UDHCPD void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type) @@ -105,38 +106,47 @@ int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd) return bytes; } -/* Construct a ip/udp header for a packet, send packet */ +/* Construct a eth/ip/udp header for a packet, send packet */ int FAST_FUNC udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt, - uint32_t source_nip, int source_port, - uint32_t dest_nip, int dest_port, const uint8_t *dest_arp, - int ifindex) + uint32_t source_nip, int source_port, + uint32_t dest_nip, int dest_port, const uint8_t *dest_arp, + const char *if_name) { struct sockaddr_ll dest_sll; - struct ip_udp_dhcp_packet packet; + struct ethhdr *eth; + struct ip_udp_dhcp_packet *packet; + struct ifreq if_dev; unsigned padding; int fd; int result = -1; const char *msg; - fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + const int packet_size = sizeof(struct ethhdr) + sizeof(struct ip_udp_dhcp_packet); + char datagram[packet_size]; + + fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); if (fd < 0) { msg = "socket(%s)"; goto ret_msg; } memset(&dest_sll, 0, sizeof(dest_sll)); - memset(&packet, 0, offsetof(struct ip_udp_dhcp_packet, data)); - packet.data = *dhcp_pkt; /* struct copy */ + memset(&datagram, 0, packet_size); + eth = (struct ethhdr*)&datagram[0]; + packet = (struct ip_udp_dhcp_packet*)&datagram[sizeof(struct ethhdr)]; + + // Copy data into the packet + memcpy(&packet->data, dhcp_pkt, sizeof(struct dhcp_packet)); dest_sll.sll_family = AF_PACKET; dest_sll.sll_protocol = htons(ETH_P_IP); - dest_sll.sll_ifindex = ifindex; + dest_sll.sll_ifindex = if_nametoindex(if_name); /*dest_sll.sll_hatype = ARPHRD_???;*/ /*dest_sll.sll_pkttype = PACKET_???;*/ dest_sll.sll_halen = 6; memcpy(dest_sll.sll_addr, dest_arp, 6); -//TODO: is bind() necessary? we sendto() to this destination, should work anyway + //TODO: is bind() necessary? we sendto() to this destination, should work anyway if (bind(fd, (struct sockaddr *)&dest_sll, sizeof(dest_sll)) < 0) { msg = "bind(%s)"; goto ret_close; @@ -156,36 +166,52 @@ int FAST_FUNC udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt, * Thus, we retain enough padding to not go below 300 BOOTP bytes. * Some devices have filters which drop DHCP packets shorter than that. */ - padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(packet.data.options); + padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(packet->data.options); if (padding > DHCP_SIZE - 300) padding = DHCP_SIZE - 300; - packet.ip.protocol = IPPROTO_UDP; - packet.ip.saddr = source_nip; - packet.ip.daddr = dest_nip; - packet.udp.source = htons(source_port); - packet.udp.dest = htons(dest_port); + // Get source address + strcpy(if_dev.ifr_name, if_name); + result = ioctl(fd, SIOCGIFHWADDR, &if_dev); + if (result < 0) { + msg = "ioctl"; + goto ret_close; + } + + // Setup Ethernet packet + memcpy(eth->h_dest, (void*)dest_arp, 6); /* Dest MAC */ + memcpy(eth->h_source, (void*)if_dev.ifr_hwaddr.sa_data, 6); /* Source MAC */ + eth->h_proto = htons(ETH_P_IP); + + packet->ip.protocol = IPPROTO_UDP; + packet->ip.saddr = source_nip; + packet->ip.daddr = dest_nip; + packet->udp.source = htons(source_port); + packet->udp.dest = htons(dest_port); /* size, excluding IP header: */ - packet.udp.len = htons(UDP_DHCP_SIZE - padding); + packet->udp.len = htons(UDP_DHCP_SIZE - padding); /* for UDP checksumming, ip.len is set to UDP packet len */ - packet.ip.tot_len = packet.udp.len; - packet.udp.check = inet_cksum(&packet, - IP_UDP_DHCP_SIZE - padding); + packet->ip.tot_len = packet->udp.len; + packet->udp.check = inet_cksum(packet, + IP_UDP_DHCP_SIZE - padding); /* but for sending, it is set to IP packet len */ - packet.ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding); - packet.ip.ihl = sizeof(packet.ip) >> 2; - packet.ip.version = IPVERSION; - packet.ip.ttl = IPDEFTTL; - packet.ip.check = inet_cksum(&packet.ip, sizeof(packet.ip)); + packet->ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding); + packet->ip.ihl = sizeof(packet->ip) >> 2; + packet->ip.version = IPVERSION; + packet->ip.ttl = IPDEFTTL; + packet->ip.check = inet_cksum(&packet->ip, sizeof(packet->ip)); udhcp_dump_packet(dhcp_pkt); - result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, /*flags:*/ 0, - (struct sockaddr *) &dest_sll, sizeof(dest_sll)); + + result = sendto(fd, &datagram, sizeof(struct ethhdr) + IP_UDP_DHCP_SIZE - padding, /*flags:*/ 0, + (struct sockaddr *) &dest_sll, sizeof(dest_sll)); + msg = "sendto"; - ret_close: + + ret_close: close(fd); if (result < 0) { - ret_msg: + ret_msg: bb_perror_msg(msg, "PACKET"); } return result; -- 2.47.1 _______________________________________________ busybox mailing list [email protected] https://lists.busybox.net/mailman/listinfo/busybox
