/** * 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; }
/** * Apply a new configuration to a managed socket. This might close and * reopen sockets because of changed binding IPs or ports. * @param managed pointer to managed packet socket * @param config pointer to new configuration * @return -1 if an error happened, 0 otherwise */ int oonf_packet_apply_managed(struct oonf_packet_managed *managed, const struct oonf_packet_managed_config *config) { bool if_changed; int result; if_changed = strcmp(config->interface, managed->_managed_config.interface) != 0 || !list_is_node_added(&managed->_if_listener._node); oonf_packet_copy_managed_config(&managed->_managed_config, config); /* handle change in interface listener */ if (if_changed) { /* interface changed, remove old listener if necessary */ os_interface_remove(&managed->_if_listener); /* create new interface listener */ managed->_if_listener.mesh = managed->_managed_config.mesh; os_interface_add(&managed->_if_listener); } OONF_DEBUG(LOG_PACKET, "Apply changes for managed socket (if %s) with port %d/%d", config->interface[0] == 0 ? "any" : config->interface, config->port, config->multicast_port); result = _apply_managed(managed); if (result) { /* did not work, trigger interface handler to try later again */ os_interface_trigger_handler(&managed->_if_listener); } return result; }
/** * Send a packet out over one of the managed sockets, depending on the * address family type of the remote address * @param managed pointer to managed packet socket * @param remote pointer to remote socket * @param data pointer to data to send * @param length length of data * @return -1 if an error happened, 0 if packet was sent, 1 if this * type of address was switched off */ int oonf_packet_send_managed(struct oonf_packet_managed *managed, union netaddr_socket *remote, const void *data, size_t length) { #ifdef OONF_LOG_DEBUG_INFO struct netaddr_str buf; #endif if (netaddr_socket_get_addressfamily(remote) == AF_UNSPEC) { return 0; } if (list_is_node_added(&managed->socket_v4.scheduler_entry._node) && netaddr_socket_get_addressfamily(remote) == AF_INET) { return oonf_packet_send(&managed->socket_v4, remote, data, length); } if (list_is_node_added(&managed->socket_v6.scheduler_entry._node) && netaddr_socket_get_addressfamily(remote) == AF_INET6) { return oonf_packet_send(&managed->socket_v6, remote, data, length); } errno = 0; OONF_DEBUG(LOG_PACKET, "Managed socket did not sent packet to %s because socket was not active", netaddr_socket_to_string(&buf, remote)); 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); }
/** * Remove dlep router interface * @param interface dlep router interface */ void dlep_if_remove(struct dlep_if *interface) { struct dlep_extension *ext; OONF_DEBUG(interface->session.log_source, "remove session %s", interface->l2_ifname); avl_for_each_element(dlep_extension_get_tree(), ext, _node) { if (interface->session.radio) { if (ext->cb_session_cleanup_radio) { ext->cb_session_cleanup_radio(&interface->session); } } else { if (ext->cb_session_cleanup_router) { ext->cb_session_cleanup_router(&interface->session); } } } /* close UDP interface */ oonf_packet_remove_managed(&interface->udp, true); /* kill dlep session */ dlep_session_remove(&interface->session); }
/** * Callback to apply new network settings to a router session * @param session dlep session */ static void _cb_apply_router(struct dlep_session *session) { OONF_DEBUG(session->log_source, "Initialize base router session"); if (session->restrict_signal == DLEP_PEER_OFFER) { /* * we are waiting for a Peer Offer, * so we need to send Peer Discovery messages */ session->local_event_timer.class = &_peer_discovery_class; OONF_DEBUG(session->log_source, "Activate discovery with interval %" PRIu64, session->cfg.discovery_interval); /* use the "local event" for the discovery timer */ oonf_timer_start(&session->local_event_timer, session->cfg.discovery_interval); }
/** * Callbacks for events on the interface * @param l * @return -1 if an error happened, 0 otherwise */ static int _cb_interface_listener(struct os_interface_listener *l) { struct oonf_packet_managed *managed; int result; /* calculate managed socket for this event */ managed = container_of(l, struct oonf_packet_managed, _if_listener); result = _apply_managed(managed); OONF_DEBUG(LOG_PACKET, "Result from interface triggered socket reconfiguration: %d", result); return result; }
/** * Process NL80211_CMD_NEW_MPATH message * @param interf nl80211 listener interface * @param hdr pointer to netlink message header */ void nl80211_process_get_mpp_result(struct nl80211_if *interf, struct nlmsghdr *hdr) { struct oonf_layer2_destination *l2dst; struct oonf_layer2_neigh *l2neigh; struct netaddr remote_mac, destination_mac; #ifdef OONF_LOG_DEBUG_INFO struct netaddr_str nbuf1, nbuf2; #endif struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh; gnlh = nlmsg_data(hdr); nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (nl80211_get_if_index(interf) != nla_get_u32(tb[NL80211_ATTR_IFINDEX])) { /* wrong interface ? */ return; } netaddr_from_binary(&remote_mac, nla_data(tb[NL80211_ATTR_MPATH_NEXT_HOP]), 6, AF_MAC48); netaddr_from_binary(&destination_mac, nla_data(tb[NL80211_ATTR_MAC]), 6, AF_MAC48); l2neigh = oonf_layer2_neigh_get(interf->l2net, &remote_mac); if (!l2neigh) { /* don't create a neighbor, just ignore the MPP data */ return; } l2dst = nl80211_add_dst(l2neigh, &destination_mac); if (!l2dst) { return; } OONF_DEBUG(LOG_NL80211, "Neighbor %s was proxied by mesh node %s", netaddr_to_string(&nbuf1, &destination_mac), netaddr_to_string(&nbuf2, &remote_mac)); }
/** * 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; }