/** * Finish a DLEP signal/message * @param writer dlep writer * @param source logging source for error messages * @return -1 if an error happened, 0 otherwise */ int dlep_writer_finish_signal(struct dlep_writer *writer, enum oonf_log_source source) { size_t length; uint16_t tmp16; char *dst; if (abuf_has_failed(writer->out)) { OONF_WARN(source, "Could not build signal: %u", writer->signal_type); return -1; } length = abuf_getlen(writer->out) - writer->signal_start; if (length > 65535 + 4) { OONF_WARN(source, "Signal %u became too long: %" PRINTF_SIZE_T_SPECIFIER, writer->signal_type, abuf_getlen(writer->out)); return -1; } /* calculate network ordered size */ tmp16 = htons(length - 4); /* put it into the signal */ dst = abuf_getptr(writer->out); memcpy(&dst[writer->signal_start + 2], &tmp16, sizeof(tmp16)); OONF_DEBUG_HEX(source, &dst[writer->signal_start], length, "Finished signal %u:", writer->signal_type); return 0; }
/** * Creates a new raw socket and configures it * @param sock empty socket instance * @param bind_to address to bind the socket to * @param protocol IP protocol number * @param recvbuf size of input buffer for socket * @param os_if pointer to interface to bind socket on, * NULL if socket should not be bound to an interface * @param log_src logging source for error messages * @return -1 if an error happened, 0 otherwise */ int os_fd_generic_getrawsocket(struct os_fd *sock, const union netaddr_socket *bind_to, int protocol, size_t recvbuf, const struct os_interface *os_if, enum oonf_log_source log_src __attribute__((unused))) { static const int zero = 0; int family; family = bind_to->std.sa_family; sock->fd = socket(family, SOCK_RAW, protocol); if (sock->fd < 0) { OONF_WARN(log_src, "Cannot open socket: %s (%d)", strerror(errno), errno); return -1; } if (family == AF_INET) { if (setsockopt (sock->fd, IPPROTO_IP, IP_HDRINCL, &zero, sizeof(zero)) < 0) { OONF_WARN(log_src, "Cannot disable IP_HDRINCL for socket: %s (%d)", strerror(errno), errno); os_fd_close(sock); return -1; } } if (os_fd_configsocket(sock, bind_to, recvbuf, true, os_if, log_src)) { os_fd_close(sock); return -1; } return 0; }
/** * Write process ID into file * @param filename * @return -1 if an error happened, 0 otherwise */ static int _write_pidfile(const char *filename) { int pid_fd; char buffer[16]; pid_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (pid_fd < 0) { OONF_WARN(LOG_MAIN, "Could not open pidfile '%s': %s (%d)", filename, strerror(errno), errno); return -1; } snprintf(buffer, sizeof(buffer), "%d\n", getpid()); if (write(pid_fd, buffer, strlen(buffer)+1) < 0) { OONF_WARN(LOG_MAIN, "Could not write pid %d into pidfile '%s': %s (%d)", getpid(), filename, strerror(errno), errno); close(pid_fd); return -1; } close(pid_fd); return 0; }
/** * Join a socket into a multicast group * @param sock filedescriptor of socket * @param multicast multicast-group to join * @param os_if pointer to outgoing interface data for multicast * @param log_src logging source for error messages * @return -1 if an error happened, 0 otherwise */ int os_fd_generic_join_mcast_recv(struct os_fd *sock, const struct netaddr *multicast, const struct os_interface *os_if, enum oonf_log_source log_src __attribute__((unused))) { struct netaddr_str buf1, buf2; struct ip_mreq v4_mreq; struct ipv6_mreq v6_mreq; const char *ifname = "*"; if (os_if) { ifname = os_if->name; } if (netaddr_get_address_family(multicast) == AF_INET) { const struct netaddr *src; src = os_if == NULL ? &NETADDR_IPV4_ANY : os_if->if_v4; OONF_DEBUG(log_src, "Socket on interface %s joining receiving multicast %s (src %s)\n", ifname, netaddr_to_string(&buf2, multicast), netaddr_to_string(&buf1, src)); netaddr_to_binary(&v4_mreq.imr_multiaddr, multicast, 4); netaddr_to_binary(&v4_mreq.imr_interface, src, 4); if (setsockopt(sock->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &v4_mreq, sizeof(v4_mreq)) < 0) { OONF_WARN(log_src, "Cannot join multicast group %s (src %s) on interface %s: %s (%d)\n", netaddr_to_string(&buf1, multicast), netaddr_to_string(&buf2, src), ifname, strerror(errno), errno); return -1; } } else { int if_index; if_index = os_if == NULL ? 0 : os_if->index; OONF_DEBUG(log_src, "Socket on interface %s joining receiving multicast %s (if %d)\n", ifname, netaddr_to_string(&buf2, multicast), if_index); netaddr_to_binary(&v6_mreq.ipv6mr_multiaddr, multicast, 16); v6_mreq.ipv6mr_interface = if_index; if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &v6_mreq, sizeof(v6_mreq)) < 0) { OONF_WARN(log_src, "Cannot join multicast group %s on interface %s: %s (%d)\n", netaddr_to_string(&buf1, multicast), ifname, strerror(errno), errno); return -1; } } return 0; }
/** * Join a socket into a multicast group * @param sock filedescriptor of socket * @param multicast multicast ip/port to join * @param os_if pointer to outgoing interface data for multicast * @param loop true if multicast loop should be activated, false otherwise * @param log_src logging source for error messages * @return -1 if an error happened, 0 otherwise */ int os_fd_generic_join_mcast_send(struct os_fd *sock, const struct netaddr *multicast, const struct os_interface *os_if, bool loop, enum oonf_log_source log_src __attribute__((unused))) { struct netaddr_str buf1, buf2; unsigned i; if (netaddr_get_address_family(multicast) == AF_INET) { OONF_DEBUG(log_src, "Socket on interface %s joining sending multicast %s (src %s)\n", os_if->name, netaddr_to_string(&buf2, multicast), netaddr_to_string(&buf1, os_if->if_v4)); if (setsockopt(sock->fd, IPPROTO_IP, IP_MULTICAST_IF, netaddr_get_binptr(os_if->if_v4), 4) < 0) { OONF_WARN(log_src, "Cannot set multicast %s on interface %s (src %s): %s (%d)\n", netaddr_to_string(&buf2, multicast), os_if->name, netaddr_to_string(&buf1, os_if->if_v4), strerror(errno), errno); return -1; } i = loop ? 1 : 0; if(setsockopt(sock->fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&i, sizeof(i)) < 0) { OONF_WARN(log_src, "Cannot %sactivate local loop of multicast interface: %s (%d)\n", loop ? "" : "de", strerror(errno), errno); return -1; } } else { OONF_DEBUG(log_src, "Socket on interface %s (%d) joining sending multicast %s (src %s)\n", os_if->name, os_if->index, netaddr_to_string(&buf2, multicast), netaddr_to_string(&buf1, os_if->if_linklocal_v6)); if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &os_if->index, sizeof(os_if->index)) < 0) { OONF_WARN(log_src, "Cannot set multicast %s on interface %s (src %s): %s (%d)\n", netaddr_to_string(&buf2, multicast), os_if->name, netaddr_to_string(&buf1, os_if->if_linklocal_v6), strerror(errno), errno); return -1; } i = loop ? 1 : 0; if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &i, sizeof(i)) < 0) { OONF_WARN(log_src, "Cannot deactivate local loop of multicast interface: %s (%d)\n", strerror(errno), errno); return -1; } } return 0; }
/** * Send a data packet through a packet socket. The transmission might not * be happen synchronously if the socket would block. * @param pktsocket pointer to packet socket * @param remote ip/address to send packet to * @param data pointer to data to be sent * @param length length of data * @return -1 if an error happened, 0 otherwise */ int oonf_packet_send(struct oonf_packet_socket *pktsocket, union netaddr_socket *remote, const void *data, size_t length) { int result; struct netaddr_str buf; if (abuf_getlen(&pktsocket->out) == 0) { /* no backlog of outgoing packets, try to send directly */ result = os_fd_sendto(&pktsocket->scheduler_entry.fd, data, length, remote, pktsocket->config.dont_route); if (result > 0) { /* successful */ OONF_DEBUG(LOG_PACKET, "Sent %d bytes to %s %s", result, netaddr_socket_to_string(&buf, remote), pktsocket->os_if != NULL ? pktsocket->os_if->name : ""); return 0; } if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { OONF_WARN(LOG_PACKET, "Cannot send UDP packet to %s: %s (%d)", netaddr_socket_to_string(&buf, remote), strerror(errno), errno); return -1; } } /* append destination */ abuf_memcpy(&pktsocket->out, remote, sizeof(*remote)); /* append data length */ abuf_append_uint16(&pktsocket->out, length); /* append data */ abuf_memcpy(&pktsocket->out, data, length); /* activate outgoing socket scheduler */ oonf_socket_set_write(&pktsocket->scheduler_entry, true); return 0; }
/** * Callback to handle data from the olsr socket scheduler * @param fd filedescriptor to read data from * @param data custom data pointer * @param event_read true if read-event is incoming * @param event_write true if write-event is incoming */ static void _cb_packet_event(struct oonf_socket_entry *entry, bool multicast __attribute__((unused))) { struct oonf_packet_socket *pktsocket; union netaddr_socket sock; uint16_t length; char *pkt; ssize_t result; struct netaddr_str netbuf; #ifdef OONF_LOG_DEBUG_INFO const char *interf = ""; #endif pktsocket = container_of(entry, typeof(*pktsocket), scheduler_entry); #ifdef OONF_LOG_DEBUG_INFO if (pktsocket->os_if) { interf = pktsocket->os_if->name; } #endif if (oonf_socket_is_read(entry)) { uint8_t *buf; /* clear recvfrom memory */ memset(&sock, 0, sizeof(sock)); /* handle incoming data */ buf = pktsocket->config.input_buffer; result = os_fd_recvfrom(&entry->fd, buf, pktsocket->config.input_buffer_length-1, &sock, pktsocket->os_if); if (result > 0 && pktsocket->config.receive_data != NULL) { /* handle raw socket */ if (pktsocket->protocol) { buf = os_fd_skip_rawsocket_prefix(buf, &result, pktsocket->local_socket.std.sa_family); if (!buf) { OONF_WARN(LOG_PACKET, "Error while skipping IP header for socket %s:", netaddr_socket_to_string(&netbuf, &pktsocket->local_socket)); return; } } /* null terminate it */ buf[result] = 0; /* received valid packet */ OONF_DEBUG(LOG_PACKET, "Received %"PRINTF_SSIZE_T_SPECIFIER" bytes from %s %s (%s)", result, netaddr_socket_to_string(&netbuf, &sock), interf, multicast ? "multicast" : "unicast"); pktsocket->config.receive_data(pktsocket, &sock, buf, result); } else if (result < 0 && (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)) { OONF_WARN(LOG_PACKET, "Cannot read packet from socket %s: %s (%d)", netaddr_socket_to_string(&netbuf, &pktsocket->local_socket), strerror(errno), errno); } } if (oonf_socket_is_write(entry) && abuf_getlen(&pktsocket->out) > 0) { /* handle outgoing data */ pkt = abuf_getptr(&pktsocket->out); /* copy remote socket */ memcpy(&sock, pkt, sizeof(sock)); pkt += sizeof(sock); /* copy length */ memcpy(&length, pkt, 2); pkt += 2; /* try to send packet */ result = os_fd_sendto(&entry->fd, pkt, length, &sock, pktsocket->config.dont_route); if (result < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) { /* try again later */ OONF_DEBUG(LOG_PACKET, "Sending to %s %s could block, try again later", netaddr_socket_to_string(&netbuf, &sock), interf); return; } if (result < 0) { /* display error message */ OONF_WARN(LOG_PACKET, "Cannot send UDP packet to %s: %s (%d)", netaddr_socket_to_string(&netbuf, &sock), strerror(errno), errno); } else { OONF_DEBUG(LOG_PACKET, "Sent %"PRINTF_SSIZE_T_SPECIFIER" bytes to %s %s", result, netaddr_socket_to_string(&netbuf, &sock), interf); } /* remove data from outgoing buffer (both for success and for final error */ abuf_pull(&pktsocket->out, sizeof(sock) + 2 + length); } if (abuf_getlen(&pktsocket->out) == 0) { /* nothing left to send, disable outgoing events */ oonf_socket_set_write(&pktsocket->scheduler_entry, false); } }
/** * Apply new configuration to a managed stream socket * @param managed pointer to managed stream * @param stream pointer to TCP stream to configure * @param bindto local address to bind socket to * set to AF_UNSPEC for simple reinitialization * @param port local port number * @param dscp dscp value for outgoing traffic * @param protocol IP protocol for raw IP socket, 0 otherwise * @param data interface data to bind socket to, might be NULL * @return -1 if an error happened, 0 if everything is okay, * 1 if the socket wasn't touched. */ static int _apply_managed_socket(struct oonf_packet_managed *managed, struct oonf_packet_socket *packet, const struct netaddr *bindto, int port, uint8_t dscp, int protocol, struct os_interface *data) { union netaddr_socket sock; struct netaddr_str buf; /* create binding socket */ if (netaddr_socket_init(&sock, bindto, port, data == NULL ? 0 : data->index)) { OONF_WARN(LOG_PACKET, "Cannot create managed socket address: %s/%u", netaddr_to_string(&buf, bindto), port); return -1; } if (list_is_node_added(&packet->node)) { if (data == packet->os_if && memcmp(&sock, &packet->local_socket, sizeof(sock)) == 0 && protocol == packet->protocol) { /* nothing changed */ return 1; } } else { if (data != NULL && !data->flags.up) { /* nothing changed */ return 1; } } /* remove old socket */ oonf_packet_remove(packet, true); if (data != NULL && !data->flags.up) { OONF_DEBUG(LOG_PACKET, "Interface %s of socket is down", data->name); return 0; } /* copy configuration */ memcpy(&packet->config, &managed->config, sizeof(packet->config)); if (packet->config.user == NULL) { packet->config.user = managed; } /* create new socket */ if (protocol) { if (oonf_packet_raw_add(packet, protocol, &sock, data)) { return -1; } } else { if (oonf_packet_add(packet, &sock, data)) { return -1; } } if (os_fd_set_dscp(&packet->scheduler_entry.fd, dscp, netaddr_get_address_family(bindto) == AF_INET6)) { OONF_WARN(LOG_PACKET, "Could not set DSCP value for socket: %s (%d)", strerror(errno), errno); oonf_packet_remove(packet, true); return -1; } packet->os_if = data; OONF_DEBUG(LOG_PACKET, "Opened new socket and bound it to %s (if %s)", netaddr_to_string(&buf, bindto), data != NULL ? data->name : "any"); return 0; }
/** * Callback to receive UDP data through oonf_packet_managed API * @param pkt packet socket * @param from network socket the packet was received from * @param ptr pointer to packet data * @param length length of packet data */ static void _cb_receive_udp(struct oonf_packet_socket *pkt, union netaddr_socket *from, void *ptr, size_t length) { struct dlep_if *interf; uint8_t *buffer; ssize_t processed; struct netaddr_str nbuf; interf = pkt->config.user; buffer = ptr; if (interf->session_tree.count > 0 && interf->single_session) { /* ignore UDP traffic as long as we have a connection */ return; } if (length < sizeof(_DLEP_PREFIX) - 1) { /* ignore unknown prefix */ return; } if (memcmp(buffer, _DLEP_PREFIX, sizeof(_DLEP_PREFIX)-1) != 0) { OONF_WARN(interf->session.log_source, "Incoming UDP packet with unknown signature"); return; } /* advance pointer and fix length */ buffer += (sizeof(_DLEP_PREFIX) - 1); length -= (sizeof(_DLEP_PREFIX) - 1); /* copy socket information */ memcpy(&interf->session.remote_socket, from, sizeof(interf->session.remote_socket)); processed = dlep_session_process_buffer(&interf->session, buffer, length); if (processed < 0) { return ; } if ((size_t)processed < length) { OONF_WARN(interf->session.log_source, "Received malformed or too short UDP packet from %s", netaddr_socket_to_string(&nbuf, from)); /* incomplete or bad packet, just ignore it */ return; } if (abuf_getlen(interf->session.writer.out) > sizeof(_DLEP_PREFIX) - 1) { /* send an unicast response */ oonf_packet_send_managed(&interf->udp, from, abuf_getptr(interf->session.writer.out), abuf_getlen(interf->session.writer.out)); abuf_clear(interf->session.writer.out); /* add dlep prefix to buffer */ abuf_memcpy(interf->session.writer.out, _DLEP_PREFIX, sizeof(_DLEP_PREFIX) - 1); } netaddr_socket_invalidate(&interf->session.remote_socket); }
/** * Main program * @param argc argument counter * @param argv argument vector * @param appdata application data * @return application return code to shell */ int oonf_main(int argc, char **argv, const struct oonf_appdata *appdata) { int return_code; int result; /* early initialization */ return_code = 1; _schema_name = NULL; _display_schema = false; _debug_early = false; _ignore_unknown = false; /* setup signal handler */ _end_oonf_signal = false; setup_signalhandler(); /* parse "early" command line arguments */ parse_early_commandline(argc, argv); /* initialize core */ os_core_init(appdata->app_name); /* initialize logger */ if (oonf_log_init(appdata, _debug_early ? LOG_SEVERITY_DEBUG : LOG_SEVERITY_WARN)) { goto oonf_cleanup; } /* prepare plugin initialization */ oonf_subsystem_init(); /* initialize configuration system */ if (oonf_cfg_init(argc, argv, appdata->default_cfg_handler)) { goto oonf_cleanup; } /* add custom configuration definitions */ oonf_logcfg_init(); /* parse command line and read configuration files */ return_code = parse_commandline(argc, argv, appdata, false); if (return_code != -1) { /* end OONFd now */ goto oonf_cleanup; } /* prepare for an error during initialization */ return_code = 1; /* read global section early */ if ((result = oonf_cfg_update_globalcfg(true))) { OONF_WARN(LOG_MAIN, "Cannot read global configuration section (%d)", result); goto oonf_cleanup; } /* configure logger */ if (oonf_logcfg_apply(oonf_cfg_get_rawdb())) { goto oonf_cleanup; } /* load plugins */ if (oonf_cfg_load_subsystems()) { goto oonf_cleanup; } /* show schema if necessary */ if (_display_schema) { return_code = display_schema(); goto oonf_cleanup; } /* check if we are root, otherwise stop */ if (appdata->need_root) { if (geteuid() != 0) { OONF_WARN(LOG_MAIN, "You must be root(uid = 0) to run %s!\n", appdata->app_name); goto oonf_cleanup; } } if (appdata->need_lock && config_global.lockfile != NULL && *config_global.lockfile != 0) { /* create application lock */ if (os_core_create_lockfile(config_global.lockfile)) { OONF_WARN(LOG_MAIN, "Could not acquire application lock '%s'", config_global.lockfile); goto oonf_cleanup; } } /* call initialization callbacks of dynamic plugins */ oonf_cfg_initplugins(); /* apply configuration */ if (oonf_cfg_apply()) { goto oonf_cleanup; } if (!oonf_cfg_is_running()) { /* * mayor error during late initialization * or maybe the user decided otherwise and pressed CTRL-C */ return_code = _end_oonf_signal ? 0 : 1; goto oonf_cleanup; } if (!_handle_scheduling) { OONF_WARN(LOG_MAIN, "No event scheduler present"); return_code = 1; goto oonf_cleanup; } /* see if we need to fork */ if (config_global.fork && !_display_schema) { /* tell main process that we are finished with initialization */ if (daemon(0,0) < 0) { OONF_WARN(LOG_MAIN, "Could not fork into background: %s (%d)", strerror(errno), errno); goto oonf_cleanup; } if (config_global.pidfile && *config_global.pidfile != 0) { if (_write_pidfile(config_global.pidfile)) { goto oonf_cleanup; } } } /* activate mainloop */ return_code = mainloop(argc, argv, appdata); /* tell plugins shutdown is in progress */ oonf_subsystem_initiate_shutdown(); /* wait for 500 ms and process socket events */ while(!_handle_scheduling()); oonf_cleanup: /* free plugins */ oonf_cfg_unconfigure_subsystems(); oonf_subsystem_cleanup(); /* free logging/config bridge resources */ oonf_logcfg_cleanup(); /* free configuration resources */ oonf_cfg_cleanup(); /* free logger resources */ oonf_log_cleanup(); /* free core resources */ os_core_cleanup(); return return_code; }