/** @file * @brief IPv4 related functions */ /* * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #if defined(CONFIG_NET_DEBUG_IPV4) #define SYS_LOG_DOMAIN "net/ipv4" #define NET_LOG_ENABLED 1 #endif #include #include #include #include #include #include "net_private.h" #include "connection.h" #include "net_stats.h" #include "icmpv4.h" #include "udp_internal.h" #include "tcp.h" #include "ipv4.h" struct net_pkt *net_ipv4_create_raw(struct net_pkt *pkt, const struct in_addr *src, const struct in_addr *dst, struct net_if *iface, u8_t next_header) { struct net_buf *header; header = net_pkt_get_frag(pkt, K_FOREVER); net_pkt_frag_insert(pkt, header); NET_IPV4_HDR(pkt)->vhl = 0x45; NET_IPV4_HDR(pkt)->tos = 0x00; NET_IPV4_HDR(pkt)->proto = 0; /* User can tweak the default TTL if needed */ NET_IPV4_HDR(pkt)->ttl = net_pkt_ipv4_ttl(pkt); if (NET_IPV4_HDR(pkt)->ttl == 0) { NET_IPV4_HDR(pkt)->ttl = net_if_ipv4_get_ttl(iface); } NET_IPV4_HDR(pkt)->offset[0] = NET_IPV4_HDR(pkt)->offset[1] = 0; NET_IPV4_HDR(pkt)->id[0] = NET_IPV4_HDR(pkt)->id[1] = 0; net_ipaddr_copy(&NET_IPV4_HDR(pkt)->dst, dst); net_ipaddr_copy(&NET_IPV4_HDR(pkt)->src, src); NET_IPV4_HDR(pkt)->proto = next_header; net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv4_hdr)); net_pkt_set_family(pkt, AF_INET); net_buf_add(header, sizeof(struct net_ipv4_hdr)); return pkt; } struct net_pkt *net_ipv4_create(struct net_context *context, struct net_pkt *pkt, const struct in_addr *src, const struct in_addr *dst) { struct net_if_ipv4 *ipv4 = net_pkt_iface(pkt)->config.ip.ipv4; NET_ASSERT(ipv4); NET_ASSERT(((struct sockaddr_in_ptr *)&context->local)->sin_addr); if (!src) { src = ((struct sockaddr_in_ptr *)&context->local)->sin_addr; } if (net_is_ipv4_addr_unspecified(src) || net_is_ipv4_addr_mcast(src)) { src = &ipv4->unicast[0].address.in_addr; } return net_ipv4_create_raw(pkt, src, dst, net_context_get_iface(context), net_context_get_ip_proto(context)); } int net_ipv4_finalize_raw(struct net_pkt *pkt, u8_t next_header) { /* Set the length of the IPv4 header */ size_t total_len; net_pkt_compact(pkt); total_len = net_pkt_get_len(pkt); NET_IPV4_HDR(pkt)->len[0] = total_len >> 8; NET_IPV4_HDR(pkt)->len[1] = total_len & 0xff; NET_IPV4_HDR(pkt)->chksum = 0; #if defined(CONFIG_NET_UDP) if (next_header == IPPROTO_UDP && net_if_need_calc_tx_checksum(net_pkt_iface(pkt))) { NET_IPV4_HDR(pkt)->chksum = ~net_calc_chksum_ipv4(pkt); net_udp_set_chksum(pkt, pkt->frags); } #endif #if defined(CONFIG_NET_TCP) if (next_header == IPPROTO_TCP && net_if_need_calc_tx_checksum(net_pkt_iface(pkt))) { NET_IPV4_HDR(pkt)->chksum = ~net_calc_chksum_ipv4(pkt); net_tcp_set_chksum(pkt, pkt->frags); } #endif return 0; } int net_ipv4_finalize(struct net_context *context, struct net_pkt *pkt) { return net_ipv4_finalize_raw(pkt, net_context_get_ip_proto(context)); } const struct in_addr *net_ipv4_unspecified_address(void) { static const struct in_addr addr; return &addr; } const struct in_addr *net_ipv4_broadcast_address(void) { static const struct in_addr addr = { { { 255, 255, 255, 255 } } }; return &addr; } static inline enum net_verdict process_icmpv4_pkt(struct net_pkt *pkt, struct net_ipv4_hdr *ipv4) { struct net_icmp_hdr hdr, *icmp_hdr; icmp_hdr = net_icmpv4_get_hdr(pkt, &hdr); if (!icmp_hdr) { NET_DBG("NULL ICMPv4 header - dropping"); return NET_DROP; } NET_DBG("ICMPv4 packet received type %d code %d", icmp_hdr->type, icmp_hdr->code); return net_icmpv4_input(pkt, icmp_hdr->type, icmp_hdr->code); } enum net_verdict net_ipv4_process_pkt(struct net_pkt *pkt) { struct net_ipv4_hdr *hdr = NET_IPV4_HDR(pkt); int real_len = net_pkt_get_len(pkt); int pkt_len = (hdr->len[0] << 8) + hdr->len[1]; enum net_verdict verdict = NET_DROP; if (real_len != pkt_len) { NET_DBG("IPv4 packet size %d pkt len %d", pkt_len, real_len); goto drop; } #if defined(CONFIG_NET_DEBUG_IPV4) do { char out[sizeof("xxx.xxx.xxx.xxx")]; snprintk(out, sizeof(out), "%s", net_sprint_ipv4_addr(&hdr->dst)); NET_DBG("IPv4 packet received from %s to %s", net_sprint_ipv4_addr(&hdr->src), out); } while (0); #endif /* CONFIG_NET_DEBUG_IPV4 */ net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv4_hdr)); if (!net_is_my_ipv4_addr(&hdr->dst) && !net_is_ipv4_addr_mcast(&hdr->dst)) { #if defined(CONFIG_NET_DHCPV4) if (hdr->proto == IPPROTO_UDP && net_ipv4_addr_cmp(&hdr->dst, net_ipv4_broadcast_address())) { verdict = net_conn_input(IPPROTO_UDP, pkt); if (verdict != NET_DROP) { return verdict; } } #endif NET_DBG("IPv4 packet in pkt %p not for me", pkt); goto drop; } switch (hdr->proto) { case IPPROTO_ICMP: verdict = process_icmpv4_pkt(pkt, hdr); break; case IPPROTO_UDP: verdict = net_conn_input(IPPROTO_UDP, pkt); break; case IPPROTO_TCP: verdict = net_conn_input(IPPROTO_TCP, pkt); break; } if (verdict != NET_DROP) { return verdict; } drop: net_stats_update_ipv4_drop(net_pkt_iface(pkt)); return NET_DROP; }