[PATCH phosphor-networkd v3] Enabling updates to /etc/resolve.conf either manually or using DHCP supplied DNS IP

OpenBMC Patches openbmc-patches at stwcx.xyz
Tue May 24 22:50:43 AEST 2016


From: vishwa <vishwanath at in.ibm.com>

---
 Makefile           |  29 ++++
 netman.py          |  36 +++++
 netman_watch_dns.c | 391 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 456 insertions(+)
 create mode 100644 Makefile
 create mode 100644 netman_watch_dns.c

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..110d284
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+CC?= $(CROSS_COMPILE)gcc
+
+EXE = netman_watch_dns
+EXE_OBJ = $(EXE).o
+
+INSTALLED_EXE += $(EXE)
+
+CFLAGS += -Wall -Wno-unused-result
+
+INC_FLAG += $(shell pkg-config --cflags --libs libsystemd) -I. -O2
+LIB_FLAG += $(shell pkg-config  --libs libsystemd)
+
+DESTDIR ?= /
+SBINDIR ?= /usr/sbin
+
+all: $(EXE)
+
+%.o: %.C
+	$(CC) -c $< $(CFLAGS) $(INC_FLAG)-o $@
+
+$(EXE): $(EXE_OBJ)
+	$(CC) $^ $(LDFLAGS) $(LIB_FLAG) -o $@
+
+clean:
+	rm -f $(EXE) *.o
+
+install:
+		install -m 0755 -d $(DESTDIR)$(SBINDIR)
+		install -m 0755 $(EXE) $(DESTDIR)$(SBINDIR)
diff --git a/netman.py b/netman.py
index e2b89ca..90cb022 100755
--- a/netman.py
+++ b/netman.py
@@ -245,6 +245,42 @@ class NetMan (dbus.service.Object):
         rc = call(["systemctl", "restart", "systemd-networkd.service"])
         return rc
 
+    #string of nameservers
+    @dbus.service.method(DBUS_NAME,"s", "s")
+    def SetNameServers (self, nameservers):
+        dns_entry = nameservers.split()
+        fail_msg = ''
+        dhcp_auto = False
+        file_opened = False
+        if len(dns_entry) > 0:
+            for dns in dns_entry:
+                if not self._isvalidip (socket.AF_INET, dns):
+                    if dns == "DHCP_AUTO=":
+                        #This DNS is supplied by DHCP.
+                        dhcp_auto = True
+                    else:
+                        print "Malformed DNS Address [" + dns + "]"
+                        fail_msg = fail_msg + '[' + dns + ']'
+                else:
+                    #Only over write on a first valid input
+                    if file_opened == False:
+                        resolv_conf = open("/etc/resolv.conf",'w')
+                        file_opened = True
+                        if dhcp_auto == True:
+                            resolv_conf.write("### Generated automatically via DHCP ###\n")
+                        else:
+                            resolv_conf.write("### Generated manually via dbus settings ###\n")
+                    dns_ip = 'nameserver ' + dns + '\n'
+                    resolv_conf.write(dns_ip)
+            if file_opened == True:
+                resolv_conf.close()
+        else:
+            raise ValueError, "Invalid DNS entry"
+        if len(fail_msg) > 0:
+            return 'Failures encountered processing' + fail_msg
+        else:
+            return "DNS entries updated Successfully"
+
 def main():
     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
     bus = dbus.SystemBus()
