@@ -114,6 +114,12 @@ int sd_dhcp_server_is_running(sd_dhcp_server *server) {
114114 return !!server -> receive_message ;
115115}
116116
117+ int sd_dhcp_server_is_in_relay_mode (sd_dhcp_server * server ) {
118+ assert_return (server , - EINVAL );
119+
120+ return in4_addr_is_set (& server -> relay_target );
121+ }
122+
117123void client_id_hash_func (const DHCPClientId * id , struct siphash * state ) {
118124 assert (id );
119125 assert (id -> length );
@@ -343,10 +349,27 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
343349 return 0 ;
344350}
345351
346- static bool requested_broadcast (DHCPRequest * req ) {
347- assert (req );
352+ static bool requested_broadcast (DHCPMessage * message ) {
353+ assert (message );
354+ return message -> flags & htobe16 (0x8000 );
355+ }
348356
349- return req -> message -> flags & htobe16 (0x8000 );
357+ static int dhcp_server_send (sd_dhcp_server * server , be32_t destination , uint16_t destination_port ,
358+ DHCPPacket * packet , size_t optoffset , bool l2_broadcast ) {
359+ if (destination != INADDR_ANY )
360+ return dhcp_server_send_udp (server , destination ,
361+ destination_port , & packet -> dhcp ,
362+ sizeof (DHCPMessage ) + optoffset );
363+ else if (l2_broadcast )
364+ return dhcp_server_send_udp (server , INADDR_BROADCAST ,
365+ destination_port , & packet -> dhcp ,
366+ sizeof (DHCPMessage ) + optoffset );
367+ else
368+ /* we cannot send UDP packet to specific MAC address when the
369+ address is not yet configured, so must fall back to raw
370+ packets */
371+ return dhcp_server_send_unicast_raw (server , packet ,
372+ sizeof (DHCPPacket ) + optoffset );
350373}
351374
352375int dhcp_server_send_packet (sd_dhcp_server * server ,
@@ -404,20 +427,8 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
404427 } else if (req -> message -> ciaddr && type != DHCP_NAK )
405428 destination = req -> message -> ciaddr ;
406429
407- if (destination != INADDR_ANY )
408- return dhcp_server_send_udp (server , destination ,
409- destination_port , & packet -> dhcp ,
410- sizeof (DHCPMessage ) + optoffset );
411- else if (requested_broadcast (req ) || type == DHCP_NAK )
412- return dhcp_server_send_udp (server , INADDR_BROADCAST ,
413- destination_port , & packet -> dhcp ,
414- sizeof (DHCPMessage ) + optoffset );
415- else
416- /* we cannot send UDP packet to specific MAC address when the
417- address is not yet configured, so must fall back to raw
418- packets */
419- return dhcp_server_send_unicast_raw (server , packet ,
420- sizeof (DHCPPacket ) + optoffset );
430+ bool l2_broadcast = requested_broadcast (req -> message ) || type == DHCP_NAK ;
431+ return dhcp_server_send (server , destination , destination_port , packet , optoffset , l2_broadcast );
421432}
422433
423434static int server_message_init (sd_dhcp_server * server , DHCPPacket * * ret ,
@@ -701,6 +712,47 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
701712 return be32toh (requested_ip & ~server -> netmask ) - server -> pool_offset ;
702713}
703714
715+ static int dhcp_server_relay_message (sd_dhcp_server * server , DHCPMessage * message , size_t opt_length ) {
716+ _cleanup_free_ DHCPPacket * packet = NULL ;
717+
718+ assert (server );
719+ assert (message );
720+ assert (sd_dhcp_server_is_in_relay_mode (server ));
721+
722+ if (message -> op == BOOTREQUEST ) {
723+ log_dhcp_server (server , "(relay agent) BOOTREQUEST (0x%x)" , be32toh (message -> xid ));
724+ if (message -> hops >= 16 )
725+ return - ETIME ;
726+ message -> hops ++ ;
727+
728+ /* https://tools.ietf.org/html/rfc1542#section-4.1.1 */
729+ if (message -> giaddr == 0 )
730+ message -> giaddr = server -> address ;
731+
732+ return dhcp_server_send_udp (server , server -> relay_target .s_addr , DHCP_PORT_SERVER , message , sizeof (DHCPMessage ) + opt_length );
733+ } else if (message -> op == BOOTREPLY ) {
734+ log_dhcp_server (server , "(relay agent) BOOTREPLY (0x%x)" , be32toh (message -> xid ));
735+ if (message -> giaddr != server -> address ) {
736+ return log_dhcp_server_errno (server , SYNTHETIC_ERRNO (EBADMSG ),
737+ "(relay agent) BOOTREPLY giaddr mismatch, discarding" );
738+ }
739+
740+ int message_type = dhcp_option_parse (message , sizeof (DHCPMessage ) + opt_length , NULL , NULL , NULL );
741+ if (message_type < 0 )
742+ return message_type ;
743+
744+ packet = malloc0 (sizeof (DHCPPacket ) + opt_length );
745+ if (!packet )
746+ return - ENOMEM ;
747+ memcpy (& packet -> dhcp , message , sizeof (DHCPMessage ) + opt_length );
748+
749+ bool l2_broadcast = requested_broadcast (message ) || message_type == DHCP_NAK ;
750+ const be32_t destination = message_type == DHCP_NAK ? INADDR_ANY : message -> ciaddr ;
751+ return dhcp_server_send (server , destination , DHCP_PORT_CLIENT , packet , opt_length , l2_broadcast );
752+ }
753+ return - EBADMSG ;
754+ }
755+
704756#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
705757
706758int dhcp_server_handle_message (sd_dhcp_server * server , DHCPMessage * message ,
@@ -999,10 +1051,15 @@ static int server_receive_message(sd_event_source *s, int fd,
9991051 }
10001052 }
10011053
1002- r = dhcp_server_handle_message (server , message , (size_t ) len );
1003- if (r < 0 )
1004- log_dhcp_server_errno (server , r , "Couldn't process incoming message: %m" );
1005-
1054+ if (sd_dhcp_server_is_in_relay_mode (server )) {
1055+ r = dhcp_server_relay_message (server , message , len - sizeof (DHCPMessage ));
1056+ if (r < 0 )
1057+ log_dhcp_server_errno (server , r , "Couldn't relay message: %m" );
1058+ } else {
1059+ r = dhcp_server_handle_message (server , message , (size_t ) len );
1060+ if (r < 0 )
1061+ log_dhcp_server_errno (server , r , "Couldn't process incoming message: %m" );
1062+ }
10061063 return 0 ;
10071064}
10081065
@@ -1238,3 +1295,14 @@ int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_
12381295
12391296 return 0 ;
12401297}
1298+
1299+ int sd_dhcp_server_set_relay_target (sd_dhcp_server * server , const struct in_addr * address ) {
1300+ assert_return (server , - EINVAL );
1301+ assert_return (!sd_dhcp_server_is_running (server ), - EBUSY );
1302+
1303+ if (memcmp (address , & server -> relay_target , sizeof (struct in_addr )) == 0 )
1304+ return 0 ;
1305+
1306+ server -> relay_target = * address ;
1307+ return 1 ;
1308+ }
0 commit comments