[libvirt] [libvirt PATCHv4 2/2] add leasefile support

Stefan Berger stefanb at linux.vnet.ibm.com
Tue Oct 25 12:55:51 UTC 2011


On 10/24/2011 07:12 PM, David L Stevens wrote:
> 	This patch adds support for saving DHCP snooping leases to an on-disk
> file and restoring saved leases that are still active on restart.
>
> Signed-off-by: David L Stevens<dlstevens at us.ibm.com>
> ---
>   src/nwfilter/nwfilter_dhcpsnoop.c |  312 ++++++++++++++++++++++++++++++++++++-
>   1 files changed, 306 insertions(+), 6 deletions(-)
>
> diff --git a/src/nwfilter/nwfilter_dhcpsnoop.c b/src/nwfilter/nwfilter_dhcpsnoop.c
> index b77e2b0..b2e6326 100644
> --- a/src/nwfilter/nwfilter_dhcpsnoop.c
> +++ b/src/nwfilter/nwfilter_dhcpsnoop.c
> @@ -55,10 +55,18 @@
>   #include "nwfilter_gentech_driver.h"
>   #include "nwfilter_ebiptables_driver.h"
>   #include "nwfilter_dhcpsnoop.h"
> +#include "virfile.h"
> +#include "configmake.h"
>
>   #define VIR_FROM_THIS VIR_FROM_NWFILTER
>
>   #ifdef HAVE_LIBPCAP
> +
> +#define LEASEFILE LOCALSTATEDIR "/run/libvirt/network/nwfilter.leases"
> +#define TMPLEASEFILE LOCALSTATEDIR "/run/libvirt/network/nwfilter.ltmp"
> +static int lease_fd = -1;
> +static int nleases = 0; /* number of active leases */
> +static int wleases = 0; /* number of written leases */
>
>   static virHashTablePtr SnoopReqs;
>   static pthread_mutex_t SnoopLock;
> @@ -97,7 +105,14 @@ struct iplease {
>
>   static struct iplease *ipl_getbyip(struct iplease *start, uint32_t ipaddr);
>   static void ipl_update(struct iplease *pl, uint32_t timeout);
> -
> +
> +static struct iflease *getiflease(const char *ifname);
> +static void lease_open(void);
> +static void lease_close(void);
> +static void lease_load(void);
> +static void lease_save(struct iplease *ipl);
> +static void lease_refresh(void);
> +static void lease_restore(struct virNWFilterSnoopReq *req);
>
>   /*
>    * ipl_ladd - add an IP lease to a list
> @@ -209,6 +224,8 @@ ipl_add(struct iplease *plnew)
>           return;
>       }
>       ipl_tadd(pl);
> +    nleases++;
> +    lease_save(pl);
>   }
>
>   /*
> @@ -254,6 +271,7 @@ ipl_del(struct iplease *ipl)
>       req = ipl->ipl_req;
>
>       ipl_tdel(ipl);
> +    lease_save(ipl);
>
>       /* for multiple address support, this needs to remove those rules
>        * referencing "IP" with ipl's ip value.
> @@ -262,6 +280,7 @@ ipl_del(struct iplease *ipl)
>           virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, "ipl_ldel failed");
>       }
>       VIR_FREE(ipl);
> +    nleases--;
>   }
>
>   /*
> @@ -273,6 +292,7 @@ ipl_update(struct iplease *ipl, uint32_t timeout)
>       ipl_tdel(ipl);
>       ipl->ipl_timeout = timeout;
>       ipl_tadd(ipl);
> +    lease_save(ipl);
>       return;
>   }
>
> @@ -289,8 +309,6 @@ ipl_getbyip(struct iplease *start, uint32_t ipaddr)
>       return pl;
>   }
>
> -#define GRACE   5
> -
>   /*
>    * ipl_trun - run the IP lease timeout list
>    */
> @@ -484,6 +502,9 @@ snoopReqFree(struct virNWFilterSnoopReq *req)
>   {
>       struct iplease *ipl;
>
> +    if (!req)
> +        return;
> +
>       /* free all leases */
>       snoop_lock();
>       for (ipl = req->start; ipl; ipl = req->start)
> @@ -514,6 +535,11 @@ virNWFilterDHCPSnoop(void *req0)
>       handle = dhcpopen(req->ifname);
>       if (!handle)
>           return 0;
> +
> +    /* restore any saved leases for this interface */
> +    snoop_lock();
> +    lease_restore(req);
> +    snoop_unlock();
>
>       ifindex = if_nametoindex(req->ifname);
>
> @@ -637,6 +663,20 @@ freeReq(void *req0, const void *name ATTRIBUTE_UNUSED)
>       req->die = 1;
>   }
>
> +static void
> +lease_close(void)
> +{
> +    VIR_FORCE_CLOSE(lease_fd);
> +}
> +
> +static void
> +lease_open(void)
> +{
> +    lease_close();
> +
> +    lease_fd = open(LEASEFILE, O_CREAT|O_RDWR|O_APPEND, 0644);
> +}
> +
>   int
>   virNWFilterDHCPSnoopInit(void)
>   {
> @@ -654,6 +694,8 @@ virNWFilterDHCPSnoopInit(void)
>           virReportOOMError();
>           return -1;
>       }
> +    lease_load();
> +    lease_open();
>       snoop_unlock();
>       return 0;
>   }
> @@ -666,13 +708,271 @@ virNWFilterDHCPSnoopEnd(const char *ifname)
>           snoop_unlock();
>           return;
>       }
> -    if (ifname)
> -        virHashRemoveEntry(SnoopReqs, ifname);
> -    else                        /* free all of them */
> +    if (!ifname) {
>           virHashFree(SnoopReqs);
> +        lease_refresh();
> +    } else
> +        virHashRemoveEntry(SnoopReqs, ifname);
> +    lease_close();
>       snoop_unlock();
>   }
>
> +
> +/* lease file handling */
> +
> +struct iflease {
> +    char           *ifl_ifname;
> +    struct iplease *ifl_start;
> +    struct iplease *ifl_end;
> +    struct iflease *ifl_prev;
> +    struct iflease *ifl_next;
> +};
> +
> +struct iflease *leases;
> +
> +static int
> +lease_write(int lfd, const char *ifname, struct iplease *ipl)
> +{
> +    char lbuf[256],ipstr[16],dhcpstr[16];
> +
> +    if (inet_ntop(AF_INET,&ipl->ipl_ipaddr, ipstr, sizeof ipstr) == 0) {
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("inet_ntop(0x%08X) failed"), ipl->ipl_ipaddr);
> +        return -1;
> +    }
> +    if (inet_ntop(AF_INET,&ipl->ipl_server, dhcpstr, sizeof dhcpstr) == 0) {
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("inet_ntop(0x%08X) failed"), ipl->ipl_server);
> +        return -1;
> +    }
> +    /* time intf ip dhcpserver */
> +    snprintf(lbuf, sizeof(lbuf), "%u %s %s %s\n", ipl->ipl_timeout,
> +             ifname, ipstr, dhcpstr);
> +    if (write(lfd, lbuf, strlen(lbuf))<  0) {
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("lease file write failed: %s"),
> +                               strerror(errno));
> +        return -1;
> +    }
> +    (void) fsync(lfd);
> +    return 0;
> +}
> +
> +static void
> +lease_save(struct iplease *ipl)
> +{
> +    struct virNWFilterSnoopReq *req = ipl->ipl_req;
> +    struct iflease *ifl;
> +
> +    /* add to the lease file list */
> +    ifl = getiflease(ipl->ipl_req->ifname);
> +    if (ifl) {
> +        struct iplease *ifipl = ipl_getbyip(ifl->ifl_start, ipl->ipl_ipaddr);
> +
> +        if (ifipl) {
> +            if (ipl->ipl_timeout) {
> +                ifipl->ipl_timeout = ipl->ipl_timeout;
> +                ifipl->ipl_server = ipl->ipl_server;
> +            } else {
> +                ipl_ldel(ifipl,&ifl->ifl_start,&ifl->ifl_end);
> +                VIR_FREE(ifipl);
> +            }
> +        } else if (!VIR_ALLOC(ifipl)) {
> +            ifipl->ipl_ipaddr = ipl->ipl_ipaddr;
> +            ifipl->ipl_server = ipl->ipl_server;
> +            ifipl->ipl_timeout = ipl->ipl_timeout;
> +            ipl_ladd(ifipl,&ifl->ifl_start,&ifl->ifl_end);
> +        }
> +    }
> +    if (lease_fd<  0)
> +       lease_open();
> +    if (lease_write(lease_fd, req->ifname, ipl)<  0)
> +       return;
> +    /* keep dead leases at<  ~95% of file size */
> +    if (++wleases>= nleases*20)
> +        lease_load();   /* load&  refresh lease file */
> +}
> +
> +static void
> +lease_restore(struct virNWFilterSnoopReq *req)
> +{
> +    struct iflease *ifl;
> +    struct iplease *ipl;
> +
> +    ifl = getiflease(req->ifname);
> +    if (!ifl)
> +        return;
> +    for (ipl = ifl->ifl_start; ipl; ipl = ipl->ipl_next) {
> +        ipl->ipl_req = req;
> +        ipl_add(ipl);
> +    }
> +}
> +
> +static struct iflease *
> +getiflease(const char *ifname)
> +{
> +    struct iflease *ifl;
> +
> +    for (ifl=leases; ifl; ifl=ifl->ifl_next)
> +         if (strcmp(ifname, ifl->ifl_ifname) == 0)
> +             return ifl;
> +    if (VIR_ALLOC(ifl)) {
check for VIR_ALLOC() < 0 as done everywhere else.
> +        virReportOOMError();
> +        return 0;
> +    }
> +    ifl->ifl_ifname = strdup(ifname);
> +    ifl->ifl_next = leases;
> +    leases = ifl;
> +    return ifl;
> +}
> +
> +static void
> +lease_refresh(void)
> +{
> +    struct iflease *ifl;
> +    struct iplease *ipl;
> +    int tfd;
> +
> +    (void) unlink(TMPLEASEFILE);
> +    /* lease file loaded, delete old one */
> +    tfd = open(TMPLEASEFILE, O_CREAT|O_RDWR|O_TRUNC|O_EXCL, 0644);
> +    if (tfd<  0) {
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("open(\"%s\"): %s"),
> +                               TMPLEASEFILE, strerror(errno));
> +        return;
> +    }
> +    for (ifl=leases; ifl; ifl=ifl->ifl_next)
> +        for (ipl = ifl->ifl_start; ipl; ipl = ipl->ipl_next)
> +             if (lease_write(tfd, ifl->ifl_ifname, ipl)<  0)
> +                 break;
> +    (void) close(tfd);
VIR_CLOSE()
I commented that on already on the previous submission.
> +    if (rename(TMPLEASEFILE, LEASEFILE)<  0) {
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("rename(\"%s\", \"%s\"): %s"),
> +                               TMPLEASEFILE, LEASEFILE, strerror(errno));
> +        (void) unlink(TMPLEASEFILE);
> +    }
> +    wleases = 0;
> +    lease_open();
> +}
> +
> +static void
> +LoadSnoopReqIter(void *payload,
> +                 const void *name ATTRIBUTE_UNUSED,
> +                 void *data ATTRIBUTE_UNUSED)
> +{
> +    struct virNWFilterSnoopReq *req = payload;
> +    struct iplease *ipl, *ifipl;
> +    struct iflease *ifl;
> +
> +    ifl = getiflease(req->ifname);
> +    if (!ifl)
> +        return;
> +    for (ipl = req->start; ipl; ipl = ipl->ipl_next) {
> +        ifipl = ipl_getbyip(ifl->ifl_start, ipl->ipl_ipaddr);
> +        if (ifipl) {
> +            if (ifipl->ipl_timeout<  ipl->ipl_timeout) {
> +                ifipl->ipl_timeout = ipl->ipl_timeout;
> +                ifipl->ipl_server = ipl->ipl_server;
> +            }
> +            continue;
> +        }
> +        if (VIR_ALLOC(ifipl)) {
check via (VIR_ALLOC() < 0)
> +            virReportOOMError();
> +            continue;
> +        }
> +        ifipl->ipl_ipaddr = ipl->ipl_ipaddr;
> +        ifipl->ipl_server = ipl->ipl_server;
> +        ifipl->ipl_timeout = ipl->ipl_timeout;
> +        ipl_ladd(ifipl,&ifl->ifl_start,&ifl->ifl_end);
> +    }
> +}
> +
> +static void
> +lease_load(void)
> +{
> +    char line[256], ifname[16], ipstr[16], srvstr[16];
> +    uint32_t ipaddr, svaddr;
> +    FILE *fp;
> +    int ln = 0;
> +    time_t timeout, now;
> +    struct iflease *ifl;
> +    struct iplease *ipl;
> +
> +    fp = fopen(LEASEFILE, "r");
> +    time(&now);
> +    while (fp&&  fgets(line, sizeof(line), fp)) {
Check return value of fgets().
> +        if (line[strlen(line)-1] != '\n') {
> +            virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
> +                                   _("lease_load lease file line %d corrupt"),
> +                                   ln);
> +            break;
> +        }
> +        ln++;
> +        if (sscanf(line, "%lu %16s %16s %16s", (unsigned long *)&timeout,
> +                   ifname, ipstr, srvstr)<  4) {
> +            virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
> +                                   _("lease_load lease file line %d corrupt"),
> +                                   ln);
> +            break;;
> +        }
> +        if (timeout&&  timeout<  now)
> +            continue;
> +        ifl = getiflease(ifname);
> +        if (!ifl)
> +            break;
> +
> +        if (inet_pton(AF_INET, ipstr,&ipaddr)<= 0) {
> +            virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,
> +                                  _("line %d corrupt ipaddr \"%s\""),
> +                                  ln, ipstr);
> +            VIR_FREE(ipl);
> +            continue;
> +        }
> +        (void) inet_pton(AF_INET, srvstr,&svaddr);
> +
> +        ipl = ipl_getbyip(ifl->ifl_start, ipaddr);
> +        if (ipl) {
> +            if (timeout&&  timeout<  ipl->ipl_timeout)
> +                continue; /* out of order lease? skip. */
> +            ipl->ipl_timeout = timeout;
> +            ipl->ipl_server = svaddr;
> +            continue;
> +        }
> +        if (!timeout)
> +            continue; /* don't add new lease deletions */
> +        if (VIR_ALLOC(ipl)) {
VIR_ALLOC() < 0
> +            virReportOOMError();
> +            break;
> +        }
> +        ipl->ipl_ipaddr = ipaddr;
> +        ipl->ipl_server = svaddr;
> +        ipl->ipl_timeout = timeout;
> +        ipl_ladd(ipl,&ifl->ifl_start,&ifl->ifl_end);
> +    }
> +    (void) fclose(fp);
> +    /* also load any active leases from memory, in case lease writes may
> +     * have failed.
> +     */
> +    if (SnoopReqs)
> +        virHashForEach(SnoopReqs, LoadSnoopReqIter, 0);
> +    /* remove any deleted leases */
> +    for (ifl = leases; ifl; ifl = ifl->ifl_next) {
> +        struct iplease *iplnext;
> +
> +        for (ipl = ifl->ifl_start; ipl; ipl = iplnext) {
> +            iplnext = ipl->ipl_next;
> +            if (ipl->ipl_timeout == 0) {
> +               ipl_ldel(ipl,&ifl->ifl_start,&ifl->ifl_end);
> +               VIR_FREE(ipl);
> +            }
> +        }
> +    }
> +
> +    lease_refresh();
> +}
> +
>   #else /* HAVE_LIBPCAP */
>   int
>   virNWFilterDHCPSnoopInit(void)
    Stefan




More information about the libvir-list mailing list