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;
178 ipeer = inet_connect(peer);
181 peer->proto_priv = ipeer;
185 return send(ipeer->socket, buf, len, 0);
188 /*----------------------------------------------------------------------
189 Portable function to set a socket into nonblocking mode.
190 Calling this on a socket causes all future read() and write() calls on
191 that socket to do only as much as they can immediately, and return
193 If no data can be read or written, they return -1 and set errno
194 to EAGAIN (or EWOULDBLOCK).
195 Thanks to Bjorn Reese for this code.
196 ----------------------------------------------------------------------*/
197 int setnonblocking(int fd)
201 /* If they have O_NONBLOCK, use the Posix way to do it */
202 #if defined(O_NONBLOCK)
203 /* Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */
204 if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
206 return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
208 /* Otherwise, use the old way of doing it */
210 return ioctl(fd, FIOBIO, &flags);
214 int inet_accept_connection(struct inet_port *p)
217 struct sockaddr_in addr;
218 socklen_t addrlen = sizeof(addr);
219 struct epoll_event ev;
222 client = accept(p->listen_socket, (struct sockaddr *) &addr, &addrlen);
227 ret = setnonblocking(client);
232 memset(&ev, 0, sizeof(ev));
233 ev.events = EPOLLIN | EPOLLET;
235 return epoll_ctl(p->epoll_fd, EPOLL_CTL_ADD, client, &ev);
239 inet_recv(forb_port_t *port, void *buf, size_t len)
241 struct inet_port *iport = port->desc.proto_priv;
243 struct epoll_event ev;
246 if (iport->last_recv_fd == -1) {
247 nfds = epoll_wait(iport->epoll_fd, &ev, 1, -1);
250 if (ev.data.fd == iport->listen_socket) {
251 ret = inet_accept_connection(iport);
257 iport->last_recv_fd = ev.data.fd;
260 ret = recv(iport->last_recv_fd, buf, len, 0);
261 if (ret == -1 && errno == EAGAIN) {
262 iport->last_recv_fd = -1;
266 close(iport->last_recv_fd);
267 /* TODO: Notify FORB about peer disconnect */
268 iport->last_recv_fd = -1;
274 return recv(iport->udp_socket, buf, len, 0);
279 inet_port_destroy(forb_port_t * port)
281 struct inet_port *pd = port->desc.proto_priv;
283 close(pd->udp_socket);
284 close(pd->listen_socket);
290 inet_broadcast(forb_port_t *port, const void *buf, size_t len)
292 struct inet_port *p = port->desc.proto_priv;
293 struct sockaddr_in addr;
296 addr.sin_family = AF_INET;
297 addr.sin_port = htons(MCAST_PORT);
298 addr.sin_addr = p->multicast_addr;
300 ret = sendto(p->udp_socket, buf, len, 0,
301 (struct sockaddr*)&addr, sizeof(addr));
306 inet_peer_destroy(forb_peer_t *peer)
308 struct inet_peer *ipeer = peer->proto_priv;
310 peer->proto_priv = NULL;
311 close(ipeer->socket);
316 static const forb_proto_t proto_inet = {
317 .hello_interval = 40 /* seconds */,
318 .port_destroy = inet_port_destroy,
319 .peer_destroy = inet_peer_destroy,
322 .broadcast = inet_broadcast,
323 .serialize_addr = inet_serialize_addr,
324 .deserialize_addr = inet_deserialize_addr,
327 #define MAX_INTERFACES 10
328 int get_local_address(int sock, struct in_addr *addr)
331 struct ifreq *ifr, req[MAX_INTERFACES];
334 ifc.ifc_len = sizeof(req);
337 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0)
339 for (ifr = req; ifr < &req[MAX_INTERFACES]; ifr++) {
340 struct sockaddr_in ia;
341 memcpy(&ia, &ifr->ifr_addr, sizeof(ia));
342 ioctl(sock, SIOCGIFFLAGS, ifr);
343 if ((ifr->ifr_flags & IFF_UP) && !(ifr->ifr_flags & IFF_LOOPBACK)) {
352 * Initializes INET protocol port.
354 * @param port_desc Port description to initialize.
355 * @return Zero on success, -1 on error and errno is set
359 forb_inet_port_init(struct forb_port_desc *port_desc, struct in_addr listen_on)
362 struct inet_port *port_priv;
363 struct sockaddr_in addr;
365 struct epoll_event ev;
367 port_priv = forb_malloc(sizeof(*port_priv));
371 memset(port_priv, 0, sizeof(*port_priv));
372 port_priv->last_recv_fd = -1;
374 /* Initialize UDP multicast socket */
375 port_priv->udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
376 if (port_priv->udp_socket == -1) goto err_free;
379 ret = setsockopt(port_priv->udp_socket, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
384 setsockopt(port_priv->udp_socket, SOL_SOCKET, SO_REUSEADDR, (int *) &reuse, sizeof(reuse));
388 setnonblocking(port_priv->udp_socket);
391 inet_aton(MCAST_ADDR, &port_priv->multicast_addr);
392 mreq.imr_multiaddr = port_priv->multicast_addr;
393 mreq.imr_interface.s_addr = INADDR_ANY;
394 ret = setsockopt(port_priv->udp_socket, IPPROTO_IP,
395 IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
399 addr.sin_family = AF_INET;
400 addr.sin_port = htons(MCAST_PORT);
401 addr.sin_addr = port_priv->multicast_addr;
403 ret = bind(port_priv->udp_socket, (struct sockaddr *)&addr, sizeof(addr));
404 if (ret != 0) goto err_close_udp;
407 unsigned loop_size = sizeof(loop);
408 ret = setsockopt(port_priv->udp_socket, IPPROTO_IP,
409 IP_MULTICAST_LOOP, &loop, loop_size);
414 /* Initialize TCP socket */
415 port_priv->listen_socket = socket(PF_INET, SOCK_STREAM, 0);
416 if (port_priv->listen_socket == -1) goto err_close_udp;
418 addr.sin_family = AF_INET;
419 addr.sin_port = htons(0); /* Random port */
420 addr.sin_addr = listen_on;
421 ret = bind(port_priv->listen_socket, (struct sockaddr *)&addr, sizeof(addr));
422 if (ret != 0) goto err_close_listen;
423 if (setnonblocking(port_priv->listen_socket))
424 goto err_close_listen;
426 ret = listen(port_priv->listen_socket, 10);
428 goto err_close_listen;
430 /* Determine our address and port*/
432 ret = getsockname(port_priv->listen_socket, (struct sockaddr *)&addr, &len);
434 ul_logerr("Non-loopback inet address not found\n");
435 goto err_close_listen;
438 port_priv->addr.port = addr.sin_port;
439 if (listen_on.s_addr == INADDR_ANY)
440 get_local_address(port_priv->listen_socket, &port_priv->addr.addr);
442 port_priv->addr.addr = listen_on;
444 /* Initialize epoll descriptor */
445 port_priv->epoll_fd = epoll_create(10);
446 if (port_priv->epoll_fd == -1)
447 goto err_close_listen;
449 memset(&ev, 0, sizeof(ev));
450 ev.events = EPOLLIN | EPOLLET;
451 ev.data.fd = port_priv->listen_socket;
452 ret = epoll_ctl(port_priv->epoll_fd, EPOLL_CTL_ADD,
453 port_priv->listen_socket, &ev);
455 goto err_close_epoll;
457 ev.events = EPOLLIN | EPOLLET;
458 ev.data.fd = port_priv->udp_socket;
459 ret = epoll_ctl(port_priv->epoll_fd, EPOLL_CTL_ADD,
460 port_priv->udp_socket, &ev);
462 goto err_close_epoll;
464 port_desc->proto = &proto_inet;
465 port_desc->proto_priv = port_priv;
466 port_desc->addr = &port_priv->addr;
469 close(port_priv->epoll_fd);
472 close(port_priv->listen_socket);
476 close(port_priv->udp_socket);
480 forb_free(port_priv);