]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Detect link address changes on Linux.
authorRoy Marples <roy@marples.name>
Tue, 25 Jun 2013 08:31:11 +0000 (08:31 +0000)
committerRoy Marples <roy@marples.name>
Tue, 25 Jun 2013 08:31:11 +0000 (08:31 +0000)
Only NetBSD emits RTM_CHGADDR for link address changes.
Sadly no other BSD emits anything for link address changes so we have
to do a full discovery on carrier up.

When a link address does change, simply carry on as we are, no need
to drop any existing lease as the carrier change will do that for us.

dhcpcd.c
dhcpcd.h
if-bsd.c
if-linux.c

index 627085c7d68e8ea89c67f573a5adbe8911753d34..c2cb2eac3b309673af6bc78772cc3b426fe77e8e 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -412,6 +412,12 @@ handle_carrier(int carrier, int flags, const char *ifname)
                if (ifp->carrier != LINK_UP) {
                        syslog(LOG_INFO, "%s: carrier acquired", ifp->name);
                        ifp->carrier = LINK_UP;
+#if !defined(__linux__) && !defined(__NetBSD__)
+                       /* BSD does not emit RTM_NEWADDR or RTM_CHGADDR when the
+                        * hardware address changes so we have to go
+                        * through the disovery process to work it out. */
+                       handle_interface(0, ifp->name);
+#endif
                        if (ifp->wireless)
                                getifssid(ifp->name, ifp->ssid);
                        configure_interface(ifp, margc, margv);
@@ -560,8 +566,10 @@ handle_interface(int action, const char *ifname)
                        TAILQ_REMOVE(ifs, ifp, next);
                        TAILQ_INSERT_TAIL(ifaces, ifp, next);
                }
-               init_state(ifp, margc, margv);
-               start_interface(ifp);
+               if (action == 1) {
+                       init_state(ifp, margc, margv);
+                       start_interface(ifp);
+               }
        }
 
        /* Free our discovered list */
@@ -572,47 +580,29 @@ handle_interface(int action, const char *ifname)
        free(ifs);
 }
 
-#ifdef RTM_CHGADDR
 void
