| /** @file |
| * @brief Generic connection related functions |
| */ |
| |
| /* |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #if defined(CONFIG_NET_DEBUG_CONN) |
| #define SYS_LOG_DOMAIN "net/conn" |
| #define NET_LOG_ENABLED 1 |
| #endif |
| |
| #include <errno.h> |
| #include <misc/util.h> |
| |
| #include <net/net_core.h> |
| #include <net/nbuf.h> |
| |
| #include "net_private.h" |
| #include "icmpv6.h" |
| #include "icmpv4.h" |
| #include "connection.h" |
| #include "net_stats.h" |
| |
| /** Is this connection used or not */ |
| #define NET_CONN_IN_USE BIT(0) |
| |
| /** Remote address set */ |
| #define NET_CONN_REMOTE_ADDR_SET BIT(1) |
| |
| /** Local address set */ |
| #define NET_CONN_LOCAL_ADDR_SET BIT(2) |
| |
| /** Rank bits */ |
| #define NET_RANK_LOCAL_PORT BIT(0) |
| #define NET_RANK_REMOTE_PORT BIT(1) |
| #define NET_RANK_LOCAL_UNSPEC_ADDR BIT(2) |
| #define NET_RANK_REMOTE_UNSPEC_ADDR BIT(3) |
| #define NET_RANK_LOCAL_SPEC_ADDR BIT(4) |
| #define NET_RANK_REMOTE_SPEC_ADDR BIT(5) |
| |
| static struct net_conn conns[CONFIG_NET_MAX_CONN]; |
| |
| /* This is only used for getting source and destination ports. Because |
| * both TCP and UDP header have these in the same location, we can check |
| * them both using the UDP struct. |
| */ |
| #define NET_CONN_BUF(buf) ((struct net_udp_hdr *)(net_nbuf_udp_data(buf))) |
| |
| #if defined(CONFIG_NET_DEBUG_CONN) |
| static inline const char *proto2str(enum net_ip_protocol proto) |
| { |
| switch (proto) { |
| case IPPROTO_ICMP: |
| return "ICMPv4"; |
| case IPPROTO_TCP: |
| return "TCP"; |
| case IPPROTO_UDP: |
| return "UDP"; |
| case IPPROTO_ICMPV6: |
| return "ICMPv6"; |
| default: |
| break; |
| } |
| |
| return "<unknown>"; |
| } |
| #endif /* CONFIG_NET_DEBUG_CONN */ |
| |
| #if defined(CONFIG_NET_CONN_CACHE) |
| |
| /* Cache the connection so that we do not have to go |
| * through the full list of connections when receiving |
| * a network packet. The cache contains an index to |
| * corresponding entry in conns array. |
| * |
| * Hash value is constructed like this: |
| * |
| * bit description |
| * 0 - 3 bits from remote port |
| * 4 - 7 bits from local port |
| * 8 - 18 bits from remote address |
| * 19 - 29 bits from local address |
| * 30 family |
| * 31 protocol |
| */ |
| struct conn_hash { |
| uint32_t value; |
| int32_t idx; |
| }; |
| |
| struct conn_hash_neg { |
| uint32_t value; |
| }; |
| |
| /** Connection cache */ |
| static struct conn_hash conn_cache[CONFIG_NET_MAX_CONN]; |
| |
| /** Negative cache, we definitely do not have a connection |
| * to these hosts. |
| */ |
| static struct conn_hash_neg conn_cache_neg[CONFIG_NET_MAX_CONN]; |
| |
| #define TAKE_BIT(val, bit, max, used) \ |
| (((val & BIT(bit)) >> bit) << (max - used)) |
| |
| static inline uint8_t ports_to_hash(uint16_t remote_port, |
| uint16_t local_port) |
| { |
| /* Note that we do not convert port value to network byte order */ |
| return (remote_port & BIT(0)) | |
| ((remote_port & BIT(4)) >> 3) | |
| ((remote_port & BIT(8)) >> 6) | |
| ((remote_port & BIT(15)) >> 12) | |
| (((local_port & BIT(0)) | |
| ((local_port & BIT(4)) >> 3) | |
| ((local_port & BIT(8)) >> 6) | |
| ((local_port & BIT(15)) >> 12)) << 4); |
| } |
| |
| static inline uint16_t ipv6_to_hash(struct in6_addr *addr) |
| { |
| /* There is 11 bits available for IPv6 address */ |
| /* Use more bits from the lower part of address space */ |
| return |
| /* Take 3 bits from higher values */ |
| TAKE_BIT(addr->s6_addr32[0], 31, 11, 1) | |
| TAKE_BIT(addr->s6_addr32[0], 15, 11, 2) | |
| TAKE_BIT(addr->s6_addr32[0], 7, 11, 3) | |
| |
| /* Take 2 bits from higher middle values */ |
| TAKE_BIT(addr->s6_addr32[1], 31, 11, 4) | |
| TAKE_BIT(addr->s6_addr32[1], 15, 11, 5) | |
| |
| /* Take 2 bits from lower middle values */ |
| TAKE_BIT(addr->s6_addr32[2], 31, 11, 6) | |
| TAKE_BIT(addr->s6_addr32[2], 15, 11, 7) | |
| |
| /* Take 4 bits from lower values */ |
| TAKE_BIT(addr->s6_addr32[3], 31, 11, 8) | |
| TAKE_BIT(addr->s6_addr32[3], 15, 11, 9) | |
| TAKE_BIT(addr->s6_addr32[3], 7, 11, 10) | |
| TAKE_BIT(addr->s6_addr32[3], 0, 11, 11); |
| } |
| |
| static inline uint16_t ipv4_to_hash(struct in_addr *addr) |
| { |
| /* There is 11 bits available for IPv4 address */ |
| /* Use more bits from the lower part of address space */ |
| return |
| TAKE_BIT(addr->s_addr[0], 31, 11, 1) | |
| TAKE_BIT(addr->s_addr[0], 27, 11, 2) | |
| TAKE_BIT(addr->s_addr[0], 21, 11, 3) | |
| TAKE_BIT(addr->s_addr[0], 17, 11, 4) | |
| TAKE_BIT(addr->s_addr[0], 14, 11, 5) | |
| TAKE_BIT(addr->s_addr[0], 11, 11, 6) | |
| TAKE_BIT(addr->s_addr[0], 8, 11, 7) | |
| TAKE_BIT(addr->s_addr[0], 5, 11, 8) | |
| TAKE_BIT(addr->s_addr[0], 3, 11, 9) | |
| TAKE_BIT(addr->s_addr[0], 2, 11, 10) | |
| TAKE_BIT(addr->s_addr[0], 0, 11, 11); |
| } |
| |
| /* Return either the first free position in the cache (idx < 0) or |
| * the existing cached position (idx >= 0) |
| */ |
| static int32_t check_hash(enum net_ip_protocol proto, |
| sa_family_t family, |
| void *remote_addr, |
| void *local_addr, |
| uint16_t remote_port, |
| uint16_t local_port, |
| uint32_t *cache_value) |
| { |
| int i, free_pos = -1; |
| uint32_t value = 0; |
| |
| value = ports_to_hash(remote_port, local_port); |
| |
| #if defined(CONFIG_NET_UDP) |
| if (proto == IPPROTO_UDP) { |
| value |= BIT(31); |
| } |
| #endif |
| |
| #if defined(CONFIG_NET_TCP) |
| if (proto == IPPROTO_TCP) { |
| value &= ~BIT(31); |
| } |
| #endif |
| |
| #if defined(CONFIG_NET_IPV6) |
| if (family == AF_INET6) { |
| value |= BIT(30); |
| value |= ipv6_to_hash((struct in6_addr *)remote_addr) << 8; |
| value |= ipv6_to_hash((struct in6_addr *)local_addr) << 19; |
| } |
| #endif |
| |
| #if defined(CONFIG_NET_IPV4) |
| if (family == AF_INET) { |
| value &= ~BIT(30); |
| value |= ipv4_to_hash((struct in_addr *)remote_addr) << 8; |
| value |= ipv4_to_hash((struct in_addr *)local_addr) << 19; |
| } |
| #endif |
| |
| for (i = 0; i < CONFIG_NET_MAX_CONN; i++) { |
| if (conn_cache[i].value == value) { |
| return i; |
| } |
| |
| if (conn_cache[i].idx < 0 && free_pos < 0) { |
| free_pos = i; |
| } |
| } |
| |
| if (free_pos >= 0) { |
| conn_cache[free_pos].value = value; |
| return free_pos; |
| } |
| |
| *cache_value = value; |
| |
| return -ENOENT; |
| } |
| |
| static inline int32_t get_conn(enum net_ip_protocol proto, |
| sa_family_t family, |
| struct net_buf *buf, |
| uint32_t *cache_value) |
| { |
| #if defined(CONFIG_NET_IPV4) |
| if (family == AF_INET) { |
| return check_hash(proto, family, |
| &NET_IPV4_BUF(buf)->src, |
| &NET_IPV4_BUF(buf)->dst, |
| NET_UDP_BUF(buf)->src_port, |
| NET_UDP_BUF(buf)->dst_port, |
| cache_value); |
| } |
| #endif |
| |
| #if defined(CONFIG_NET_IPV6) |
| if (family == AF_INET6) { |
| return check_hash(proto, family, |
| &NET_IPV6_BUF(buf)->src, |
| &NET_IPV6_BUF(buf)->dst, |
| NET_UDP_BUF(buf)->src_port, |
| NET_UDP_BUF(buf)->dst_port, |
| cache_value); |
| } |
| #endif |
| |
| return -1; |
| } |
| |
| static inline void cache_add_neg(uint32_t cache_value) |
| { |
| int i; |
| |
| for (i = 0; i < CONFIG_NET_MAX_CONN && cache_value > 0; i++) { |
| if (conn_cache_neg[i].value) { |
| continue; |
| } |
| |
| NET_DBG("Add to neg cache value 0x%x", cache_value); |
| |
| conn_cache_neg[i].value = cache_value; |
| break; |
| } |
| } |
| |
| static inline bool cache_check_neg(uint32_t cache_value) |
| { |
| int i; |
| |
| for (i = 0; i < CONFIG_NET_MAX_CONN && cache_value > 0; i++) { |
| if (conn_cache_neg[i].value == cache_value) { |
| NET_DBG("Cache neg [%d] value 0x%x found", |
| i, cache_value); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static void cache_clear(void) |
| { |
| int i; |
| |
| for (i = 0; i < CONFIG_NET_MAX_CONN; i++) { |
| conn_cache[i].idx = -1; |
| conn_cache_neg[i].value = 0; |
| } |
| } |
| |
| static inline enum net_verdict cache_check(enum net_ip_protocol proto, |
| struct net_buf *buf, |
| uint32_t *cache_value, |
| int32_t *pos) |
| { |
| *pos = get_conn(proto, net_nbuf_family(buf), buf, cache_value); |
| if (*pos >= 0) { |
| if (conn_cache[*pos].idx >= 0) { |
| /* Connection is in the cache */ |
| struct net_conn *conn; |
| |
| conn = &conns[conn_cache[*pos].idx]; |
| |
| NET_DBG("Cache %s listener for buf %p src port %u " |
| "dst port %u family %d cache[%d] 0x%x", |
| proto2str(proto), buf, |
| ntohs(NET_CONN_BUF(buf)->src_port), |
| ntohs(NET_CONN_BUF(buf)->dst_port), |
| net_nbuf_family(buf), *pos, |
| conn_cache[*pos].value); |
| |
| return conn->cb(conn, buf, conn->user_data); |
| } |
| } else if (*cache_value > 0) { |
| if (cache_check_neg(*cache_value)) { |
| NET_DBG("Drop by cache"); |
| return NET_DROP; |
| } |
| } |
| |
| return NET_CONTINUE; |
| } |
| #else |
| #define cache_clear(...) |
| #define cache_add_neg(...) |
| #define cache_check(...) NET_CONTINUE |
| #endif /* CONFIG_NET_CONN_CACHE */ |
| |
| int net_conn_unregister(struct net_conn_handle *handle) |
| { |
| struct net_conn *conn = (struct net_conn *)handle; |
| |
| if (conn < &conns[0] || conn > &conns[CONFIG_NET_MAX_CONN]) { |
| return -EINVAL; |
| } |
| |
| if (!(conn->flags & NET_CONN_IN_USE)) { |
| return -ENOENT; |
| } |
| |
| NET_DBG("[%zu] connection handler %p removed", |
| (conn - conns) / sizeof(*conn), conn); |
| |
| conn->flags = 0; |
| |
| return 0; |
| } |
| |
| int net_conn_change_callback(struct net_conn_handle *handle, |
| net_conn_cb_t cb, void *user_data) |
| { |
| struct net_conn *conn = (struct net_conn *)handle; |
| |
| if (conn < &conns[0] || conn > &conns[CONFIG_NET_MAX_CONN]) { |
| return -EINVAL; |
| } |
| |
| if (!(conn->flags & NET_CONN_IN_USE)) { |
| return -ENOENT; |
| } |
| |
| NET_DBG("[%zu] connection handler %p changed callback", |
| (conn - conns) / sizeof(*conn), conn); |
| |
| conn->cb = cb; |
| conn->user_data = user_data; |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_NET_DEBUG_CONN) |
| static inline |
| void prepare_register_debug_print(char *dst, int dst_len, |
| char *src, int src_len, |
| const struct sockaddr *remote_addr, |
| const struct sockaddr *local_addr) |
| { |
| if (remote_addr && remote_addr->family == AF_INET6) { |
| #if defined(CONFIG_NET_IPV6) |
| snprintk(dst, dst_len, "%s", |
| net_sprint_ipv6_addr(&net_sin6(remote_addr)-> |
| sin6_addr)); |
| #else |
| snprintk(dst, dst_len, "%s", "?"); |
| #endif |
| |
| } else if (remote_addr && remote_addr->family == AF_INET) { |
| #if defined(CONFIG_NET_IPV4) |
| snprintk(dst, dst_len, "%s", |
| net_sprint_ipv4_addr(&net_sin(remote_addr)-> |
| sin_addr)); |
| #else |
| snprintk(dst, dst_len, "%s", "?"); |
| #endif |
| |
| } else { |
| snprintk(dst, dst_len, "%s", "-"); |
| } |
| |
| if (local_addr && local_addr->family == AF_INET6) { |
| #if defined(CONFIG_NET_IPV6) |
| snprintk(src, src_len, "%s", |
| net_sprint_ipv6_addr(&net_sin6(local_addr)-> |
| sin6_addr)); |
| #else |
| snprintk(src, src_len, "%s", "?"); |
| #endif |
| |
| } else if (local_addr && local_addr->family == AF_INET) { |
| #if defined(CONFIG_NET_IPV4) |
| snprintk(src, src_len, "%s", |
| net_sprint_ipv4_addr(&net_sin(local_addr)-> |
| sin_addr)); |
| #else |
| snprintk(src, src_len, "%s", "?"); |
| #endif |
| |
| } else { |
| snprintk(src, src_len, "%s", "-"); |
| } |
| } |
| #endif /* CONFIG_NET_DEBUG_CONN */ |
| |
| int net_conn_register(enum net_ip_protocol proto, |
| const struct sockaddr *remote_addr, |
| const struct sockaddr *local_addr, |
| uint16_t remote_port, |
| uint16_t local_port, |
| net_conn_cb_t cb, |
| void *user_data, |
| struct net_conn_handle **handle) |
| { |
| int i; |
| uint8_t rank = 0; |
| |
| for (i = 0; i < CONFIG_NET_MAX_CONN; i++) { |
| if (conns[i].flags & NET_CONN_IN_USE) { |
| continue; |
| } |
| |
| if (remote_addr) { |
| if (remote_addr->family != AF_INET && |
| remote_addr->family != AF_INET6) { |
| NET_ERR("Remote address family not set."); |
| return -EINVAL; |
| } |
| |
| conns[i].flags |= NET_CONN_REMOTE_ADDR_SET; |
| |
| memcpy(&conns[i].remote_addr, remote_addr, |
| sizeof(struct sockaddr)); |
| |
| #if defined(CONFIG_NET_IPV6) |
| if (remote_addr->family == AF_INET6) { |
| if (net_is_ipv6_addr_unspecified( |
| &net_sin6(remote_addr)-> |
| sin6_addr)) { |
| rank |= NET_RANK_REMOTE_UNSPEC_ADDR; |
| } else { |
| rank |= NET_RANK_REMOTE_SPEC_ADDR; |
| } |
| } |
| #endif |
| |
| #if defined(CONFIG_NET_IPV4) |
| if (remote_addr->family == AF_INET) { |
| if (!net_sin(remote_addr)-> |
| sin_addr.s_addr[0]) { |
| rank |= NET_RANK_REMOTE_UNSPEC_ADDR; |
| } else { |
| rank |= NET_RANK_REMOTE_SPEC_ADDR; |
| } |
| } |
| #endif |
| } |
| |
| if (local_addr) { |
| if (local_addr->family != AF_INET && |
| local_addr->family != AF_INET6) { |
| NET_ERR("Local address family not set."); |
| return -EINVAL; |
| } |
| |
| conns[i].flags |= NET_CONN_LOCAL_ADDR_SET; |
| |
| memcpy(&conns[i].local_addr, local_addr, |
| sizeof(struct sockaddr)); |
| |
| #if defined(CONFIG_NET_IPV6) |
| if (local_addr->family == AF_INET6) { |
| if (net_is_ipv6_addr_unspecified( |
| &net_sin6(local_addr)-> |
| sin6_addr)) { |
| rank |= NET_RANK_LOCAL_UNSPEC_ADDR; |
| } else { |
| rank |= NET_RANK_LOCAL_SPEC_ADDR; |
| } |
| } |
| #endif |
| |
| #if defined(CONFIG_NET_IPV4) |
| if (local_addr->family == AF_INET) { |
| if (!net_sin(local_addr)->sin_addr.s_addr[0]) { |
| rank |= NET_RANK_LOCAL_UNSPEC_ADDR; |
| } else { |
| rank |= NET_RANK_LOCAL_SPEC_ADDR; |
| } |
| } |
| #endif |
| } |
| |
| if (remote_addr && local_addr) { |
| if (remote_addr->family != local_addr->family) { |
| NET_ERR("Address families different."); |
| return -EINVAL; |
| } |
| } |
| |
| if (remote_port) { |
| rank |= NET_RANK_REMOTE_PORT; |
| net_sin(&conns[i].remote_addr)->sin_port = |
| htons(remote_port); |
| } |
| |
| if (local_port) { |
| rank |= NET_RANK_LOCAL_PORT; |
| net_sin(&conns[i].local_addr)->sin_port = |
| htons(local_port); |
| } |
| |
| conns[i].flags |= NET_CONN_IN_USE; |
| conns[i].cb = cb; |
| conns[i].user_data = user_data; |
| conns[i].rank = rank; |
| conns[i].proto = proto; |
| |
| /* Cache needs to be cleared if new entries are added. */ |
| cache_clear(); |
| |
| #if defined(CONFIG_NET_DEBUG_CONN) |
| do { |
| char dst[NET_IPV6_ADDR_LEN]; |
| char src[NET_IPV6_ADDR_LEN]; |
| |
| prepare_register_debug_print(dst, sizeof(dst), |
| src, sizeof(src), |
| remote_addr, |
| local_addr); |
| |
| NET_DBG("[%d/%d/%u/0x%02x] remote %p/%s/%u " |
| "local %p/%s/%u cb %p ud %p", |
| i, local_addr->family, proto, rank, |
| remote_addr, dst, remote_port, |
| local_addr, src, local_port, |
| cb, user_data); |
| } while (0); |
| #endif /* CONFIG_NET_DEBUG_CONN */ |
| |
| if (handle) { |
| *handle = (struct net_conn_handle *)&conns[i]; |
| } |
| |
| return 0; |
| } |
| |
| return -ENOENT; |
| } |
| |
| static bool check_addr(struct net_buf *buf, |
| struct sockaddr *addr, |
| bool is_remote) |
| { |
| if (addr->family != net_nbuf_family(buf)) { |
| return false; |
| } |
| |
| #if defined(CONFIG_NET_IPV6) |
| if (net_nbuf_family(buf) == AF_INET6 && addr->family == AF_INET6) { |
| struct in6_addr *addr6; |
| |
| if (is_remote) { |
| addr6 = &NET_IPV6_BUF(buf)->src; |
| } else { |
| addr6 = &NET_IPV6_BUF(buf)->dst; |
| } |
| |
| if (!net_is_ipv6_addr_unspecified( |
| &net_sin6(addr)->sin6_addr)) { |
| if (!net_ipv6_addr_cmp(&net_sin6(addr)->sin6_addr, |
| addr6)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| #endif /* CONFIG_NET_IPV6 */ |
| |
| #if defined(CONFIG_NET_IPV4) |
| if (net_nbuf_family(buf) == AF_INET && addr->family == AF_INET) { |
| struct in_addr *addr4; |
| |
| if (is_remote) { |
| addr4 = &NET_IPV4_BUF(buf)->src; |
| } else { |
| addr4 = &NET_IPV4_BUF(buf)->dst; |
| } |
| |
| if (net_sin(addr)->sin_addr.s_addr[0]) { |
| if (!net_ipv4_addr_cmp(&net_sin(addr)->sin_addr, |
| addr4)) { |
| return false; |
| } |
| } |
| } |
| #endif /* CONFIG_NET_IPV4 */ |
| |
| return true; |
| } |
| |
| static inline void send_icmp_error(struct net_buf *buf) |
| { |
| if (net_nbuf_family(buf) == AF_INET6) { |
| #if defined(CONFIG_NET_IPV6) |
| net_icmpv6_send_error(buf, NET_ICMPV6_DST_UNREACH, |
| NET_ICMPV6_DST_UNREACH_NO_PORT, 0); |
| #endif /* CONFIG_NET_IPV6 */ |
| |
| } else { |
| |
| #if defined(CONFIG_NET_IPV4) |
| net_icmpv4_send_error(buf, NET_ICMPV4_DST_UNREACH, |
| NET_ICMPV4_DST_UNREACH_NO_PORT); |
| #endif /* CONFIG_NET_IPV4 */ |
| } |
| } |
| |
| enum net_verdict net_conn_input(enum net_ip_protocol proto, struct net_buf *buf) |
| { |
| int i, best_match = -1; |
| int16_t best_rank = -1; |
| |
| #if defined(CONFIG_NET_CONN_CACHE) |
| enum net_verdict verdict; |
| uint32_t cache_value = 0; |
| int32_t pos; |
| |
| verdict = cache_check(proto, buf, &cache_value, &pos); |
| if (verdict != NET_CONTINUE) { |
| return verdict; |
| } |
| #endif |
| |
| NET_DBG("Check %s listener for buf %p src port %u dst port %u " |
| "family %d", proto2str(proto), buf, |
| ntohs(NET_CONN_BUF(buf)->src_port), |
| ntohs(NET_CONN_BUF(buf)->dst_port), |
| net_nbuf_family(buf)); |
| |
| for (i = 0; i < CONFIG_NET_MAX_CONN; i++) { |
| if (!(conns[i].flags & NET_CONN_IN_USE)) { |
| continue; |
| } |
| |
| if (conns[i].proto != proto) { |
| continue; |
| } |
| |
| if (net_sin(&conns[i].remote_addr)->sin_port) { |
| if (net_sin(&conns[i].remote_addr)->sin_port != |
| NET_CONN_BUF(buf)->src_port) { |
| continue; |
| } |
| } |
| |
| if (net_sin(&conns[i].local_addr)->sin_port) { |
| if (net_sin(&conns[i].local_addr)->sin_port != |
| NET_CONN_BUF(buf)->dst_port) { |
| continue; |
| } |
| } |
| |
| if (conns[i].flags & NET_CONN_REMOTE_ADDR_SET) { |
| if (!check_addr(buf, &conns[i].remote_addr, true)) { |
| continue; |
| } |
| } |
| |
| if (conns[i].flags & NET_CONN_LOCAL_ADDR_SET) { |
| if (!check_addr(buf, &conns[i].local_addr, false)) { |
| continue; |
| } |
| } |
| |
| /* If we have an existing best_match, and that one |
| * specifies a remote port, then we've matched to a |
| * LISTENING connection that should not override. |
| */ |
| if (best_match >= 0 && |
| net_sin(&conns[best_match].remote_addr)->sin_port) { |
| continue; |
| } |
| |
| if (best_rank < conns[i].rank) { |
| best_rank = conns[i].rank; |
| best_match = i; |
| } |
| } |
| |
| if (best_match >= 0) { |
| #if defined(CONFIG_NET_CONN_CACHE) |
| NET_DBG("[%d] match found cb %p ud %p rank 0x%02x cache 0x%x", |
| best_match, |
| conns[best_match].cb, |
| conns[best_match].user_data, |
| conns[best_match].rank, |
| pos < 0 ? 0 : conn_cache[pos].value); |
| |
| if (pos >= 0) { |
| conn_cache[pos].idx = best_match; |
| } |
| #else |
| NET_DBG("[%d] match found cb %p ud %p rank 0x%02x", |
| best_match, |
| conns[best_match].cb, |
| conns[best_match].user_data, |
| conns[best_match].rank); |
| #endif /* CONFIG_NET_CONN_CACHE */ |
| |
| if (conns[best_match].cb(&conns[best_match], buf, |
| conns[best_match].user_data) == NET_DROP) { |
| goto drop; |
| } |
| |
| if (proto == IPPROTO_UDP) { |
| net_stats_update_udp_recv(); |
| } |
| |
| return NET_OK; |
| } |
| |
| NET_DBG("No match found."); |
| |
| cache_add_neg(cache_value); |
| |
| #if defined(CONFIG_NET_IPV6) |
| /* If the destination address is multicast address, |
| * we do not send ICMP error as that makes no sense. |
| */ |
| if (net_nbuf_family(buf) == AF_INET6 && |
| net_is_ipv6_addr_mcast(&NET_IPV6_BUF(buf)->dst)) { |
| ; |
| } else |
| #endif |
| #if defined(CONFIG_NET_IPV4) |
| if (net_nbuf_family(buf) == AF_INET && |
| net_is_ipv4_addr_mcast(&NET_IPV4_BUF(buf)->dst)) { |
| ; |
| } else |
| #endif |
| { |
| send_icmp_error(buf); |
| } |
| |
| drop: |
| if (proto == IPPROTO_UDP) { |
| net_stats_update_udp_drop(); |
| } |
| |
| return NET_DROP; |
| } |
| |
| void net_conn_init(void) |
| { |
| #if defined(CONFIG_NET_CONN_CACHE) |
| do { |
| int i; |
| |
| for (i = 0; i < CONFIG_NET_MAX_CONN; i++) { |
| conn_cache[i].idx = -1; |
| } |
| } while (0); |
| #endif /* CONFIG_NET_CONN_CACHE */ |
| } |