unix-socket.c 16.2 KB
Newer Older
1
/*
2 3
 * Copyright (C) 2011 CERN (www.cern.ch)
 * Author: Aurelio Colosimo
4
 * Based on PTPd project v. 2.1.0 (see AUTHORS for details)
5 6
 *
 * Released according to the GNU LGPL, version 2.1 or any later version.
7 8
 */

9
/* Socket interface for GNU/Linux (and most likely other posix systems) */
10

11
#include <stdlib.h>
12 13 14 15 16 17
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
18
#include <net/ethernet.h>
19
#include <arpa/inet.h>
20 21
#include <linux/if_packet.h>
#include <linux/if_vlan.h>
22

Alessandro Rubini's avatar
Alessandro Rubini committed
23
#include <ppsi/ppsi.h>
24
#include "ptpdump.h"
25
#include "../arch-unix/ppsi-unix.h"
26

27 28
/* unix_recv_msg uses recvmsg for timestamp query */
static int unix_recv_msg(struct pp_instance *ppi, int fd, void *pkt, int len,
29
			 TimeInternal *t)
30
{
31
	struct ethhdr *hdr = pkt;
32 33 34
	ssize_t ret;
	struct msghdr msg;
	struct iovec vec[1];
35
	int i;
36 37 38

	union {
		struct cmsghdr cm;
39
		char control[512];
40 41 42 43
	} cmsg_un;

	struct cmsghdr *cmsg;
	struct timeval *tv;
44
	struct tpacket_auxdata *aux = NULL;
45 46

	vec[0].iov_base = pkt;
47
	vec[0].iov_len = PP_MAX_FRAME_LENGTH;
48 49 50 51

	memset(&msg, 0, sizeof(msg));
	memset(&cmsg_un, 0, sizeof(cmsg_un));

52
	/* msg_name, msg_namelen == 0: not used */
53 54 55 56 57 58 59 60 61 62 63 64 65
	msg.msg_iov = vec;
	msg.msg_iovlen = 1;
	msg.msg_control = cmsg_un.control;
	msg.msg_controllen = sizeof(cmsg_un.control);

	ret = recvmsg(fd, &msg, MSG_DONTWAIT);
	if (ret <= 0) {
		if (errno == EAGAIN || errno == EINTR)
			return 0;

		return ret;
	}
	if (msg.msg_flags & MSG_TRUNC) {
66 67 68 69
		/* If we are in VLAN mode, we get everything. This is ok */
		if (ppi->proto != PPSI_PROTO_VLAN)
			pp_error("%s: truncated message\n", __func__);
		return -2; /* like "dropped" */
70 71 72
	}
	/* get time stamp of packet */
	if (msg.msg_flags & MSG_CTRUNC) {
73
		pp_error("%s: truncated ancillary data\n", __func__);
74 75 76
		return 0;
	}

77
	tv = NULL;
78 79
	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
	     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
80

81 82 83
		if (cmsg->cmsg_level == SOL_SOCKET &&
		    cmsg->cmsg_type == SCM_TIMESTAMP)
			tv = (struct timeval *)CMSG_DATA(cmsg);
84 85 86 87

		if (cmsg->cmsg_level == SOL_PACKET &&
		    cmsg->cmsg_type == PACKET_AUXDATA)
			aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg);
88 89 90
	}

	if (tv) {
91
		t->seconds = tv->tv_sec + DSPRO(ppi)->currentUtcOffset;
92
		t->nanoseconds = tv->tv_usec * 1000;
93
		t->correct = 1;
94 95
	} else {
		/*
96 97
		 * get the recording time here, even though it may  put a big
		 * spike in the offset signal sent to the clock servo
98
		 */
99
		ppi->t_ops->get(ppi, t);
100
	}
101

102 103 104 105 106 107 108 109 110 111 112
	/* aux is only there if we asked for it, thus PROTO_VLAN */
	if (aux) {
		/* With PROTO_VLAN, we bound to ETH_P_ALL: we got all frames */
		if (hdr->h_proto != htons(ETH_P_1588))
			return -2; /* like "dropped", so no error message */
		/* Also, we got the vlan, and we can discard it if not ours */
		for (i = 0; i < ppi->nvlans; i++)
			if (ppi->vlans[i] == (aux->tp_vlan_tci & 0xfff))
				break; /* ok */
		if (i == ppi->nvlans)
			return -2; /* not ours: say it's dropped */
113
		ppi->peer_vid = ppi->vlans[i];
114
	} else {
115
		ppi->peer_vid = 0;
116 117
	}

