blob: efdee74efaab46ccf450546356ec407618eb63d1 [file] [log] [blame]
/** @file
* @brief IPv4 related functions
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_ipv4, CONFIG_NET_IPV4_LOG_LEVEL);
#include <errno.h>
#include <net/net_core.h>
#include <net/net_pkt.h>
#include <net/net_stats.h>
#include <net/net_context.h>
#include <net/tcp.h>
#include "net_private.h"
#include "connection.h"
#include "net_stats.h"
#include "icmpv4.h"
#include "udp_internal.h"
#include "tcp_internal.h"
#include "ipv4.h"
/* Timeout for various buffer allocations in this file. */
#define NET_BUF_TIMEOUT K_MSEC(50)
struct net_pkt *net_ipv4_create(struct net_pkt *pkt,
const struct in_addr *src,
const struct in_addr *dst,
struct net_if *iface,
u8_t next_header_proto)
{
struct net_buf *header;
header = net_pkt_get_frag(pkt, NET_BUF_TIMEOUT);
if (!header) {
return NULL;
}
net_pkt_frag_insert(pkt, header);
NET_IPV4_HDR(pkt)->vhl = 0x45;
NET_IPV4_HDR(pkt)->tos = 0x00;
NET_IPV4_HDR(pkt)->proto = next_header_proto;
/* 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_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;
}
void net_ipv4_finalize(struct net_pkt *pkt, u8_t next_header_proto)
{
/* 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 = htons(total_len);
NET_IPV4_HDR(pkt)->chksum = 0;
if (net_if_need_calc_tx_checksum(net_pkt_iface(pkt)) ||
next_header_proto == IPPROTO_ICMP) {
NET_IPV4_HDR(pkt)->chksum = net_calc_chksum_ipv4(pkt);
if (IS_ENABLED(CONFIG_NET_UDP) &&
next_header_proto == IPPROTO_UDP) {
net_udp_set_chksum(pkt, pkt->frags);
} else if (IS_ENABLED(CONFIG_NET_TCP) &&
next_header_proto == IPPROTO_TCP) {
net_tcp_set_chksum(pkt, pkt->frags);
} else if (next_header_proto == IPPROTO_ICMP) {
net_icmpv4_set_chksum(pkt);
}
}
}
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;
}
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 = ntohs(hdr->len);
enum net_verdict verdict = NET_DROP;
if (real_len < pkt_len) {
NET_DBG("DROP: pkt len per hdr %d != pkt real len %d",
pkt_len, real_len);
goto drop;
} else if (real_len > pkt_len) {
net_pkt_pull(pkt, pkt_len, real_len - pkt_len);
}
if (net_ipv4_is_addr_mcast(&hdr->src)) {
NET_DBG("DROP: src addr is mcast");
goto drop;
}
if (net_ipv4_is_addr_bcast(net_pkt_iface(pkt), &hdr->src)) {
NET_DBG("DROP: src addr is bcast");
goto drop;
}
if (net_if_need_calc_rx_checksum(net_pkt_iface(pkt)) &&
net_calc_chksum_ipv4(pkt) != 0) {
NET_DBG("DROP: invalid chksum");
goto drop;
}
if (!net_ipv4_is_my_addr(&hdr->dst) &&
!net_ipv4_is_addr_mcast(&hdr->dst) &&
((hdr->proto == IPPROTO_UDP &&
net_ipv4_addr_cmp(&hdr->dst, net_ipv4_broadcast_address()) &&
!IS_ENABLED(CONFIG_NET_DHCPV4)) ||
(hdr->proto == IPPROTO_TCP &&
net_ipv4_is_addr_bcast(net_pkt_iface(pkt), &hdr->dst)))) {
NET_DBG("DROP: not for me");
goto drop;
}
NET_DBG("IPv4 packet received from %s to %s",
log_strdup(net_sprint_ipv4_addr(&hdr->src)),
log_strdup(net_sprint_ipv4_addr(&hdr->dst)));
net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv4_hdr));
net_pkt_set_ipv4_ttl(pkt, hdr->ttl);
net_pkt_set_transport_proto(pkt, hdr->proto);
switch (hdr->proto) {
case IPPROTO_ICMP:
verdict = net_icmpv4_input(
pkt, net_ipv4_is_addr_bcast(net_pkt_iface(pkt),
&hdr->dst));
break;
case IPPROTO_TCP:
/* Fall through */
case IPPROTO_UDP:
verdict = net_conn_input(hdr->proto, pkt);
break;
}
if (verdict != NET_DROP) {
return verdict;
}
drop:
net_stats_update_ipv4_drop(net_pkt_iface(pkt));
return NET_DROP;
}