Send source link-layer address option in router advertisements.
authorflorian <florian@openbsd.org>
Fri, 17 May 2024 06:50:14 +0000 (06:50 +0000)
committerflorian <florian@openbsd.org>
Fri, 17 May 2024 06:50:14 +0000 (06:50 +0000)
With this, hosts immediately learn the layer 2 (i.e. ethernet mac)
address of their default router and don't need to do another round
trip.

It also turns out that apple devices (macOS & iOS) install the default
route as what they call "interface scoped" if a DNS option is present
and the source link-layer address option is absent. This effectively
makes the default route unusable.

Problem with fruit devices tracked down & diff by Ryan Vogt (rvogt.ca
AT gmail), thanks!

OK sthen, bket

usr.sbin/rad/frontend.c
usr.sbin/rad/parse.y
usr.sbin/rad/printconf.c
usr.sbin/rad/rad.c
usr.sbin/rad/rad.conf.5
usr.sbin/rad/rad.h

index 6748e43..ca8719a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: frontend.c,v 1.45 2024/04/23 22:11:59 florian Exp $   */
+/*     $OpenBSD: frontend.c,v 1.46 2024/05/17 06:50:14 florian Exp $   */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -56,6 +56,7 @@
 #include <sys/uio.h>
 
 #include <net/if.h>
+#include <net/if_dl.h>
 #include <net/if_types.h>
 #include <net/route.h>
 
@@ -120,6 +121,12 @@ struct nd_opt_pref64 {
        u_int8_t        nd_opt_pref64[12];
 };
 
+struct nd_opt_source_link_addr {
+       u_int8_t                nd_opt_source_link_addr_type;
+       u_int8_t                nd_opt_source_link_addr_len;
+       struct ether_addr       nd_opt_source_link_addr_hw_addr;
+};
+
 TAILQ_HEAD(, ra_iface) ra_interfaces;
 
 __dead void             frontend_shutdown(void);
