1 /**************************************************************************/
2 /* ---------------------------------------------------------------------- */
3 /* Copyright (C) 2006 - 2008 FRESCOR consortium partners: */
5 /* Universidad de Cantabria, SPAIN */
6 /* University of York, UK */
7 /* Scuola Superiore Sant'Anna, ITALY */
8 /* Kaiserslautern University, GERMANY */
9 /* Univ. Politécnica Valencia, SPAIN */
10 /* Czech Technical University in Prague, CZECH REPUBLIC */
12 /* Thales Communication S.A. FRANCE */
13 /* Visual Tools S.A. SPAIN */
14 /* Rapita Systems Ltd UK */
17 /* See http://www.frescor.org for a link to partners' websites */
19 /* FRESCOR project (FP6/2005/IST/5-034026) is funded */
20 /* in part by the European Union Sixth Framework Programme */
21 /* The European Union is not liable of any use that may be */
22 /* made of this code. */
25 /* This file is part of FORB (Frescor Object Request Broker) */
27 /* FORB is free software; you can redistribute it and/or modify it */
28 /* under terms of the GNU General Public License as published by the */
29 /* Free Software Foundation; either version 2, or (at your option) any */
30 /* later version. FORB is distributed in the hope that it will be */
31 /* useful, but WITHOUT ANY WARRANTY; without even the implied warranty */
32 /* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
33 /* General Public License for more details. You should have received a */
34 /* copy of the GNU General Public License along with FORB; see file */
35 /* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, */
36 /* Cambridge, MA 02139, USA. */
38 /* As a special exception, including FORB header files in a file, */
39 /* instantiating FORB generics or templates, or linking other files */
40 /* with FORB objects to produce an executable application, does not */
41 /* by itself cause the resulting executable application to be covered */
42 /* by the GNU General Public License. This exception does not */
43 /* however invalidate any other reasons why the executable file might be */
44 /* covered by the GNU Public License. */
45 /**************************************************************************/
48 #include <arpa/inet.h>
51 #include <forb/proto_inet.h>
53 #include <netinet/in.h>
55 #include <sys/epoll.h>
56 #include <sys/ioctl.h>
57 #include <sys/socket.h>
58 #include <sys/types.h>
64 * @author Michal Sojka <sojkam1@fel.cvut.cz>
65 * @date Sun Oct 12 16:10:23 2008
67 * @brief FORB transport protocol based on INET family sockets.
69 * UDP is used for broadcasts and TCP for requests/replies. There
70 * exist two uni-drectional connections between any two communicating
74 extern UL_LOG_CUST(ulogd_forb_proto_inet);
76 #define MCAST_PORT 15514 /**< Port used for multicasts */
77 #define MCAST_ADDR "225.15.5.14"
79 /** Address used by inet protocol. All values are stored in network
83 uint16_t port; /**< TCP listening port */
86 /** INET protocol data for ports. */
88 int udp_socket; /**< Socket for sending and receiving broadcasts */
89 int listen_socket; /* */
90 int epoll_fd; /**< File descriptor used by epoll() in inet_receive(). */
91 struct inet_addr addr; /**< Address of this port */
92 int last_recv_fd; /**< Used in inet_recv() to read data longer than receiving buffer */
93 struct in_addr multicast_addr;
96 /** INET protocol data associated with every peer */
98 int socket; /**< Connected socket to the peer */
101 /* static struct inet_port* */
102 /* peer_to_inet_port(forb_peer_t *peer) { return peer->port->desc.proto_priv; } */
105 inet_serialize_addr(FORB_CDR_Codec *codec, const void *addr)
107 const struct inet_addr *a = addr;
108 CORBA_unsigned_long haddr = ntohl(a->addr.s_addr);
109 CORBA_unsigned_short hport = ntohs(a->port);
111 ret = CORBA_unsigned_long_serialize(codec, &haddr);
114 return CORBA_unsigned_short_serialize(codec, &hport);
118 inet_deserialize_addr(FORB_CDR_Codec *codec, void **addr)
121 CORBA_unsigned_long s_addr;
122 CORBA_unsigned_short port;
125 a = forb_malloc(sizeof(*a));
128 ret = CORBA_unsigned_long_deserialize(codec, &s_addr);
131 ret = CORBA_unsigned_short_deserialize(codec, &port);
132 a->addr.s_addr = htonl(s_addr);
133 a->port = htons(port);
138 static struct inet_peer *
139 inet_connect(forb_peer_t *peer)
141 struct inet_peer *ipeer;
142 struct sockaddr_in sa;
143 struct inet_addr *addr = peer->addr;
148 ipeer = forb_malloc(sizeof(*ipeer));
151 ipeer->socket = socket(PF_INET, SOCK_STREAM, 0);
154 sa.sin_family = AF_INET;
155 sa.sin_port = addr->port;
156 sa.sin_addr = addr->addr;
157 ul_logdeb("connect to %s:%u\n", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
158 ret = connect(ipeer->socket, (struct sockaddr*)&sa, sizeof(sa));
164 close(ipeer->socket);
173 inet_send(forb_peer_t *peer, const void *buf, size_t len)
175 struct inet_peer *ipeer = peer->proto_priv;
179 ipeer = inet_connect(peer);
182 peer->proto_priv = ipeer;
188 ret = send(ipeer->socket, buf, len, 0);
199 /*----------------------------------------------------------------------
200 Portable function to set a socket into nonblocking mode.
201 Calling this on a socket causes all future read() and write() calls on
202 that socket to do only as much as they can immediately, and return
204 If no data can be read or written, they return -1 and set errno
205 to EAGAIN (or EWOULDBLOCK).
206 Thanks to Bjorn Reese for this code.
207 ----------------------------------------------------------------------*/
208 int setnonblocking(int fd)
212 /* If they have O_NONBLOCK, use the Posix way to do it */
213 #if defined(O_NONBLOCK)
214 /* Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */
215 if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
217 return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
219 /* Otherwise, use the old way of doing it */
221 return ioctl(fd, FIOBIO, &flags);
225 int inet_accept_connection(struct inet_port *p)
228 struct sockaddr_in addr;
229 socklen_t addrlen = sizeof(addr);
230 struct epoll_event ev;
233 client = accept(p->listen_socket, (struct sockaddr *) &addr, &addrlen);
238 ret = setnonblocking(client);
243 memset(&ev, 0, sizeof(ev));
244 ev.events = EPOLLIN | EPOLLET;
246 return epoll_ctl(p->epoll_fd, EPOLL_CTL_ADD, client, &ev);
250 inet_recv(forb_port_t *port, void *buf, size_t len)
252 struct inet_port *iport = port->desc.proto_priv;
254 struct epoll_event ev;
257 if (iport->last_recv_fd == -1) {
258 nfds = epoll_wait(iport->epoll_fd, &ev, 1, -1);
261 if (ev.data.fd == iport->listen_socket) {
262 ret = inet_accept_connection(iport);
268 iport->last_recv_fd = ev.data.fd;
271 ret = recv(iport->last_recv_fd, buf, len, 0);
272 if (ret == -1 && errno == EAGAIN) {
273 iport->last_recv_fd = -1;
277 close(iport->last_recv_fd);
278 /* TODO: Notify FORB about peer disconnect */
279 iport->last_recv_fd = -1;
285 return recv(iport->udp_socket, buf, len, 0);
290 inet_port_destroy(forb_port_t * port)
292 struct inet_port *pd = port->desc.proto_priv;
294 close(pd->udp_socket);
295 close(pd->listen_socket);
301 inet_broadcast(forb_port_t *port, const void *buf, size_t len)
303 struct inet_port *p = port->desc.proto_priv;
304 struct sockaddr_in addr;
307 addr.sin_family = AF_INET;
308 addr.sin_port = htons(MCAST_PORT);
309 addr.sin_addr = p->multicast_addr;
311 ret = sendto(p->udp_socket, buf, len, 0,
312 (struct sockaddr*)&addr, sizeof(addr));
317 inet_peer_destroy(forb_peer_t *peer)
319 struct inet_peer *ipeer = peer->proto_priv;
321 peer->proto_priv = NULL;
322 close(ipeer->socket);
327 size_t inet_addr2str(char *dest, size_t maxlen, const void *addr)
329 const struct inet_addr *a = addr;
332 snprintf(dest, maxlen, "%s:%d", inet_ntoa(a->addr), ntohs(a->port));
338 static const forb_proto_t proto_inet = {
339 .hello_interval = 40 /* seconds */,
340 .port_destroy = inet_port_destroy,
341 .peer_destroy = inet_peer_destroy,
344 .broadcast = inet_broadcast,
345 .serialize_addr = inet_serialize_addr,
346 .deserialize_addr = inet_deserialize_addr,
347 .addr2str = inet_addr2str,
350 #define MAX_INTERFACES 10
351 int get_local_address(int sock, struct in_addr *addr)
354 struct ifreq *ifr, req[MAX_INTERFACES];
357 ifc.ifc_len = sizeof(req);
360 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0)
362 for (ifr = req; ifr < &req[MAX_INTERFACES]; ifr++) {
363 struct sockaddr_in ia;
364 memcpy(&ia, &ifr->ifr_addr, sizeof(ia));
365 ioctl(sock, SIOCGIFFLAGS, ifr);
366 if ((ifr->ifr_flags & IFF_UP) && !(ifr->ifr_flags & IFF_LOOPBACK)) {
375 * Initializes INET protocol port.
377 * @param port_desc Port description to initialize.
378 * @return Zero on success, -1 on error and errno is set
382 forb_inet_port_init(struct forb_port_desc *port_desc, struct in_addr listen_on)
385 struct inet_port *port_priv;
386 struct sockaddr_in addr;
388 struct epoll_event ev;
390 port_priv = forb_malloc(sizeof(*port_priv));
394 memset(port_priv, 0, sizeof(*port_priv));
395 port_priv->last_recv_fd = -1;
397 /* Initialize UDP multicast socket */
398 port_priv->udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
399 if (port_priv->udp_socket == -1) goto err_free;
402 ret = setsockopt(port_priv->udp_socket, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
407 setsockopt(port_priv->udp_socket, SOL_SOCKET, SO_REUSEADDR, (int *) &reuse, sizeof(reuse));
411 setnonblocking(port_priv->udp_socket);
414 inet_aton(MCAST_ADDR, &port_priv->multicast_addr);
415 mreq.imr_multiaddr = port_priv->multicast_addr;
416 mreq.imr_interface.s_addr = INADDR_ANY;
417 ret = setsockopt(port_priv->udp_socket, IPPROTO_IP,
418 IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
422 addr.sin_family = AF_INET;
423 addr.sin_port = htons(MCAST_PORT);
424 addr.sin_addr = port_priv->multicast_addr;
426 ret = bind(port_priv->udp_socket, (struct sockaddr *)&addr, sizeof(addr));
427 if (ret != 0) goto err_close_udp;
430 unsigned loop_size = sizeof(loop);
431 ret = setsockopt(port_priv->udp_socket, IPPROTO_IP,
432 IP_MULTICAST_LOOP, &loop, loop_size);
437 /* Initialize TCP socket */
438 port_priv->listen_socket = socket(PF_INET, SOCK_STREAM, 0);
439 if (port_priv->listen_socket == -1) goto err_close_udp;
441 addr.sin_family = AF_INET;
442 addr.sin_port = htons(0); /* Random port */
443 addr.sin_addr = listen_on;
444 ret = bind(port_priv->listen_socket, (struct sockaddr *)&addr, sizeof(addr));
445 if (ret != 0) goto err_close_listen;
446 if (setnonblocking(port_priv->listen_socket))
447 goto err_close_listen;
449 ret = listen(port_priv->listen_socket, 10);
451 goto err_close_listen;
453 /* Determine our address and port*/
455 ret = getsockname(port_priv->listen_socket, (struct sockaddr *)&addr, &len);
457 ul_logerr("Non-loopback inet address not found\n");
458 goto err_close_listen;
461 port_priv->addr.port = addr.sin_port;
462 if (listen_on.s_addr == INADDR_ANY)
463 get_local_address(port_priv->listen_socket, &port_priv->addr.addr);
465 port_priv->addr.addr = listen_on;
467 /* Initialize epoll descriptor */
468 port_priv->epoll_fd = epoll_create(10);
469 if (port_priv->epoll_fd == -1)
470 goto err_close_listen;
472 memset(&ev, 0, sizeof(ev));
473 ev.events = EPOLLIN | EPOLLET;
474 ev.data.fd = port_priv->listen_socket;
475 ret = epoll_ctl(port_priv->epoll_fd, EPOLL_CTL_ADD,
476 port_priv->listen_socket, &ev);
478 goto err_close_epoll;
480 ev.events = EPOLLIN | EPOLLET;
481 ev.data.fd = port_priv->udp_socket;
482 ret = epoll_ctl(port_priv->epoll_fd, EPOLL_CTL_ADD,
483 port_priv->udp_socket, &ev);
485 goto err_close_epoll;
487 port_desc->proto = &proto_inet;
488 port_desc->proto_priv = port_priv;
489 port_desc->addr = &port_priv->addr;
492 close(port_priv->epoll_fd);
495 close(port_priv->listen_socket);
499 close(port_priv->udp_socket);
503 forb_free(port_priv);