1/* 2 * arping.c 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 * 9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 10 */ 11 12#include <stdlib.h> 13#include <sys/param.h> 14#include <sys/socket.h> 15#include <linux/sockios.h> 16#include <sys/file.h> 17#include <sys/time.h> 18#include <sys/signal.h> 19#include <sys/ioctl.h> 20#include <linux/if.h> 21#include <linux/if_arp.h> 22#include <sys/uio.h> 23 24#include <netdb.h> 25#include <unistd.h> 26#include <stdio.h> 27#include <ctype.h> 28#include <errno.h> 29#include <string.h> 30#include <netinet/in.h> 31#include <arpa/inet.h> 32 33#include "SNAPSHOT.h" 34 35static void usage(void) __attribute__((noreturn)); 36 37int quit_on_reply=0; 38char *device="eth0"; 39int ifindex; 40char *source; 41struct in_addr src, dst; 42char *target; 43int dad, unsolicited, advert; 44int quiet; 45int count=-1; 46int timeout; 47int unicasting; 48int s; 49int broadcast_only; 50 51struct sockaddr_ll me; 52struct sockaddr_ll he; 53 54struct timeval start, last; 55 56int sent, brd_sent; 57int received, brd_recv, req_recv; 58 59#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \ 60 ((tv1).tv_usec-(tv2).tv_usec)/1000 ) 61 62void usage(void) 63{ 64 fprintf(stderr, 65 "Usage: arping [-fqbDUAV] [-c count] [-w timeout] [-I device] [-s source] destination\n" 66 " -f : quit on first reply\n" 67 " -q : be quiet\n" 68 " -b : keep broadcasting, don't go unicast\n" 69 " -D : duplicate address detection mode\n" 70 " -U : Unsolicited ARP mode, update your neighbours\n" 71 " -A : ARP answer mode, update your neighbours\n" 72 " -V : print version and exit\n" 73 " -c count : how many packets to send\n" 74 " -w timeout : how long to wait for a reply\n" 75 " -I device : which ethernet device to use (eth0)\n" 76 " -s source : source ip address\n" 77 " destination : ask for what ip address\n" 78 ); 79 exit(2); 80} 81 82void set_signal(int signo, void (*handler)(void)) 83{ 84 struct sigaction sa; 85 86 memset(&sa, 0, sizeof(sa)); 87 sa.sa_handler = (void (*)(int))handler; 88 sa.sa_flags = SA_RESTART; 89 sigaction(signo, &sa, NULL); 90} 91 92int send_pack(int s, struct in_addr src, struct in_addr dst, 93 struct sockaddr_ll *ME, struct sockaddr_ll *HE) 94{ 95 int err; 96 struct timeval now; 97 unsigned char buf[256]; 98 struct arphdr *ah = (struct arphdr*)buf; 99 unsigned char *p = (unsigned char *)(ah+1); 100 101 ah->ar_hrd = htons(ME->sll_hatype); 102 if (ah->ar_hrd == htons(ARPHRD_FDDI)) 103 ah->ar_hrd = htons(ARPHRD_ETHER); 104 ah->ar_pro = htons(ETH_P_IP); 105 ah->ar_hln = ME->sll_halen; 106 ah->ar_pln = 4; 107 ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST); 108 109 memcpy(p, &ME->sll_addr, ah->ar_hln); 110 p+=ME->sll_halen; 111 112 memcpy(p, &src, 4); 113 p+=4; 114 115 if (advert) 116 memcpy(p, &ME->sll_addr, ah->ar_hln); 117 else 118 memcpy(p, &HE->sll_addr, ah->ar_hln); 119 p+=ah->ar_hln; 120 121 memcpy(p, &dst, 4); 122 p+=4; 123 124 gettimeofday(&now, NULL); 125 err = sendto(s, buf, p-buf, 0, (struct sockaddr*)HE, sizeof(*HE)); 126 if (err == p-buf) { 127 last = now; 128 sent++; 129 if (!unicasting) 130 brd_sent++; 131 } 132 return err; 133} 134 135void finish(void) 136{ 137 if (!quiet) { 138 printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent); 139 printf("Received %d response(s)", received); 140 if (brd_recv || req_recv) { 141 printf(" ("); 142 if (req_recv) 143 printf("%d request(s)", req_recv); 144 if (brd_recv) 145 printf("%s%d broadcast(s)", 146 req_recv ? ", " : "", 147 brd_recv); 148 printf(")"); 149 } 150 printf("\n"); 151 fflush(stdout); 152 } 153 if (dad) 154 exit(!!received); 155 if (unsolicited) 156 exit(0); 157 exit(!received); 158} 159 160void catcher(void) 161{ 162 struct timeval tv; 163 164 gettimeofday(&tv, NULL); 165 166 if (start.tv_sec==0) 167 start = tv; 168 169 if (count-- == 0 || (timeout && MS_TDIFF(tv,start) > timeout*1000 + 500)) 170 finish(); 171 172 if (last.tv_sec==0 || MS_TDIFF(tv,last) > 500) { 173 send_pack(s, src, dst, &me, &he); 174 if (count == 0 && unsolicited) 175 finish(); 176 } 177 alarm(1); 178} 179 180void print_hex(unsigned char *p, int len) 181{ 182 int i; 183 for (i=0; i<len; i++) { 184 printf("%02X", p[i]); 185 if (i != len-1) 186 printf(":"); 187 } 188} 189 190int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM) 191{ 192 struct timeval tv; 193 struct arphdr *ah = (struct arphdr*)buf; 194 unsigned char *p = (unsigned char *)(ah+1); 195 struct in_addr src_ip, dst_ip; 196 197 gettimeofday(&tv, NULL); 198 199 /* Filter out wild packets */ 200 if (FROM->sll_pkttype != PACKET_HOST && 201 FROM->sll_pkttype != PACKET_BROADCAST && 202 FROM->sll_pkttype != PACKET_MULTICAST) 203 return 0; 204 205 /* Only these types are recognised */ 206 if (ah->ar_op != htons(ARPOP_REQUEST) && 207 ah->ar_op != htons(ARPOP_REPLY)) 208 return 0; 209 210 /* ARPHRD check and this darned FDDI hack here :-( */ 211 if (ah->ar_hrd != htons(FROM->sll_hatype) && 212 (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER))) 213 return 0; 214 215 /* Protocol must be IP. */ 216 if (ah->ar_pro != htons(ETH_P_IP)) 217 return 0; 218 if (ah->ar_pln != 4) 219 return 0; 220 if (ah->ar_hln != me.sll_halen) 221 return 0; 222 if (len < sizeof(*ah) + 2*(4 + ah->ar_hln)) 223 return 0; 224 memcpy(&src_ip, p+ah->ar_hln, 4); 225 memcpy(&dst_ip, p+ah->ar_hln+4+ah->ar_hln, 4); 226 if (!dad) { 227 if (src_ip.s_addr != dst.s_addr) 228 return 0; 229 if (src.s_addr != dst_ip.s_addr) 230 return 0; 231 if (memcmp(p+ah->ar_hln+4, &me.sll_addr, ah->ar_hln)) 232 return 0; 233 } else { 234 /* DAD packet was: 235 src_ip = 0 (or some src) 236 src_hw = ME 237 dst_ip = tested address 238 dst_hw = <unspec> 239 240 We fail, if receive request/reply with: 241 src_ip = tested_address 242 src_hw != ME 243 if src_ip in request was not zero, check 244 also that it matches to dst_ip, otherwise 245 dst_ip/dst_hw do not matter. 246 */ 247 if (src_ip.s_addr != dst.s_addr) 248 return 0; 249 if (memcmp(p, &me.sll_addr, me.sll_halen) == 0) 250 return 0; 251 if (src.s_addr && src.s_addr != dst_ip.s_addr) 252 return 0; 253 } 254 if (!quiet) { 255 int s_printed = 0; 256 printf("%s ", FROM->sll_pkttype==PACKET_HOST ? "Unicast" : "Broadcast"); 257 printf("%s from ", ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request"); 258 printf("%s [", inet_ntoa(src_ip)); 259 print_hex(p, ah->ar_hln); 260 printf("] "); 261 if (dst_ip.s_addr != src.s_addr) { 262 printf("for %s ", inet_ntoa(dst_ip)); 263 s_printed = 1; 264 } 265 if (memcmp(p+ah->ar_hln+4, me.sll_addr, ah->ar_hln)) { 266 if (!s_printed) 267 printf("for "); 268 printf("["); 269 print_hex(p+ah->ar_hln+4, ah->ar_hln); 270 printf("]"); 271 } 272 if (last.tv_sec) { 273 long usecs = (tv.tv_sec-last.tv_sec) * 1000000 + 274 tv.tv_usec-last.tv_usec; 275 long msecs = (usecs+500)/1000; 276 usecs -= msecs*1000 - 500; 277 printf(" %ld.%03ldms\n", msecs, usecs); 278 } else { 279 printf(" UNSOLICITED?\n"); 280 } 281 fflush(stdout); 282 } 283 received++; 284 if (FROM->sll_pkttype != PACKET_HOST) 285 brd_recv++; 286 if (ah->ar_op == htons(ARPOP_REQUEST)) 287 req_recv++; 288 if (quit_on_reply) 289 finish(); 290 if(!broadcast_only) { 291 memcpy(he.sll_addr, p, me.sll_halen); 292 unicasting=1; 293 } 294 return 1; 295} 296 297int 298main(int argc, char **argv) 299{ 300 int socket_errno; 301 int ch; 302 uid_t uid = getuid(); 303 304 s = socket(PF_PACKET, SOCK_DGRAM, 0); 305 socket_errno = errno; 306 307 setuid(uid); 308 309 while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:V")) != EOF) { 310 switch(ch) { 311 case 'b': 312 broadcast_only=1; 313 break; 314 case 'D': 315 dad++; 316 quit_on_reply=1; 317 break; 318 case 'U': 319 unsolicited++; 320 break; 321 case 'A': 322 advert++; 323 unsolicited++; 324 break; 325 case 'q': 326 quiet++; 327 break; 328 case 'c': 329 count = atoi(optarg); 330 break; 331 case 'w': 332 timeout = atoi(optarg); 333 break; 334 case 'I': 335 device = optarg; 336 break; 337 case 'f': 338 quit_on_reply=1; 339 break; 340 case 's': 341 source = optarg; 342 break; 343 case 'V': 344 printf("arping utility, iputils-ss%s\n", SNAPSHOT); 345 exit(0); 346 case 'h': 347 case '?': 348 default: 349 usage(); 350 } 351 } 352 argc -= optind; 353 argv += optind; 354 355 if (argc != 1) 356 usage(); 357 358 target = *argv; 359 360 if (device == NULL) { 361 fprintf(stderr, "arping: device (option -I) is required\n"); 362 usage(); 363 } 364 365 if (s < 0) { 366 errno = socket_errno; 367 perror("arping: socket"); 368 exit(2); 369 } 370 371 if (1) { 372 struct ifreq ifr; 373 memset(&ifr, 0, sizeof(ifr)); 374 strncpy(ifr.ifr_name, device, IFNAMSIZ-1); 375 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { 376 fprintf(stderr, "arping: unknown iface %s\n", device); 377 exit(2); 378 } 379 ifindex = ifr.ifr_ifindex; 380 381 if (ioctl(s, SIOCGIFFLAGS, (char*)&ifr)) { 382 perror("ioctl(SIOCGIFFLAGS)"); 383 exit(2); 384 } 385 if (!(ifr.ifr_flags&IFF_UP)) { 386 if (!quiet) 387 printf("Interface \"%s\" is down\n", device); 388 exit(2); 389 } 390 if (ifr.ifr_flags&(IFF_NOARP|IFF_LOOPBACK)) { 391 if (!quiet) 392 printf("Interface \"%s\" is not ARPable\n", device); 393 exit(dad?0:2); 394 } 395 } 396 397 if (inet_aton(target, &dst) != 1) { 398 struct hostent *hp; 399 hp = gethostbyname2(target, AF_INET); 400 if (!hp) { 401 fprintf(stderr, "arping: unknown host %s\n", target); 402 exit(2); 403 } 404 memcpy(&dst, hp->h_addr, 4); 405 } 406 407 if (source && inet_aton(source, &src) != 1) { 408 fprintf(stderr, "arping: invalid source %s\n", source); 409 exit(2); 410 } 411 412 if (!dad && unsolicited && src.s_addr == 0) 413 src = dst; 414 415 if (!dad || src.s_addr) { 416 struct sockaddr_in saddr; 417 int probe_fd = socket(AF_INET, SOCK_DGRAM, 0); 418 419 if (probe_fd < 0) { 420 perror("socket"); 421 exit(2); 422 } 423 if (device) { 424 if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) 425 perror("WARNING: interface is ignored"); 426 } 427 memset(&saddr, 0, sizeof(saddr)); 428 saddr.sin_family = AF_INET; 429 if (src.s_addr) { 430 saddr.sin_addr = src; 431 if (bind(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { 432 perror("bind"); 433 exit(2); 434 } 435 } else if (!dad) { 436 int on = 1; 437 int alen = sizeof(saddr); 438 439 saddr.sin_port = htons(1025); 440 saddr.sin_addr = dst; 441 442 if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char*)&on, sizeof(on)) == -1) 443 perror("WARNING: setsockopt(SO_DONTROUTE)"); 444 if (connect(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { 445 perror("connect"); 446 exit(2); 447 } 448 if (getsockname(probe_fd, (struct sockaddr*)&saddr, &alen) == -1) { 449 perror("getsockname"); 450 exit(2); 451 } 452 src = saddr.sin_addr; 453 } 454 close(probe_fd); 455 }; 456 457 me.sll_family = AF_PACKET; 458 me.sll_ifindex = ifindex; 459 me.sll_protocol = htons(ETH_P_ARP); 460 if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { 461 perror("bind"); 462 exit(2); 463 } 464 465 if (1) { 466 int alen = sizeof(me); 467 if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { 468 perror("getsockname"); 469 exit(2); 470 } 471 } 472 if (me.sll_halen == 0) { 473 if (!quiet) 474 printf("Interface \"%s\" is not ARPable (no ll address)\n", device); 475 exit(dad?0:2); 476 } 477 478 he = me; 479 memset(he.sll_addr, -1, he.sll_halen); 480 481 if (!quiet) { 482 printf("ARPING %s ", inet_ntoa(dst)); 483 printf("from %s %s\n", inet_ntoa(src), device ? : ""); 484 } 485 486 if (!src.s_addr && !dad) { 487 fprintf(stderr, "arping: no source address in not-DAD mode\n"); 488 exit(2); 489 } 490 491 set_signal(SIGINT, finish); 492 set_signal(SIGALRM, catcher); 493 494 catcher(); 495 496 while(1) { 497 sigset_t sset, osset; 498 char packet[4096]; 499 struct sockaddr_ll from; 500 int alen = sizeof(from); 501 int cc; 502 503 if ((cc = recvfrom(s, packet, sizeof(packet), 0, 504 (struct sockaddr *)&from, &alen)) < 0) { 505 perror("arping: recvfrom"); 506 continue; 507 } 508 sigemptyset(&sset); 509 sigaddset(&sset, SIGALRM); 510 sigaddset(&sset, SIGINT); 511 sigprocmask(SIG_BLOCK, &sset, &osset); 512 recv_pack(packet, cc, &from); 513 sigprocmask(SIG_SETMASK, &osset, NULL); 514 } 515} 516