/** * 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; }
/** * Callback to send multicast over interface * @param session dlep session * @param af_family address family for multicast */ static void _cb_send_multicast(struct dlep_session *session, int af_family) { struct dlep_if *interf; if (abuf_getlen(session->writer.out) <= sizeof(_DLEP_PREFIX) - 1 || !netaddr_socket_is_unspec(&session->remote_socket)) { return; } /* get pointer to radio interface */ interf = container_of(session, struct dlep_if, session); if (interf->session_tree.count > 0 && interf->single_session) { /* do not produce UDP traffic as long as we are connected */ return; } OONF_DEBUG(session->log_source, "Send multicast %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(session->writer.out)); oonf_packet_send_managed_multicast(&interf->udp, abuf_getptr(session->writer.out), abuf_getlen(session->writer.out), af_family); abuf_clear(session->writer.out); /* add dlep prefix to buffer */ abuf_memcpy(session->writer.out, _DLEP_PREFIX, sizeof(_DLEP_PREFIX) - 1); }
/** * Start to write a new DLEP signal/message into a buffer * @param writer dlep writer * @param signal_type signal/message type */ void dlep_writer_start_signal(struct dlep_writer *writer, uint16_t signal_type) { writer->signal_type = signal_type; writer->signal_start = abuf_getlen(writer->out); abuf_append_uint16(writer->out, htons(signal_type)); abuf_append_uint16(writer->out, 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); } }
/** * 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); }