| 1 | --- a/pcap_wire.cc |
| 2 | +++ b/pcap_wire.cc |
| 3 | @@ -18,6 +18,7 @@ |
| 4 | |
| 5 | #include <sys/time.h> |
| 6 | #include <sys/select.h> |
| 7 | +#include <sys/poll.h> |
| 8 | |
| 9 | /* Ways of finding the hardware MAC on this machine... */ |
| 10 | /* This is the Linux only fallback. */ |
| 11 | @@ -130,20 +131,18 @@ namespace NSLU2Upgrade { |
| 12 | * non-static (real) Handler. |
| 13 | */ |
| 14 | void Handler(const struct pcap_pkthdr *packet_header, const u_char *packet) { |
| 15 | - /* This should only be called once... */ |
| 16 | - if (captured) |
| 17 | - throw std::logic_error("Handler called twice"); |
| 18 | - |
| 19 | /* Verify the protocol and originating address of the packet, then |
| 20 | * return this packet. |
| 21 | */ |
| 22 | + if (captured) |
| 23 | + return; |
| 24 | if (packet_header->caplen > 14 && (broadcast || |
| 25 | std::memcmp(packet+6, header, 6) == 0)) { |
| 26 | - /* Record the address and copy the data */ |
| 27 | - std::memcpy(source, packet+6, 6); |
| 28 | const size_t len(packet_header->caplen - 14); |
| 29 | if (len > captureSize) |
| 30 | - throw std::logic_error("packet too long"); |
| 31 | + return; |
| 32 | + /* Record the address and copy the data */ |
| 33 | + std::memcpy(source, packet+6, 6); |
| 34 | std::memcpy(captureBuffer, packet+14, len); |
| 35 | captureSize = len; |
| 36 | captured = true; |
| 37 | @@ -156,7 +155,7 @@ namespace NSLU2Upgrade { |
| 38 | * packet and the buffer should be big enough. |
| 39 | */ |
| 40 | if (packet_header->caplen < packet_header->len) |
| 41 | - throw std::logic_error("truncated packet"); |
| 42 | + return; |
| 43 | |
| 44 | /*IGNORE EVIL: known evil cast */ |
| 45 | reinterpret_cast<PCapWire*>(user)->Handler(packet_header, packet); |
| 46 | @@ -173,56 +172,24 @@ namespace NSLU2Upgrade { |
| 47 | virtual void Receive(void *buffer, size_t &size, unsigned long timeout) { |
| 48 | /* Now try to read packets until the timeout has been consumed. |
| 49 | */ |
| 50 | - struct timeval tvStart; |
| 51 | - if (timeout > 0 && gettimeofday(&tvStart, 0) != 0) |
| 52 | - throw OSError(errno, "gettimeofday(base)"); |
| 53 | + int time_count; |
| 54 | |
| 55 | captureBuffer = buffer; |
| 56 | captureSize = size; |
| 57 | captured = false; |
| 58 | + time_count = timeout / 2000; /* 2 ms intervals */ |
| 59 | + time_count++; |
| 60 | do { |
| 61 | /*IGNORE EVIL: known evil cast */ |
| 62 | - int count(pcap_dispatch(pcap, 1, PCapHandler, |
| 63 | - reinterpret_cast<u_char*>(this))); |
| 64 | + int count = pcap_dispatch(pcap, 1, PCapHandler, |
| 65 | + reinterpret_cast<u_char*>(this)); |
| 66 | |
| 67 | - if (count > 0) { |
| 68 | - /* Were any packets handled? */ |
| 69 | - if (captured) { |
| 70 | - size = captureSize; |
| 71 | - return; |
| 72 | - } |
| 73 | - /* else try again. */ |
| 74 | - } else if (count == 0) { |
| 75 | - /* Nothing to handle - do the timeout, do this |
| 76 | - * by waiting a bit then trying again, the trick |
| 77 | - * to this is to work out how long to wait each |
| 78 | - * time, for the moment a 10ms delay is used. |
| 79 | - */ |
| 80 | - if (timeout == 0) |
| 81 | - break; |
| 82 | - |
| 83 | - struct timeval tvNow; |
| 84 | - if (gettimeofday(&tvNow, 0) != 0) |
| 85 | - throw OSError(errno, "gettimeofday(now)"); |
| 86 | - |
| 87 | - unsigned long t(tvNow.tv_sec - tvStart.tv_sec); |
| 88 | - t *= 1000000; |
| 89 | - t += tvNow.tv_usec; |
| 90 | - t -= tvStart.tv_usec; |
| 91 | - if (t > timeout) |
| 92 | - break; |
| 93 | - |
| 94 | - tvNow.tv_sec = 0; |
| 95 | - tvNow.tv_usec = timeout-t; |
| 96 | - if (tvNow.tv_usec > 10000) |
| 97 | - tvNow.tv_usec = 10000; |
| 98 | - |
| 99 | - /* Delay, may be interrupted - this should |
| 100 | - * be portable to the BSDs (since the |
| 101 | - * technique originates in BSD.) |
| 102 | - */ |
| 103 | - (void)select(0, 0, 0, 0, &tvNow); |
| 104 | - } else { |
| 105 | + /* Were any packets handled? */ |
| 106 | + if (captured) { |
| 107 | + size = captureSize; |
| 108 | + return; |
| 109 | + } |
| 110 | + if (count < 0) { |
| 111 | /* Error condition. */ |
| 112 | if (count == -1) { |
| 113 | if (errno != EINTR) |
| 114 | @@ -232,7 +199,8 @@ namespace NSLU2Upgrade { |
| 115 | } else |
| 116 | throw std::logic_error("pcap unexpected result"); |
| 117 | } |
| 118 | - } while (timeout != 0); |
| 119 | + time_count--; |
| 120 | + } while (time_count > 0); |
| 121 | |
| 122 | /* Here on timeout. */ |
| 123 | size = 0; |
| 124 | @@ -288,6 +256,7 @@ NSLU2Upgrade::Wire *NSLU2Upgrade::Wire:: |
| 125 | const unsigned char *mac, const unsigned char *address, int uid) { |
| 126 | /* This is used to store the error passed to throw. */ |
| 127 | static char PCapErrbuf[PCAP_ERRBUF_SIZE]; |
| 128 | + struct bpf_program fp; |
| 129 | |
| 130 | /* Check the device name. If not given use 'DEFAULT_ETHERNET_IF'. */ |
| 131 | if (device == NULL) |
| 132 | @@ -301,20 +270,12 @@ NSLU2Upgrade::Wire *NSLU2Upgrade::Wire:: |
| 133 | * for other ethernet MACs. (Because the code above does not |
| 134 | * check that the destination matches the device in use). |
| 135 | */ |
| 136 | - pcap = pcap_open_live(device, 1540, false/*promiscuous*/, 1/*ms*/, PCapErrbuf); |
| 137 | + pcap = pcap_open_live(device, 1540, false/*promiscuous*/, 2/*ms*/, PCapErrbuf); |
| 138 | |
| 139 | if (pcap == NULL) |
| 140 | throw WireError(errno, PCapErrbuf); |
| 141 | } |
| 142 | |
| 143 | - /* Always do a non-blocking read, because the 'timeout' above |
| 144 | - * doesn't work on Linux (return is immediate) and on OSX (and |
| 145 | - * maybe other BSDs) the interface tends to hang waiting for |
| 146 | - * the timeout to expire even after receiving a single packet. |
| 147 | - */ |
| 148 | - if (pcap_setnonblock(pcap, true, PCapErrbuf)) |
| 149 | - throw WireError(errno, PCapErrbuf); |
| 150 | - |
| 151 | try { |
| 152 | /* The MAC of the transmitting device is needed - without |
| 153 | * this the return packet won't go to the right place! |
| 154 | |