zephyr/subsys/net/ip/ipv4.h
Robert Lubos 0a95423421 net: ipv4: Implement IPv4 address conflict detection
Add support for IPv4 conflict detection, as specified in RFC 5227.
The new feature is optional and disabled by default.

Address conflict detection was implemented as a part of the IPv4
autoconf feature can be generalized to be available for all address
types.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2024-06-10 00:59:28 -07:00

469 lines
12 KiB
C

/** @file
@brief IPv4 related functions
This is not to be included by the application.
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __IPV4_H
#define __IPV4_H
#include <zephyr/types.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_context.h>
#define NET_IPV4_IHL_MASK 0x0F
#define NET_IPV4_DSCP_MASK 0xFC
#define NET_IPV4_DSCP_OFFSET 2
#define NET_IPV4_ECN_MASK 0x03
/* IPv4 Options */
#define NET_IPV4_OPTS_EO 0 /* End of Options */
#define NET_IPV4_OPTS_NOP 1 /* No operation */
#define NET_IPV4_OPTS_RR 7 /* Record Route */
#define NET_IPV4_OPTS_TS 68 /* Timestamp */
#define NET_IPV4_OPTS_RA 148 /* Router Alert */
/* IPv4 Options Timestamp flags */
#define NET_IPV4_TS_OPT_TS_ONLY 0 /* Timestamp only */
#define NET_IPV4_TS_OPT_TS_ADDR 1 /* Timestamp and address */
#define NET_IPV4_TS_OPT_TS_PRES 3 /* Timestamp prespecified hops*/
#define NET_IPV4_HDR_OPTNS_MAX_LEN 40
/* Fragment bits */
#define NET_IPV4_MF BIT(0) /* More fragments */
#define NET_IPV4_DF BIT(1) /* Do not fragment */
#define NET_IPV4_IGMP_QUERY 0x11 /* Membership query */
#define NET_IPV4_IGMP_REPORT_V1 0x12 /* v1 Membership report */
#define NET_IPV4_IGMP_REPORT_V2 0x16 /* v2 Membership report */
#define NET_IPV4_IGMP_LEAVE 0x17 /* v2 Leave group */
#define NET_IPV4_IGMP_REPORT_V3 0x22 /* v3 Membership report */
struct net_ipv4_igmp_v2_query {
/* IGMP message type */
uint8_t type;
/* Max response code */
uint8_t max_rsp;
/* 16-bit ones' complement of the entire message */
uint16_t chksum;
/* The multicast address being queried */
struct in_addr address;
} __packed;
struct net_ipv4_igmp_v2_report {
/* IGMP message type */
uint8_t type;
/* Max response code */
uint8_t max_rsp;
/* 16-bit ones' complement of the entire message */
uint16_t chksum;
/* The multicast address being queried */
struct in_addr address;
} __packed;
struct net_ipv4_igmp_v3_query {
/* IGMP message type */
uint8_t type;
/* Max response code */
uint8_t max_rsp;
/* 16-bit ones' complement of the entire message */
uint16_t chksum;
/* The multicast address being queried */
struct in_addr address;
/* Reserved field, ignore */
uint8_t reserved: 4;
/* Suppress Router-side Processing Flag */
uint8_t suppress: 1;
/* Querier's Robustness Variable */
uint8_t qrv: 3;
/* Querier's Query Interval Code */
uint8_t qqic;
/* Number of Source Addresses */
uint16_t sources_len;
} __packed;
struct net_ipv4_igmp_v3_group_record {
/* Record type */
uint8_t type;
/* Aux Data Len */
uint8_t aux_len;
/* Number of Source Addresses */
uint16_t sources_len;
/* The multicast address to report to*/
struct in_addr address;
} __packed;
struct net_ipv4_igmp_v3_report {
/* IGMP message type */
uint8_t type;
/* Reserved field, ignore */
uint8_t reserved_1;
/* 16-bit ones' complement of the entire message */
uint16_t chksum;
/* Reserved field, ignore */
uint16_t reserved_2;
/* Number of Group Records */
uint16_t groups_len;
} __packed;
/**
* @brief Create IPv4 packet in provided net_pkt with option to set all the
* caller settable values.
*
* @param pkt Network packet
* @param src Source IPv4 address
* @param dst Destination IPv4 address
* @param tos Type of service
* @param id Fragment id
* @param flags Fragmentation flags
* @param offset Fragment offset
*
* @return 0 on success, negative errno otherwise.
*/
#if defined(CONFIG_NET_NATIVE_IPV4)
int net_ipv4_create_full(struct net_pkt *pkt,
const struct in_addr *src,
const struct in_addr *dst,
uint8_t tos,
uint16_t id,
uint8_t flags,
uint16_t offset);
#else
static inline int net_ipv4_create_full(struct net_pkt *pkt,
const struct in_addr *src,
const struct in_addr *dst,
uint8_t tos,
uint16_t id,
uint8_t flags,
uint16_t offset)
{
ARG_UNUSED(pkt);
ARG_UNUSED(src);
ARG_UNUSED(dst);
ARG_UNUSED(tos);
ARG_UNUSED(id);
ARG_UNUSED(flags);
ARG_UNUSED(offset);
return -ENOTSUP;
}
#endif
/**
* @brief Create IPv4 packet in provided net_pkt.
*
* @param pkt Network packet
* @param src Source IPv4 address
* @param dst Destination IPv4 address
*
* @return 0 on success, negative errno otherwise.
*/
#if defined(CONFIG_NET_NATIVE_IPV4)
int net_ipv4_create(struct net_pkt *pkt,
const struct in_addr *src,
const struct in_addr *dst);
#else
static inline int net_ipv4_create(struct net_pkt *pkt,
const struct in_addr *src,
const struct in_addr *dst)
{
ARG_UNUSED(pkt);
ARG_UNUSED(src);
ARG_UNUSED(dst);
return -ENOTSUP;
}
#endif
/**
* @brief Finalize IPv4 packet. It should be called right before
* sending the packet and after all the data has been added into
* the packet. This function will set the length of the
* packet and calculate the higher protocol checksum if needed.
*
* @param pkt Network packet
* @param next_header_proto Protocol type of the next header after IPv4 header.
*
* @return 0 on success, negative errno otherwise.
*/
#if defined(CONFIG_NET_NATIVE_IPV4)
int net_ipv4_finalize(struct net_pkt *pkt, uint8_t next_header_proto);
#else
static inline int net_ipv4_finalize(struct net_pkt *pkt,
uint8_t next_header_proto)
{
ARG_UNUSED(pkt);
ARG_UNUSED(next_header_proto);
return -ENOTSUP;
}
#endif
/**
* @typedef net_ipv4_parse_hdr_options_cb_t
* @brief IPv4 header options handle callback
*
* @details The callback is called when parser encounter
* supported options.
*
* @param opt_type Option type
* @param opt_data Option data
* @param opt_len Option length
* @param user_data Userdata given in net_ipv4_parse_hdr_options()
*
* @return 0 on success, negative otherwise.
*/
typedef int (*net_ipv4_parse_hdr_options_cb_t)(uint8_t opt_type,
uint8_t *opt_data,
uint8_t opt_len,
void *user_data);
/**
* @brief Parse IPv4 header options.
* Parse the IPv4 header options and call the callback with
* options type, data and length along with user_data.
*
* @param pkt Network packet
* @param cb callback to handle IPv4 header options
* @param user_data User data
*
* @return 0 on success, negative otherwise.
*/
#if defined(CONFIG_NET_IPV4_HDR_OPTIONS)
int net_ipv4_parse_hdr_options(struct net_pkt *pkt,
net_ipv4_parse_hdr_options_cb_t cb,
void *user_data);
#else
static inline int net_ipv4_parse_hdr_options(struct net_pkt *pkt,
net_ipv4_parse_hdr_options_cb_t cb,
void *user_data)
{
ARG_UNUSED(pkt);
ARG_UNUSED(cb);
ARG_UNUSED(user_data);
return -ENOTSUP;
}
#endif
/**
* @brief Decode DSCP value from ToS field.
*
* @param tos ToS field value from the IPv4 header.
*
* @return Decoded DSCP value.
*/
static inline uint8_t net_ipv4_get_dscp(uint8_t tos)
{
return (tos & NET_IPV4_DSCP_MASK) >> NET_IPV4_DSCP_OFFSET;
}
/**
* @brief Encode DSCP value into ToS field.
*
* @param tos A pointer to the ToS field.
* @param dscp DSCP value to set.
*/
static inline void net_ipv4_set_dscp(uint8_t *tos, uint8_t dscp)
{
*tos &= ~NET_IPV4_DSCP_MASK;
*tos |= (dscp << NET_IPV4_DSCP_OFFSET) & NET_IPV4_DSCP_MASK;
}
/**
* @brief Convert DSCP value to priority.
*
* @param dscp DSCP value.
*/
static inline uint8_t net_ipv4_dscp_to_priority(uint8_t dscp)
{
return dscp >> 3;
}
/**
* @brief Decode ECN value from ToS field.
*
* @param tos ToS field value from the IPv4 header.
*
* @return Decoded ECN value.
*/
static inline uint8_t net_ipv4_get_ecn(uint8_t tos)
{
return tos & NET_IPV4_ECN_MASK;
}
/**
* @brief Encode ECN value into ToS field.
*
* @param tos A pointer to the ToS field.
* @param ecn ECN value to set.
*/
static inline void net_ipv4_set_ecn(uint8_t *tos, uint8_t ecn)
{
*tos &= ~NET_IPV4_ECN_MASK;
*tos |= ecn & NET_IPV4_ECN_MASK;
}
#if defined(CONFIG_NET_IPV4_FRAGMENT)
/** Store pending IPv4 fragment information that is needed for reassembly. */
struct net_ipv4_reassembly {
/** IPv4 source address of the fragment */
struct in_addr src;
/** IPv4 destination address of the fragment */
struct in_addr dst;
/**
* Timeout for cancelling the reassembly. The timer is used
* also to detect if this reassembly slot is used or not.
*/
struct k_work_delayable timer;
/** Pointers to pending fragments */
struct net_pkt *pkt[CONFIG_NET_IPV4_FRAGMENT_MAX_PKT];
/** IPv4 fragment identification */
uint16_t id;
uint8_t protocol;
};
#else
struct net_ipv4_reassembly;
#endif
/**
* @typedef net_ipv4_frag_cb_t
* @brief Callback used while iterating over pending IPv4 fragments.
*
* @param reass IPv4 fragment reassembly struct
* @param user_data A valid pointer on some user data or NULL
*/
typedef void (*net_ipv4_frag_cb_t)(struct net_ipv4_reassembly *reass, void *user_data);
/**
* @brief Go through all the currently pending IPv4 fragments.
*
* @param cb Callback to call for each pending IPv4 fragment.
* @param user_data User specified data or NULL.
*/
void net_ipv4_frag_foreach(net_ipv4_frag_cb_t cb, void *user_data);
#if defined(CONFIG_NET_NATIVE_IPV4)
/**
* @brief Initialises IPv4
*/
void net_ipv4_init(void);
/**
* @brief Handles IPv4 fragmented packets.
*
* @param pkt Network head packet.
* @param hdr The IPv4 header of the current packet
*
* @return Return verdict about the packet.
*/
#if defined(CONFIG_NET_IPV4_FRAGMENT)
enum net_verdict net_ipv4_handle_fragment_hdr(struct net_pkt *pkt, struct net_ipv4_hdr *hdr);
#else
static inline enum net_verdict net_ipv4_handle_fragment_hdr(struct net_pkt *pkt,
struct net_ipv4_hdr *hdr)
{
ARG_UNUSED(pkt);
ARG_UNUSED(hdr);
return NET_DROP;
}
#endif /* CONFIG_NET_IPV4_FRAGMENT */
/**
* @brief Prepare packet for sending, this will split up a packet that is too large to send into
* multiple fragments so that it can be sent.
*
* @param pkt Network packet
*
* @return Return verdict about the packet.
*/
#if defined(CONFIG_NET_IPV4_FRAGMENT)
enum net_verdict net_ipv4_prepare_for_send(struct net_pkt *pkt);
#else
static inline enum net_verdict net_ipv4_prepare_for_send(struct net_pkt *pkt)
{
return NET_OK;
}
#endif /* CONFIG_NET_IPV4_FRAGMENT */
/**
* @brief Sets up fragment buffers for usage, should only be called by the SYS_INIT() handler in
* net_core.c
*/
#if defined(CONFIG_NET_IPV4_FRAGMENT)
void net_ipv4_setup_fragment_buffers(void);
#else
static inline void net_ipv4_setup_fragment_buffers(void)
{
}
#endif /* CONFIG_NET_IPV4_FRAGMENT */
#else
#define net_ipv4_init(...)
#endif /* CONFIG_NET_NATIVE_IPV4 */
/**
* @brief Starts address conflict detection for an IPv4 address.
*
* @param iface Network interface the address belongs to.
* @param ifaddr IPv4 address to probe.
*
* @return 0 on success, negative otherwise.
*/
int net_ipv4_acd_start(struct net_if *iface, struct net_if_addr *ifaddr);
/**
* @brief Cancel address conflict detection for an IPv4 address.
*
* @param iface Network interface the address belongs to.
* @param ifaddr IPv4 address to probe.
*/
void net_ipv4_acd_cancel(struct net_if *iface, struct net_if_addr *ifaddr);
/**
* @brief Notify no conflict was detected for an IPv4 address.
*
* @param iface Network interface the address belongs to.
* @param ifaddr IPv4 address.
*/
void net_if_ipv4_acd_succeeded(struct net_if *iface, struct net_if_addr *ifaddr);
/**
* @brief Notify conflict for an IPv4 address.
*
* @param iface Network interface the address belongs to.
* @param ifaddr IPv4 address.
*/
void net_if_ipv4_acd_failed(struct net_if *iface, struct net_if_addr *ifaddr);
/**
* @brief Initialize IPv4 address conflict detection module.
*/
void net_ipv4_acd_init(void);
/**
* @brief Process ARP packet in terms of conflict detection.
*
* @param iface Network interface the packet was received on.
* @param pkt ARP packet to process.
*
* @return Return verdict about the packet.
*/
enum net_verdict net_ipv4_acd_input(struct net_if *iface, struct net_pkt *pkt);
#endif /* __IPV4_H */