diff --git a/netman_watch_dns.c b/netman_watch_dns.c
new file mode 100644
index 0000000..33e6f14
--- /dev/null
+++ b/netman_watch_dns.c
@@ -0,0 +1,391 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <limits.h>
+#include <errno.h>
+#include <unistd.h>
+#include <systemd/sd-bus.h>
+
+/* Dbus settings to get the DNS entries updated in resolv.conf */
+const char *bus_name = "org.openbmc.NetworkManager";
+const char *object_path = "/org/openbmc/NetworkManager/Interface";
+const char *intf_name = "org.openbmc.NetworkManager";
+
+/* Used to tell Network Manager that Name Server listing is coming from DHCP */
+const char *DHCP_MARKER = "DHCP_AUTO= ";
+
+#define DHCP_MARKER_LEN strlen(DHCP_MARKER)
+
+/*
+ * ----------------------------------------------
+ *  Receives the buffer that has the IPs of DNS
+ *  and then makes a dbus call to have these DNS
+ *  entries updated in /etc/resolv.conf
+ *  --------------------------------------------
+ */
+int update_resolv_conf(const char *dns_entry)
+{
+    /* To read the message sent by dbus handler */
+    const char *resp_msg = NULL;
+
+    /* Generic error handler */
+    int rc = 0;
+
+    if(dns_entry == NULL || !(strlen(dns_entry)))
+    {
+        fprintf(stderr,"Invalid DNS entry\n");
+        return -1;
+    }
+
+    /*
+     * Since 'state' file gets touched many a times during the network setting,
+     * it does not make sense to  have the same DNS entry updated in
+     * resolv.conf. SO we can actually have a cache of what was previously
+     * updated and not update if the same DNS info is supplied again.
+     * Eventhough this approach is a gain performance wise, it will
+     * open up windows. Assume a case where DHCP has given 1.2.3.4 as IP and
+     * user goes and updates the DNS as 4.5.6.7 and then user restarts the
+     * network and thus getting the value of 1.2.3.4. If we maintain a cache of
+     * what was previously updated, since we get 1.2.3.4 again, we will not
+     * update with 1.2.3.4 eventhough that is needed since 4.5.6.7 would not
+     * make sense. So doing updates to resolv.conf each time is bit of a
+     * overkill but its error proof.
+     */
+
+    /* Encapsulated respose by dbus handler */
+    sd_bus_message *response = NULL;
+
+    /* Errors reported by dbus handler */
+    sd_bus_error bus_error = SD_BUS_ERROR_NULL;
+
+    /*
+     * Gets a hook onto SYSTEM bus. This API may get called multiple
+     * times so do not want to have so many instances of bus as it
+     * leads to system resource issues. Re use the one that is present.
+     */
+    static sd_bus *bus_type = NULL;
+    if(bus_type == NULL)
+    {
+        rc = sd_bus_open_system(&bus_type);
+        if(rc < 0)
+        {
+            fprintf(stderr,"Error:[%s] getting system bus\n",strerror(-rc));
+            return rc;
+        }
+    }
+
+    rc = sd_bus_call_method(
+                            bus_type,        /* In the System Bus */
+                            bus_name,        /* Service to contact */
+                            object_path,     /* Object path */
+                            intf_name,       /* Interface name */
+                            "SetNameServers",/* Method to be called */
+                            &bus_error,      /* object to return error */
+                            &response,       /* Response buffer if any */
+                            "s",             /* Input as strings */
+                            dns_entry,       /* string of DNS IPs */
+                            NULL);           /* No return message expected */
+    if(rc < 0)
+    {
+        fprintf(stderr,"ERROR updating DNS entries:[%s]\n",bus_error.message);
+        goto finish;
+    }
+
+    /* Extract the encapsulated response message */
+    rc = sd_bus_message_read(response, "s", &resp_msg);
+    if (rc < 0)
+    {
+        fprintf(stderr,"Error:[%s] reading dns"
+                " updation status\n",strerror(-rc));
+    }
+    else
+    {
+        printf("%s\n",resp_msg);
+    }
+
+finish:
+    sd_bus_error_free(&bus_error);
+    response = sd_bus_message_unref(response);
+
+    return rc;
+}
+
+/*
+ * ----------------------------------------------
+ *  Gets invoked by inotify handler whenever the
+ *  netif/state file gets modified
+ *  -or- when this binary first gets launched.
+ *  --------------------------------------------
+ */
+int read_netif_state_file(const char *netif_dir, const char *state_file)
+{
+    FILE *fp;
+
+    /* Each line read from 'state' file */
+    char *line = NULL;
+
+    /* A list containing all the DNS IPs that are mentioned in 'state' file. */
+    char *dns_list = NULL;
+
+    /* length of each line read from 'state' file */
+    size_t len = 0;
+
+    /* Length of current and updated dns list */
+    size_t list_len = 0;
+    size_t new_list_len = 0;
+
+    /* Generic error reporter */
+    int rc = 0;
+
+    /* Extract the 'state' file */
+    char netif_state_file[strlen(netif_dir) + strlen(state_file) + 2];
+    sprintf(netif_state_file,"%s%s", netif_dir,state_file);
+
+    fp = fopen(netif_state_file,"r");
+    if(fp == NULL)
+    {
+        fprintf(stderr,"Error opening[%s]\n",netif_state_file);
+        return -1;
+    }
+
+    /*
+     * Read the file line by line and look for the one that starts with DNS
+     * If there is one, then what appears after DNS= are the IPs of the DNS
+     * Just checking for DNS here since any non standard IP is rejected by
+     * the dbus handler. This is to cater to cases where file may have DNS =
+     */
+    while ((getline(&line, &len, fp)) != -1)
+    {
+        if(!(strncmp(line,"DNS",3)))
+        {
+            /* Go all the way until the start of IPs */
+            char *dns_entry = strrchr(line, '=');
+
+            /* Advance to the first character after = */
+            dns_entry = &((char *)dns_entry)[1];
+
+            /* If we have never populated anything into the list */
+            if(dns_list == NULL)
+            {
+                /* The extra 2 characters to leave some gaps between DNS
+                 * entries that would come from multiple lines since each line
+                 * would start with DNS= and this overlaps with previous DNS IP.
+                 * Although I don't see this as any reality to have DNS IPs
+                 * spreading multiple lines.
+                 */
+                list_len = strlen(dns_entry) + DHCP_MARKER_LEN + 2;
+                dns_list = (char *)malloc(list_len);
+
+                /*
+                 * Populate DHCP_AUTO= along with the first line of DNS entries
+                 * This will help in putting the appropriate comments in
+                 * /etc/resolv.conf indicating the mode of DNS setting.
+                 */
+                memset(dns_list, ' ', list_len);
+                memcpy(dns_list, DHCP_MARKER, DHCP_MARKER_LEN);
+                memcpy(&dns_list[DHCP_MARKER_LEN], dns_entry, strlen(dns_entry));
+                dns_list[list_len]='\0';
+            }
+            else
+            {
+                /* This would be the entries that are coming from second+ line */
+                new_list_len = strlen(dns_entry) + list_len + 2;
+                dns_list = (char *)realloc(dns_list, new_list_len);
+
+                memset(&dns_list[list_len], ' ', strlen(dns_entry) + 2);
+                memmove(&dns_list[list_len], dns_entry, strlen(dns_entry));
+
+                /* Starting offset for next line */
+                list_len = new_list_len;
+                dns_list[list_len] = '\0';
+            }
+        }
+
+        /* Memory is allocated by getline and user needs to free */
+        if(line)
+        {
+            free(line);
+            line = NULL;
+        }
+    }
+
+    /* If we have found some or more DNS entries */
+    if(dns_list)
+    {
+        /*
+         * Being extra cautious if string somehow is not null terminated in the loop
+         */
+        dns_list[list_len] = '\0';
+
+        rc = update_resolv_conf(dns_list);
+        if(rc < 0)
+        {
+            fprintf(stderr,"Error updating resolv.conf with:[%s]\n",dns_list);
+        }
+
+        free(dns_list);
+        dns_list = NULL;
+    }
+
+    return 0;
+}
+
+void usage(void)
+{
+    printf("Usage: netman_watch_dns <Absolute path of DHCP netif state file>\n"
+            "Example: netman_watch_dns /run/systemd/netif/state\n");
+    return;
+}
+
+/*
+ * ------------------------------------------------------
+ *  Registers a inotify watch on the state file and calls
+ *  into handling the state file whenever there is a change.
+ * ------------------------------------------------------
+ */
+int watch_for_dns_change(char *netif_dir, char *state_file)
+{
+    int inotify_fd, wd;
+
+    /* the aligned statement below is per the recommendation by inotify(7) */
+    char event_data[4096]
+        __attribute__ ((aligned(__alignof__(struct inotify_event))));
+
+    /* To check the number of bytes read from inotify event */
+    ssize_t bytes_read = 0;
+
+    /* To walk event by event when inotify returns */
+    char *ptr = NULL;
+
+    /* Variable to hold individual event notification */
+    const struct inotify_event *event = NULL;
+
+    /* Generic error handler */
+    int rc = 0;
+
+    /* Create inotify instance */
+    inotify_fd = inotify_init();
+    if(inotify_fd == -1)
+    {
+        fprintf(stderr,"Error:[%s] initializing Inotify",strerror(errno));
+        return -1;
+    }
+
+    /* Register to write actions on the netif directory */
+    wd = inotify_add_watch(inotify_fd, netif_dir, IN_MODIFY);
+    if(wd == -1)
+    {
+        fprintf(stderr,"Error:[%s] adding watch for:[%s]\n",
+                strerror(errno), netif_dir);
+        return -1;
+    }
+
+    /*
+     * When this is first launched, we need to go see
+     * if there is anything present in the state file.
+     * Doing it here to close any gaps between the file
+     * getting created before registering inotifier.
+     */
+    rc = read_netif_state_file(netif_dir, state_file);
+    if(rc < 0)
+    {
+        fprintf(stderr,"Error doing initial processing of state file\n");
+    }
+
+    /* Read events forever */
+    for (;;)
+    {
+        memset(event_data, 0x0, sizeof(event_data));
+        bytes_read = read(inotify_fd, event_data, sizeof(event_data));
+        if(bytes_read <= 0)
+        {
+            fprintf(stderr,"event_data read from inotify fd was Invalid\n");
+            continue;
+        }
+
+        /* Process all of the events in buffer returned by read() */
+        for(ptr = event_data; ptr < event_data + bytes_read;
+            ptr += sizeof(struct inotify_event) + event->len)
+        {
+            event = (struct inotify_event *)ptr;
+
+            /*
+             * We are not interested in anything other than updates to
+             * state file. Now when this code is being written, its in
+             * /run/systemd/netif/state.
+             */
+            if((event->len > 0) && (strstr(event->name, state_file)))
+            {
+                rc = read_netif_state_file(netif_dir, state_file);
+                if(rc < 0)
+                {
+                    fprintf(stderr,"Error processing inotify event\n");
+                }
+            }
+        } /* Processing all inotify events. */
+    } /* Endless loop waiting for events. */
+
+    /*
+     * Technically, we should not reach here since the monitor function
+     * is not supposed to stop even on error. But for completeness.....
+     */
+    inotify_rm_watch(inotify_fd, wd);
+    close(inotify_fd);
+
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    /* Generic error handler */
+    int rc = 0;
+
+    /* Sanity checking */
+    if(argc != 2 || argv[1] == NULL)
+    {
+        usage();
+        return -1;
+    }
+
+    /*
+     * We now have the job of extracting the directory and
+     * the state file from the user supplied input.
+     */
+    char netif_dir[strlen(argv[1]) + 1];
+    memset(netif_dir, 0x0, sizeof(netif_dir));
+
+    /* File where the actual DNS= entry is found */
+    char *state_file = NULL;
+
+    /* Filter invalid inputs */
+    state_file = strrchr(argv[1], '/');
+    if(strlen(state_file) <= 1)
+    {
+        fprintf(stderr,"Invalid state file :[%s] specified\n",state_file);
+        return -1;
+    }
+    else
+    {
+        /* we have /state now and what we need is just the 'state' */
+        state_file = &((char *)state_file)[1];
+
+        /*
+         * Also extract the Absolute Path of the directory
+         * containing this state file
+         */
+        strncpy(netif_dir, argv[1], strlen(argv[1]) - strlen(state_file));
+        strcat(netif_dir,"\0");
+    }
+
+    printf("Watching for changes in DNS settings..\n");
+
+    /* Now that we have checked it once. rest is all notification bases. */
+    rc = watch_for_dns_change(netif_dir, state_file);
+    if(rc < 0)
+    {
+        fprintf(stderr,"Error watching for DNS changes\n");
+    }
+
+    return 0;
+}
-- 
2.8.3




More information about the openbmc mailing list