118 119 120 121 122
	if (ppsi_drop_rx()) {
		pp_diag(ppi, frames, 1, "Drop received frame\n");
		return -2;
	}

123 124 125
	/* This is not really hw... */
	pp_diag(ppi, time, 2, "recv stamp: %i.%09i (%s)\n",
		(int)t->seconds, (int)t->nanoseconds, tv ? "kernel" : "user");
126 127
	return ret;
}
128

129
/* Receive and send is *not* so trivial */
130
static int unix_net_recv(struct pp_instance *ppi, void *pkt, int len,
131
		   TimeInternal *t)
132
{
133
	struct pp_channel *ch1, *ch2;
134 135
	struct ethhdr *hdr = pkt;
	int ret;
136

137 138
	switch(ppi->proto) {
	case PPSI_PROTO_RAW:
139 140
	case PPSI_PROTO_VLAN:
		ch2 = ppi->ch + PP_NP_GEN;
141

142
		ret = unix_recv_msg(ppi, ch2->fd, pkt, len, t);
143 144 145
		if (ret <= 0)
			return ret;
		if (hdr->h_proto != htons(ETH_P_1588))
146
			return -2; /* like "dropped", so no error message */
147

148
		memcpy(ppi->peer, hdr->h_source, ETH_ALEN);
149 150
		if (pp_diag_allow(ppi, frames, 2)) {
			if (ppi->proto == PPSI_PROTO_VLAN)
151
				pp_printf("recv: VLAN %i\n", ppi->peer_vid);
152
			dump_1588pkt("recv: ", pkt, ret, t);
153
		}
154
		return ret;
155

156 157 158
	case PPSI_PROTO_UDP:
		/* we can return one frame only, always handle EVT msgs
		 * before GEN */
159 160
		ch1 = &(ppi->ch[PP_NP_EVT]);
		ch2 = &(ppi->ch[PP_NP_GEN]);
161

162 163
		ret = -1;
		if (ch1->pkt_present)
164
			ret = unix_recv_msg(ppi, ch1->fd, pkt, len, t);
165
		else if (ch2->pkt_present)
166
			ret = unix_recv_msg(ppi, ch2->fd, pkt, len, t);
167 168 169 170
		if (ret <= 0)
			return ret;
		/* We can't save the peer's mac address in UDP mode */
		if (pp_diag_allow(ppi, frames, 2))
171 172 173 174 175 176
			dump_payloadpkt("recv: ", pkt, ret, t);
		return ret;

	default:
		return -1;
	}
177 178
}

179
static int unix_net_send(struct pp_instance *ppi, void *pkt, int len,
180
			 int msgtype)