-handle_hwaddr(const char *ifname, unsigned char *hwaddr, size_t hwlen)
+handle_hwaddr(const char *ifname, const uint8_t *hwaddr, size_t hwlen)
 {
        struct interface *ifp;
-       struct if_options *ifo;
-       struct dhcp_state *state;
 
-       TAILQ_FOREACH(ifp, ifaces, next) {
-               if (strcmp(ifp->name, ifname) == 0 && ifp->hwlen <= hwlen) {
-                       state = D_STATE(ifp);
-                       if (state == NULL)
-                               continue;
-                       ifo = ifp->options;
-                       if (!(ifo->options &
-                           (DHCPCD_INFORM | DHCPCD_STATIC | DHCPCD_CLIENTID))
-                           && state->new != NULL &&
-                           state->new->cookie == htonl(MAGIC_COOKIE))
-                       {
-                               syslog(LOG_INFO,
-                                   "%s: expiring for new hardware address",
-                                   ifp->name);
-                               dhcp_drop(ifp, "EXPIRE");
-                       }
-                       memcpy(ifp->hwaddr, hwaddr, hwlen);
-                       ifp->hwlen = hwlen;
-                       if (!(ifo->options &
-                           (DHCPCD_INFORM | DHCPCD_STATIC | DHCPCD_CLIENTID)))
-                       {
-                               syslog(LOG_DEBUG, "%s: using hwaddr %s",
-                                   ifp->name,
-                                   hwaddr_ntoa(ifp->hwaddr, ifp->hwlen));
-                               state->interval = 0;
-                               state->nakoff = 0;
-                               start_interface(ifp);
-                       }
-               }
+       ifp = find_interface(ifname);
+       if (ifp == NULL)
+               return;
+
+       if (hwlen > sizeof(ifp->hwaddr)) {
+               errno = ENOBUFS;
+               syslog(LOG_ERR, "%s: %s: %m", ifp->name, __func__);
+               return;
        }
-       free(hwaddr);
+
+       if (ifp->hwlen == hwlen && memcmp(ifp->hwaddr, hwaddr, hwlen) == 0)
+               return;
+
+       syslog(LOG_INFO, "%s: new hardware address: %s", ifp->name,
+           hwaddr_ntoa(hwaddr, hwlen));
+       ifp->hwlen = hwlen;
+       memcpy(ifp->hwaddr, hwaddr, hwlen);
 }
-#endif
 
 static void
 if_reboot(struct interface *ifp, int argc, char **argv)
index 50b0174b2ff79e70b8897a7b9aa145dda0fae70d..0fad506a3dad41a1d3a92f25f92c3f22cd3257d1 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -84,7 +84,7 @@ struct interface *find_interface(const char *);
 int handle_args(struct fd_list *, int, char **);
 void handle_carrier(int, int, const char *);
 void handle_interface(int, const char *);
-void handle_hwaddr(const char *, unsigned char *, size_t);
+void handle_hwaddr(const char *, const unsigned char *, size_t);
 void drop_interface(struct interface *, const char *);
 int select_profile(struct interface *, const char *);
 
index 96f483d92c94c1eb7b0455bacd801b2fb82d269c..4cf87187e54eec54c65877040cd59cb88840f7e9 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
        sin.s6_addr = ((sa) != NULL) ?                                        \
            (((struct sockaddr_in6 *)(void *)sa)->sin6_addr).s6_addr : 0
 
+#ifndef CLLADDR
+#  define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
+#endif
+
 static int r_fd = -1;
 static char *link_buf;
 static ssize_t link_buflen;
@@ -531,10 +535,7 @@ manage_link(int fd)
        struct ifa_msghdr *ifam;
        struct sockaddr *sa, *rti_info[RTAX_MAX];
        int len;
-#ifdef RTM_CHGADDR
        struct sockaddr_dl sdl;
-       unsigned char *hwaddr;
-#endif
 #ifdef INET
        struct rt rt;
 #endif
@@ -637,23 +638,20 @@ manage_link(int fd)
                                if (rti_info[RTAX_IFA] == NULL)
                                        break;
                                switch (rti_info[RTAX_IFA]->sa_family) {
-#ifdef RTM_CHGADDR
                                case AF_LINK:
+#ifdef RTM_CHGADDR
                                        if (rtm->rtm_type != RTM_CHGADDR)
                                                break;
+#else
+                                       if (rtm->rtm_type != RTM_NEWADDR)
+                                               break;
+#endif
                                        memcpy(&sdl, rti_info[RTAX_IFA],
                                            rti_info[RTAX_IFA]->sa_len);
-                                       hwaddr = malloc(sdl.sdl_alen);
-                                       if (hwaddr) {
-                                               memcpy(hwaddr, LLADDR(&sdl),
-                                                   sdl.sdl_alen);
-                                               handle_hwaddr(ifname, hwaddr,
-                                                   sdl.sdl_alen);
-                                       } else
-                                               syslog(LOG_ERR, "%s: %m",
-                                                   __func__);
+                                       handle_hwaddr(ifname,
+                                           (const unsigned char*)CLLADDR(&sdl),
+                                           sdl.sdl_alen);
                                        break;
-#endif
 #ifdef INET
                                case AF_INET:
                                case 255: /* FIXME: Why 255? */
index fc3da1999052e0b64cfd917c4663e7e5a34b6b85..899ce6d005804d361ba909dd58a29d6e5b8b10ab 100644 (file)
@@ -386,11 +386,28 @@ link_addr(struct nlmsghdr *nlm)
        return 1;
 }
 
+static short l2addr_len(unsigned short if_type)
+{
+
+       switch (if_type) {
+       case ARPHRD_ETHER: /* FALLTHROUGH */
+       case ARPHRD_IEEE802: /*FALLTHROUGH */
+       case ARPHRD_IEEE80211:
+               return 6;
+       case ARPHRD_IEEE1394:
+               return 8;
+       case ARPHRD_INFINIBAND:
+               return 20;
+       default:
+               return -1;
+       }
+}
+
 static int
 link_netlink(struct nlmsghdr *nlm)
 {
        int len;
-       struct rtattr *rta;
+       struct rtattr *rta, *hwaddr;
        struct ifinfomsg *ifi;
        char ifn[IF_NAMESIZE + 1];
 
@@ -414,6 +431,7 @@ link_netlink(struct nlmsghdr *nlm)
        rta = (struct rtattr *)(void *)((char *)ifi +NLMSG_ALIGN(sizeof(*ifi)));
        len = NLMSG_PAYLOAD(nlm, sizeof(*ifi));
        *ifn = '\0';
+       hwaddr = NULL;
        while (RTA_OK(rta, len)) {
                switch (rta->rta_type) {
                case IFLA_WIRELESS:
@@ -425,6 +443,9 @@ link_netlink(struct nlmsghdr *nlm)
                case IFLA_IFNAME:
                        strlcpy(ifn, RTA_DATA(rta), sizeof(ifn));
                        break;
+               case IFLA_ADDRESS:
+                       hwaddr = rta;
+                       break;
                }
                rta = RTA_NEXT(rta, len);
        }
@@ -443,6 +464,13 @@ link_netlink(struct nlmsghdr *nlm)
                return 1;
        }
 
+       /* Re-read hardware address and friends */
+       if (!(ifi->ifi_flags & IFF_UP) && hwaddr) {
+               len = l2addr_len(ifi->ifi_type);
+               if (hwaddr->rta_len == RTA_LENGTH(len))
+                       handle_hwaddr(ifn, RTA_DATA(hwaddr), len);
+       }
+
        handle_carrier(ifi->ifi_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN,
            ifi->ifi_flags, ifn);
        return 1;