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