181
{
182
	int chtype = pp_msgtype_info[msgtype].chtype;
183
	struct sockaddr_in addr;
184
	struct ethhdr *hdr = pkt;
185 186
	struct pp_vlanhdr *vhdr = pkt;
	struct pp_channel *ch = ppi->ch + chtype;
187 188 189
	TimeInternal *t = &ppi->last_snt_time;
	int is_pdelay = pp_msgtype_info[msgtype].is_pdelay;
	static const uint16_t udpport[] = {
190 191 192
		[PP_NP_GEN] = PP_GEN_PORT,
		[PP_NP_EVT] = PP_EVT_PORT,
	};
193 194 195 196
	static const uint8_t macaddr[2][ETH_ALEN] = {
		[PP_E2E_MECH] = PP_MCAST_MACADDRESS,
		[PP_P2P_MECH] = PP_PDELAY_MACADDRESS,
	};
197
	int ret;
198

199 200 201 202 203 204 205 206
	/* To fake a network frame loss, set the timestamp and do not send */
	if (ppsi_drop_tx()) {
		if (t)
			ppi->t_ops->get(ppi, t);
		pp_diag(ppi, frames, 1, "Drop sent frame\n");
		return len;
	}

207 208
	switch(ppi->proto) {
	case PPSI_PROTO_RAW:
209 210
		/* raw socket implementation always uses gen socket */
		ch = ppi->ch + PP_NP_GEN;
211
		hdr->h_proto = htons(ETH_P_1588);
212

213
		memcpy(hdr->h_dest, macaddr[is_pdelay], ETH_ALEN);
214
		memcpy(hdr->h_source, ch->addr, ETH_ALEN);
215 216

		if (t)
217
			ppi->t_ops->get(ppi, t);
218

219
		ret = send(ch->fd, hdr, len, 0);
220 221 222 223 224
		if (ret < 0) {
			pp_diag(ppi, frames, 0, "send failed: %s\n",
				strerror(errno));
			return ret;
		}
225 226 227
		if (pp_diag_allow(ppi, frames, 2))
			dump_1588pkt("send: ", pkt, len, t);
		return ret;
228

229 230 231 232
	case PPSI_PROTO_VLAN:
		/* similar to sending raw frames, but w/ different header */
		ch = ppi->ch + PP_NP_GEN;
		vhdr->h_proto = htons(ETH_P_1588);
233
		vhdr->h_tci = htons(ppi->peer_vid); /* prio is 0 */
234 235
		vhdr->h_tpid = htons(0x8100);

236
		memcpy(hdr->h_dest, macaddr[is_pdelay], ETH_ALEN);
237 238 239 240 241 242
		memcpy(vhdr->h_source, ch->addr, ETH_ALEN);

		if (t)
			ppi->t_ops->get(ppi, t);

		ret = send(ch->fd, vhdr, len, 0);
243 244 245 246 247
		if (ret < 0) {
			pp_diag(ppi, frames, 0, "send failed: %s\n",
				strerror(errno));
			return ret;
		}
248 249 250
		if (pp_diag_allow(ppi, frames, 2))
			dump_1588pkt("send: ", vhdr, len, t);

251 252
	case PPSI_PROTO_UDP:
		addr.sin_family = AF_INET;
253
		addr.sin_port = htons(udpport[chtype]);
254
		addr.sin_addr.s_addr = ppi->mcast_addr[is_pdelay];
255

256 257
		if (t)
			ppi->t_ops->get(ppi, t);
258

259
		ret = sendto(ppi->ch[chtype].fd, pkt, len, 0,
260 261
			     (struct sockaddr *)&addr,
			     sizeof(struct sockaddr_in));
262 263 264 265 266
		if (ret < 0) {
			pp_diag(ppi, frames, 0, "send failed: %s\n",
				strerror(errno));
			return ret;
		}
267
		return ret;
268

269 270 271
	default:
		return -1;
	}
272 273 274
}

/* To open a channel we must bind to an interface and so on */
275
static int unix_open_ch_raw(struct pp_instance *ppi, char *ifname, int chtype)
276
{
277 278 279 280
	int sock = -1;
	int temp, iindex;
	struct ifreq ifr;
	struct sockaddr_in addr;
281
	struct sockaddr_ll addr_ll;
282
	struct packet_mreq pmr;
283
	char *context;
284

285 286
	/* open socket */
	context = "socket()";
287
	sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK, ETH_P_1588);
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
	if (sock < 0)
		goto err_out;

	/* hw interface information */
	memset(&ifr, 0, sizeof(ifr));
	strcpy(ifr.ifr_name, ifname);
	context = "ioctl(SIOCGIFINDEX)";
	if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0)
		goto err_out;

	iindex = ifr.ifr_ifindex;
	context = "ioctl(SIOCGIFHWADDR)";
	if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
		goto err_out;

303
	memcpy(ppi->ch[chtype].addr, ifr.ifr_hwaddr.sa_data, 6);
304 305 306 307

	/* bind */
	memset(&addr_ll, 0, sizeof(addr));
	addr_ll.sll_family = AF_PACKET;
308 309 310 311
	if (ppi->nvlans)
		addr_ll.sll_protocol = htons(ETH_P_ALL);
	else
		addr_ll.sll_protocol = htons(ETH_P_1588);
