/** * Add a TLV to a DLEP writer buffer * @param writer dlep writer * @param type TLV type * @param data1 first part of TLV value * @param len1 length of first value * @param data2 second part of TLV value * @param len2 length of second value */ void dlep_writer_add_tlv2( struct dlep_writer *writer, uint16_t type, const void *data1, uint16_t len1, const void *data2, uint16_t len2) { abuf_append_uint16(writer->out, htons(type)); abuf_append_uint16(writer->out, htons(len1 + len2)); abuf_memcpy(writer->out, data1, len1); abuf_memcpy(writer->out, data2, len2); }
/** * Add a TLV to a DLEP writer buffer * @param writer dlep writer * @param type TLV type * @param data pointer to TLV value * @param len length of value, can be 0 */ void dlep_writer_add_tlv(struct dlep_writer *writer, uint16_t type, const void *data, uint16_t len) { abuf_append_uint16(writer->out, htons(type)); abuf_append_uint16(writer->out, htons(len)); abuf_memcpy(writer->out, data, len); }
/** * Append the result of a template engine into an autobuffer. * Each usage of a key will be replaced with the corresponding * value. * @param autobuf pointer to autobuf object * @param file code file where this function was called (supplied by macro) * @param line line number in file where this function was called (supplied by macro) * @param format format string (as supplied to abuf_template_init() * @param values array of values (same number as keys) * @param table pointer to index table initialized by abuf_template_init() * @param indexCount length of index table as returned by abuf_template_init() * @return -1 if an out-of-memory error happened, 0 otherwise */ int abuf_templatef (struct autobuf *autobuf, const char *format, char **values, size_t *table, size_t indexCount) { size_t i, last = 0; if (autobuf == NULL) return 0; for (i=0; i<indexCount; i+=3) { /* copy prefix text */ if (last < table[i+1]) { if (abuf_memcpy(autobuf, &format[last], table[i+1] - last) < 0) { return -1; } } if (abuf_puts(autobuf, values[table[i]]) < 0) { return -1; } last = table[i+2]; } if (last < strlen(format)) { if (abuf_puts(autobuf, &format[last]) < 0) { return -1; } } 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); }
/** * Append the result of a template engine into an autobuffer. * Each usage of a key will be replaced with the corresponding * value. * @param out pointer to autobuf object * @param format format string (as supplied to abuf_template_init() * @param storage pointer to template storage object, which will be filled by * this function * @return -1 if an out-of-memory error happened, 0 otherwise */ int abuf_add_template(struct autobuf *out, const char *format, struct abuf_template_storage *storage) { struct abuf_template_storage_entry *entry; size_t i, last = 0; if (out == NULL) return 0; for (i=0; i<storage->count; i++) { entry = &storage->indices[i]; /* copy prefix text */ if (last < entry->start) { if (abuf_memcpy(out, &format[last], entry->start - last) < 0) { return -1; } } if (entry->data->value) { if (abuf_puts(out, entry->data->value) < 0) { return -1; } } last = entry->end; } if (last < strlen(format)) { if (abuf_puts(out, &format[last]) < 0) { return -1; } } return 0; }
/** * Add a new interface to this dlep instance * @param interf pointer to interface * @param ifname name of interface * @param l2_origin layer2 originator that shall be used * @param log_src logging source that shall be used * @param radio true if it is a radio interface, false for router * @return -1 if an error happened, 0 otherwise */ int dlep_if_add(struct dlep_if *interf, const char *ifname, uint32_t l2_origin, enum oonf_log_source log_src, bool radio) { struct dlep_extension *ext; /* initialize key */ strscpy(interf->l2_ifname, ifname, sizeof(interf->l2_ifname)); interf->_node.key = interf->l2_ifname; if (abuf_init(&interf->udp_out)) { return -1; } /* add dlep prefix to buffer */ abuf_memcpy(&interf->udp_out, _DLEP_PREFIX, sizeof(_DLEP_PREFIX) - 1); if (dlep_session_add(&interf->session, interf->l2_ifname, l2_origin, &interf->udp_out, radio, log_src)) { abuf_free(&interf->udp_out); return -1; } /* initialize stream list */ avl_init(&interf->session_tree, avl_comp_netaddr_socket, false); /* initialize discovery socket */ interf->udp.config.user = interf; interf->udp.config.receive_data = _cb_receive_udp; oonf_packet_add_managed(&interf->udp); /* initialize session */ interf->session.cb_send_buffer = _cb_send_multicast; interf->session.cb_end_session = NULL; interf->session.restrict_signal = radio ? DLEP_PEER_DISCOVERY : DLEP_PEER_OFFER; interf->session.writer.out = &interf->udp_out; /* inform all extension */ avl_for_each_element(dlep_extension_get_tree(), ext, _node) { if (radio) { if (ext->cb_session_init_radio) { ext->cb_session_init_radio(&interf->session); } } else { if (ext->cb_session_init_router) { ext->cb_session_init_router(&interf->session); } } } return 0; }
/** * Prints a string to an autobuffer, using JSON escape rules * @param out pointer to output buffer * @param txt string to print * @param delimiter true if string must be enclosed in quotation marks * @return -1 if an error happened, 0 otherwise */ static int _json_printvalue(struct autobuf *out, const char *txt, bool delimiter) { const char *ptr; bool unprintable; if (delimiter) { if (abuf_puts(out, "\"") < 0) { return -1; } } else if (*txt == 0) { abuf_puts(out, "0"); } ptr = txt; while (*ptr) { unprintable = !str_char_is_printable(*ptr); if (unprintable || *ptr == '\\' || *ptr == '\"') { if (ptr != txt) { if (abuf_memcpy(out, txt, ptr - txt) < 0) { return -1; } } if (unprintable) { if (abuf_appendf(out, "\\u00%02x", (unsigned char)(*ptr++)) < 0) { return -1; } } else { if (abuf_appendf(out, "\\%c", *ptr++) < 0) { return -1; } } txt = ptr; } else { ptr++; } } if (abuf_puts(out, txt) < 0) { return -1; } if (delimiter) { if (abuf_puts(out, "\"") < 0) { 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 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); }