#include #include #include #include #include #include #include #include #include #include #include #include #include #define E(x, y) if ((x) y) { fprintf(stderr, "%s:%d %s()\n\t%s\n[%d] %s\n", \ __FILE__, __LINE__, __func__, #x, errno, strerror(errno)); exit(1); } #ifndef MGROUP_DEFAULT #define MGROUP_DEFAULT "233.252.88.24" #endif #ifndef MPORT_DEFAULT #define MPORT_DEFAULT 31338 #endif void usage(int status) { FILE *out = status ? stderr : stdout; fprintf(out, "usage: mcrecv [-l ] [-b ] [-p ]\n" "\t[-i | -s ] []\n" "\n" "default:\n" "\t(-i none) -l 0.0.0.0 -b 0.0.0.0 -p %d (-s none) " MGROUP_DEFAULT "\n", MPORT_DEFAULT); exit(1); } struct in_addr maddr; unsigned numpkts = 0; unsigned bytes = 0; void sigint(int signal) { printf("--- %s beacon statistics ---\n" "%d packets received, %d bytes total\n", inet_ntoa(maddr), numpkts, bytes); exit(0); } void loop(int sock) { struct sockaddr_in from; socklen_t fromlen; char buf[2048]; ssize_t size; signal(SIGINT, sigint); fromlen = sizeof(from); while ((size = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&from, &fromlen)) > 0) { buf[size] = '\0'; printf("%ld bytes from %s:%d: %s\n", size, inet_ntoa(from.sin_addr), ntohs(from.sin_port), buf); bytes += size; numpkts++; fromlen = sizeof(from); } E("read",); } int main(int argc, char **argv) { struct sockaddr_in rcvr; struct in_addr laddr = { INADDR_ANY }; struct in_addr saddr = { INADDR_ANY }; int sock; int c, ifindex = 0; int one = 1; maddr.s_addr = inet_addr(MGROUP_DEFAULT); rcvr.sin_family = AF_INET; rcvr.sin_port = htons(MPORT_DEFAULT); rcvr.sin_addr.s_addr = INADDR_ANY; while (1) { c = getopt(argc, argv, "i:l:b:p:s:h"); if (c == -1) break; switch (c) { case 'b': if (!inet_aton(optarg, &rcvr.sin_addr)) { fprintf(stderr, "invalid address %s\n", optarg); return 1; } break; case 'l': if (!inet_aton(optarg, &laddr)) { fprintf(stderr, "invalid address %s\n", optarg); return 1; } break; case 's': if (ifindex) { fprintf(stderr, "can't combine -s and -i\n" "(interface is looked up from routing table for SSM joins)\n"); return 1; } if (!inet_aton(optarg, &saddr)) { fprintf(stderr, "invalid address %s\n", optarg); return 1; } break; case 'i': if (saddr.s_addr) { fprintf(stderr, "can't combine -l and -i\n" "(interface is looked up from routing table for SSM joins)\n"); return 1; } ifindex = if_nametoindex(optarg); if (ifindex <= 0) { fprintf(stderr, "invalid interface name %s\n", optarg); return 1; } break; case 'p': rcvr.sin_port = htons(atoi(optarg)); break; case 'h': usage(0); case 0: default: usage(1); } } if (optind < argc - 1) { fprintf(stderr, "too many arguments\n"); return 1; } if (optind < argc) { if (!inet_aton(argv[optind], &maddr)) { fprintf(stderr, "invalid address %s\n", optarg); return 1; } } E(sock = socket(AF_INET, SOCK_DGRAM, 0), == -1); E(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)), ); E(bind(sock, (struct sockaddr *)&rcvr, sizeof(rcvr)), ); if (saddr.s_addr) { struct ip_mreq_source req; char buf0[16], buf1[16], buf2[16]; strcpy(buf0, inet_ntoa(maddr)); strcpy(buf1, inet_ntoa(saddr)); strcpy(buf2, laddr.s_addr ? inet_ntoa(laddr) : "*"); printf("BEACON (%s,%s) on %s\n", buf0, buf1, buf2); req.imr_multiaddr.s_addr = maddr.s_addr; req.imr_interface.s_addr = laddr.s_addr; req.imr_sourceaddr.s_addr = saddr.s_addr; E(setsockopt(sock, SOL_IP, IP_ADD_SOURCE_MEMBERSHIP, &req, sizeof(req)), ); } else { struct ip_mreqn req; char buf0[16], buf1[16], buf2[16]; strcpy(buf0, inet_ntoa(maddr)); strcpy(buf1, laddr.s_addr ? inet_ntoa(laddr) : "*"); printf("BEACON (%s,*) on %s%%%s\n", buf0, buf1, ifindex ? if_indextoname(ifindex, buf2) : "*"); req.imr_multiaddr.s_addr = maddr.s_addr; req.imr_address.s_addr = laddr.s_addr; req.imr_ifindex = ifindex; E(setsockopt(sock, SOL_IP, IP_ADD_MEMBERSHIP, &req, sizeof(req)), ); } loop(sock); E(close(sock),); return 0; }