312 313 314 315 316 317 318 319 320 321 322 323 324 325
	addr_ll.sll_ifindex = iindex;
	context = "bind()";
	if (bind(sock, (struct sockaddr *)&addr_ll,
		 sizeof(addr_ll)) < 0)
		goto err_out;

	/* accept the multicast address for raw-ethernet ptp */
	memset(&pmr, 0, sizeof(pmr));
	pmr.mr_ifindex = iindex;
	pmr.mr_type = PACKET_MR_MULTICAST;
	pmr.mr_alen = ETH_ALEN;
	memcpy(pmr.mr_address, PP_MCAST_MACADDRESS, ETH_ALEN);
	setsockopt(sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
		   &pmr, sizeof(pmr)); /* lazily ignore errors */
326 327 328 329 330
	/* add peer delay multicast address */
	memcpy(pmr.mr_address, PP_PDELAY_MACADDRESS, ETH_ALEN);
	setsockopt(sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
		   &pmr, sizeof(pmr)); /* lazily ignore errors */

331 332

	/* make timestamps available through recvmsg() -- FIXME: hw? */
333
	temp = 1;
334 335 336
	setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
		   &temp, sizeof(int));

337 338 339 340 341 342 343
	if (ppi->proto == PPSI_PROTO_VLAN) {
		/* allow the kernel to tell us the source VLAN */
		setsockopt(sock, SOL_PACKET, PACKET_AUXDATA,
			   &temp, sizeof(temp));
	}

	ppi->ch[chtype].fd = sock;
344
	return 0;
345

346 347 348 349
err_out:
	pp_printf("%s: %s: %s\n", __func__, context, strerror(errno));
	if (sock >= 0)
		close(sock);
350
	ppi->ch[chtype].fd = -1;
351 352
	return -1;
}
353

354 355 356 357 358 359 360 361 362 363
static int unix_open_ch_udp(struct pp_instance *ppi, char *ifname, int chtype)
{
	int sock = -1;
	int temp;
	struct in_addr iface_addr, net_addr;
	struct ifreq ifr;
	struct sockaddr_in addr;
	struct ip_mreq imr;
	char addr_str[INET_ADDRSTRLEN];
	char *context;
364

365
	context = "socket()";
366
	sock = socket(PF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
367 368 369
	if (sock < 0)
		goto err_out;

370
	ppi->ch[chtype].fd = sock;
371 372 373 374 375 376 377 378 379 380 381 382

	/* hw interface information */
	memset(&ifr, 0, sizeof(ifr));
	strcpy(ifr.ifr_name, ifname);
	context = "ioctl(SIOCGIFINDEX)";
	if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0)
		goto err_out;

	context = "ioctl(SIOCGIFHWADDR)";
	if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
		goto err_out;

383
	memcpy(ppi->ch[chtype].addr, ifr.ifr_hwaddr.sa_data, 6);
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
	context = "ioctl(SIOCGIFADDR)";
	if (ioctl(sock, SIOCGIFADDR, &ifr) < 0)
		goto err_out;

	iface_addr.s_addr =
		((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;

	pp_diag(ppi, frames, 2, "Local IP address used : %s\n",
		inet_ntoa(iface_addr));

	temp = 1; /* allow address reuse */
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
		       &temp, sizeof(int)) < 0)
		pp_printf("%s: ioctl(SO_REUSEADDR): %s\n", __func__,
			  strerror(errno));

	/* bind sockets */
	/* need INADDR_ANY to allow receipt of multi-cast and uni-cast
	 * messages */
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(chtype == PP_NP_GEN
			      ? PP_GEN_PORT : PP_EVT_PORT);
	context = "bind()";
	if (bind(sock, (struct sockaddr *)&addr,
		 sizeof(struct sockaddr_in)) < 0)
		goto err_out;

	/* Init General multicast IP address */
	memcpy(addr_str, PP_DEFAULT_DOMAIN_ADDRESS, INET_ADDRSTRLEN);

	context = addr_str; errno = EINVAL;
	if (!inet_aton(addr_str, &net_addr))
		goto err_out;
418
	ppi->mcast_addr[PP_E2E_MECH] = net_addr.s_addr;
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433

	/* multicast sends only on specified interface */
	imr.imr_multiaddr.s_addr = net_addr.s_addr;
	imr.imr_interface.s_addr = iface_addr.s_addr;
	context = "setsockopt(IP_MULTICAST_IF)";
	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
		       &imr.imr_interface.s_addr,
		       sizeof(struct in_addr)) < 0)
		goto err_out;

	/* join multicast group (for recv) on specified interface */
	context = "setsockopt(IP_ADD_MEMBERSHIP)";
	if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
		       &imr, sizeof(struct ip_mreq)) < 0)
		goto err_out;
