]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Add support for link carrier detection. For Linux this involved a big change to the...
authorRoy Marples <roy@marples.name>
Wed, 16 Jul 2008 22:23:07 +0000 (22:23 +0000)
committerRoy Marples <roy@marples.name>
Wed, 16 Jul 2008 22:23:07 +0000 (22:23 +0000)
client.c
dhcpcd.8.in
dhcpcd.c
dhcpcd.conf.5.in
dhcpcd.h
if-bsd.c
if-linux.c
net.c
net.h

index 404a64903a1cdad233fae0f768f788fd0ed38bcd..240373cd8420b73be0f6177f2f3d38c963d39403 100644 (file)
--- a/client.c
+++ b/client.c
@@ -76,6 +76,7 @@
 #define STATE_INIT_IPV4LL      8
 #define STATE_PROBING          9
 #define STATE_ANNOUNCING       10
+#define STATE_CARRIER          11
 
 /* Constants taken from RFC 2131. */
 #define T1                     0.5
@@ -691,7 +692,8 @@ send_message(struct if_state *state, int type, const struct options *options)
 }
 
 static void
-drop_config(struct if_state *state, const char *reason, const struct options *options)
+drop_config(struct if_state *state, const char *reason,
+           const struct options *options)
 {
        configure(state->interface, reason, NULL, state->new,
                  &state->lease, options, 0);
@@ -717,7 +719,17 @@ wait_for_packet(struct if_state *state)
        fds[nfds].fd = state->signal_fd;
        fds[nfds].events = POLLIN;
        nfds++;
-       if (state->lease.leasetime == ~0U && state->state == STATE_BOUND) {
+       /* And links */
+       if (state->interface->link_fd != -1) {
+               fds[nfds].fd = state->interface->link_fd;
+               fds[nfds].events = POLLIN;
+               nfds++;
+       }
+       if (state->state == STATE_CARRIER) {
+               timeout = INFTIM;
+       } else if (state->lease.leasetime == ~0U &&
+                  state->state == STATE_BOUND)
+       {
                logger(LOG_DEBUG, "waiting for infinity");
                timeout = INFTIM;
        } else {
@@ -1035,7 +1047,10 @@ handle_timeout_fail(struct if_state *state, const struct options *options)
                logger(LOG_ERR, "failed to renew, attempting to rebind");
                lease->addr.s_addr = 0;
                state->state = STATE_REBINDING;
-               tv.tv_sec = lease->rebindtime - lease->renewaltime;
+               if (lease->server.s_addr == 0)
+                       tv.tv_sec = options->timeout;
+               else
+                       tv.tv_sec = lease->rebindtime - lease->renewaltime;
                break;
        case STATE_REBINDING:
                logger(LOG_ERR, "failed to rebind, attempting to discover");
@@ -1155,9 +1170,14 @@ handle_timeout(struct if_state *state, const struct options *options)
        timerclear(&tv);
 
        switch (state->state) {
-       case STATE_INIT:  /* FALLTHROUGH */
-       case STATE_BOUND: /* FALLTHROUGH */
        case STATE_RENEW_REQUESTED:
+               /* If a renew was requested (ie, didn't timeout)
+                * we need to remove the server address so we enter the
+                * INIT-REBOOT state correctly. */
+               lease->server.s_addr = 0;
+               /* FALLTHROUGH */
+       case STATE_INIT:  /* FALLTHROUGH */
+       case STATE_BOUND:
                up_interface(iface->name);
                do_socket(state, SOCKET_OPEN);
                state->xid = arc4random();
@@ -1202,6 +1222,13 @@ handle_timeout(struct if_state *state, const struct options *options)
                        timerclear(&state->timeout);
                        return 0;
                }
+               if (lease->addr.s_addr) {
+                       logger(LOG_INFO, "renewing lease of %s",inet_ntoa(lease->addr));
+                       state->state = STATE_RENEWING;
+                       tv.tv_sec = options->timeout;
+                       timeradd(&state->start, &tv, &state->stop);
+                       break;
+               }
                /* FALLTHROUGH */
        case STATE_BOUND:
                if (lease->addr.s_addr == 0 ||
@@ -1564,6 +1591,37 @@ handle_arp_fail(struct if_state *state, const struct options *options)
 }
 #endif
 
+static int
+handle_link(struct if_state *state)
+{
+       int retval;
+
+       retval = link_changed(state->interface);
+       if (retval == -1) {
+               logger(LOG_ERR, "link_changed: %s", strerror(errno));
+               return -1;
+       }
+       if (retval == 0)
+               return 0;
+       switch (carrier_status(state->interface->name)) {
+       case -1:
+               logger(LOG_ERR, "carrier_status: %s", strerror(errno));
+               return -1;
+       case 0:
+               logger(LOG_INFO, "carrier lost");
+               state->state = STATE_CARRIER;
+               do_socket(state, SOCKET_CLOSED);
+               break;
+       default:
+               logger(LOG_INFO, "carrier acquired");
+               state->state = STATE_RENEW_REQUESTED;
+               break;
+       }
+       timerclear(&state->timeout);
+       timerclear(&state->stop);
+       return 0;
+}
+
 int
 dhcp_run(const struct options *options, int *pid_fd)
 {
@@ -1593,6 +1651,18 @@ dhcp_run(const struct options *options, int *pid_fd)
                goto eexit;
 
        state->signal_fd = signal_fd();
+       if (state->options & DHCPCD_LINK) {
+               open_link_socket(iface);
+               if (carrier_status(iface->name) == 0) {
+                       if (!(state->options & DHCPCD_NOWAIT))
+                               logger(LOG_INFO, "waiting for carrier");
+                       state->state = STATE_CARRIER;
+               }
+       }
+
+       if (state->options & DHCPCD_NOWAIT)
+               if (daemonise(state, options) == -1)
+                       goto eexit;
 
        for (;;) {
                retval = wait_for_packet(state);
@@ -1607,10 +1677,12 @@ dhcp_run(const struct options *options, int *pid_fd)
                                /* The interupt will be handled above */
                                retval = 0;
                } else if (retval > 0) {
-                       if (fd_hasdata(state->interface->raw_fd) == 1)
+                       if (fd_hasdata(iface->link_fd) == 1)
+                               retval = handle_link(state);
+                       else if (fd_hasdata(iface->raw_fd) == 1)
                                retval = handle_dhcp_packet(state, options);
 #ifdef ENABLE_ARP
-                       else if (fd_hasdata(state->interface->arp_fd) == 1) {
+                       else if (fd_hasdata(iface->arp_fd) == 1) {
                                retval = handle_arp_packet(state);
                                if (retval == -1)
                                        retval = handle_arp_fail(state, options);
index 4fd6f27d05e2d103148a4a4e713ac3833d160a75..16717733a7eae62eb20f623e70ce8bda9653d4ab 100644 (file)
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd Jul 12, 2008
+.Dd Jul 16, 2008
 .Dt DHCPCD 8 SMM
 .Sh NAME
 .Nm dhcpcd
 .Nd an RFC 2131 compliant DHCP client
 .Sh SYNOPSIS
 .Nm
-.Op Fl dknpqADEGLSTXV
+.Op Fl dknpqADEGKLSTVWX
 .Op Fl c , -script Ar script
 .Op Fl f , -config Ar file
 .Op Fl h , -hostname Ar hostname
@@ -299,6 +299,10 @@ itself never does any DNS updates.
 Change the default clientid sent from the interface hardware address.
 If the string is of the format 01:02:03 then it is encoded as hex.
 If not set then none is sent.
+.It Fl W , -nowait
+Don't wait for a DHCP lease, background immediately.
+This is useful for startup scripts which don't disable link messages for
+carrier status.
 .El
 .Ss Restriciting behaviour
 .Nm
@@ -315,8 +319,6 @@ The messages are still logged though.
 .It Fl A , -noarp
 Don't request or claim the address by ARP.
 This also disables IPv4LL.
-.It Fl G , -nogateway
-Don't set any default routes.
 .It Fl C , -nohook Ar script
 Don't run this hook script.
 Matches full name, or prefixed with 2 numbers optionally ending with
@@ -324,10 +326,11 @@ Matches full name, or prefixed with 2 numbers optionally ending with
 .Pp
 So to stop dhcpcd from touching your DNS or MTU settings you would do:-
 .D1 dhcpcd -C resolv.conf -C mtu eth0
-.It Fl X , -nodaemonise
-Don't daemonise when we acquire a lease.
-This is mainly useful for running under the control of another process, such
-as a debugger or a network manager.
+.It Fl G , -nogateway
+Don't set any default routes.
+.It Fl K , -nolink
+Don't receive link messages for carrier status.
+You should only have to use this with buggy device drivers.
 .It Fl L , -noipv4ll
 Don't use IPv4LL at all.
 .It Fl O , -nooption Ar option
@@ -344,6 +347,10 @@ files.
 .It Fl V, -variables
 Display a list of option codes and the associated variable for use in
 .Xr dhcpcd-run-hooks 8 .
+.It Fl X , -nodaemonise
+Don't daemonise when we acquire a lease.
+This is mainly useful for running under the control of another process, such
+as a debugger or a network manager.
 .El
 .Sh NOTES
 .Nm
index f8447cf9fce99472e989ab0328a234d24f5f18f7..2e3441325e987e699197311ab654df76c47a80d2 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -52,7 +52,7 @@ const char copyright[] = "Copyright (c) 2006-2008 Roy Marples";
 
 /* Don't set any optional arguments here so we retain POSIX
  * compatibility with getopt */
-#define OPTS "c:df:h:i:kl:m:no:pqr:s:t:u:v:xAC:DEF:GI:LO:TVX"
+#define OPTS "c:df:h:i:kl:m:no:pqr:s:t:u:v:xAC:DEF:GI:KLO:TVWX"
 
 static int doversion = 0;
 static int dohelp = 0;
@@ -82,10 +82,12 @@ static const struct option longopts[] = {
        {"fqdn",        optional_argument,  NULL, 'F'},
        {"nogateway",   no_argument,        NULL, 'G'},
        {"clientid",    optional_argument,  NULL, 'I'},
+       {"nolink",      no_argument,        NULL, 'K'},
        {"noipv4ll",    no_argument,        NULL, 'L'},
        {"nooption",    optional_argument,  NULL, 'O'},
        {"test",        no_argument,        NULL, 'T'},
        {"variables",   no_argument,        NULL, 'V'},
+       {"nowait",      no_argument,        NULL, 'W'},
        {"nodaemonise", no_argument,        NULL, 'X'},
        {"help",        no_argument,        &dohelp, 1},
        {"version",     no_argument,        &doversion, 1},
@@ -539,6 +541,9 @@ parse_option(int opt, char *oarg, struct options *options)
                }
 #endif
                break;
+       case 'K':
+               options->options &= ~DHCPCD_LINK;
+               break;
        case 'L':
                options->options &= ~DHCPCD_IPV4LL;
                break;
@@ -550,6 +555,9 @@ parse_option(int opt, char *oarg, struct options *options)
                        return -1;
                }
                break;
+       case 'W':
+               options->options |= DHCPCD_NOWAIT;
+               break;
        case 'X':
                options->options &= ~DHCPCD_DAEMONISE;
                break;
@@ -623,6 +631,8 @@ main(int argc, char **argv)
  #endif
 #endif
 
+       options->options |= DHCPCD_LINK;
+
 #ifdef CMDLINE_COMPAT
        add_reqmask(options->reqmask, DHCP_DNSSERVER);
        add_reqmask(options->reqmask, DHCP_DNSDOMAIN);
index 5ba825f2dc090fa3be991edf831b3c20a3de6d94..f2f624b7ee23520d31c138d6c8b9e0847af3645c 100644 (file)
@@ -71,7 +71,7 @@ if a FQDN (ie, contains a .) then it will be encoded as such.
 none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
 record of the host in DNS whereas both also updates the A record.
 The current hostname or the hostname specified using the
-.Fl h , -hostname
+.Ic hostname
 option must be a FQDN.
 .Nm dhcpcd
 itself never does any DNS updates.
@@ -96,6 +96,11 @@ See
 .Rs
 .%T "RFC 3927"
 .Re
+.It Ic nolink
+Don't receive link messages about carrier status.
+You should only set this for buggy interface drivers.
+.It Ic nowait
+Don't wait to obtain a DHCP lease, fork to the background right away.
 .It Ic option Ar dhcp-option
 Requests the
 .Ar dhcp-option
index 10d23ffb97603fa3671b3b857a6d9015b849c4c7..c6c98504d6c07012b9b712bd4c3754993e7409cf 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -68,6 +68,8 @@ extern char *dhcpcd_skiproutes;
 #define DHCPCD_FORKED          (1 << 17)
 #define DHCPCD_HOSTNAME                (1 << 18)
 #define DHCPCD_CLIENTID                (1 << 19)
+#define DHCPCD_LINK            (1 << 20)
+#define DHCPCD_NOWAIT          (1 << 21)
 
 struct options {
        char interface[IF_NAMESIZE];
index 2cc0c2f35e8de3326a4b2ff5eafaf72c4747e6aa..bbf1a95d0a82a3108ad530bf76189ed7c4f94694 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -185,3 +185,57 @@ if_route(const char *ifname, const struct in_addr *destination,
        close(s);
        return retval;
 }
+
+int
+open_link_socket(struct interface *iface)
+{
+       int fd;
+
+       fd = socket(PF_ROUTE, SOCK_RAW, 0);
+       if (fd == -1)
+               return -1;
+       set_cloexec(fd);
+       if (iface->link_fd != -1)
+               close(iface->link_fd);
+       iface->link_fd = fd;
+       return 0;
+}
+
+#define BUFFER_LEN     2048
+int
+link_changed(struct interface *iface)
+{
+       char buffer[2048], *p;
+       ssize_t bytes;
+       struct rt_msghdr *rtm;
+       struct if_msghdr *ifm;
+       int i;
+
+       if ((i = if_nametoindex(iface->name)) == -1)
+               return -1;
+       for (;;) {
+               bytes = recv(iface->link_fd, buffer, BUFFER_LEN, MSG_DONTWAIT);
+               if (bytes == -1) {
+                       if (errno == EAGAIN)
+                               return 0;
+                       if (errno == EINTR)
+                               continue;
+                       return -1;
+               }
+               for (p = buffer; bytes > 0;
+                    bytes -= ((struct rt_msghdr *)p)->rtm_msglen,
+                    p += ((struct rt_msghdr *)p)->rtm_msglen)
+               {
+                       rtm = (struct rt_msghdr *)p;
+                       if (rtm->rtm_type != RTM_IFINFO)
+                               continue;
+                       ifm = (struct if_msghdr *)p;
+                       if (ifm->ifm_index != i)
+                               continue;
+
+                       /* Link changed */
+                       return 1;
+               }
+       }
+       return 0;
+}
index 30b2f6e74ab3e2155e1295cec4b19e1bc467a3c5..9c7ce974e3d0345f4fa70410a3cc5e99491a2b71 100644 (file)
 #include <string.h>
 #include <unistd.h>
 
+/* Support older kernels */
+#ifndef IFLA_WIRELESS
+# define IFLA_WIRELSSS (IFLFA_MASTER + 1)
+#endif
+
 #include "config.h"
 #include "common.h"
 #include "dhcp.h"
 #include "net.h"
 
-/* This netlink stuff is overly compex IMO.
- * The BSD implementation is much cleaner and a lot less code.
- * send_netlink handles the actual transmission so we can work out
- * if there was an error or not. */
 #define BUFFERLEN 256
-static int
-send_netlink(struct nlmsghdr *hdr)
+
+int
+open_link_socket(struct interface *iface)
 {
-       int s;
-       pid_t mypid = getpid ();
+       int fd;
        struct sockaddr_nl nl;
-       struct iovec iov;
-       struct msghdr msg;
-       static unsigned int seq;
-       char *buffer = NULL;
-       ssize_t bytes;
-       union
-       {
-               char *buffer;
-               struct nlmsghdr *nlm;
-       } h;
-       int len, l;
-       struct nlmsgerr *err;
 
-       if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
+       if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
                return -1;
-
        memset(&nl, 0, sizeof(nl));
        nl.nl_family = AF_NETLINK;
-       if (bind(s, (struct sockaddr *)&nl, sizeof(nl)) == -1)
-               goto eexit;
-
-       memset(&iov, 0, sizeof(iov));
-       iov.iov_base = hdr;
-       iov.iov_len = hdr->nlmsg_len;
-
-       memset(&msg, 0, sizeof(msg));
-       msg.msg_name = &nl;
-       msg.msg_namelen = sizeof(nl);
-       msg.msg_iov = &iov;
-       msg.msg_iovlen = 1;
-
-       /* Request a reply */
-       hdr->nlmsg_flags |= NLM_F_ACK;
-       hdr->nlmsg_seq = ++seq;
+       nl.nl_groups = RTMGRP_LINK;
+       if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1)
+               return -1;
+       set_cloexec(fd);
+       if (iface->link_fd != -1)
+               close(iface->link_fd);
+       iface->link_fd = fd;
+       return 0;
+}
 
-       if (sendmsg(s, &msg, 0) == -1)
-               goto eexit;
+static int
+get_netlink(int fd, int flags,
+           int (*callback)(struct nlmsghdr *, const char *),
+           const char *ifname)
+{
+       char *buffer = NULL;
+       ssize_t bytes;
+       struct nlmsghdr *nlm;
+       int r = -1;
 
        buffer = xzalloc(sizeof(char) * BUFFERLEN);
-       iov.iov_base = buffer;
-
        for (;;) {
-               iov.iov_len = BUFFERLEN;
-               bytes = recvmsg(s, &msg, 0);
-
+               bytes = recv(fd, buffer, BUFFERLEN, flags);
                if (bytes == -1) {
+                       if (errno == EAGAIN) {
+                               r == 0;
+                               goto eexit;
+                       }
                        if (errno == EINTR)
                                continue;
                        goto eexit;
                }
-
-               if (bytes == 0) {
-                       errno = ENODATA;
-                       goto eexit;
+               for (nlm = (struct nlmsghdr *)buffer;
+                    NLMSG_OK(nlm, (size_t)bytes);
+                    nlm = NLMSG_NEXT(nlm, bytes))
+               {
+                       r = callback(nlm, ifname);
+                       if (r != 0)
+                               goto eexit;
                }
+       }
 
-               if (msg.msg_namelen != sizeof(nl)) {
-                       errno = EBADMSG;
-                       goto eexit;
-               }
+eexit:
+       free(buffer);
+       return r;
+}
 
-               for (h.buffer = buffer; bytes >= (signed) sizeof(*h.nlm); ) {
-                       len = h.nlm->nlmsg_len;
-                       l = len - sizeof(*h.nlm);
-                       err = (struct nlmsgerr *)NLMSG_DATA(h.nlm);
+static int
+err_netlink(struct nlmsghdr *nlm, _unused const char *ifname)
+{
+       struct nlmsgerr *err;
+       int l;
 
-                       if (l < 0 || len > bytes) {
-                               errno = EBADMSG;
-                               goto eexit;
-                       }
+       if (nlm->nlmsg_type != NLMSG_ERROR)
+               return 0;
+       l = nlm->nlmsg_len - sizeof(*nlm);
+       if ((size_t)l < sizeof(*err)) {
+               errno = EBADMSG;
+               return -1;
+       }
+       err = (struct nlmsgerr *)NLMSG_DATA(nlm);
+       if (err->error == 0)
+               return l;
+       errno = -err->error;
+       return -1;
+}
 
-                       /* Ensure it's our message */
-                       if (nl.nl_pid != 0 ||
-                           (pid_t)h.nlm->nlmsg_pid != mypid ||
-                           h.nlm->nlmsg_seq != seq)
-                       {
-                               /* Next Message */
-                               bytes -= NLMSG_ALIGN(len);
-                               h.buffer += NLMSG_ALIGN(len);
-                               continue;
-                       }
+static int
+link_netlink(struct nlmsghdr *nlm, const char *ifname)
+{
+       int len;
+       struct rtattr *rta;
+       struct ifinfomsg *ifi;
+       char ifn[IF_NAMESIZE + 1];
+
+       if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK)
+               return 0;
+       len = nlm->nlmsg_len - sizeof(*nlm);
+       if ((size_t)len < sizeof(*ifi)) {
+               errno = EBADMSG;
+               return -1;
+       }
+       ifi = NLMSG_DATA(nlm);
+       if (ifi->ifi_flags & IFF_LOOPBACK)
+               return 0;
+       rta = (struct rtattr *) ((char *)ifi + NLMSG_ALIGN(sizeof(*ifi)));
+       len = NLMSG_PAYLOAD(nlm, sizeof(*ifi));
+       *ifn = '\0';
+       while (RTA_OK(rta, len)) {
+               switch (rta->rta_type) {
+               case IFLA_WIRELESS:
+                       /* Ignore wireless messages */
+                       if (nlm->nlmsg_type == RTM_NEWLINK &&
+                           ifi->ifi_change  == 0)
+                               return 0;
+                       break;
+               case IFLA_IFNAME:
+                       strlcpy(ifn, RTA_DATA(rta), sizeof(ifn));
+                       break;
+               }
+               rta = RTA_NEXT(rta, len);
+       }
 
-                       /* We get an NLMSG_ERROR back with a code of zero for success */
-                       if (h.nlm->nlmsg_type != NLMSG_ERROR)
-                               continue;
+       if (strncmp(ifname, ifn, sizeof(ifn)) == 0)
+               return 1;
+       return 0;
+}
 
-                       if ((unsigned)l < sizeof(*err)) {
-                               errno = EBADMSG;
-                               goto eexit;
-                       }
+int
+link_changed(struct interface *iface)
+{
+       return get_netlink(iface->link_fd, MSG_DONTWAIT,
+                          &link_netlink, iface->name);
+}
 
-                       if (err->error == 0) {
-                               close(s);
-                               free(buffer);
-                               return l;
-                       }
+static int
+send_netlink(struct nlmsghdr *hdr)
+{
+       int fd, r;
+       struct sockaddr_nl nl;
+       struct iovec iov;
+       struct msghdr msg;
+       static unsigned int seq;
 
-                       errno = -err->error;
-                       goto eexit;
-               }
+       if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
+               return -1;
+       memset(&nl, 0, sizeof(nl));
+       nl.nl_family = AF_NETLINK;
+       if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) {
+               close(fd);
+               return -1;
        }
+       memset(&iov, 0, sizeof(iov));
+       iov.iov_base = hdr;
+       iov.iov_len = hdr->nlmsg_len;
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_name = &nl;
+       msg.msg_namelen = sizeof(nl);
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       /* Request a reply */
+       hdr->nlmsg_flags |= NLM_F_ACK;
+       hdr->nlmsg_seq = ++seq;
 
-eexit:
-       close(s);
-       free(buffer);
-       return -1;
+       if (sendmsg(fd, &msg, 0) != -1)
+               r = get_netlink(fd, 0, &err_netlink, NULL);
+       else
+               r = -1;
+       close(fd);
+       return r;
 }
 
 #define NLMSG_TAIL(nmsg) \
diff --git a/net.c b/net.c
index c364d7fe455756fb36d8a8bbab088195b3d96fc9..c89a52ebdaf68f8cee78776ed336ec3d9934ac1c 100644 (file)
--- a/net.c
+++ b/net.c
@@ -43,6 +43,9 @@
 #define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
 #include <netinet/udp.h>
 #undef __FAVOR_BSD
+#ifdef SIOCGIFMEDIA
+#include <net/if_media.h>
+#endif
 #include <arpa/inet.h>
 #ifdef AF_LINK
 # include <net/if_dl.h>
@@ -310,6 +313,51 @@ up_interface(const char *ifname)
        return retval;
 }
 
+int
+carrier_status(const char *ifname)
+{
+       int s;
+       struct ifreq ifr;
+       int retval = -1;
+#ifdef SIOCGIFMEDIA
+       struct ifmediareq ifmr;
+#endif
+#ifdef __linux__
+       char *p;
+#endif
+
+       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+               return -1;
+       memset(&ifr, 0, sizeof(ifr));
+       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+#ifdef __linux__
+       /* We can only test the real interface up */
+       if ((p = strchr(ifr.ifr_name, ':')))
+               *p = '\0';
+#endif
+       if ((retval = ioctl(s, SIOCGIFFLAGS, &ifr)) == 0) {
+               if (ifr.ifr_flags & IFF_UP && ifr.ifr_flags & IFF_RUNNING)
+                       retval = 1;
+               else
+                       retval = 0;
+       }
+
+#ifdef SIOCGIFMEDIA
+       if (retval == 1) {
+               memset(&ifmr, 0, sizeof(ifmr));
+               strncpy(ifmr.ifm_name, ifr.ifr_name, sizeof(ifmr.ifm_name));
+               if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1 &&
+                   ifmr.ifm_status & IFM_AVALID)
+               {
+                       if (!(ifmr.ifm_status & IFM_ACTIVE))
+                               retval = 0;
+               }
+       }
+#endif
+       close(s);
+       return retval;
+}
+
 struct interface *
 read_interface(const char *ifname, _unused int metric)
 {
@@ -389,6 +437,7 @@ read_interface(const char *ifname, _unused int metric)
 #ifdef ENABLE_ARP
        iface->arp_fd = -1;
 #endif
+       iface->link_fd = -1;
 
 eexit:
        close(s);
diff --git a/net.h b/net.h
index 12f13211a1823a9a624659804e7072f46b9fb632..f2b3dbe11871f66d40839a057b2799d68db92386 100644 (file)
--- a/net.h
+++ b/net.h
@@ -71,8 +71,8 @@
 #endif
 
 #define LINKLOCAL_ADDR 0xa9fe0000
-#define LINKLOCAL_MASK 0xffff0000
-#define LINKLOCAL_BRDC 0xa9feffff
+#define LINKLOCAL_MASK IN_CLASSB_NET
+#define LINKLOCAL_BRDC (LINKLOCAL_ADDR | ~LINKLOCAL_MASK)
 
 #ifndef IN_LINKLOCAL
 # define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
@@ -103,11 +103,12 @@ struct interface
 
        int raw_fd;
        int udp_fd;
-       size_t buffer_size, buffer_len, buffer_pos;
-       unsigned char *buffer;
 #ifdef ENABLE_ARP
        int arp_fd;
 #endif
+       int link_fd;
+       size_t buffer_size, buffer_len, buffer_pos;
+       unsigned char *buffer;
 
        struct in_addr addr;
        struct in_addr net;
@@ -172,4 +173,8 @@ ssize_t get_raw_packet(struct interface *, int, void *, ssize_t);
 #ifdef ENABLE_ARP
 int send_arp(const struct interface *, int, in_addr_t, in_addr_t);
 #endif
+
+int open_link_socket(struct interface *);
+int link_changed(struct interface *);
+int carrier_status(const char *);
 #endif