summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c')
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c185
1 files changed, 182 insertions, 3 deletions
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c
index ffd4d14f59..28b5d33004 100644
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c
@@ -27,7 +27,6 @@
#include <sys/types.h>
#include <stdlib.h>
-#include <assert.h>
#include <errno.h>
#include <locale.h>
#include <string.h>
@@ -36,10 +35,11 @@
#include <stdio.h>
#include <stdio_ext.h>
#include <dhcp_hostconf.h>
-#include <dhcp_symbol.h>
#include <dhcpagent_ipc.h>
#include <dhcpmsg.h>
#include <netinet/dhcp.h>
+#include <net/route.h>
+#include <sys/sockio.h>
#include "async.h"
#include "agent.h"
@@ -59,11 +59,12 @@ char *class_id;
iu_eh_t *eh;
iu_tq_t *tq;
pid_t grandparent;
+int rtsock_fd;
static boolean_t shutdown_started = B_FALSE;
static boolean_t do_adopt = B_FALSE;
static unsigned int debug_level = 0;
-static iu_eh_callback_t accept_event, ipc_event;
+static iu_eh_callback_t accept_event, ipc_event, rtsock_event;
/*
* The ipc_cmd_allowed[] table indicates which IPC commands are allowed in
@@ -89,6 +90,7 @@ static int ipc_cmd_allowed[DHCP_NSTATES][DHCP_NIPC] = {
/* INIT */ { 1, 0, 1, 0, 1, 1, 1, 0 },
/* SELECTING */ { 1, 0, 1, 0, 1, 1, 0, 0 },
/* REQUESTING */ { 1, 0, 1, 0, 1, 1, 0, 0 },
+ /* PRE_BOUND */ { 1, 1, 1, 1, 0, 1, 0, 1 },
/* BOUND */ { 1, 1, 1, 1, 0, 1, 0, 1 },
/* RENEWING */ { 1, 1, 1, 1, 0, 1, 0, 1 },
/* REBINDING */ { 1, 1, 1, 1, 0, 1, 0, 1 },
@@ -245,6 +247,22 @@ main(int argc, char **argv)
}
/*
+ * Create the global routing socket. This is used for monitoring
+ * interface transitions, so that we learn about the kernel's Duplicate
+ * Address Detection status, and for inserting and removing default
+ * routes as learned from DHCP servers.
+ */
+ rtsock_fd = socket(PF_ROUTE, SOCK_RAW, AF_INET);
+ if (rtsock_fd == -1) {
+ dhcpmsg(MSG_ERR, "cannot open routing socket");
+ return (EXIT_FAILURE);
+ }
+ if (iu_register_event(eh, rtsock_fd, POLLIN, rtsock_event, 0) == -1) {
+ dhcpmsg(MSG_ERR, "cannot register routing socket for messages");
+ return (EXIT_FAILURE);
+ }
+
+ /*
* if the -a (adopt) option was specified, try to adopt the
* kernel-managed interface before we start. Our grandparent
* will be waiting for us to finish this, so signal him when
@@ -844,3 +862,164 @@ load_option:
return;
}
}
+
+/*
+ * check_rtm_addr(): determine if routing socket message matches interface
+ * address
+ *
+ * input: struct if_msghdr *: pointer to routing socket message
+ * struct in_addr: IP address
+ * output: boolean_t
+ */
+static boolean_t
+check_rtm_addr(struct ifa_msghdr *ifam, int msglen, struct in_addr addr)
+{
+ char *cp, *lim;
+ uint_t flag;
+ struct sockaddr *sa;
+ struct sockaddr_in *sinp;
+
+ if (!(ifam->ifam_addrs & RTA_IFA))
+ return (B_FALSE);
+
+ cp = (char *)(ifam + 1);
+ lim = (char *)ifam + msglen;
+ for (flag = 1; flag < RTA_IFA; flag <<= 1) {
+ if (ifam->ifam_addrs & flag) {
+ /* LINTED: alignment */
+ sa = (struct sockaddr *)cp;
+ if ((char *)(sa + 1) > lim)
+ return (B_FALSE);
+ switch (sa->sa_family) {
+ case AF_UNIX:
+ cp += sizeof (struct sockaddr_un);
+ break;
+ case AF_INET:
+ cp += sizeof (struct sockaddr_in);
+ break;
+ case AF_LINK:
+ cp += sizeof (struct sockaddr_dl);
+ break;
+ case AF_INET6:
+ cp += sizeof (struct sockaddr_in6);
+ break;
+ default:
+ cp += sizeof (struct sockaddr);
+ break;
+ }
+ }
+ }
+ /* LINTED: alignment */
+ sinp = (struct sockaddr_in *)cp;
+ if ((char *)(sinp + 1) > lim)
+ return (B_FALSE);
+ return (sinp->sin_addr.s_addr == addr.s_addr);
+}
+
+/*
+ * rtsock_event(): fetches routing socket messages and updates internal
+ * interface state based on those messages.
+ *
+ * input: iu_eh_t *: unused
+ * int: the routing socket file descriptor
+ * (other arguments unused)
+ * output: void
+ */
+
+/* ARGSUSED */
+static void
+rtsock_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
+{
+ struct ifslist *ifs;
+ union {
+ struct ifa_msghdr ifam;
+ char buf[1024];
+ } msg;
+ uint16_t ifindex;
+ struct lifreq lifr;
+ char *fail;
+ int msglen;
+
+ if ((msglen = read(fd, &msg, sizeof (msg))) <= 0)
+ return;
+
+ /*
+ * These are the messages that can identify a particular logical
+ * interface by local IP address.
+ */
+ if (msg.ifam.ifam_type != RTM_DELADDR &&
+ msg.ifam.ifam_type != RTM_NEWADDR)
+ return;
+
+ /* Note that ifam_index is just 16 bits */
+ ifindex = msg.ifam.ifam_index;
+
+ for (ifs = lookup_ifs_by_uindex(ifindex, NULL);
+ ifs != NULL;
+ ifs = lookup_ifs_by_uindex(ifindex, ifs)) {
+
+ /*
+ * The if_sock_ip_fd is set to a non-negative integer by
+ * configure_bound(). If it's negative, then DHCP doesn't
+ * think we're bound.
+ *
+ * For pre-bound interfaces, we want to check to see if the
+ * IFF_UP bit has been reported. This means that DAD is
+ * complete.
+ */
+ if (ifs->if_sock_ip_fd == -1 && ifs->if_state != PRE_BOUND)
+ continue;
+
+ /*
+ * Since we cannot trust the flags reported by the routing
+ * socket (they're just 32 bits -- and thus never include
+ * IFF_DUPLICATE), and we can't trust the ifindex (it's only 16
+ * bits and also doesn't reflect the alias in use), we get
+ * flags on all matching interfaces, and go by that.
+ */
+ (void) strlcpy(lifr.lifr_name, ifs->if_name,
+ sizeof (lifr.lifr_name));
+ if (ioctl(ifs->if_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
+ fail = "unable to retrieve interface flags";
+ } else if (!check_rtm_addr(&msg.ifam, msglen, ifs->if_addr)) {
+ /*
+ * If the message is not about this logical interface,
+ * then just ignore it.
+ */
+ continue;
+ } else if (lifr.lifr_flags & IFF_DUPLICATE) {
+ fail = "interface has duplicate address";
+ } else {
+ /*
+ * If we're now up and we were waiting for that, then
+ * kick off this interface. DAD is done.
+ */
+ if ((lifr.lifr_flags & IFF_UP) &&
+ ifs->if_state == PRE_BOUND)
+ dhcp_bound_complete(ifs);
+
+ continue;
+ }
+
+ if (ifs->if_sock_ip_fd != -1) {
+ (void) close(ifs->if_sock_ip_fd);
+ ifs->if_sock_ip_fd = -1;
+ }
+ dhcpmsg(MSG_ERROR, fail);
+
+ /*
+ * The binding has evidently failed, so it's as though it never
+ * happened. We need to do switch back to PRE_BOUND state so
+ * that send_pkt_internal() uses DLPI instead of sockets. Our
+ * logical interface has already been torn down by the kernel,
+ * and thus we can't send DHCPDECLINE by way of regular IP.
+ */
+ ifs->if_state = PRE_BOUND;
+
+ if (ifs->if_ack->opts[CD_DHCP_TYPE] != NULL)
+ send_decline(ifs, fail, &ifs->if_addr);
+
+ ifs->if_bad_offers++;
+ dhcp_restart(ifs);
+ }
+}