434 435 436 437 438 439 440 441

	/* Init Peer multicast IP address */
	memcpy(addr_str, PP_PDELAY_DOMAIN_ADDRESS, INET_ADDRSTRLEN);

	context = addr_str;
	errno = EINVAL;
	if (!inet_aton(addr_str, &net_addr))
		goto err_out;
442
	ppi->mcast_addr[PP_P2P_MECH] = net_addr.s_addr;
443 444 445 446 447 448 449 450
	imr.imr_multiaddr.s_addr = net_addr.s_addr;

	/* join multicast group (for receiving) on specified interface */
	context = "setsockopt(IP_ADD_MEMBERSHIP)";
	if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
		       &imr, sizeof(struct ip_mreq)) < 0)
		goto err_out;

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
	/* End of General multicast Ip address init */

	/* set socket time-to-live */
	context = "setsockopt(IP_MULTICAST_TTL)";
	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
		       &OPTS(ppi)->ttl, sizeof(int)) < 0)
		goto err_out;

	/* forcibly disable loopback */
	temp = 0;
	context = "setsockopt(IP_MULTICAST_LOOP)";
	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
		       &temp, sizeof(int)) < 0)
		goto err_out;

	/* make timestamps available through recvmsg() */
	context = "setsockopt(SO_TIMESTAMP)";
468
	temp = 1;
469 470 471 472
	if (setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
		       &temp, sizeof(int)) < 0)
		goto err_out;

473
	ppi->ch[chtype].fd = sock;
474
	return 0;
475 476 477 478 479

err_out:
	pp_printf("%s: %s: %s\n", __func__, context, strerror(errno));
	if (sock >= 0)
		close(sock);
480
	ppi->ch[chtype].fd = -1;
481
	return -1;
482 483
}

484
static int unix_net_exit(struct pp_instance *ppi);
485

486 487 488
/*
 * Inits all the network stuff
 */
489 490

/* This function must be able to be called twice, and clean-up internally */
491
static int unix_net_init(struct pp_instance *ppi)
492
{
493
	int i;
494

495
	if (ppi->ch[0].fd > 0)
496
		unix_net_exit(ppi);
497

498 499
	/* The buffer is inside ppi, but we need to set pointers and align */
	pp_prepare_pointers(ppi);
500

501 502
	switch(ppi->proto) {
	case PPSI_PROTO_RAW:
503
		pp_diag(ppi, frames, 1, "unix_net_init raw Ethernet\n");
504 505

		/* raw sockets implementation always use gen socket */
506
		return unix_open_ch_raw(ppi, ppi->iface_name, PP_NP_GEN);
507

508 509 510 511 512 513 514
	case PPSI_PROTO_VLAN:
		pp_diag(ppi, frames, 1, "unix_net_init raw Ethernet "
			"with VLAN\n");

		/* same as PROTO_RAW above, the differences are minimal */
		return unix_open_ch_raw(ppi, ppi->iface_name, PP_NP_GEN);

515
	case PPSI_PROTO_UDP:
516 517 518 519 520
		if (ppi->nvlans) {
			/* If "proto udp" is set after setting vlans... */
			pp_printf("Error: can't use UDP with VLAN support\n");
			exit(1);
		}
521 522
		pp_diag(ppi, frames, 1, "unix_net_init UDP\n");
		for (i = PP_NP_GEN; i <= PP_NP_EVT; i++) {
523
			if (unix_open_ch_udp(ppi, ppi->iface_name, i))
524 525 526
				return -1;
		}
		return 0;
527

528 529 530
	default:
		return -1;
	}
531 532
}

533 534 535
/*
 * Shutdown all the network stuff
 */
