<tt><font size=2>David Stevens/Beaverton/IBM@IBMUS wrote on 03/26/2012
04:25:48 PM:<br>
<br>
</font></tt>
<br><tt><font size=2>> <br>
> This patch adds DHCP snooping support to libvirt. The learning method
for<br>
> IP addresses is specified by setting the "ip_learning" variable
to one of<br>
> "any" [default] (existing IP learning code), "none"
(static only addresses)<br>
> or "dhcp" (DHCP snooping).<br>
> <br>
> Active leases are saved in a lease file and reloaded on restart or
HUP.<br>
> <br>
> Changes since v6:<br>
> - replace pthread_cancel() with synchronous cancelation method<br>
> <br>
> Signed-off-by: David L Stevens <dlstevens@us.ibm.com><br>
> ---<br>
[...]<br>
> +<br>
> +static char *<br>
> +SnoopActivate(pthread_t thread)<br>
> +{<br>
> +    int len;<br>
> +    char *key;<br>
> +<br>
> +    len = sizeof(thread)*2 + 3; /* "0x"+'\0'
*/<br>
> +    if (VIR_ALLOC_N(key, len)) {<br>
> +        virReportOOMError();<br>
> +        return NULL;<br>
> +    }<br>
> +    (void) snprintf(key, len, "0x%0*lX", sizeof(thread)*2,<br>
> +                    (unsigned
long int)thread);<br>
</font></tt>
<br>
<br>
<br><tt><font size=2>> +    key[len-1] = '\0';<br>
</font></tt>
<br>
<br><tt><font size=2>Why does this string have to be closed here? You should
use virAsprintf() rather than the malloc+snprintf.</font></tt>
<br>
<br>
<br><tt><font size=2>> +<br>
> +    active_lock();<br>
> +    if (virHashAddEntry(Active, key, strdup("1")))<br>
> +        VIR_FREE(key);</font></tt>
<br>
<br><tt><font size=2>Check strdup() for NULL.</font></tt>
<br>
<br><tt><font size=2><br>
> +}<br>
> + <br>
> +/*<br>
> + * ipl_install - install rule for a lease<br>
> + */<br>
> +static int<br>
> +ipl_install(struct iplease *ipl)<br>
> +{<br>
> +    char              
      ipbuf[20];    /* dotted decimal IP <br>
> addr string */</font></tt>
<br>
<br><tt><font size=2>Use INET_ADDRSTRLEN, which is 16 and corresponds to
exactly the number of bytes needed. Also you use 16 further below.</font></tt>
<br>
<br>
<br><tt><font size=2><br>
> +    int              
       rc;<br>
> +    virNWFilterVarValuePtr   ipVar;<br>
> +<br>
> +    if (!inet_ntop(AF_INET, &ipl->ipl_ipaddr, ipbuf,
sizeof(ipbuf))) {<br>
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,<br>
> +                    
          _("ipl_install inet_ntop "
"failed (0x%08X)"),<br>
</font></tt>
<br>
<br><tt><font size=2>Why are there two separate strings?</font></tt>
<br>
<br><tt><font size=2>> +<br>
> +/*<br>
> + * ipl_trun - run the IP lease timeout list<br>
> + */<br>
> +static unsigned int<br>
> +ipl_trun(struct virNWFilterSnoopReq *req)<br>
> +{<br>
> +    uint32_t now;</font></tt>
<br>
<br>
<br><tt><font size=2>time_t ?</font></tt>
<br>
<br><tt><font size=2>> +<br>
> +unsigned char dhcp_magic[4] = { 99, 130, 83, 99 };</font></tt>
<br>
<br>
<br><tt><font size=2>static const unsigned char dhcp_magic[4] ...</font></tt>
<br>
<br>
<br><tt><font size=2>> +    pip = (struct iphdr *) pep->eh_data;<br>
> +    len -= sizeof(*pep);</font></tt>
<br>
<br><tt><font size=2>before you used sizeof xyz and here sizeof(xyz). Can
you converge to one style? Preferably the latter.</font></tt>
<br>
<br><tt><font size=2>Check len for < 0 to not step into memory that
may not have been allocated (for example pip->ihl).</font></tt>
<br>
<br>
<br><tt><font size=2>> +<br>
> +#define PBUFSIZE        576     /*
>= IP/TCP/DHCP headers */<br>
> +#define TIMEOUT          30 /* secs */<br>
> +<br>
> +static pcap_t *<br>
> +dhcpopen(const char *intf)<br>
> +{<br>
> +    pcap_t             *handle
= NULL;<br>
> +    struct bpf_program  fp;<br>
> +    char              
 filter[64];<br>
> +    char              
 pcap_errbuf[PCAP_ERRBUF_SIZE];<br>
> +    time_t              start;<br>
> +<br>
> +    start = time(0);<br>
> +    while (handle == NULL && time(0) - start <
TIMEOUT)<br>
> +        handle = pcap_open_live(intf, PBUFSIZE,
0, POLL_INTERVAL, <br>
> pcap_errbuf);<br>
> +<br>
> +    if (handle == NULL) {<br>
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,<br>
> +                    
          _("pcap_open_live: %s"), pcap_errbuf);<br>
> +        return 0;<br>
> +    }<br>
> +<br>
> +    sprintf(filter, "port 67 or dst port 68");<br>
> +    if (pcap_compile(handle, &fp, filter, 1, PCAP_NETMASK_UNKNOWN)
!= 0) {<br>
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,<br>
> +                    
          _("pcap_compile: %s"), pcap_geterr(handle));<br>
> +        return 0;<br>
> +    }<br>
> +    if (pcap_setfilter(handle, &fp) != 0) {<br>
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,<br>
> +                    
          _("pcap_setfilter: %s"), pcap_geterr(handle));<br>
</font></tt>
<br>
<br><tt><font size=2>pcap_freecode()...</font></tt>
<br>
<br><tt><font size=2>> +<br>
> +static void *<br>
> +virNWFilterDHCPSnoop(void *req0)<br>
> +{<br>
> +    struct virNWFilterSnoopReq *req = req0;<br>
> +    pcap_t              
      *handle;<br>
> +    struct pcap_pkthdr         *hdr;<br>
> +    struct eth            
    *packet;<br>
> +    int              
          ifindex;<br>
> +    int              
          errcount;<br>
> +    char              
        *threadkey;<br>
> +<br>
> +    handle = dhcpopen(req->ifname);<br>
> +    if (!handle)<br>
> +        return 0;<br>
> +<br>
> +    req->threadkey = SnoopActivate(pthread_self());<br>
> +    threadkey = strdup(req->threadkey);</font></tt>
<br>
<br>
<br><tt><font size=2>Check for NULL.</font></tt>
<br>
<br><tt><font size=2>> +<br>
> +int<br>
> +virNWFilterDHCPSnoopReq(virNWFilterTechDriverPtr techdriver,<br>
> +                    
   const char *ifname,<br>
> +                    
   const char *linkdev,<br>
> +                    
   enum virDomainNetType nettype,<br>
> +                    
   const unsigned char *vmuuid,<br>
> +                    
   const unsigned char *macaddr,<br>
> +                    
   const char *filtername,<br>
> +                    
   virNWFilterHashTablePtr filterparams,<br>
> +                    
   virNWFilterDriverStatePtr driver)<br>
> +{<br>
> +    struct virNWFilterSnoopReq *req;<br>
> +    bool              
         isnewreq;<br>
> +    char              
         ifkey[VIR_IFKEY_LEN];<br>
> +    pthread_t            
      thread;<br>
> +<br>
> +    ifkeyFormat(ifkey, vmuuid, macaddr);<br>
> +    snoop_lock();<br>
> +    req = virHashLookup(SnoopReqs, ifkey);<br>
> +    isnewreq = req == NULL;<br>
> +    if (!isnewreq) {<br>
> +        if (req->threadkey) {<br>
> +            snoop_unlock();<br>
> +            return 0;<br>
> +        }<br>
> +    } else {<br>
> +        req = newreq(ifkey);<br>
> +        if (!req) {<br>
> +            snoop_unlock();<br>
> +            return 1;</font></tt>
<br>
<br>
<br><tt><font size=2>All functions now return -1 on error.</font></tt>
<br>
<br><tt><font size=2><br>
> +        }<br>
> +    }<br>
> +<br>
> +    req->techdriver = techdriver;<br>
> +    req->ifindex = if_nametoindex(ifname);<br>
> +    req->linkdev = linkdev ? strdup(linkdev) : NULL;<br>
> +    req->nettype = nettype;<br>
> +    req->ifname = strdup(ifname);<br>
> +    memcpy(req->macaddr, macaddr, sizeof(req->macaddr));<br>
> +    req->filtername = strdup(filtername);<br>
> +    if (req->filtername == NULL) {</font></tt>
<br>
<br>
<br><tt><font size=2>Also check req->ifname for NULL.</font></tt>
<br>
<br><tt><font size=2><br>
> +        snoop_unlock();<br>
> +        snoopReqFree(req);<br>
> +        virReportOOMError();<br>
> +        return 1;<br>
> +    }</font></tt>
<br>
<br><tt><font size=2>return -1;</font></tt>
<br>
<br><tt><font size=2>> +<br>
> +static void<br>
> +lease_close(void)<br>
> +{<br>
> +    VIR_FORCE_CLOSE(lease_fd);<br>
> +}<br>
> +<br>
> +static void<br>
> +lease_open(void)<br>
> +{<br>
> +    lease_close();<br>
> +<br>
> +    lease_fd = open(LEASEFILE, O_CREAT|O_RDWR|O_APPEND,
0644);</font></tt>
<br>
<br>
<br><tt><font size=2>Does this have to be a global variable?</font></tt>
<br>
<br><tt><font size=2><br>
> +}<br>
> +<br>
> +int<br>
> +virNWFilterDHCPSnoopInit(void)<br>
> +{<br>
> +    if (SnoopReqs)<br>
> +        return 0;<br>
> +<br>
> +    snoop_lock();<br>
> +    IfnameToKey = virHashCreate(0, NULL);<br>
> +    SnoopReqs = virHashCreate(0, freeReq);<br>
> +    if (!SnoopReqs) {<br>
> +        snoop_unlock();<br>
</font></tt>
<br><tt><font size=2>free IfnameToKey? Have a common exit for error cases
.</font></tt>
<br>
<br>
<br><tt><font size=2>> +        virReportOOMError();<br>
> +        return -1;<br>
> +    }<br>
> +    lease_load();<br>
> +    lease_open();<br>
> +<br>
> +    snoop_unlock();<br>
> +<br>
> +    active_lock();<br>
> +    Active = virHashCreate(0, 0);<br>
> +    if (!Active) {<br>
> +        active_unlock();<br>
> +        virReportOOMError();<br>
</font></tt>
<br>
<br><tt><font size=2>free hash tables above?</font></tt>
<br>
<br>
<br><tt><font size=2>> +        return -1;<br>
> +    }<br>
> +    active_unlock();<br>
> +    return 0;<br>
> +}<br>
> +<br>
> +static int<br>
> +lease_write(int lfd, const char *ifkey, struct iplease *ipl)<br>
> +{<br>
> +    char lbuf[256],ipstr[16],dhcpstr[16];<br>
> +</font></tt>
<br>
<br><tt><font size=2>See above regarding the '16'. Space after comma.</font></tt>
<br>
<br><tt><font size=2><br>
> +<br>
> +static struct virNWFilterSnoopReq *<br>
> +newreq(const char *ifkey)<br>
> +{<br>
> +    struct virNWFilterSnoopReq *req;<br>
> +<br>
> +    if (VIR_ALLOC(req) < 0) {<br>
> +        virReportOOMError();<br>
> +        return NULL;<br>
> +    }<br>
> +    strncpy(req->ifkey, ifkey, sizeof req->ifkey);</font></tt>
<br>
<br><tt><font size=2>?? req->ifkey is NULL.</font></tt>
<br>
<br><tt><font size=2>Also see Hacking for usage of strncpy. Do not use
it.</font></tt>
<br>
<br><tt><font size=2><br>
> +<br>
> +    return req;<br>
> +}<br>
> +<br>
> +static void<br>
> +SaveSnoopReqIter(void *payload,<br>
> +                 const void
*name ATTRIBUTE_UNUSED,<br>
> +                 void *data)<br>
> +{<br>
> +    struct virNWFilterSnoopReq *req = payload;<br>
> +    int tfd = (int)data;</font></tt>
<br>
<br>
<br><tt><font size=2>This gives the case of different size error (on 64bit)
as I had said in previous patch reviews already.</font></tt>
<br>
<br><tt><font size=2>Use *(int *)data.</font></tt>
<br>
<br><tt><font size=2><br>
> +    struct iplease *ipl;<br>
> +<br>
> +    /* clean up orphaned, expired leases */<br>
> +    if (!req->threadkey) {<br>
> +        uint32_t now;<br>
</font></tt>
<br>
<br><tt><font size=2>time_t</font></tt>
<br>
<br>
<br><tt><font size=2>> +<br>
> +        now = time(0);<br>
> +        for (ipl = req->start; ipl; ipl =
ipl->ipl_next)<br>
> +            if (ipl->ipl_timeout
< now)<br>
> +                ipl_del(req,
ipl->ipl_ipaddr , 0);<br>
> +        if (!req->start) {<br>
> +            snoopReqFree(req);<br>
> +            return;<br>
> +        }<br>
> +    }<br>
> +    for (ipl = req->start; ipl; ipl = ipl->ipl_next)<br>
> +        (void) lease_write(tfd, req->ifkey,
ipl);<br>
> +}<br>
> +<br>
> +static void<br>
> +lease_refresh(void)<br>
> +{<br>
> +    int tfd;<br>
> +<br>
> +    (void) unlink(TMPLEASEFILE);<br>
> +    /* lease file loaded, delete old one */<br>
> +    tfd = open(TMPLEASEFILE, O_CREAT|O_RDWR|O_TRUNC|O_EXCL,
0644);<br>
> +    if (tfd < 0) {<br>
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,<br>
> +                    
          _("open(\"%s\"): %s"),<br>
> +                    
          TMPLEASEFILE, strerror(errno));<br>
> +        return;<br>
> +    }<br>
> +    if (SnoopReqs)<br>
> +        virHashForEach(SnoopReqs, SaveSnoopReqIter,
(void *)tfd);<br>
> +    (void) close(tfd);</font></tt>
<br>
<br>
<br><tt><font size=2>Use VIR_FORCE_CLOSE as already shown in previous reviews.</font></tt>
<br>
<br>
<br><tt><font size=2><br>
> +    if (rename(TMPLEASEFILE, LEASEFILE) < 0) {<br>
> +        virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,<br>
> +                    
          _("rename(\"%s\", \"%s\"):
%s"),<br>
> +                    
          TMPLEASEFILE, LEASEFILE, strerror(errno));<br>
> +        (void) unlink(TMPLEASEFILE);<br>
> +    }<br>
> +    wleases = 0;<br>
> +    lease_open();<br>
> +}<br>
> +<br>
> +<br>
> +static void<br>
> +lease_load(void)<br>
> +{<br>
> +    char line[256], ifkey[VIR_IFKEY_LEN], ipstr[16], srvstr[16];</font></tt>
<br>
<br><tt><font size=2>Same comment as above.</font></tt>
<br>
<br><tt><font size=2><br>
> +    struct iplease ipl;<br>
> +    struct virNWFilterSnoopReq *req;<br>
> +    time_t now;<br>
> +    FILE *fp;<br>
> +    int ln = 0;<br>
> +<br>
> +    fp = fopen(LEASEFILE, "r");<br>
> +    time(&now);<br>
> +    while (fp && fgets(line, sizeof(line), fp))
{<br>
> +        if (line[strlen(line)-1] != '\n') {<br>
> +            virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,<br>
> +                    
              _("lease_load lease
file line %dcorrupt"),<br>
> +                    
              ln);<br>
> +            break;<br>
> +        }<br>
> +        ln++;<br>
> +        /* key len 55 = "VMUUID"+'-'+"MAC"
*/<br>
> +        if (sscanf(line, "%u %55s %16s %16s",
&ipl.ipl_timeout,<br>
> +                   ifkey,
ipstr, srvstr) < 4) {<br>
> +            virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,<br>
> +                    
              _("lease_load lease
file line %dcorrupt"),<br>
> +                    
              ln);<br>
> +            break;;</font></tt>
<br>
<br><tt><font size=2>Remove ';'</font></tt>
<br>
<br><tt><font size=2><br>
> +        }<br>
> +        if (ipl.ipl_timeout && ipl.ipl_timeout
< now)<br>
> +            continue;<br>
> +        req = virHashLookup(SnoopReqs, ifkey);<br>
> +        if (!req) {<br>
> +            req = newreq(ifkey);<br>
> +            if (!req)<br>
> +               break;<br>
> +            if (virHashAddEntry(SnoopReqs,
ifkey, req)) {<br>
> +                snoopReqFree(req);<br>
> +                virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,<br>
> +                    
                  _("lease_load
req add failed on "<br>
> +                    
                  "interface
\"%s\""), ifkey);<br>
> +                continue;<br>
> +            }<br>
> +        }<br>
> +<br>
> +        if (inet_pton(AF_INET, ipstr, &ipl.ipl_ipaddr)
<= 0) {<br>
> +            virNWFilterReportError(VIR_ERR_INTERNAL_ERROR,<br>
> +                    
             _("line %d corrupt
ipaddr \"%s\""),<br>
> +                    
             ln, ipstr);<br>
> +            continue;<br>
> +        }<br>
> +        (void) inet_pton(AF_INET, srvstr, &ipl.ipl_server);<br>
> +        ipl.ipl_req = req;<br>
> +<br>
> +        if (ipl.ipl_timeout)<br>
> +            ipl_add(&ipl, 0);<br>
> +        else<br>
> +            ipl_del(req, ipl.ipl_ipaddr,
0);<br>
> +    }<br>
> +    if (fp != NULL)<br>
> +        (void) fclose(fp);<br>
> +    lease_refresh();<br>
> +}<br>
> +<br>
</font></tt>
<br>
<br><tt><font size=2>   Stefan</font></tt>
<br>