/** @file * @brief Misc network utility functions * */ /* * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #if defined(CONFIG_NET_DEBUG_UTILS) #define SYS_LOG_DOMAIN "net/utils" #define NET_LOG_ENABLED 1 #endif #include #include #include #include #include #include #include #include const char *net_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 "UNK_PROTO"; } char *net_byte_to_hex(char *ptr, u8_t byte, char base, bool pad) { int i, val; for (i = 0, val = (byte & 0xf0) >> 4; i < 2; i++, val = byte & 0x0f) { if (i == 0 && !pad && !val) { continue; } if (val < 10) { *ptr++ = (char) (val + '0'); } else { *ptr++ = (char) (val - 10 + base); } } *ptr = '\0'; return ptr; } char *net_sprint_ll_addr_buf(const u8_t *ll, u8_t ll_len, char *buf, int buflen) { u8_t i, len, blen; char *ptr = buf; switch (ll_len) { case 8: len = 8; break; case 6: len = 6; break; default: len = 6; break; } for (i = 0, blen = buflen; i < len && blen > 0; i++) { ptr = net_byte_to_hex(ptr, (char)ll[i], 'A', true); *ptr++ = ':'; blen -= 3; } if (!(ptr - buf)) { return NULL; } *(ptr - 1) = '\0'; return buf; } static int net_value_to_udec(char *buf, u32_t value, int precision) { u32_t divisor; int i; int temp; char *start = buf; divisor = 1000000000; if (precision < 0) precision = 1; for (i = 9; i >= 0; i--, divisor /= 10) { temp = value / divisor; value = value % divisor; if ((precision > i) || (temp != 0)) { precision = i; *buf++ = (char) (temp + '0'); } } *buf = 0; return buf - start; } char *net_addr_ntop(sa_family_t family, const void *src, char *dst, size_t size) { struct in_addr *addr; struct in6_addr *addr6; u16_t *w; u8_t i, bl, bh, longest = 1; s8_t pos = -1; char delim = ':'; unsigned char zeros[8] = { 0 }; char *ptr = dst; int len = -1; u16_t value; bool needcolon = false; if (family == AF_INET6) { addr6 = (struct in6_addr *)src; w = (u16_t *)addr6->s6_addr16; len = 8; for (i = 0; i < 8; i++) { u8_t j; for (j = i; j < 8; j++) { if (UNALIGNED_GET(&w[j]) != 0) { break; } zeros[i]++; } } for (i = 0; i < 8; i++) { if (zeros[i] > longest) { longest = zeros[i]; pos = i; } } if (longest == 1) { pos = -1; } } else if (family == AF_INET) { addr = (struct in_addr *)src; len = 4; delim = '.'; } else { return NULL; } for (i = 0; i < len; i++) { /* IPv4 address a.b.c.d */ if (len == 4) { u8_t l; value = (u32_t)addr->s4_addr[i]; /* net_byte_to_udec() eats 0 */ if (value == 0) { *ptr++ = '0'; *ptr++ = delim; continue; } l = net_value_to_udec(ptr, value, 0); ptr += l; *ptr++ = delim; continue; } /* IPv6 address */ if (i == pos) { if (needcolon || i == 0) { *ptr++ = ':'; } *ptr++ = ':'; needcolon = false; i += longest - 1; continue; } if (needcolon) { *ptr++ = ':'; needcolon = false; } value = (u32_t)sys_be16_to_cpu(UNALIGNED_GET(&w[i])); bh = value >> 8; bl = value & 0xff; if (bh) { if (bh > 0x0f) { ptr = net_byte_to_hex(ptr, bh, 'a', false); } else { if (bh < 10) { *ptr++ = (char)(bh + '0'); } else { *ptr++ = (char) (bh - 10 + 'a'); } } ptr = net_byte_to_hex(ptr, bl, 'a', true); } else if (bl > 0x0f) { ptr = net_byte_to_hex(ptr, bl, 'a', false); } else { if (bl < 10) { *ptr++ = (char)(bl + '0'); } else { *ptr++ = (char) (bl - 10 + 'a'); } } needcolon = true; } if (!(ptr - dst)) { return NULL; } if (family == AF_INET) { *(ptr - 1) = '\0'; } else { *ptr = '\0'; } return dst; } int net_addr_pton(sa_family_t family, const char *src, void *dst) { if (family == AF_INET) { struct in_addr *addr = (struct in_addr *)dst; size_t i, len; len = strlen(src); for (i = 0; i < len; i++) { if (!(src[i] >= '0' && src[i] <= '9') && src[i] != '.') { return -EINVAL; } } memset(addr, 0, sizeof(struct in_addr)); for (i = 0; i < sizeof(struct in_addr); i++) { char *endptr; addr->s4_addr[i] = strtol(src, &endptr, 10); src = ++endptr; } } else if (family == AF_INET6) { /* If the string contains a '.', it means it's of the form * X:X:X:X:X:X:x.x.x.x, and contains only 6 16-bit pieces */ int expected_groups = strchr(src, '.') ? 6 : 8; struct in6_addr *addr = (struct in6_addr *)dst; int i, len; if (*src == ':') { /* Ignore a leading colon, makes parsing neater */ src++; } len = strlen(src); for (i = 0; i < len; i++) { if (!(src[i] >= '0' && src[i] <= '9') && !(src[i] >= 'A' && src[i] <= 'F') && !(src[i] >= 'a' && src[i] <= 'f') && src[i] != '.' && src[i] != ':') return -EINVAL; } for (i = 0; i < expected_groups; i++) { char *tmp; if (!src || *src == '\0') { return -EINVAL; } if (*src != ':') { /* Normal IPv6 16-bit piece */ UNALIGNED_PUT(htons(strtol(src, NULL, 16)), &addr->s6_addr16[i]); src = strchr(src, ':'); if (!src && i < expected_groups - 1) { return -EINVAL; } src++; continue; } /* Two colons in a row */ for (; i < expected_groups; i++) { UNALIGNED_PUT(0, &addr->s6_addr16[i]); } tmp = strrchr(src, ':'); if (src == tmp && (expected_groups == 6 || !src[1])) { src++; break; } if (expected_groups == 6) { /* we need to drop the trailing * colon since it's between the * ipv6 and ipv4 addresses, rather than being * a part of the ipv6 address */ tmp--; } /* Calculate the amount of skipped zeros */ i = expected_groups - 1; do { if (*tmp == ':') { i--; } } while (tmp-- != src); src++; } if (expected_groups == 6) { /* Parse the IPv4 part */ for (i = 0; i < 4; i++) { if (!src || !*src) { return -EINVAL; } addr->s6_addr[12 + i] = strtol(src, NULL, 10); src = strchr(src, '.'); if (!src && i < 3) { return -EINVAL; } src++; } } } else { return -EINVAL; } return 0; } static u16_t calc_chksum(u16_t sum, const u8_t *ptr, u16_t len) { u16_t tmp; const u8_t *end; end = ptr + len - 1; while (ptr < end) { tmp = (ptr[0] << 8) + ptr[1]; sum += tmp; if (sum < tmp) { sum++; } ptr += 2; } if (ptr == end) { tmp = ptr[0] << 8; sum += tmp; if (sum < tmp) { sum++; } } return sum; } static inline u16_t calc_chksum_pkt(u16_t sum, struct net_pkt *pkt, u16_t upper_layer_len) { struct net_buf *frag = pkt->frags; u16_t proto_len = net_pkt_ip_hdr_len(pkt) + net_pkt_ipv6_ext_len(pkt); s16_t len = frag->len - proto_len; u8_t *ptr = frag->data + proto_len; ARG_UNUSED(upper_layer_len); if (len < 0) { NET_DBG("1st fragment len %u < IP header len %u", frag->len, proto_len); return 0; } while (frag) { sum = calc_chksum(sum, ptr, len); frag = frag->frags; if (!frag) { break; } ptr = frag->data; /* Do we need to take first byte from next fragment */ if (len % 2) { u16_t tmp = *ptr; sum += tmp; if (sum < tmp) { sum++; } len = frag->len - 1; ptr++; } else { len = frag->len; } } return sum; } u16_t net_calc_chksum(struct net_pkt *pkt, u8_t proto) { u16_t upper_layer_len; u16_t sum; switch (net_pkt_family(pkt)) { #if defined(CONFIG_NET_IPV4) case AF_INET: upper_layer_len = (NET_IPV4_HDR(pkt)->len[0] << 8) + NET_IPV4_HDR(pkt)->len[1] - net_pkt_ipv6_ext_len(pkt) - net_pkt_ip_hdr_len(pkt); if (proto == IPPROTO_ICMP) { return htons(calc_chksum(0, net_pkt_ip_data(pkt) + net_pkt_ip_hdr_len(pkt), upper_layer_len)); } else { sum = calc_chksum(upper_layer_len + proto, (u8_t *)&NET_IPV4_HDR(pkt)->src, 2 * sizeof(struct in_addr)); } break; #endif #if defined(CONFIG_NET_IPV6) case AF_INET6: upper_layer_len = (NET_IPV6_HDR(pkt)->len[0] << 8) + NET_IPV6_HDR(pkt)->len[1] - net_pkt_ipv6_ext_len(pkt); sum = calc_chksum(upper_layer_len + proto, (u8_t *)&NET_IPV6_HDR(pkt)->src, 2 * sizeof(struct in6_addr)); break; #endif default: NET_DBG("Unknown protocol family %d", net_pkt_family(pkt)); return 0; } sum = calc_chksum_pkt(sum, pkt, upper_layer_len); sum = (sum == 0) ? 0xffff : htons(sum); return sum; } #if defined(CONFIG_NET_IPV4) u16_t net_calc_chksum_ipv4(struct net_pkt *pkt) { u16_t sum; sum = calc_chksum(0, (u8_t *)NET_IPV4_HDR(pkt), NET_IPV4H_LEN); sum = (sum == 0) ? 0xffff : htons(sum); return sum; } #endif /* CONFIG_NET_IPV4 */