@@ -1099,6 +1106,7 @@ void
 build_packet(struct ra_iface *ra_iface)
 {
        struct nd_router_advert         *ra;
+       struct nd_opt_source_link_addr  *ndopt_source_link_addr;
        struct nd_opt_mtu               *ndopt_mtu;
        struct nd_opt_prefix_info       *ndopt_pi;
        struct ra_iface_conf            *ra_iface_conf;
@@ -1110,6 +1118,8 @@ build_packet(struct ra_iface *ra_iface)
        struct ra_rdnss_conf            *ra_rdnss;
        struct ra_dnssl_conf            *ra_dnssl;
        struct ra_pref64_conf           *pref64;
+       struct ifaddrs                  *ifap, *ifa;
+       struct sockaddr_dl              *sdl;
        size_t                           len, label_len;
        uint8_t                         *p, buf[RA_MAX_SIZE];
        char                            *label_start, *label_end;
@@ -1119,6 +1129,8 @@ build_packet(struct ra_iface *ra_iface)
        ra_options_conf = &ra_iface_conf->ra_options;
 
        len = sizeof(*ra);
+       if (ra_iface_conf->ra_options.source_link_addr)
+               len += sizeof(*ndopt_source_link_addr);
        if (ra_options_conf->mtu > 0)
                len += sizeof(*ndopt_mtu);
        len += sizeof(*ndopt_pi) * ra_iface->prefix_count;
@@ -1170,6 +1182,37 @@ build_packet(struct ra_iface *ra_iface)
        ra->nd_ra_retransmit = htonl(ra_options_conf->retrans_timer);
        p += sizeof(*ra);
 
+       if (ra_iface_conf->ra_options.source_link_addr) {
+               ndopt_source_link_addr = (struct nd_opt_source_link_addr *)p;
+               ndopt_source_link_addr->nd_opt_source_link_addr_type =
+                   ND_OPT_SOURCE_LINKADDR;
+               ndopt_source_link_addr->nd_opt_source_link_addr_len = 1;
+               if (getifaddrs(&ifap) != 0) {
+                       ifap = NULL;
+                       log_warn("getifaddrs");
+               }
+               for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+                       if (ifa->ifa_addr == NULL ||
+                           ifa->ifa_addr->sa_family != AF_LINK)
+                               continue;
+                       if (strcmp(ra_iface->name, ifa->ifa_name) != 0)
+                               continue;
+                       sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+                       if (sdl->sdl_type != IFT_ETHER ||
+                           sdl->sdl_alen != ETHER_ADDR_LEN)
+                               continue;
+                       memcpy(&ndopt_source_link_addr->
+                           nd_opt_source_link_addr_hw_addr,
+                           LLADDR(sdl), ETHER_ADDR_LEN);
+                       break;
+               }
+               if (ifap != NULL) {
+                       freeifaddrs(ifap);
+                       p += sizeof(*ndopt_source_link_addr);
+               } else
+                       len -= sizeof(*ndopt_source_link_addr);
+       }
+
        if (ra_options_conf->mtu > 0) {
                ndopt_mtu = (struct nd_opt_mtu *)p;
                ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
index 66cc0c0..bf6b6e6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.22 2024/04/23 22:11:59 florian Exp $      */
+/*     $OpenBSD: parse.y,v 1.23 2024/05/17 06:50:14 florian Exp $      */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -122,6 +122,7 @@ typedef struct {
 %token CONFIGURATION OTHER LIFETIME REACHABLE TIME RETRANS TIMER
 %token AUTO PREFIX VALID PREFERENCE PREFERRED LIFETIME ONLINK AUTONOMOUS
 %token ADDRESS_CONFIGURATION DNS NAMESERVER SEARCH MTU NAT64 HIGH MEDIUM LOW
+%token SOURCE LINK_LAYER
 
 %token <v.string>      STRING
 %token <v.number>      NUMBER
@@ -226,6 +227,9 @@ ra_opt_block        : DEFAULT ROUTER yesno {
                | RETRANS TIMER NUMBER {
                        ra_options->retrans_timer = $3;
                }
+               | SOURCE LINK_LAYER ADDRESS yesno {
+                       ra_options->source_link_addr = $4;
+               }
                | MTU NUMBER {
                        ra_options->mtu = $2;
                }
@@ -523,6 +527,7 @@ lookup(char *s)
                {"interface",           RA_IFACE},
                {"lifetime",            LIFETIME},
                {"limit",               LIMIT},
+               {"link-layer",          LINK_LAYER},
                {"low",                 LOW},
                {"managed",             MANAGED},
                {"medium",              MEDIUM},
@@ -539,6 +544,7 @@ lookup(char *s)
                {"retrans",             RETRANS},
                {"router",              ROUTER},
                {"search",              SEARCH},
+               {"source",              SOURCE},
                {"time",                TIME},
                {"timer",               TIMER},
                {"valid",               VALID},
index 184a5df..b49a774 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: printconf.c,v 1.8 2024/04/23 22:11:59 florian Exp $   */
+/*     $OpenBSD: printconf.c,v 1.9 2024/05/17 06:50:14 florian Exp $   */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -78,6 +78,8 @@ print_ra_options(const char *indent, const struct ra_options_conf *ra_options)
        printf("%srouter lifetime %d\n", indent, ra_options->router_lifetime);
        printf("%sreachable time %u\n", indent, ra_options->reachable_time);
        printf("%sretrans timer %u\n", indent, ra_options->retrans_timer);
+       printf("%ssource link-layer address %s\n", indent,
+           yesno(ra_options->source_link_addr));
        if (ra_options->mtu > 0)
                printf("%smtu %u\n", indent, ra_options->mtu);
 
index b283e58..a0c3172 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rad.c,v 1.29 2023/04/19 12:58:16 jsg Exp $    */
+/*     $OpenBSD: rad.c,v 1.30 2024/05/17 06:50:14 florian Exp $        */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -757,6 +757,7 @@ config_new_empty(void)
        xconf->ra_options.router_lifetime = ADV_DEFAULT_LIFETIME;
        xconf->ra_options.reachable_time = 0;
        xconf->ra_options.retrans_timer = 0;
+       xconf->ra_options.source_link_addr = 1;
        xconf->ra_options.mtu = 0;
        xconf->ra_options.rdns_lifetime = DEFAULT_RDNS_LIFETIME;
        SIMPLEQ_INIT(&xconf->ra_options.ra_rdnss_list);
index f82ccfd..87c2b32 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: rad.conf.5,v 1.23 2024/04/23 22:17:49 florian Exp $
+.\"    $OpenBSD: rad.conf.5,v 1.24 2024/05/17 06:50:14 florian Exp $
 .\"
 .\" Copyright (c) 2018 Florian Obser <florian@openbsd.org>
 .\" Copyright (c) 2005 Esben Norby <norby@openbsd.org>
@@ -18,7 +18,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: April 23 2024 $
+.Dd $Mdocdate: May 17 2024 $
 .Dt RAD.CONF 5
 .Os
 .Sh NAME
@@ -127,6 +127,10 @@ The default is medium.
 .\" XXX
 .\" .It Ic retrans timer Ar number
 .\" XXX
+.It Ic source link-layer address Pq Ic yes Ns | Ns Ic no
+Add a source link-layer address option to router advertisement messages, to
+communicate the link-layer address of the sending interface.
+The default is yes.
 .El
 .Sh INTERFACES
 A list of interfaces or interface groups to send advertisements on:
index 787e78c..442ff00 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rad.h,v 1.26 2024/04/23 22:11:59 florian Exp $        */
+/*     $OpenBSD: rad.h,v 1.27 2024/05/17 06:50:14 florian Exp $        */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -97,6 +97,7 @@ struct ra_options_conf {
        int             router_lifetime;        /* default router lifetime */
        uint32_t        reachable_time;
        uint32_t        retrans_timer;
+       int             source_link_addr;       /* source link-layer address */
        uint32_t        mtu;
        uint32_t        rdns_lifetime;
        SIMPLEQ_HEAD(, ra_rdnss_conf)            ra_rdnss_list;