static void ind_ovs_upcall_request_pktin(uint32_t port_no, struct ind_ovs_port *port, struct nlattr *packet, struct nlattr *key, uint8_t reason, uint64_t metadata) { if (ind_ovs_benchmark_mode || aim_ratelimiter_limit(&port->pktin_limiter, monotonic_us()) == 0) { ind_ovs_bh_request_pktin(port_no, packet, key, reason, metadata); } else { if (aim_ratelimiter_limit(&port->upcall_log_limiter, monotonic_us()) == 0) { LOG_WARN("rate limiting packet-ins from port %s", port->ifname); } } }
/* Run command CMD and return statistics on it. Put the statistics in *RESP. */ static void run_command(char *const *cmd, resource_t *resp) { pid_t pid; /* Pid of child. */ void (*interrupt_signal)(int); void (*quit_signal)(int); resp->elapsed_ms = monotonic_us() / 1000; pid = vfork(); /* Run CMD as child process. */ if (pid < 0) bb_perror_msg_and_die("fork"); if (pid == 0) { /* If child. */ /* Don't cast execvp arguments; that causes errors on some systems, versus merely warnings if the cast is left off. */ BB_EXECVP(cmd[0], cmd); xfunc_error_retval = (errno == ENOENT ? 127 : 126); bb_error_msg_and_die("cannot run %s", cmd[0]); } /* Have signals kill the child but not self (if possible). */ //TODO: just block all sigs? and reenable them in the very end in main? interrupt_signal = signal(SIGINT, SIG_IGN); quit_signal = signal(SIGQUIT, SIG_IGN); resuse_end(pid, resp); /* Re-enable signals. */ signal(SIGINT, interrupt_signal); signal(SIGQUIT, quit_signal); }
/* Run command CMD and return statistics on it. Put the statistics in *RESP. */ static void run_command(char *const *cmd, resource_t * resp) { pid_t pid; /* Pid of child. */ __sighandler_t interrupt_signal, quit_signal; resp->elapsed_ms = monotonic_us() / 1000; pid = vfork(); /* Run CMD as child process. */ if (pid < 0) bb_error_msg_and_die("cannot fork"); else if (pid == 0) { /* If child. */ /* Don't cast execvp arguments; that causes errors on some systems, versus merely warnings if the cast is left off. */ BB_EXECVP(cmd[0], cmd); bb_error_msg("cannot run %s", cmd[0]); _exit(errno == ENOENT ? 127 : 126); } /* Have signals kill the child but not self (if possible). */ interrupt_signal = signal(SIGINT, SIG_IGN); quit_signal = signal(SIGQUIT, SIG_IGN); if (resuse_end(pid, resp) == 0) bb_error_msg("error waiting for child process"); /* Re-enable signals. */ signal(SIGINT, interrupt_signal); signal(SIGQUIT, quit_signal); }
uint32_t FAST_FUNC next_random(random_t *rnd) { /* Galois LFSR parameter */ /* Taps at 32 31 29 1: */ enum { MASK = 0x8000000b }; /* Another example - taps at 32 31 30 10: */ /* MASK = 0x00400007 */ uint32_t t; if (UNINITED_RANDOM_T(rnd)) { /* Can use monotonic_ns() for better randomness but for now * it is not used anywhere else in busybox... so avoid bloat */ INIT_RANDOM_T(rnd, getpid(), monotonic_us()); } /* LCG has period of 2^32 and alternating lowest bit */ rnd->LCG = 1664525 * rnd->LCG + 1013904223; /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */ t = (rnd->galois_LFSR << 1); if (rnd->galois_LFSR < 0) /* if we just shifted 1 out of msb... */ t ^= MASK; rnd->galois_LFSR = t; /* Both are weak, combining them gives better randomness * and ~2^64 period. & 0x7fff is probably bash compat * for $RANDOM range. Combining with subtraction is * just for fun. + and ^ would work equally well. */ t = (t - rnd->LCG) & 0x7fff; return t; }
/* pid_t is short on BSDI, so don't try to promote it. */ static void resuse_end(pid_t pid, resource_t *resp) { pid_t caught; /* Ignore signals, but don't ignore the children. When wait3 returns the child process, set the time the command finished. */ while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) { if (caught == -1 && errno != EINTR) { bb_perror_msg("wait"); return; } } resp->elapsed_ms = (monotonic_us() / 1000) - resp->elapsed_ms; }
/* pid_t is short on BSDI, so don't try to promote it. */ static int resuse_end(pid_t pid, resource_t * resp) { int status; pid_t caught; /* Ignore signals, but don't ignore the children. When wait3 returns the child process, set the time the command finished. */ while ((caught = wait3(&status, 0, &resp->ru)) != pid) { if (caught == -1) return 0; } resp->elapsed_ms = (monotonic_us() / 1000) - resp->elapsed_ms; resp->waitstatus = status; return 1; }
int FAST_FUNC crypt_make_salt(char *p, int cnt /*, int x */) { /* was: x += ... */ int x = getpid() + monotonic_us(); do { /* x = (x*1664525 + 1013904223) % 2^32 generator is lame * (low-order bit is not "random", etc...), * but for our purposes it is good enough */ x = x*1664525 + 1013904223; /* BTW, Park and Miller's "minimal standard generator" is * x = x*16807 % ((2^31)-1) * It has no problem with visibly alternating lowest bit * but is also weak in cryptographic sense + needs div, * which needs more code (and slower) on many CPUs */ *p++ = i64c(x >> 16); *p++ = i64c(x >> 22); } while (--cnt); *p = '\0'; return x; }
static int wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms) { struct pollfd pfd[1]; int read_len = 0; pfd[0].fd = rcvsock; pfd[0].events = POLLIN; if (*left_ms >= 0 && safe_poll(pfd, 1, *left_ms) > 0) { unsigned t; read_len = recv_from_to(rcvsock, recv_pkt, sizeof(recv_pkt), /*flags:*/ MSG_DONTWAIT, &from_lsa->u.sa, to, from_lsa->len); t = monotonic_us(); *left_ms -= (t - *timestamp_us) / 1000; *timestamp_us = t; } return read_len; }
void FAST_FUNC generate_uuid(uint8_t *buf) { /* http://www.ietf.org/rfc/rfc4122.txt * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | time_low | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | time_mid | time_hi_and_version | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |clk_seq_and_variant | node (0-1) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | node (2-5) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * IOW, uuid has this layout: * uint32_t time_low (big endian) * uint16_t time_mid (big endian) * uint16_t time_hi_and_version (big endian) * version is a 4-bit field: * 1 Time-based * 2 DCE Security, with embedded POSIX UIDs * 3 Name-based (MD5) * 4 Randomly generated * 5 Name-based (SHA-1) * uint16_t clk_seq_and_variant (big endian) * variant is a 3-bit field: * 0xx Reserved, NCS backward compatibility * 10x The variant specified in rfc4122 * 110 Reserved, Microsoft backward compatibility * 111 Reserved for future definition * uint8_t node[6] * * For version 4, these bits are set/cleared: * time_hi_and_version & 0x0fff | 0x4000 * clk_seq_and_variant & 0x3fff | 0x8000 */ pid_t pid; int i; i = open("/dev/urandom", O_RDONLY); if (i >= 0) { read(i, buf, 16); close(i); } /* Paranoia. /dev/urandom may be missing. * rand() is guaranteed to generate at least [0, 2^15) range, * but lowest bits in some libc are not so "random". */ srand(monotonic_us()); pid = getpid(); while (1) { for (i = 0; i < 16; i++) buf[i] ^= rand() >> 5; if (pid == 0) break; srand(pid); pid = 0; } /* version = 4 */ buf[4 + 2 ] = (buf[4 + 2 ] & 0x0f) | 0x40; /* variant = 10x */ buf[4 + 2 + 2] = (buf[4 + 2 + 2] & 0x3f) | 0x80; }
int makemime_main(int argc UNUSED_PARAM, char **argv) { llist_t *opt_headers = NULL, *l; const char *opt_output; #define boundary opt_output enum { OPT_c = 1 << 0, // Content-Type: OPT_e = 1 << 1, // Content-Transfer-Encoding. Ignored. Assumed base64 OPT_o = 1 << 2, // output to OPT_C = 1 << 3, // charset OPT_N = 1 << 4, // COMPAT OPT_a = 1 << 5, // additional headers OPT_m = 1 << 6, // COMPAT OPT_j = 1 << 7, // COMPAT }; INIT_G(); // parse options opt_complementary = "a::"; opts = getopt32(argv, "c:e:o:C:N:a:m:j:", &G.content_type, NULL, &opt_output, &G.opt_charset, NULL, &opt_headers, NULL, NULL ); //argc -= optind; argv += optind; // respect -o output if (opts & OPT_o) freopen(opt_output, "w", stdout); // no files given on command line? -> use stdin if (!*argv) *--argv = (char *)"-"; // put additional headers for (l = opt_headers; l; l = l->link) puts(l->data); // make a random string -- it will delimit message parts srand(monotonic_us()); boundary = xasprintf("%d-%d-%d", rand(), rand(), rand()); // put multipart header printf( "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"%s\"\n" , boundary ); // put attachments while (*argv) { printf( "\n--%s\n" "Content-Type: %s; charset=%s\n" "Content-Disposition: inline; filename=\"%s\"\n" "Content-Transfer-Encoding: base64\n" , boundary , G.content_type , G.opt_charset , bb_get_last_path_component_strip(*argv) ); encode_base64(*argv++, (const char *)stdin, ""); } // put multipart footer printf("\n--%s--\n" "\n", boundary); return EXIT_SUCCESS; #undef boundary }
static int parse(const char *boundary, char **argv) { char *line, *s, *p; const char *type; int boundary_len = strlen(boundary); const char *delims = " ;\"\t\r\n"; const char *uniq; int ntokens; const char *tokens[32]; // 32 is enough // prepare unique string pattern uniq = xasprintf("%%llu.%u.%s", (unsigned)getpid(), safe_gethostname()); //bb_info_msg("PARSE[%s]", terminator); while ((line = xmalloc_fgets_str(stdin, "\r\n\r\n")) != NULL) { // seek to start of MIME section // N.B. to avoid false positives let us seek to the _last_ occurance p = NULL; s = line; while ((s=strcasestr(s, "Content-Type:")) != NULL) p = s++; if (!p) goto next; //bb_info_msg("L[%s]", p); // split to tokens // TODO: strip of comments which are of form: (comment-text) ntokens = 0; tokens[ntokens] = NULL; for (s = strtok(p, delims); s; s = strtok(NULL, delims)) { tokens[ntokens] = s; if (ntokens < ARRAY_SIZE(tokens) - 1) ntokens++; //bb_info_msg("L[%d][%s]", ntokens, s); } tokens[ntokens] = NULL; //bb_info_msg("N[%d]", ntokens); // analyse tokens type = find_token(tokens, "Content-Type:", "text/plain"); //bb_info_msg("T[%s]", type); if (0 == strncasecmp(type, "multipart/", 10)) { if (0 == strcasecmp(type+10, "mixed")) { parse(xfind_token(tokens, "boundary="), argv); } else bb_error_msg_and_die("no support of content type '%s'", type); } else { pid_t pid = pid; int rc; FILE *fp; // fetch charset const char *charset = find_token(tokens, "charset=", CONFIG_FEATURE_MIME_CHARSET); // fetch encoding const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit"); // compose target filename char *filename = (char *)find_token(tokens, "filename=", NULL); if (!filename) filename = xasprintf(uniq, monotonic_us()); else filename = bb_get_last_path_component_strip(xstrdup(filename)); // start external helper, if any if (opts & OPT_X) { int fd[2]; xpipe(fd); pid = vfork(); if (0 == pid) { // child reads from fd[0] xdup2(fd[0], STDIN_FILENO); close(fd[0]); close(fd[1]); xsetenv("CONTENT_TYPE", type); xsetenv("CHARSET", charset); xsetenv("ENCODING", encoding); xsetenv("FILENAME", filename); BB_EXECVP(*argv, argv); _exit(EXIT_FAILURE); } // parent dumps to fd[1] close(fd[0]); fp = fdopen(fd[1], "w"); signal(SIGPIPE, SIG_IGN); // ignore EPIPE // or create a file for dump } else { char *fname = xasprintf("%s%s", *argv, filename); fp = xfopen_for_write(fname); free(fname); } // housekeeping free(filename); // dump to fp if (0 == strcasecmp(encoding, "base64")) { decode_base64(stdin, fp); } else if (0 != strcasecmp(encoding, "7bit") && 0 != strcasecmp(encoding, "8bit")) { // quoted-printable, binary, user-defined are unsupported so far bb_error_msg_and_die("no support of encoding '%s'", encoding); } else { // N.B. we have written redundant \n. so truncate the file // The following weird 2-tacts reading technique is due to // we have to not write extra \n at the end of the file // In case of -x option we could truncate the resulting file as // fseek(fp, -1, SEEK_END); // if (ftruncate(fileno(fp), ftell(fp))) // bb_perror_msg("ftruncate"); // But in case of -X we have to be much more careful. There is // no means to truncate what we already have sent to the helper. p = xmalloc_fgets_str(stdin, "\r\n"); while (p) { if ((s = xmalloc_fgets_str(stdin, "\r\n")) == NULL) break; if ('-' == s[0] && '-' == s[1] && 0 == strncmp(s+2, boundary, boundary_len)) break; fputs(p, fp); p = s; } /* while ((s = xmalloc_fgetline_str(stdin, "\r\n")) != NULL) { if ('-' == s[0] && '-' == s[1] && 0 == strncmp(s+2, boundary, boundary_len)) break; fprintf(fp, "%s\n", s); } // N.B. we have written redundant \n. so truncate the file fseek(fp, -1, SEEK_END); if (ftruncate(fileno(fp), ftell(fp))) bb_perror_msg("ftruncate"); */ } fclose(fp); // finalize helper if (opts & OPT_X) { signal(SIGPIPE, SIG_DFL); // exit if helper exited >0 rc = wait4pid(pid); if (rc) return rc+20; } // check multipart finalized if (s && '-' == s[2+boundary_len] && '-' == s[2+boundary_len+1]) { free(line); break; } } next: free(line); } //bb_info_msg("ENDPARSE[%s]", boundary); return EXIT_SUCCESS; }
int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface) { int timeout_ms; struct pollfd pfd[1]; #define s (pfd[0].fd) /* socket */ int rv = 1; /* "no reply received" yet */ struct sockaddr addr; /* for interface name */ struct arpMsg arp; s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); if (s == -1) { bb_perror_msg(bb_msg_can_not_create_raw_socket); return -1; } if (setsockopt_broadcast(s) == -1) { bb_perror_msg("cannot enable bcast on raw socket"); goto ret; } /* send arp request */ memset(&arp, 0, sizeof(arp)); memset(arp.h_dest, 0xff, 6); /* MAC DA */ memcpy(arp.h_source, from_mac, 6); /* MAC SA */ arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */ arp.htype = htons(ARPHRD_ETHER); /* hardware type */ arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */ arp.hlen = 6; /* hardware address length */ arp.plen = 4; /* protocol address length */ arp.operation = htons(ARPOP_REQUEST); /* ARP op code */ memcpy(arp.sHaddr, from_mac, 6); /* source hardware address */ memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */ /* tHaddr is zero-fiiled */ /* target hardware address */ memcpy(arp.tInaddr, &test_ip, sizeof(test_ip)); /* target IP address */ memset(&addr, 0, sizeof(addr)); safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data)); if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) { // TODO: error message? caller didn't expect us to fail, // just returning 1 "no reply received" misleads it. goto ret; } /* wait for arp reply, and check it */ timeout_ms = 2000; do { int r; unsigned prevTime = monotonic_us(); pfd[0].events = POLLIN; r = safe_poll(pfd, 1, timeout_ms); if (r < 0) break; if (r) { r = read(s, &arp, sizeof(arp)); if (r < 0) break; if (r >= ARP_MSG_SIZE && arp.operation == htons(ARPOP_REPLY) /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */ /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */ && *((uint32_t *) arp.sInaddr) == test_ip ) { rv = 0; break; } } timeout_ms -= (monotonic_us() - prevTime) / 1000; } while (timeout_ms > 0); ret: close(s); DEBUG("%srp reply received for this address", rv ? "No a" : "A"); return rv; }
int popmaildir_main(int argc UNUSED_PARAM, char **argv) { char *buf; unsigned nmsg; char *hostname; pid_t pid; const char *retr; #if ENABLE_FEATURE_POPMAILDIR_DELIVERY const char *delivery; #endif unsigned opt_nlines = 0; enum { OPT_b = 1 << 0, // -b binary mode. Ignored OPT_d = 1 << 1, // -d,-dd,-ddd debug. Ignored OPT_m = 1 << 2, // -m show ugsed memory. Ignored OPT_V = 1 << 3, // -V version. Ignored OPT_c = 1 << 4, // -c use tcpclient. Ignored OPT_a = 1 << 5, // -a use APOP protocol OPT_s = 1 << 6, // -s skip authorization OPT_T = 1 << 7, // -T get messages with TOP instead with RETR OPT_k = 1 << 8, // -k keep retrieved messages on the server OPT_t = 1 << 9, // -t90 set timeout to 90 sec OPT_R = 1 << 10, // -R20000 remove old messages on the server >= 20000 bytes (requires -k). Ignored OPT_Z = 1 << 11, // -Z11-23 remove messages from 11 to 23 (dangerous). Ignored OPT_L = 1 << 12, // -L50000 not retrieve new messages >= 50000 bytes. Ignored OPT_H = 1 << 13, // -H30 type first 30 lines of a message; (-L12000 -H30). Ignored OPT_M = 1 << 14, // -M\"program arg1 arg2 ...\"; deliver by program. Treated like -F OPT_F = 1 << 15, // -F\"program arg1 arg2 ...\"; filter by program. Treated like -M }; // init global variables INIT_G(); // parse options opt_complementary = "-1:dd:t+:R+:L+:H+"; opts = getopt32(argv, "bdmVcasTkt:" "R:Z:L:H:" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:"), &timeout, NULL, NULL, NULL, &opt_nlines IF_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same ); //argc -= optind; argv += optind; // get auth info if (!(opts & OPT_s)) get_cred_or_die(STDIN_FILENO); // goto maildir xchdir(*argv++); // launch connect helper, if any if (*argv) launch_helper((const char **)argv); // get server greeting pop3_checkr(NULL, NULL, &buf); // authenticate (if no -s given) if (!(opts & OPT_s)) { // server supports APOP and we want it? if ('<' == buf[0] && (opts & OPT_a)) { union { // save a bit of stack md5_ctx_t ctx; char hex[16 * 2 + 1]; } md5; uint32_t res[16 / 4]; char *s = strchr(buf, '>'); if (s) s[1] = '\0'; // get md5 sum of "<stamp>password" string md5_begin(&md5.ctx); md5_hash(&md5.ctx, buf, strlen(buf)); md5_hash(&md5.ctx, G.pass, strlen(G.pass)); md5_end(&md5.ctx, res); *bin2hex(md5.hex, (char*)res, 16) = '\0'; // APOP s = xasprintf("%s %s", G.user, md5.hex); pop3_check("APOP %s", s); free(s); free(buf); // server ignores APOP -> use simple text authentication } else { // USER pop3_check("USER %s", G.user); // PASS pop3_check("PASS %s", G.pass); } } // get mailbox statistics pop3_checkr("STAT", NULL, &buf); // prepare message filename suffix hostname = safe_gethostname(); pid = getpid(); // get messages counter // NOTE: we don't use xatou(buf) since buf is "nmsg nbytes" // we only need nmsg and atoi is just exactly what we need // if atoi fails to convert buf into number it returns 0 // in this case the following loop simply will not be executed nmsg = atoi(buf); free(buf); // loop through messages retr = (opts & OPT_T) ? xasprintf("TOP %%u %u", opt_nlines) : "RETR %u"; for (; nmsg; nmsg--) { char *filename; char *target; char *answer; FILE *fp; #if ENABLE_FEATURE_POPMAILDIR_DELIVERY int rc; #endif // generate unique filename filename = xasprintf("tmp/%llu.%u.%s", monotonic_us(), (unsigned)pid, hostname); // retrieve message in ./tmp/ unless filter is specified pop3_check(retr, (const char *)(ptrdiff_t)nmsg); #if ENABLE_FEATURE_POPMAILDIR_DELIVERY // delivery helper ordered? -> setup pipe if (opts & (OPT_F|OPT_M)) { // helper will have $FILENAME set to filename xsetenv("FILENAME", filename); fp = popen(delivery, "w"); unsetenv("FILENAME"); if (!fp) { bb_perror_msg("delivery helper"); break; } } else #endif // create and open file filename fp = xfopen_for_write(filename); // copy stdin to fp (either filename or delivery helper) while ((answer = xmalloc_fgets_str(stdin, "\r\n")) != NULL) { char *s = answer; if ('.' == answer[0]) { if ('.' == answer[1]) s++; else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3]) break; } //*strchrnul(s, '\r') = '\n'; fputs(s, fp); free(answer); } #if ENABLE_FEATURE_POPMAILDIR_DELIVERY // analyse delivery status if (opts & (OPT_F|OPT_M)) { rc = pclose(fp); if (99 == rc) // 99 means bail out break; // if (rc) // !0 means skip to the next message goto skip; // // 0 means continue } else { // close filename fclose(fp); } #endif // delete message from server if (!(opts & OPT_k)) pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg); // atomically move message to ./new/ target = xstrdup(filename); strncpy(target, "new", 3); // ... or just stop receiving on failure if (rename_or_warn(filename, target)) break; free(target); #if ENABLE_FEATURE_POPMAILDIR_DELIVERY skip: #endif free(filename); } // Bye pop3_check("QUIT", NULL); if (ENABLE_FEATURE_CLEAN_UP) { free(G.user); free(G.pass); } return EXIT_SUCCESS; }
/* Returns 1 if no reply received */ int FAST_FUNC arpping(uint32_t test_nip, const uint8_t *safe_mac, uint32_t from_ip, uint8_t *from_mac, const char *interface) { int timeout_ms; struct pollfd pfd[1]; #define s (pfd[0].fd) /* socket */ int rv = 1; /* "no reply received" yet */ struct sockaddr addr; /* for interface name */ struct arpMsg arp; s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); if (s == -1) { bb_perror_msg(bb_msg_can_not_create_raw_socket); return -1; } if (setsockopt_broadcast(s) == -1) { bb_perror_msg("can't enable bcast on raw socket"); goto ret; } /* send arp request */ memset(&arp, 0, sizeof(arp)); memset(arp.h_dest, 0xff, 6); /* MAC DA */ memcpy(arp.h_source, from_mac, 6); /* MAC SA */ arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */ arp.htype = htons(ARPHRD_ETHER); /* hardware type */ arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */ arp.hlen = 6; /* hardware address length */ arp.plen = 4; /* protocol address length */ arp.operation = htons(ARPOP_REQUEST); /* ARP op code */ memcpy(arp.sHaddr, from_mac, 6); /* source hardware address */ memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */ /* tHaddr is zero-filled */ /* target hardware address */ memcpy(arp.tInaddr, &test_nip, sizeof(test_nip));/* target IP address */ memset(&addr, 0, sizeof(addr)); safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data)); if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) { // TODO: error message? caller didn't expect us to fail, // just returning 1 "no reply received" misleads it. goto ret; } /* wait for arp reply, and check it */ timeout_ms = 2000; do { typedef uint32_t aliased_uint32_t FIX_ALIASING; int r; unsigned prevTime = monotonic_us()/1000; pfd[0].events = POLLIN; r = safe_poll(pfd, 1, timeout_ms); if (r < 0) break; if (r) { r = safe_read(s, &arp, sizeof(arp)); if (r < 0) break; //log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x", // arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2], // arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]); if (r >= ARP_MSG_SIZE && arp.operation == htons(ARPOP_REPLY) /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */ /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */ && *(aliased_uint32_t*)arp.sInaddr == test_nip ) { /* if ARP source MAC matches safe_mac * (which is client's MAC), then it's not a conflict * (client simply already has this IP and replies to ARPs!) */ if (!safe_mac || memcmp(safe_mac, arp.sHaddr, 6) != 0) rv = 0; //else log2("sHaddr == safe_mac"); break; } } timeout_ms -= (unsigned)monotonic_us()/1000 - prevTime; } while (timeout_ms > 0); ret: close(s); log1("%srp reply received for this address", rv ? "No a" : "A"); return rv; }