<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>