536
static int unix_net_exit(struct pp_instance *ppi)
537
{
538 539 540 541
	struct ip_mreq imr;
	int fd;
	int i;

542 543
	switch(ppi->proto) {
	case PPSI_PROTO_RAW:
544
	case PPSI_PROTO_VLAN:
545
		fd = ppi->ch[PP_NP_GEN].fd;
546 547
		if (fd > 0) {
			close(fd);
548
			ppi->ch[PP_NP_GEN].fd = -1;
549
		}
550 551
		return 0;

552 553
	case PPSI_PROTO_UDP:
		for (i = PP_NP_GEN; i <= PP_NP_EVT; i++) {
554
			fd = ppi->ch[i].fd;
555 556
			if (fd < 0)
				continue;
557

558 559
			/* Close General Multicast */
			imr.imr_interface.s_addr = htonl(INADDR_ANY);
560 561 562 563
			imr.imr_multiaddr.s_addr = ppi->mcast_addr[0];
			setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
				   &imr, sizeof(struct ip_mreq));
			imr.imr_multiaddr.s_addr = ppi->mcast_addr[1];
564 565 566
			setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
				   &imr, sizeof(struct ip_mreq));
			close(fd);
567

568
			ppi->ch[i].fd = -1;
569
		}
570
		ppi->mcast_addr[0] = ppi->mcast_addr[1] = 0;
571
		return 0;
572

573 574
	default:
		return -1;
575
	}
576 577
}

578
static int unix_net_check_packet(struct pp_globals *ppg, int delay_ms)
579 580
{
	fd_set set;
581
	int i, j, k;
582
	int ret = 0;
583
	int maxfd = -1;
584
	struct unix_arch_data *arch_data = POSIX_ARCH(ppg);
585
	int old_delay_ms;
586

587 588 589 590 591
	old_delay_ms = arch_data->tv.tv_sec * 1000 +
		arch_data->tv.tv_usec / 1000;

	if ((delay_ms != -1) &&
		((old_delay_ms == 0) || (delay_ms < old_delay_ms))) {
592
		/* Wait for a packet or for the timeout */
593 594
		arch_data->tv.tv_sec = delay_ms / 1000;
		arch_data->tv.tv_usec = (delay_ms % 1000) * 1000;
595 596
	}

597 598 599
	/* Detect general timeout with no needs for select stuff */
	if ((arch_data->tv.tv_sec == 0) && (arch_data->tv.tv_usec == 0))
		return 0;
600

601
	FD_ZERO(&set);
602

603
	for (j = 0; j < ppg->nlinks; j++) {
604
		struct pp_instance *ppi = INST(ppg, j);
605
		int fd_to_set;
606

607 608
		/* Use either fd that is valid, irrespective of ether/udp */
		for (k = 0; k < 2; k++) {
609 610
			ppi->ch[k].pkt_present = 0;
			fd_to_set = ppi->ch[k].fd;
611 612 613
			if (fd_to_set < 0)
				continue;

614 615 616
			FD_SET(fd_to_set, &set);
			maxfd = fd_to_set > maxfd ? fd_to_set : maxfd;
		}
617
	}
618
	i = select(maxfd + 1, &set, NULL, NULL, &arch_data->tv);
619 620 621 622

	if (i < 0 && errno != EINTR)
		exit(__LINE__);

623 624
	if (i < 0)
		return -1;
625 626

	if (i == 0)
627
		return 0;
628

629
	for (j = 0; j < ppg->nlinks; j++) {
630
		struct pp_instance *ppi = INST(ppg, j);
631
		int fd = ppi->ch[PP_NP_GEN].fd;
632

633
		if (fd >= 0 && FD_ISSET(fd, &set)) {
634
			ret++;
635
			ppi->ch[PP_NP_GEN].pkt_present = 1;
636
		}
637

638
		fd = ppi->ch[PP_NP_EVT].fd;
639 640

		if (fd >= 0 && FD_ISSET(fd, &set)) {
641
			ret++;
642
			ppi->ch[PP_NP_EVT].pkt_present = 1;
643 644
		}
	}
645
	return ret;
646 647
}

648 649 650 651 652 653 654 655 656
struct pp_network_operations unix_net_ops = {
	.init = unix_net_init,
	.exit = unix_net_exit,
	.recv = unix_net_recv,
	.send = unix_net_send,
	.check_packet = unix_net_check_packet,
};