[PATCH] Enable DHCP client to send requests via IPv6 GRE tunnel.
Emanuele Santini
emanuele.santini.88 at gmail.com
Fri Jan 17 12:01:38 UTC 2025
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 <emanuele.santini.88 at gmail.com>
---
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
More information about the busybox
mailing list