+static void *make_ping_common(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv,
+ int family, int protocol,
+ void (*init_addr)(struct sockaddr *, unsigned short),
+ size_t addr_size,
+ struct sockaddr *sin)
+{
+ struct arg connect_ = decode_arg("connect", factory->params, argc, argv);
+ bool bconnect = ARG_BOOLEAN(connect_);
+
+ struct arg bind_ = decode_arg("bind", factory->params, argc, argv);
+ bool bbind = ARG_BOOLEAN(bind_);
+
+ struct arg id = decode_arg("id", factory->params, argc, argv);
+ unsigned short iid = (unsigned short)ARG_INTEGER(id);
+
+ int sd;
+
+ free_arg(&id);
+ free_arg(&bind_);
+ free_arg(&connect_);
+
+ sd = socket(family, SOCK_DGRAM, protocol);
+ if (sd < 0)
+ err(EXIT_FAILURE,
+ _("failed to make an icmp socket"));
+
+ if (sd != fdescs[0].fd) {
+ if (dup2(sd, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", sd, fdescs[0].fd);
+ }
+ close(sd);
+ sd = fdescs[0].fd;
+ }
+
+ if (bbind) {
+ init_addr(sin, iid);
+ if (bind(sd, sin, addr_size) < 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err(EXIT_FAILURE, "failed in bind(2)");
+ }
+ }
+
+ if (bconnect) {
+ init_addr(sin, 0);
+ if (connect(sd, sin, addr_size) < 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err(EXIT_FAILURE, "failed in connect(2)");
+ }
+ }
+
+ fdescs[0] = (struct fdesc) {
+ .fd = fdescs[0].fd,
+ .close = close_fdesc,
+ .data = NULL,
+ };
+
+ return NULL;
+}
+
+static void ping_init_addr(struct sockaddr *addr, unsigned short id)
+{
+ struct sockaddr_in *in = (struct sockaddr_in *)addr;
+ memset(in, 0, sizeof(*in));
+ in->sin_family = AF_INET;
+ in->sin_port = htons(id);
+ in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+}
+
+static void *make_ping(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct sockaddr_in in;
+ return make_ping_common(factory, fdescs, argc, argv,
+ AF_INET, IPPROTO_ICMP,
+ ping_init_addr,
+ sizeof(in),
+ (struct sockaddr *)&in);
+}
+